正确的做法子类的UIView?(Proper practice for subclassing UI

2019-08-22 11:16发布

我工作的一些基于UIView的定制输入控件,我试图以确定正确的做法建立视图。 当一个UIViewController的工作,这是相当简单使用loadView和相关viewWillviewDid方法,但是继承一个UIView的时候,我有最接近methosds是`awakeFromNibdrawRect ,和layoutSubviews 。 (我想在安装和拆卸回调的条件。)在我的情况,我设置了我的框架和内部意见layoutSubviews ,但我没有看到屏幕上的任何东西。

什么是保证我的观点有正确的高度和宽度,我希望它有最好的方法是什么? (我的问题适用不管我使用自动布局,虽然可能有两个答案。)什么是正确的“最佳实践”?

Answer 1:

苹果定义非常清楚如何继承UIView的文档。

看看下面的列表中,特别是看看initWithFrame:layoutSubviews 。 前者的目的是设置您的帧UIView而后者则是为了建立框架和它的子视图的布局。

还记得initWithFrame:被称为只有当您的实例UIView编程。 如果您是从笔尖文件(或情节串连图板)加载它, initWithCoder:将被使用。 而在initWithCoder:框架尚未计算呢,所以你不能改变你在Interface Builder建立帧。 作为建议在这个答案 ,你可能会想到调用的initWithFrame:initWithCoder:为了设置边框。

最后,如果你加载你UIView从笔尖(或故事板),你也有awakeFromNib机会来进行自定义的框架和布局初始化,因为当awakeFromNib被调用它保证的是,在层次结构的每个视图已取消封存和初始化。

从商务部NSNibAwaking

消息到其他对象可以安全地从内awakeFromNib,到那个时候它是保证所有的对象是未归档和初始化(虽然不一定惊醒,当然)发送

另外值得一提的是,与自动布局,你应该没有明确设置视图的框架。 相反,你应该到指定一组足够的约束,从而使帧由布局引擎自动计算。

直接从文档 :

方法重写

初始化

  • initWithFrame:建议您实现此方法。 您还可以实现除了或者代替,这种方法自定义初始化方法。

  • initWithCoder:如果您是从Interface Builder的nib文件加载你的看法和你的看法需要自定义初始化实现此方法。

  • layerClass仅实现,如果你想你的视图使用不同的核心动画层其后备存储此方法。 例如,如果你正在使用的OpenGL ES做你的图纸,你想重写此方法并返回CAEAGLLayer类。

制图与印刷

  • drawRect:如果您的视图绘制自定义内容实现此方法。 如果您认为没有做任何自定义绘制,避免重写此方法。

  • drawRect:forViewPrintFormatter:只有当您要在打印过程中不同的绘制视图内容实现此方法。

约束

  • requiresConstraintBasedLayout如果您的视图类需要限制正常工作实现此类方法。

  • updateConstraints如果您认为需要您的子视图之间创建自定义的约束实现此方法。

  • alignmentRectForFrame: frameForAlignmentRect:实现这些方法来覆盖你的意见如何对齐其他视图。

布局

  • sizeThatFits:如果你希望你的观点有不同的默认尺寸比它通常会调整操作过程中实现此方法。 例如,您可以使用此方法,以防止你的观点,从萎缩到子视图不能正确显示的问题。

  • layoutSubviews如果你需要在你的子视图的布局比任何约束或自动尺寸调整行为提供更精准的控制实现此方法。

  • didAddSubview: willRemoveSubview:根据需要跟踪子视图的添加和移除实现这些方法。

  • willMoveToSuperview: didMoveToSuperview根据需要跟踪您的视图层次当前视图的运动实现这些方法。

  • willMoveToWindow: didMoveToWindow根据需要您的视图的运动轨迹为不同的窗口实现这些方法。

事件处理:

  • touchesBegan:withEvent:touchesMoved:withEvent:touchesEnded:withEvent:touchesCancelled:withEvent: ,如果你需要直接处理触摸事件实现这些方法。 (对于基于手势的输入,使用手势识别。)

  • gestureRecognizerShouldBegin:如果您的视图直接处理触摸事件,并可能要防止粘附手势识别从触发额外的操作实现此方法。



Answer 2:

这仍然高出现在谷歌。 下面是迅速更新的例子。

didLoad功能可以让你把你所有的自定义初始化代码。 正如其他人所说, didLoad当通过编程方式创建一个视图将被调用init(frame:)或当XIB解串器通过合并成了一个XIB模板到您的视图init(coder:)

旁白layoutSubviewsupdateConstraints被称为多次为广大的看法。 这适用于高级多通道布局和调整时,一个视图边界的变化。 就个人而言,我尽量避免多通道布局时,可能因为他们消耗CPU周期,使一切头疼。 另外,我把约束代码在初始化自己为我很少它们无效。

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


Answer 3:

有苹果在一个体面的概要文件 ,这是在免费的覆盖以及斯坦福大学的课程 iTunes上提供。 :这里DR版本;我提出我的TL

如果您的类主要包括子视图,来分配他们在正确的地方是在init方法。 对于视图,有两种不同的init ,可以被调用,取决于如果您的看法正在从代码或从笔尖/故事板实例化的方法。 我要做的就是写我自己setup方法,然后同时从调用它initWithFrame:initWithCoder:方法。

如果你正在做自定义绘制,你确实要覆盖drawRect:在您的视图。 如果您的自定义视图主要是为子视图的容器,不过,你可能不会需要做的。

只有覆盖layoutSubViews如果你想要做类似的东西添加或取决于如果你在纵向或横向上删除子视图。 否则,你应该能够息事宁人。



Answer 4:

layoutSubviews是为了设定框架上孩子的意见,而不是在视图本身。

对于UIView ,指定的构造通常是initWithFrame:(CGRect)frame和应设置帧有(或在initWithCoder:可能忽略帧值传递。 您也可以提供不同的构造函数,并设置框出现。



文章来源: Proper practice for subclassing UIView?