In real business, we often encounter a business control composed of several small controls. For example, the user profile picture component: there are profile picture, rank picture, red dot prompt view, etc. To improve encapsulation and reuse, you typically add these widgets to a custom view control. This has the side effect of adding a layer of view hierarchy, as shown below:

With one more layer of view hierarchy, layout calculations are more time consuming. One more view object consumes more memory. Is there a way to encapsulate the controls while reducing the need for a layer of views? The answer is UILayoutGuide, which looks like this:

There is one less layer of view, but the display is the same as the wrapper.

Important thing to say three times: this solution only provides an interesting idea, does not guarantee its performance! This solution only provides an interesting idea, does not guarantee its performance! This solution only provides an interesting idea, does not guarantee its performance!

LayoutContainer

LayoutContainerMaking the address

UILayoutGuide was introduced in iOS9 to address scenarios that require a placeholder view. It doesn’t appear in the view hierarchy, it doesn’t have view objects, it just works in the layout engine. Here’s the official note to peruse:

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.

So create a LayoutContainer class that inherits UILayoutGuide as a LayoutContainer for the child controls. Child controls only need to be laid out relative to LayoutContainer to achieve layout independence and encapsulation.

LayoutContainerUse the sample

LayoutContainerSubclassing example

UserAvatarContainer is an encapsulated control of the user’s avatar. The inner child controls need only be laid out relative to self.

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(a) {
        super.initializeViews()

        avatarImageView.translatesAutoresizingMaskIntoConstraints = false
        avatarImageView.layer.masksToBounds = true
        // Use owningView as the parent viewowningView? .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 = falseowningView? .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(a) {
        super.layoutSubviews()

        // If the layout is constrained, the layout can be adjusted according to layoutFrame(not frame, bounds, center)
        avatarImageView.layer.cornerRadius = layoutFrame.size.height/2}}Copy the code

addLayoutContainer

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}}Copy the code

Matters needing attention

  • LayoutContainerThe subview of theinitializeViewsMethod
  • LayoutContainerThe layout of the subview inlayoutSubviewsTo adjust
  • LayoutContainerCan only be addLayoutGuide once, not removeLayoutGuide. Otherwise initializeViews will be called many times, causing views to be created repeatedly!

Making the address

LayoutContainerMaking the address