स्विफ्ट उपवर्ग UIView


95

मैं उप-वर्ग UIViewऔर दृश्य की तरह लॉगिन दिखाना चाहता हूं । मैंने इसे Objective-C में बनाया है, लेकिन अब मैं इसे Swift में पोर्ट करना चाहता हूं। मैं स्टोरीबोर्ड का उपयोग नहीं करता हूं, इसलिए मैं कोड में अपने सभी यूआई बनाता हूं।

लेकिन पहली समस्या यह है कि मुझे लागू करना होगा initWithCoder। मैंने इसे एक डिफ़ॉल्ट कार्यान्वयन दिया क्योंकि यह कहा नहीं जाएगा। अब जब मैं प्रोग्राम चलाता हूं तो यह दुर्घटनाग्रस्त हो जाता है, क्योंकि मुझे इसे लागू करना है initWithFrame। अब मुझे यह मिल गया:

override init() {
    super.init()
    println("Default init")
}

override init(frame: CGRect) {
    super.init(frame: frame)
    println("Frame init")
}

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    println("Coder init")
}

मेरा सवाल यह है कि मुझे अपना टेक्स्टफ़ील्ड आदि कहां बनाना चाहिए ... और अगर मैंने कभी फ्रेम और कोडर लागू नहीं किया तो मैं इसे "कैसे" छिपा सकता हूं?

जवाबों:


174

मैं आमतौर पर ऐसा कुछ करता हूं, इसकी क्रिया थोड़ी है।

class MyView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        addBehavior()
    }

    convenience init() {
        self.init(frame: CGRect.zero)
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("This class does not support NSCoding")
    }

    func addBehavior() {
        print("Add all the behavior here")
    }
}



let u = MyView(frame: CGRect.zero)
let v = MyView()

(संपादित करें: मैंने अपना उत्तर संपादित कर लिया है, ताकि शुरुआती लोगों के बीच का संबंध अधिक स्पष्ट हो)


4
लेकिन addBehavior को दो बार कहा जाता है क्योंकि initFrame कहा जाता है और init होता है। यदि आप मेरे कोड को चलाते हैं तो पहला फ्रेम
इनिट

6
अच्छी चीजें, धन्यवाद। उपयोग करने के बजाय CGRectZeroमेरा मानना ​​है कि इसका उपयोग करने की सिफारिश की गई है CGRect.zeroRect
श्री रोजर्स

57
यह शुरुआती सामान पागल जटिल है।
इयान वारबर्टन

3
क्या ऑटो लेआउट के लिए ऐसा करने का कोई तरीका है? फ्रेम इतना पुराना है।
devios1

8
यह पूर्ण उत्तर नहीं है। UIView initWithCoding का समर्थन करता है। निब या स्टोरीबोर्ड से लोड किया गया कोई भी दृश्य initWithCoding विधि और क्रैश को कॉल करेगा।
इलेन डेलानी

17

यह अधिक सरल है।

override init (frame : CGRect) {
    super.init(frame : frame)
    // Do what you want.
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

10

कस्टम यूविवि उपवर्ग उदाहरण

मैं आमतौर पर स्टोरीबोर्ड या नीब का उपयोग किए बिना आईओएस ऐप बनाता हूं। मैं आपके प्रश्नों का उत्तर देने के लिए सीखी गई कुछ तकनीकों को साझा करूँगा।

 

अवांछित initतरीके छिपाना

मेरा पहला सुझाव UIViewअवांछित आरंभकर्ताओं को छिपाने के लिए एक आधार घोषित करना है। मैंने "यूबी सबक्लासेस में स्टोरीबोर्ड और निब स्पेसिफिक इनिशियलाइज़र्स को कैसे छिपाएँ" के उत्तर में इस दृष्टिकोण पर विस्तार से चर्चा की है । नोट: यह दृष्टिकोण मानता है कि आप BaseViewस्टोरीबोर्ड या निब में इसके वंशजों का उपयोग नहीं करेंगे या नहीं करेंगे क्योंकि यह जानबूझकर ऐप को क्रैश करने का कारण बनेगा।

class BaseView: UIView {

    // This initializer hides init(frame:) from subclasses
    init() {
        super.init(frame: CGRect.zero)
    }

    // This attribute hides `init(coder:)` from subclasses
    @available(*, unavailable)
    required init?(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }
}

आपके कस्टम UIView उपवर्ग को इनहेरिट करना चाहिए BaseView। इसे अपने इनिशियलाइज़र में super.init () कहना होगा। इसे लागू करने की जरूरत नहीं है init(coder:)। यह नीचे दिए गए उदाहरण में प्रदर्शित किया गया है।

 

एक UITextField जोड़ना

मैं initविधि के बाहर संदर्भित साक्षात्कार के लिए संग्रहीत गुण बनाता हूं । मैं आमतौर पर एक UITextField के लिए ऐसा करता हूं। मैं इस तरह उप-संपत्ति की घोषणा के भीतर साक्षात्कार को तुरंत पसंद करना चाहता हूं let textField = UITextField():।

UITextField तब तक दिखाई नहीं देगा जब तक आप उसे कॉल करके कस्टम दृश्य की सबव्यू सूची में नहीं जोड़ते addSubview(_:)। यह नीचे दिए गए उदाहरण में प्रदर्शित किया गया है।

 

ऑटो लेआउट के बिना प्रोग्रामेटिक लेआउट

UITextField तब तक दिखाई नहीं देगा जब तक आप उसका आकार और स्थिति निर्धारित नहीं करते। मैं अक्सर लेआउट में कोड (ऑटो लेआउट का उपयोग नहीं) लेआउट लेआउट साक्षात्कार विधि के भीतर करता हूं । layoutSubviews()शुरू में कहा जाता है और जब भी कोई आकार परिवर्तन घटना होती है। यह CustomView के आकार के आधार पर लेआउट को समायोजित करने की अनुमति देता है। उदाहरण के लिए, अगर CustomView iPhones और iPads के विभिन्न आकारों पर पूरी चौड़ाई दिखाई देता है और रोटेशन के लिए समायोजित होता है, तो इसे कई प्रारंभिक आकारों को समायोजित करने और गतिशील रूप से आकार बदलने की आवश्यकता होती है।

आप संदर्भ के लिए CustomView के आयाम प्राप्त करने के लिए frame.heightऔर frame.widthभीतर संदर्भित कर सकते हैं layoutSubviews()। यह नीचे दिए गए उदाहरण में प्रदर्शित किया गया है।

 

उदाहरण UIView उपवर्ग

एक कस्टम UIView उपवर्ग जिसमें एक UITextField होता है जिसे लागू करने की आवश्यकता नहीं होती है init?(coder:)

class CustomView: BaseView {

    let textField = UITextField()

    override init() {
        super.init()

        // configure and add textField as subview
        textField.placeholder = "placeholder text"
        textField.font = UIFont.systemFont(ofSize: 12)
        addSubview(textField)
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        // Set textField size and position
        textField.frame.size = CGSize(width: frame.width - 20, height: 30)
        textField.frame.origin = CGPoint(x: 10, y: 10)
    }
}

 

ऑटो लेआउट के साथ प्रोग्रामेटिक लेआउट

आप कोड में ऑटो लेआउट का उपयोग करके लेआउट को भी लागू कर सकते हैं। चूंकि मैं अक्सर ऐसा नहीं करता हूं, मैं एक उदाहरण नहीं दिखाऊंगा। आप इंटरनेट पर स्टैक ओवरफ्लो और अन्य जगहों पर कोड में ऑटो लेआउट को लागू करने के उदाहरण पा सकते हैं।

 

प्रोग्रामेटिक लेआउट फ्रेमवर्क

खुले स्रोत ढांचे हैं जो कोड में लेआउट को लागू करते हैं। एक मैं जिसमें दिलचस्पी है लेकिन कोशिश नहीं की है LayoutKit । इसे डेवलपमेंट टीम लिंक्डइन ने लिखा था। जीथब रिपॉजिटरी से: "लिंक्डइन ने लेआउटकिट बनाया क्योंकि हमने पाया है कि स्क्रॉल लेआउट में जटिल दृश्य पदानुक्रमों के लिए ऑटो लेआउट पर्याप्त प्रदर्शन नहीं कर रहा है।"

 

क्यों डाल fatalErrorमेंinit(coder:)

UIView उपवर्गों का निर्माण करते समय जिनका उपयोग स्टोरीबोर्ड या नीब में कभी नहीं किया जाएगा, आप अलग-अलग मापदंडों और आरंभीकरण आवश्यकताओं के साथ इनिशियलाइज़र को पेश कर सकते हैं जिन्हें init(coder:)विधि द्वारा नहीं बुलाया जा सकता है । यदि आप init (कोडर :) के साथ विफल नहीं हुए fatalError, तो यह लाइन के नीचे बहुत भ्रामक समस्याएं पैदा कर सकता है अगर गलती से स्टोरीबोर्ड / नीब में उपयोग किया जाता है। घातक इरादे इन इरादों का दावा करते हैं।

required init?(coder aDecoder: NSCoder) {
    fatalError("NSCoding not supported")
}

यदि आप कुछ कोड चलाना चाहते हैं, जब उपवर्ग कोड या स्टोरीबोर्ड / नीब में बनाया गया है, तब भी परवाह किए बिना आप निम्नलिखित की तरह कुछ कर सकते हैं ( जेफ गु कांग के जवाब के आधार पर )

class CustomView: UIView {
    override init (frame: CGRect) {
        super.init(frame: frame)
        initCommon()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initCommon()
    }

    func initCommon() {
        // Your custom initialization code
    }
}

तथा? जोड़कर fatalErrorआप xib फाइलों के साथ इस दृश्य init करने के लिए न करे
Vyachaslav Gerchicov

@VyachaslavGerchicov, मेरा उत्तर इस धारणा को बताता है कि आप xibs या स्टोरीबोर्ड का उपयोग नहीं कर रहे हैं जैसा कि स्वीकृत उत्तर और प्रश्न है। प्रश्न नोट करता है "मैं स्टोरीबोर्ड का उपयोग नहीं करता हूं, इसलिए मैं कोड में अपने सभी यूआई बनाता हूं"।
मोबाइल डान

अगली बार जब आप fatalErrorडीललॉक विधि के अंदर लिखें और हमें बताएं कि यह काम नहीं करता है क्योंकि उस वर्ग को एक सिंगलटन होना चाहिए। यदि आप कोड में UI तत्व बनाना पसंद करते हैं तो आपको अन्य सभी तरीकों से मैन्युअल रूप से मना नहीं करना चाहिए। अंत में सवाल यह है कि "स्टोरीबोर्ड के बिना प्रोग्रामेटिक रूप से" कैसे बनाया जाए, लेकिन xibs / nibs का उल्लेख नहीं किया गया है। मेरे मामले में मुझे प्रोग्रामेटिक रूप से + xib के साथ एक सरणी बनाने और उन्हें पास करने की आवश्यकता है DropDownMenuKitऔर यह काम नहीं करता है क्योंकि इस लाइब्रेरी के लेखक ने xibs को भी मना किया है।
व्याचास्लाव गेरिकोव

@VyachaslavGerchicov यह जेफ गु कांग के जवाब की तरह लगता है कि आप स्टोरीबोर्ड / Xibs को समायोजित करने के बाद से देख रहे हैं
मोबाइल दान

1
@VyachaslavGerchicov सवाल भी कहा गया है "और अगर मैं कभी भी फ्रेम और कोडर को लागू नहीं करता हूं तो मैं इसे" कैसे "छिपा सकता हूं?" UIView उपवर्गों को बनाते समय जिनका उपयोग किसी Xib / Storyboard में कभी नहीं किया जाएगा, आप इनिशियलाइज़र को विभिन्न मापदंडों के साथ पेश कर सकते हैं जिन्हें init (कोडर :) विधि द्वारा नहीं बुलाया जा सकता है। यदि आप init (कोडर :) को fatalError के साथ विफल नहीं करते हैं, तो यह लाइन के नीचे बहुत भ्रामक समस्याएँ पैदा कर सकता है अगर गलती से एक Xib / स्टोरीबोर्ड में उपयोग किया जाता है। घातक इरादे इन इरादों को बताता है। यह एक जानबूझकर और सामान्य अभ्यास है जैसा कि स्वीकृत उत्तर में देखा गया है।
मोबाइल डैन

4

यह महत्वपूर्ण है कि आपका UIView इंटरफ़ेस बिल्डर / स्टोरीबोर्ड द्वारा या कोड से बनाया जा सकता है। मुझे लगता है कि setupकिसी भी सेटअप कोड को डुप्लिकेट करने को कम करने के लिए एक विधि होना उपयोगी है । जैसे

class RedView: UIView {
    override init (frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        setup()
    }

    func setup () {
        backgroundColor = .red
    }
}

4

स्विफ्ट 4.0, यदि आप xib फाइल से व्यू का उपयोग करना चाहते हैं, तो यह आपके लिए है। मैंने UIVIV के कस्टमकॉलआउट व्यू वर्ग सब क्लास बनाया। मैंने एक xib फ़ाइल बनाई है और IB में बस फ़ाइल के मालिक का चयन करें और फिर CustomCalloutView के लिए इंस्पेक्टर इंस्पेक्टर सेट क्लास का नाम चुनें, फिर अपनी क्लास में आउटलेट बनाएं।

    import UIKit
    class CustomCalloutView: UIView {

        @IBOutlet var viewCallout: UIView! // This is main view

        @IBOutlet weak var btnCall: UIButton! // subview of viewCallout
        @IBOutlet weak var btnDirection: UIButton! // subview of viewCallout
        @IBOutlet weak var btnFavourite: UIButton! // subview of viewCallout 

       // let nibName = "CustomCalloutView" this is name of xib file

        override init(frame: CGRect) {
            super.init(frame: frame)
            nibSetup()
        }

        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            nibSetup()
        }

        func nibSetup() {
            Bundle.main.loadNibNamed(String(describing: CustomCalloutView.self), owner: self, options: nil)
            guard let contentView = viewCallout else { return } // adding main view 
            contentView.frame = self.bounds //Comment this line it take default frame of nib view
           // custom your view properties here
            self.addSubview(contentView)
        }
    }

// अब इसे जोड़ना

    let viewCustom = CustomCalloutView.init(frame: CGRect.init(x: 120, y: 120, 50, height: 50))
    self.view.addSubview(viewCustom)

-1

यहाँ एक उदाहरण है कि मैं आमतौर पर अपने उपवर्ग (UIView) का निर्माण कैसे करता हूँ। मेरे पास चर के रूप में सामग्री है, इसलिए उन्हें किसी अन्य वर्ग में बाद में एक्सेस और ट्विक किया जा सकता है। मैंने यह भी दिखाया है कि मैं ऑटो लेआउट और सामग्री जोड़ने का उपयोग कैसे करता हूं।

उदाहरण के लिए एक ViewController में मैंने इस दृश्य को ViewDidLoad () में आरंभीकृत किया है क्योंकि यह दृश्य दिखाई देने पर केवल एक बार कहा जाता है। फिर मैं इन कार्यों का उपयोग करता हूं जो मैं यहां बनाता हूं addContentToView()और फिर activateConstraints()सामग्री बनाने और बाधाओं को सेट करने के लिए। अगर मैं बाद में ViewController में चाहता हूं कि चलो का रंग लाल होने के लिए एक बटन कहता है, तो मैं बस उस ViewController में उस विशिष्ट फ़ंक्शन में करता हूं। कुछ इस तरह:func tweaksome(){ self.customView.someButton.color = UIColor.red}

class SomeView: UIView {


var leading: NSLayoutConstraint!
var trailing: NSLayoutConstraint!
var bottom: NSLayoutConstraint!
var height: NSLayoutConstraint!


var someButton: UIButton = {
    var btn: UIButton = UIButton(type: UIButtonType.system)
    btn.setImage(UIImage(named: "someImage"), for: .normal)
    btn.translatesAutoresizingMaskIntoConstraints = false
    return btn
}()

var btnLeading: NSLayoutConstraint!
var btnBottom: NSLayoutConstraint!
var btnTop: NSLayoutConstraint!
var btnWidth: NSLayoutConstraint!

var textfield: UITextField = {
    var tf: UITextField = UITextField()
    tf.adjustsFontSizeToFitWidth = true
    tf.placeholder = "Cool placeholder"
    tf.translatesAutoresizingMaskIntoConstraints = false
    tf.backgroundColor = UIColor.white
    tf.textColor = UIColor.black
    return tf
}()
var txtfieldLeading: NSLayoutConstraint!
var txtfieldTrailing: NSLayoutConstraint!
var txtfieldCenterY: NSLayoutConstraint!

override init(frame: CGRect){
    super.init(frame: frame)
    self.translatesAutoresizingMaskIntoConstraints = false
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    //fatalError("init(coder:) has not been implemented")
}



/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
    // Drawing code

}
*/
func activateConstraints(){
    NSLayoutConstraint.activate([self.btnLeading, self.btnBottom, self.btnTop, self.btnWidth])
    NSLayoutConstraint.activate([self.txtfieldCenterY, self.txtfieldLeading, self.txtfieldTrailing])
}

func addContentToView(){
    //setting the sizes
    self.addSubview(self.userLocationBtn)

    self.btnLeading = NSLayoutConstraint(
        item: someButton,
        attribute: .leading,
        relatedBy: .equal,
        toItem: self,
        attribute: .leading,
        multiplier: 1.0,
        constant: 5.0)
    self.btnBottom = NSLayoutConstraint(
        item: someButton,
        attribute: .bottom,
        relatedBy: .equal,
        toItem: self,
        attribute: .bottom,
        multiplier: 1.0,
        constant: 0.0)
    self.btnTop = NSLayoutConstraint(
        item: someButton,
        attribute: .top,
        relatedBy: .equal,
        toItem: self,
        attribute: .top,
        multiplier: 1.0,
        constant: 0.0)
    self.btnWidth = NSLayoutConstraint(
        item: someButton,
        attribute: .width,
        relatedBy: .equal,
        toItem: self,
        attribute: .height,
        multiplier: 1.0,
        constant: 0.0)        


    self.addSubview(self.textfield)
    self.txtfieldLeading = NSLayoutConstraint(
        item: self.textfield,
        attribute: .leading,
        relatedBy: .equal,
        toItem: someButton,
        attribute: .trailing,
        multiplier: 1.0,
        constant: 5)
    self.txtfieldTrailing = NSLayoutConstraint(
        item: self.textfield,
        attribute: .trailing,
        relatedBy: .equal,
        toItem: self.doneButton,
        attribute: .leading,
        multiplier: 1.0,
        constant: -5)
    self.txtfieldCenterY = NSLayoutConstraint(
        item: self.textfield,
        attribute: .centerY,
        relatedBy: .equal,
        toItem: self,
        attribute: .centerY,
        multiplier: 1.0,
        constant: 0.0)
}
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.