适当的做法,inheritanceUIView?
我正在处理一些自定义的基于UIView的input控件,我试图确定适当的实践来设置视图。 当使用UIViewController时,使用loadView
和相关的viewWill
, viewDid
方法相当简单,但是在创build一个UIView的子类时,我最接近的方法是`awakeFromNib
, drawRect
和layoutSubviews
。 (我想在设置和拆卸callback方面)。在我的情况下,我在layoutSubviews
设置我的框架和内部视图,但我没有看到任何屏幕上。
确保我的观点具有我想要的正确高度和宽度的最佳方法是什么? (我的问题适用,不pipe我是否使用自动布局,但可能有两个答案。)什么是适当的“最佳做法”?
苹果清楚地定义了如何在文档中UIView
。
看看下面的列表,特别是看看initWithFrame:
和layoutSubviews
。 前者是为了设置你的UIView
的框架,而后者是为了设置框架和它的子视图的布局。
另外请记住,只有当您以编程方式实例化您的UIView
才会调用initWithFrame:
UIView
如果你是从一个nib文件(或一个故事板)加载它, initWithCoder:
将被使用。 在initWithCoder:
框架还没有被计算,所以你不能修改你在Interface Builder中设置的框架。 正如在这个答案中的build议,你可能会想到调用initWithFrame:
从initWithCoder:
为了设置框架。
最后,如果你从一个nib(或一个storyboard)加载你的UIView
,你也有awakeFromNib
机会来执行自定义的框架和布局初始化,因为当awakeFromNib
被调用时,它保证了层次结构中的每个视图都被解除存档和初始化。
从NSNibAwaking
doc
到其他对象的消息可以从awakeFromNib中安全地发送 – 到那个时候,确保所有的对象都被解除存档和初始化(当然,不一定被唤醒)
还值得一提的是,使用自动布局,你不应该明确地设置你的视图的框架。 相反,您应该指定一组足够的约束,以便框架由布局引擎自动计算。
直接从文档 :
重写的方法
初始化
initWithFrame:
build议你实现这个方法。 除了此方法之外,您也可以实现自定义初始化方法。
initWithCoder:
如果从Interface Builder nib文件加载视图,并且视图需要自定义初始化,请实现此方法。
layerClass
仅当您希望视图为其后备存储使用不同的Core Animation图层layerClass
实现此方法。 例如,如果您正在使用OpenGL ES来执行绘图,则需要覆盖此方法并返回CAEAGLLayer类。绘图和打印
drawRect:
如果视图绘制自定义内容,则实现此方法。 如果您的视图不执行任何自定义绘图,请避免重写此方法。
drawRect:forViewPrintFormatter:
仅当您想在打印期间以不同方式绘制视图的内容时才实现此方法。约束
requiresConstraintBasedLayout
如果您的视图类需要约束才能正常工作,请实现此类方法。
updateConstraints
如果您的视图需要在子视图之间创build自定义约束,请实现此方法。
alignmentRectForFrame:
,frameForAlignmentRect:
实现这些方法来覆盖视图如何与其他视图alignment。布局
sizeThatFits:
如果您希望视图的大小与resize操作期间的正常大小不同,请实施此方法。 例如,您可以使用此方法来防止您的视图缩小到无法正确显示子视图的程度。
layoutSubviews
如果您需要对子视图的布局进行更精确的控制(比约束或自动调整行为提供的更好),请实现此方法。
didAddSubview:
,willRemoveSubview:
根据需要实现这些方法来跟踪子视图的添加和删除。
willMoveToSuperview:
,didMoveToSuperview
根据需要实现这些方法来跟踪视图层次结构中当前视图的移动。
willMoveToWindow:
,didMoveToWindow
根据需要实现这些方法,以跟踪视图到另一个窗口的移动。事件处理:
touchesBegan:withEvent:
,touchesMoved:withEvent:
,touchesEnded:withEvent:
,touchesCancelled:withEvent:
如果需要直接处理触摸事件,请实现这些方法。 (对于基于手势的input,请使用手势识别器。)
gestureRecognizerShouldBegin:
如果您的视图直接处理触摸事件,并且可能希望阻止附加的手势识别器触发其他操作,则实现此方法。
这在Google仍然很高。 以下是swift的更新示例。
didLoad
函数可以让你把你所有的自定义初始化代码。 正如其他人所提到的,当通过init(frame:)
编程方式创build视图时,或者当XIB解串器通过init(coder:)
将XIB模板合并到视图中时,将调用didLoad
另外 :
layoutSubviews
和updateConstraints
被大多数视图调用多次。 这适用于视图边界更改时的高级多遍布局和调整。 就个人而言,我尽可能地避免了多遍布局,因为它们会烧掉CPU周期,让一切都变得头痛。 此外,我把约束代码放在初始化程序本身,因为我很less使它们失效。
import UIKit class MyView: UIView { //----------------------------------------------------------------------------------------------------- //Constructors, Initializers, and UIView lifecycle //----------------------------------------------------------------------------------------------------- override init(frame: CGRect) { super.init(frame: frame) didLoad() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) didLoad() } convenience init() { self.init(frame: CGRectZero) } func didLoad() { //Place your initialization code here //I actually create & place constraints in here, instead of in //updateConstraints } override func layoutSubviews() { super.layoutSubviews() //Custom manually positioning layout goes here (auto-layout pass has already run first pass) } override func updateConstraints() { super.updateConstraints() //Disable this if you are adding constraints manually //or you're going to have a 'bad time' //self.translatesAutoresizingMaskIntoConstraints = false //Add custom constraint code here } }
苹果文档中有一个体面的摘要,这在iTunes上提供的免费斯坦福大学课程中有很好的介绍。 我在这里介绍我的TL; DR版本:
如果你的类主要由子视图组成,那么分配它们的正确位置是在init
方法中。 对于视图,有两种不同的init
方法可能会被调用,具体取决于视图是从代码实例化还是从nib / storyboard实例化。 我所做的是编写我自己的setup
方法,然后从initWithFrame:
和initWithCoder:
方法中调用它。
如果你正在做自定义绘图,你确实想在你的视图中重写drawRect:
如果你的自定义视图主要是一个子视图的容器,但是,你可能不需要这样做。
如果您想要添加或移除子视图,请根据纵向或横向来重写layoutSubViews
。 否则,你应该能够保持独立。
layoutSubviews
是为了在子视图上设置框架,而不是在视图本身上。
对于UIView
,指定的构造函数通常是initWithFrame:(CGRect)frame
,您应该在那里(或在initWithCoder:
)设置框架,可能会忽略传入的框架值。 你也可以提供一个不同的构造函数并在那里设置框架。