Swift子类UIView

Haa*_*nti 89 ios swift

我刚刚开始使用Swift.我已经阅读了这本书,并且在学习的过程中我学到了更多东西.

我想子类UIView并显示像视图一样的登录.我在Objective-C中创建了这个,但现在我想把它移植到Swift.我不使用故事板,所以我在代码中创建了所有的UI.

但第一个问题是我必须实施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")
}
Run Code Online (Sandbox Code Playgroud)

我的问题是我应该在哪里创建我的文本域等...如果我从未实现框架和编码器,我怎么能"隐藏"这个?

Ray*_*ond 166

我经常这样做,有点冗长.

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()
Run Code Online (Sandbox Code Playgroud)

(编辑:我编辑了我的答案,以便初始化者之间的关系更清晰)

  • 这个初始化的东西很疯狂. (51认同)
  • 这不是一个完整的答案.UIView确实支持initWithCoding.从笔尖或故事板加载的任何视图都将调用initWithCoding方法,并崩溃. (7认同)
  • 好东西,谢谢.而不是使用`CGRectZero`我相信它建议使用`CGRect.zeroRect`. (6认同)
  • 但是,自调用initFrame和init以来,addBehavior被调用两次.如果您运行我的代码,则首先打印init,然后打印默认的init (4认同)
  • 有没有办法为自动布局执行此操作?框架已经过时了. (3认同)
  • 添加了这个以支持 IB/AutoLayout:```required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) addBehavior() } ``` (3认同)

Jef*_*ang 15

这更简单.

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

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}
Run Code Online (Sandbox Code Playgroud)


Mob*_*Dan 10

自定义UIView子类示例

我通常在不使用故事板或笔尖的情况下创建iOS应用程序.我将分享一些我学会回答你问题的技巧.

 

隐藏不需要的init方法

我的第一个建议是声明一个基础UIView来隐藏不需要的初始化器.我在回答"如何在UI子类中隐藏故事板和Nib特定初始化器"中详细讨论了这种方法.注意:此方法假定您不会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")
    }
}
Run Code Online (Sandbox Code Playgroud)

您的自定义UIView子类应继承自BaseView.它必须在其初始化程序中调用super.init().它不需要实现init(coder:).这在以下示例中进行了演示.

 

添加UITextField

我为init方法之外引用的子视图创建存储属性.我通常会为UITextField这样做.我更喜欢在子视图属性的声明中实例化子视图,如下所示:let textField = UITextField().

除非您通过调用将其添加到自定义视图的子视图列表中,否则UITextField将不可见addSubview(_:).这在以下示例中进行了演示.

 

没有自动布局的编程布局

除非您设置其大小和位置,否则UITextField将不可见.我经常在layoutSubviews方法中进行代码布局(不使用自动布局).layoutSubviews()最初调用,每当调整大小事件发生时.这允许根据CustomView的大小调整布局.例如,如果CustomView在各种尺寸的iPhone和iPad上显示全宽并调整旋转,则需要适应许多初始尺寸并动态调整大小.

您可以参考frame.heightframe.width内部layoutSubviews()获取CustomView的尺寸以供参考.这在以下示例中进行了演示.

 

示例UIView子类

包含UITextField的自定义UIView子类,不需要实现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)
    }
}
Run Code Online (Sandbox Code Playgroud)

 

具有自动布局的编程布局

您还可以在代码中使用自动布局实现布局.由于我不经常这样做,我不会举一个例子.您可以在Stack Overflow上的代码和Internet上的其他位置找到实现Auto Layout的示例.

 

程序化布局框架

有一些开源框架可以在代码中实现布局.我感兴趣但尚未尝试的是LayoutKit.它是由开发团队编写的LinkedIn.来自Github存储库:"LinkedIn创建了LayoutKit,因为我们发现自动布局对于可滚动视图中的复杂视图层次结构而言不够高效."

 

为什么把fatalErrorinit(coder:)

在创建永远不会在故事板或nib中使用的UIView子类时,您可能会引入具有不同参数和初始化要求的初始化程序,而该init(coder:)方法无法调用这些参数.如果你没有使init(编码器:)失败fatalError,如果在故事板/笔尖中意外使用,可能会导致非常混乱的问题.fatalError断言了这些意图.

required init?(coder aDecoder: NSCoder) {
    fatalError("NSCoding not supported")
}
Run Code Online (Sandbox Code Playgroud)

如果您想在创建子类时运行一些代码,无论它是在代码中创建还是在storyboard/nib中创建,那么您可以执行以下操作(基于Jeff Gu Kang的答案)

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
    }
}
Run Code Online (Sandbox Code Playgroud)


Jas*_*ore 5

重要的是,您的 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
    }
}
Run Code Online (Sandbox Code Playgroud)