将可视格式的约束转换为 NSLayoutAnchor API

Posted

技术标签:

【中文标题】将可视格式的约束转换为 NSLayoutAnchor API【英文标题】:Translating constraints in a visual format to NSLayoutAnchor APIs 【发布时间】:2017-10-21 22:34:46 【问题描述】:

我有以下从this SO question a few months ago 获得的扩展,我想将其转换为 NSLayoutAnchor(或更详细的布局 API),原因很简单,视觉格式不支持指定安全区域布局指南。

链接中的扩展是这个:

extension UIView 

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() 
        guard let superview = self.superview else 
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    

我的视图层次结构是:

UINavigationController -> 根视图控制器:UIPageViewController -> 第一页:UIViewController -> UIScrollView -> UIImageView

当我调用bindFrameToSuperviewBounds()时,结果如下:

看起来大部分都很好。问题是图像视图位于导航栏下方,这是我不想要的。所以为了解决这个问题,我尝试将bindFrameToSuperviewBounds() 重写为:

guard let superview = self.superview else 
            fatalError("Attempting to bound to a non-existing superview.")
        

translatesAutoresizingMaskIntoConstraints = false
self.topAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.topAnchor).isActive = true
self.leadingAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.leadingAnchor).isActive = true
self.bottomAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.bottomAnchor).isActive = true
self.trailingAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.trailingAnchor).isActive = true

结果是这样的:

这一次它正确地没有进入导航栏,但是...你可以看到它被拉伸了,这是我不想要的。

将可视布局 API 转换为可以考虑安全区域布局指南的正确方法是什么?

为了完整起见,设置滚动视图和图像视图的代码如下:

override func viewDidLoad() 
    super.viewDidLoad()
    imageScrollView.addSubview(imageView)
    imageScrollView.layoutIfNeeded()
    imageView.bindFrameToSuperviewBounds()
    view.addSubview(imageScrollView)
    imageView.layoutIfNeeded()
    imageScrollView.backgroundColor = .white
    imageScrollView.bindFrameToSuperviewBounds()
    imageScrollView.delegate = self

【问题讨论】:

【参考方案1】:

通过 Fattie 提供的代码,我能够得出这个解决方案:

guard let superview = self.superview else 
    fatalError("Attempting to bound to a non-existing superview.")


translatesAutoresizingMaskIntoConstraints = false
self.topAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.topAnchor).isActive = true
self.leadingAnchor.constraint(equalTo: superview.leadingAnchor).isActive = true
self.bottomAnchor.constraint(equalTo: superview.bottomAnchor).isActive = true
self.trailingAnchor.constraint(equalTo: superview.trailingAnchor).isActive = true

(现在我只是将顶部锚定到安全布局是指南。这是否会给 iPhone X 带来问题还有待观察)。

这给出了我期望的结果:

【讨论】:

【参考方案2】:

老实说,我不会为那些奇怪的“视觉”布局而烦恼,它们很快就会消失。

这有帮助吗?

extension UIView 

    // put this view "inside" it's superview, simply stretched all four ways
    // amazingly useful

    func bindEdgesToSuperview() 

        guard let s = superview else 
            preconditionFailure("`superview` nil in bindEdgesToSuperview")
        

        translatesAutoresizingMaskIntoConstraints = false
        leadingAnchor.constraint(equalTo: s.leadingAnchor).isActive = true
        trailingAnchor.constraint(equalTo: s.trailingAnchor).isActive = true
        topAnchor.constraint(equalTo: s.topAnchor).isActive = true
        bottomAnchor.constraint(equalTo: s.bottomAnchor).isActive = true
    


就这样使用..

    textOnTop = ..  UILabel or whatever
    userPhoto.addSubview(textOnTop)
    textOnTop.bindEdgesToSuperview()

简单!

(PS - 不要忘记,如果是图片:您几乎可以肯定要设置为 AspectFit,在任何情况下都始终如此。)

安全布局指南的句柄就像...

.safeAreaLayoutGuide

【讨论】:

我可能应该提到我没有使用故事板,而是编写我的 UI 代码。我想我没有正确表达我的问题。您的示例代码进行了翻译,但它没有考虑安全布局边距,因此结果与我发布的第一张照片相同。此外,考虑到很多人都可以解决这个问题,您绝对应该解决我提供的示例代码的所有问题。你的代码和我基本相同,不同的是我尝试使用安全布局指南。 嗨@AndyIbanez,是的,这就是我的观点。 我知道你是用代码来做的。老实说,>>>不要使用这完全是愚蠢的,不起作用,而且现在任何一天都会被弃用! 哦,抱歉,我没有将.safe .. etc 作为约束之一的示例。看起来你在下面找到了它! 最后一点!在所有客户的所有团队的所有项目中,我们从不使用“安全指南”的东西。呵!许多其他团队和项目也采用此策略。对于在这里使用谷歌搜索的任何人,您可以在文件检查器下打开故事板中关闭“安全区域指南”垃圾,希望它可以帮助某人

以上是关于将可视格式的约束转换为 NSLayoutAnchor API的主要内容,如果未能解决你的问题,请参考以下文章

如何将原始 RGB 帧缓冲区文件转换为可视格式?

R语言ggplot2可视化:应用pivot_longer函数将数据从宽格式转换为长格式为dataframe的每一列绘制密度图和直方图(堆叠)

JSON时间转换格式化(将数字时间转换为可视化的yyyy-MM-dd HH:mm:ss的格式)

将十六进制 unicode 字符转换为其可视化表示

python使用openCV加载图像并将BGR格式转换成HSV格式定义HSV格式中需要分离颜色的掩码(掩模)区间(mask)并使用mask信息进行颜色分离BGR格式的图像转化为RGB并可视化

python使用openCV加载图像并将BGR格式转换成HSV格式定义HSV格式中需要分离颜色的掩码(掩模)区间(mask)并使用mask信息进行颜色分离BGR格式的图像转化为RGB并可视化