How to implement two inits with same content without code duplication in Swift?

后端 未结 7 1153
旧时难觅i
旧时难觅i 2020-12-01 00:10

Assume a class that is derived from UIView as follows:

class MyView: UIView {
    var myImageView: UIImageView

    init(frame: CGRect) {
               


        
7条回答
  •  离开以前
    2020-12-01 00:35

    Additionally, if the intention is to assign myImageView exactly once, it should be a let rather than a var. That rules out some solutions that only work for var.

    Another complication is multiple instance variables with dependencies between them. This rules out inline initializers calling static methods.

    These requirements can be addressed by overriding with convenience initializers, which delegate to a single designated initializer:

    import UIKit
    
    class MyView: UIView {
        let myImageView: UIImageView
        // Just to illustrate dependencies...
        let myContainerView: UIView
        
        override convenience init(frame: CGRect) {
            self.init(frame: frame, coder: nil)!
        }
        
        required convenience init?(coder aDecoder: NSCoder) {
            // Dummy value for `frame`
            self.init(frame: CGRect(), coder: aDecoder)
        }
        
        @objc private init?(frame: CGRect, coder aDecoder: NSCoder?) {
            // All `let`s must be assigned before
            // calling `super.init`...
            myImageView = UIImageView(frame: CGRect.zero)
            myImageView.contentMode = .scaleAspectFill
            
            // Just to illustrate dependencies...
            myContainerView = UIView()
            myContainerView.addSubview(myImageView)
            
            if let aDecoderNonNil = aDecoder {
                super.init(coder: aDecoderNonNil)
            } else {
                super.init(frame: frame)
            }
            
            // After calling `super.init`, can safely reference
            // `self` for more common setup...
            self.someMethod()
        }
    
        ...    
    }
    

    This is based on ylin0x81's answer, which I really like but doesn't work now (build with Xcode 10.2), as load from nib crashes with:

    This coder requires that replaced objects be returned from initWithCoder:
    

    This issue is covered on a separate question, with iuriimoz's answer suggesting to add @objc to the designated initializer. That entailed avoiding the Swift-only enum used by ylin0x81.

提交回复
热议问题