在实际业务中,咱们常常遇到一个业务控件,由几个小控件组合完成。好比用户头像组件:有头像图片、等级图片、红点提示视图等。为了提升封装性和重用性,通常都会自定义一个视图控件来添加这些小控件。这样有一个反作用就是增长了一层视图层级,以下图所示:git
视图层级多了一层,在布局计算时会更加耗时。视图对象多了一个,内存消耗会更多。那有没有办法即保证了控件的封装性,又能够减小一层视图的包裹呢?答案就是它:UILayoutGuide
,用了它以后的效果以下图:github
减小了一层视图,可是显示效果和封装效果同样。markdown
重要的事情讲三遍:
本方案仅提供一个有趣的思路,并不保证其性能!
本方案仅提供一个有趣的思路,并不保证其性能!
本方案仅提供一个有趣的思路,并不保证其性能!
ide
LayoutContainer
UILayoutGuide
是iOS9引入的,就是为了解决须要有占位视图的场景。它不会出如今视图层级里面,也不会有视图对象,只会在布局引擎起做用。下面是官方注释能够细细品味:布局
UILayoutGuides will not show up in the view hierarchy, but may be used as items in an NSLayoutConstraint and represent a rectangle in the layout engine.性能
因此,建立继承UILayoutGuide
的LayoutContainer
类,当作子控件的布局容器。子控件只须要相对LayoutContainer
布局,就能够实现布局独立,达到其封装性。ui
LayoutContainer
使用示例LayoutContainer
子类化示例UserAvatarContainer
就是用户头像的封装控件。内部的子控件,只须要相对self布局便可。spa
class UserAvatarContainer: LayoutContainer { lazy var avatarImageView: UIImageView = { let imageView = UIImageView() imageView.image = UIImage(named: "lufei.jpg") imageView.contentMode = UIView.ContentMode.scaleAspectFill return imageView }() lazy var vipImageView: UIImageView = { let imageView = UIImageView() imageView.image = UIImage(named: "VIP") return imageView }() override func initializeViews() { super.initializeViews() avatarImageView.translatesAutoresizingMaskIntoConstraints = false avatarImageView.layer.masksToBounds = true //须要用owningView当作父视图 owningView?.addSubview(avatarImageView) avatarImageView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true avatarImageView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true avatarImageView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true avatarImageView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true vipImageView.translatesAutoresizingMaskIntoConstraints = false owningView?.addSubview(vipImageView) vipImageView.widthAnchor.constraint(equalToConstant: 30).isActive = true vipImageView.heightAnchor.constraint(equalToConstant: 30).isActive = true vipImageView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true vipImageView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true } override func layoutSubviews() { super.layoutSubviews() //若是是经过约束布局,这里能够根据layoutFrame(不能使用frame、bounds、center)进行布局调整 avatarImageView.layer.cornerRadius = layoutFrame.size.height/2 } } 复制代码
LayoutContainer
class ListCell: UITableViewCell { lazy var avatarContainer: UserAvatarContainer = { UserAvatarContainer() }() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addLayoutGuide(avatarContainer) avatarContainer.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12).isActive = true avatarContainer.heightAnchor.constraint(equalToConstant: 100).isActive = true avatarContainer.widthAnchor.constraint(equalToConstant: 100).isActive = true avatarContainer.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true } } 复制代码
LayoutContainer
的子视图须要在initializeViews
方法里面进行初始化LayoutContainer
的子视图的布局在layoutSubviews
进行调整LayoutContainer
仅能被addLayoutGuide一次,不容许被removeLayoutGuide。否则initializeViews会被调用屡次,致使重复建立视图!