Swift alt sınıf UIView


95

UIViewGiriş gibi bir görünüm oluşturmak ve alt sınıfa eklemek istiyorum . Bunu Objective-C'de oluşturdum, ancak şimdi Swift'e taşımak istiyorum. Film şeridi kullanmıyorum, bu yüzden tüm kullanıcı arayüzümü kodda oluşturuyorum.

Ama ilk sorun benim uygulamam gerektiğidir initWithCoder. Çağrılmayacağı için ona varsayılan bir uygulama verdim. Şimdi programı çalıştırdığımda çöküyor, çünkü benim de uygulamak zorundayım initWithFrame. Şimdi anladım:

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")
}

Sorum şu: metin alanımı vb. Nerede oluşturmalıyım ... ve çerçeve ve kodlayıcıyı hiç uygulamıyorsam bunu nasıl "gizleyebilirim"?

Yanıtlar:


174

Genelde böyle bir şey yaparım, bu biraz ayrıntılı.

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()

(Düzenleme: Cevabımı, başlatıcılar arasındaki ilişkinin daha net olması için düzenledim)


4
Ancak initFrame ve init çağrıldığından addBehavior iki kez çağrılır. Kodumu çalıştırırsanız, ilk çerçeve başlangıcı yazdırılır, ardından varsayılan başlatma
Haagenti

6
İyi şeyler, teşekkürler. Kullanmak yerine kullanmanın CGRectZerotavsiye edildiğine inanıyorum CGRect.zeroRect.
Bay Rogers

57
Bu başlatıcı işi çılgınca karmaşık.
Ian Warburton

3
Otomatik Yerleşim için bunu yapmanın bir yolu var mı? Çerçeve çok eski.
devios1

8
Bu tam bir cevap değil. UIView, initWithCoding'i destekler. Bir uçtan veya film şeridinden yüklenen herhangi bir görünüm, initWithCoding yöntemini çağırır ve çöker.
Iain Delaney

17

Bu daha basit.

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

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

10

Özel UIView Alt Sınıfı Örneği

Genelde film şeridi veya uç kullanmadan iOS uygulamaları oluşturuyorum. Sorularınıza cevap vermek için öğrendiğim bazı teknikleri paylaşacağım.

 

İstenmeyen initYöntemleri Gizleme

İlk önerim, UIViewistenmeyen başlatıcıları gizlemek için bir temel bildirmek . Bu yaklaşımı, "UI Alt Sınıflarında Storyboard ve Nib'e Özel Başlatıcıları Gizleme" yanıtımda ayrıntılı olarak tartıştım . Not: Bu yaklaşım BaseView, kasıtlı olarak uygulamanın çökmesine neden olacağından, hikaye tahtalarında veya uçlarda kullanmayacağınızı veya onun soyundan gelenleri kullanmayacağınızı varsayar .

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")
    }
}

Özel UIView alt sınıfınız BaseView,. Başlatıcıda super.init () çağırmalıdır. Uygulanmasına gerek yoktur init(coder:). Bu, aşağıdaki örnekte gösterilmiştir.

 

UITextField Ekleme

Yöntemin dışında referans verilen alt görünümler için depolanmış özellikler oluşturuyorum init. Bunu genellikle bir UITextField için yapardım. Böyle Subview Mal bildiriminin içinde örneğini subviews tercih: let textField = UITextField().

UITextField, özel görünümün alt görünüm listesine arayarak eklemediğiniz sürece görünmez addSubview(_:). Bu, aşağıdaki örnekte gösterilmiştir.

 

Otomatik Düzen Olmadan Programatik Düzen

UITextField, boyutunu ve konumunu ayarlamadığınız sürece görünmeyecektir. Genellikle layoutSubviews yöntemi içinde kodda mizanpaj yapıyorum (Otomatik Yerleşimi kullanmıyorum) . layoutSubviews()başlangıçta ve bir yeniden boyutlandırma olayı olduğunda çağrılır. Bu, CustomView boyutuna bağlı olarak düzeni ayarlamaya izin verir. Örneğin, CustomView çeşitli iPhone ve iPad boyutlarında tam genişlikte görünüyorsa ve dönüş için ayarlama yapıyorsa, birçok başlangıç ​​boyutunu barındırması ve dinamik olarak yeniden boyutlandırması gerekir.

Referans için CustomView boyutlarını almak için içinde frame.heightve frame.widthiçinde başvurabilirsiniz layoutSubviews(). Bu, aşağıdaki örnekte gösterilmiştir.

 

Örnek UIView Alt Sınıfı

Uygulanması gerekmeyen bir UITextField içeren özel bir UIView alt sınıfı 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)
    }
}

 

Otomatik Düzen ile Programatik Düzen

Kodda Otomatik Yerleşimi kullanarak da düzen uygulayabilirsiniz. Bunu sık sık yapmadığım için bir örnek göstermeyeceğim. Otomatik Yerleşimin uygulanmasına ilişkin örnekleri Stack Overflow'da ve internette başka yerlerde kodda bulabilirsiniz.

 

Programatik Düzen Çerçeveleri

Kodda düzen uygulayan açık kaynaklı çerçeveler vardır. İlgilendiğim ama denemediğim biri LayoutKit . LinkedIn geliştirme ekibi tarafından yazılmıştır. Github deposundan: "LinkedIn, LayoutKit'i yarattı çünkü Otomatik Yerleşimin kaydırılabilir görünümlerdeki karmaşık görünüm hiyerarşileri için yeterince performanslı olmadığını tespit ettik."

 

Koymak Neden fatalErroriçindeinit(coder:)

Bir film şeridinde veya uçta asla kullanılmayacak UIView alt sınıfları oluştururken, init(coder:)yöntem tarafından çağrılamayan farklı parametrelere ve başlatma gereksinimlerine sahip başlatıcılar sunabilirsiniz . Eğer init (kodlayıcı :) 'ı bir ile başaramadıysanız fatalError, yanlışlıkla bir storyboard / uçta kullanılırsa, çok kafa karıştırıcı sorunlara yol açabilir. FatalError bu niyetleri ileri sürer.

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

Alt sınıf oluşturulduğunda, kodda veya film şeridi / uçta oluşturulup oluşturulmadığına bakılmaksızın bir kod çalıştırmak istiyorsanız, aşağıdakine benzer bir şey yapabilirsiniz ( Jeff Gu Kang'ın cevabına göre )

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
    }
}

ve? fatalErrorxib dosyalarıyla bu görünümü başlatmayı yasakladığınızı ekleyerek
Vyachaslav Gerchicov

@VyachaslavGerchicov, Cevabım, xibs veya storyboard kullanmadığınız varsayımını, kabul edilen cevap ve soru gibi belirtir. Soru "Film şeridi kullanmıyorum, bu nedenle tüm kullanıcı arayüzümü kodda oluşturuyorum" diyor.
Mobil Dan

bir dahaki sefere fatalErrordealloc yönteminin içine yazarsınız ve işe yaramadığını söylersiniz çünkü bu sınıf bir singleton olmalıdır. Kodda UI öğeleri oluşturmayı tercih ediyorsanız, diğer tüm yolları manuel olarak yasaklamamalısınız. Son olarak soru, "storyboard'lar olmadan programlı olarak" nasıl oluşturulacağıdır, ancak xibs / niblerden bahsedilmemiştir. Benim durumumda, programlı olarak + xib ile bir hücre dizisi oluşturmam ve bunları içine aktarmam gerekiyor DropDownMenuKitve bu yol çalışmıyor çünkü bu kitaplığın yazarı da xib'leri yasaklıyor.
Vyachaslav Gerchicov

@VyachaslavGerchicov Jeff Gu Kang'ın cevabı Storyboard / Xibs barındırdığı için aradığınız şey gibi görünüyor
Mobile Dan

1
@VyachaslavGerchicov Soru ayrıca "ve eğer çerçeve ve kodlayıcı uygulamazsam bunu nasıl" gizleyebilirim "?" Bir Xib / Storyboard'da asla kullanılmayacak olan UIView alt sınıfları oluştururken, init (coder :) yöntemi tarafından çağrılamayan farklı parametrelere sahip başlatıcıları tanıtabilirsiniz. Bir fatalError ile init (kodlayıcı :) başarısız olmazsanız, yanlışlıkla bir Xib / Storyboard'da kullanılırsa, hat boyunca çok kafa karıştırıcı sorunlara yol açabilir. FatalError bu niyetleri belirtir. Bu, kabul edilen cevapta görüldüğü gibi kasıtlı ve yaygın bir uygulamadır.
Mobil Dan

4

UIView görünümünüzün arayüz oluşturucu / film şeridi tarafından veya koddan oluşturulabilmesi önemlidir. setupHerhangi bir kurulum kodunu tekrarlamayı azaltmak için bir yönteme sahip olmanın yararlı olduğunu düşünüyorum . Örneğin

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

Swift 4.0, xib dosyasından görünümü kullanmak istiyorsanız, o zaman bu tam size göre. CustomCalloutView sınıfını UIView alt sınıfını oluşturdum. Bir xib dosyası oluşturdum ve IB'de sadece dosya sahibini seçin, ardından Attribute inspector'ı seçin sınıf adını CustomCalloutView olarak ayarlayın, ardından sınıfınızda outlet oluşturun.

    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)
        }
    }

// Şimdi ekliyoruz

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

-1

Alt sınıflarımı genellikle nasıl oluşturduğuma dair bir örnek (UIView). İçeriği değişkenler olarak aldım, böylece daha sonra başka bir sınıfta erişilebilir ve ince ayar yapılabilir. Ayrıca otomatik düzeni nasıl kullandığımı ve içerik eklediğimi de gösterdim.

Örneğin, bir ViewController'da bu görünümü ViewDidLoad () 'da başlattım çünkü bu görünüm görünür olduğunda yalnızca bir kez çağrılır. Sonra burada yaptığım bu işlevleri addContentToView()ve ardından activateConstraints()içeriği oluşturmak ve kısıtlamaları ayarlamak için kullanıyorum. Daha sonra bir ViewController'da diyelim ki bir butonun renginin kırmızı olmasını istersem, bunu ViewController'daki o belirli fonksiyonda yapıyorum. Gibi bir şey: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)
}
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.