将框架绑定到超级视图边界 UIView 扩展

Posted

技术标签:

【中文标题】将框架绑定到超级视图边界 UIView 扩展【英文标题】:Bind frame to superview bounds UIView extension 【发布时间】:2021-01-05 04:44:02 【问题描述】:

我经常发现自己在编写这些代码行:

myView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    myView.topAnchor.constraint(equalTo: myView.superview.topAnchor),
    myView.bottomAnchor.constraint(equalTo: myView.superview.bottomAnchor),
    myView.leadingAnchor.constraint(equalTo: myView.superview.leadingAnchor),
    myView.trailingAnchor.constraint(equalTo: myView.superview.trailingAnchor)
])

所以我正在考虑写一个扩展。像这样的:

extension UIView 
    func bindFrameToSuperviewBounds() 
        self.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            self.topAnchor.constraint(equalTo: self.superview.topAnchor),
            self.bottomAnchor.constraint(equalTo: self.superview.bottomAnchor),
            self.leadingAnchor.constraint(equalTo: self.superview.leadingAnchor),
            self.trailingAnchor.constraint(equalTo: self.superview.trailingAnchor)
        ])
    

我的问题:

也许像这样的一些内置功能(或技术)已经存在,我只是不知道(不过我已经用谷歌搜索了很多)

(理论上)这段代码不等同于:

myView.translatesAutoresizingMaskIntoConstraints = true   
myView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

理论上为什么?因为在实践中它绝对不是等价的。这种替代技术从未给我预期的结果。结果几乎是不可预测的。

回答cmets:

地点: 绿色(外部)矩形是一个 containerView (UIView)。 紫色(内部)矩形是我正在插入的 UIStackView。 如您所见,约束方法效果很好。

下一个是自动调整掩码方法的结果:

为什么会有三张图片? 因为每次新启动应用程序的结果都不同!

【问题讨论】:

要使非自动布局示例等效,您还必须添加myView.frame = myView.superview.bounds 第二个问题可能重复:***.com/questions/36434947/… @Rob 我已经尝试了所有这些属性的所有可以想象的组合,但我从未得到相同的结果。我刚刚又试了一次你的建议。没有。 @Sweeper 是的,它相关且有趣。但他们没有讨论如何实现对等。 这种技术 (a) 将子视图的frame 设置为其父视图的bounds; (b) 设置自动调整掩码,这正是我们在约束之前这样做的方式。我刚刚测试了它,它工作正常。您将需要创建一个MCVE 来显示问题。也许你有一些不相关的代码在事后改变了translatesAutoresizingMaskIntoConstraintsautoresizingMask。或者您还有其他一些相互冲突的约束。但是这种autoresizingMask 技术是一种久经考验的真正技术...... 【参考方案1】:

这两种技术产生相同的行为:

extension UIView 
    func bindFrameToSuperviewBoundsWithConstraints() 
        translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            topAnchor.constraint(equalTo: superview!.topAnchor),
            bottomAnchor.constraint(equalTo: superview!.bottomAnchor),
            leadingAnchor.constraint(equalTo: superview!.leadingAnchor),
            trailingAnchor.constraint(equalTo: superview!.trailingAnchor)
        ])
    

    func bindFrameToSuperviewBoundsWithAutoResizingMask() 
        translatesAutoresizingMaskIntoConstraints = true
        frame = superview!.bounds
        autoresizingMask = [.flexibleWidth, .flexibleHeight]
    

考虑:

class ViewController: UIViewController 
    @IBOutlet weak var containerView: UIView!

    weak var timer: Timer?

    deinit 
        timer?.invalidate()
    

    override func viewDidLoad() 
        super.viewDidLoad()

        containerView.clipsToBounds = false

        let blueView = UIView()
        blueView.backgroundColor = .blue
        containerView.addSubview(blueView)
        blueView.bindFrameToSuperviewBoundsWithConstraints()

        let redView = UIView()
        redView.backgroundColor = .red
        containerView.addSubview(redView)
        redView.bindFrameToSuperviewBoundsWithAutoResizingMask()

        // toggle between the red and blue views

        blueView.isHidden = true

        Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true)  _ in
            redView.isHidden = !redView.isHidden
            blueView.isHidden = !blueView.isHidden
        
    

这会产生两个交替的子视图,每个子视图使用不同的技术,说明它们的帧是相同的:

如果您发现自动调整大小的蒙版解决方案产生的结果与约束方法不同,那么一定是发生了其他事情。

【讨论】:

嗯...这可能是一个最大不可重现的例子)))我已经更新了我的帖子,请看一下:)【参考方案2】:

您好,您可以使用这样的扩展:

func constraint(to view: UIView, padding: CGFloat = 0) 
    self.translatesAutoresizingMaskIntoConstraints = false
    self.topAnchor.constraint(equalTo: view.topAnchor, constant: padding).isActive = true
    self.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -padding).isActive = true
    self.leftAnchor.constraint(equalTo: view.leftAnchor, constant: padding).isActive = true
    self.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -padding).isActive = true

并与另一个创建超级视图的捷径:

func constraintToSuperview(padding: CGFloat = 0) 
    guard let superview = self.superview else 
        return
    
    
    self.constraint(to: superview, padding: padding)

【讨论】:

以上是关于将框架绑定到超级视图边界 UIView 扩展的主要内容,如果未能解决你的问题,请参考以下文章

确保可拖动的图像视图保持在框架的边界内

UIView 框架、边界和中心

旋转放置和移动视图 (UIView)

UIView 边界/框架与 drawRect 不匹配

如何使以编程方式生成的子视图符合超级视图(iOS)的边界?

UIView 的子视图在边界外渲染