我刚刚开始使用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") }
我的问题是我应该在哪里创建我的文本域等...如果我从未实现框架和编码器,我怎么能"隐藏"这个?
我通常在不使用故事板或笔尖的情况下创建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") } }
您的自定义UIView子类应继承自BaseView
.它必须在其初始化程序中调用super.init().它不需要实现init(coder:)
.这在以下示例中进行了演示.
我为init
方法之外引用的子视图创建存储属性.我通常会为UITextField这样做.我更喜欢在子视图属性的声明中实例化子视图,如下所示:let textField = UITextField()
.
除非您通过调用将其添加到自定义视图的子视图列表中,否则UITextField将不可见addSubview(_:)
.这在以下示例中进行了演示.
除非您设置其大小和位置,否则UITextField将不可见.我经常在layoutSubviews方法中进行代码布局(不使用自动布局).layoutSubviews()
最初调用,每当调整大小事件发生时.这允许根据CustomView的大小调整布局.例如,如果CustomView在各种尺寸的iPhone和iPad上显示全宽并调整旋转,则需要适应许多初始尺寸并动态调整大小.
您可以参考frame.height
和frame.width
内部layoutSubviews()
获取CustomView的尺寸以供参考.这在以下示例中进行了演示.
包含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) } }
您还可以在代码中使用自动布局实现布局.由于我不经常这样做,我不会举一个例子.您可以在Stack Overflow上的代码和Internet上的其他位置找到实现Auto Layout的示例.
有一些开源框架可以在代码中实现布局.我感兴趣但尚未尝试的是LayoutKit.它是由开发团队编写的LinkedIn.来自Github存储库:"LinkedIn创建了LayoutKit,因为我们发现自动布局对于可滚动视图中的复杂视图层次结构而言不够高效."
fatalError
在init(coder:)
在创建永远不会在故事板或nib中使用的UIView子类时,您可能会引入具有不同参数和初始化要求的初始化程序,而该init(coder:)
方法无法调用这些参数.如果你没有使init(编码器:)失败fatalError
,如果在故事板/笔尖中意外使用,可能会导致非常混乱的问题.fatalError断言了这些意图.
required init?(coder aDecoder: NSCoder) { fatalError("NSCoding not supported") }
如果您想在创建子类时运行一些代码,无论它是在代码中创建还是在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 } }
这更简单.
override init (frame : CGRect) { super.init(frame : frame) // Do what you want. } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
我经常这样做,有点冗长.
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()
(编辑:我编辑了我的答案,以便初始化者之间的关系更清晰)