ios - 如何将 AutoLayout 与 transform 一起使用

标签 ios autolayout

苹果文档说:

In iOS 8.0 and later, the transform property does not affect Auto Layout. Auto layout calculates a view’s alignment rectangle based on its untransformed frame.

因此,如果我有一个名为 View1 的 View ,它通过使用 transform 属性进行缩放,并且 View2 想要与 View1 的边缘对齐。我如何使用 AutoLayout 做到这一点?

最佳答案

好问题。正如您引用的那样,约束适用于 View 的原始未转换边界。

为了能够使您的其他 View 遵循变换后的边界,一种方法是在应用变换后计算约束常量的偏移量。下面是一个与您的情况类似的示例,但具有顶部约束,而不是 leading/trailing/align。但基本上您也可以对其他情况应用相同的过程。

这目前有效,请随时改进此答案。


Storyboard设置:

Storyboard Setup

应用 (scaleX:3, scaleY:2.5) 转换后的输出。


我们在下面的代码中所做的是:

  1. 制作约束的 IBOutlet
  2. 将约束的原始常量存储到变量中。
  3. 应用之前存储要缩放的 View 框架。
  4. 应用转换。
  5. 变换后存储 View 框架。

注意:根据 Apple 文档,它说:

Warning
When the value of this property is anything other than the identity transform, the value in the frame property is undefined and should be ignored.

但转换后的框架似乎在应用比例后立即可用。所以我们将它保存到一个变量中以备将来使用。如果您尝试在其他地方访问框架,您将无法获得正确的框架。所以我们在应用转换后立即执行此步骤。

  1. 通过覆盖 View Controller 的 updateViewConstraints 方法,您有机会更新您的约束。现在我们简单地获取两个框架底部位置的差异并将其应用到 IBOutlet 的约束常量。

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var view1: UIView! //Dark gray view
    @IBOutlet weak var view2: UIView! //Light gray view

    @IBOutlet weak var topConstraint: NSLayoutConstraint!

    let transformScaleX:CGFloat = 3
    let transformScaleY:CGFloat = 2.5

    var rectBeforeTransform:CGRect = .zero
    var rectAfterTransform:CGRect = .zero

    var originalTopConstraintConstant:CGFloat = 0

    override func viewDidLoad() {
        super.viewDidLoad()

        //Store original constraint value
        self.originalTopConstraintConstant = self.topConstraint.constant
        //Store unaltered frame of the view
        self.rectBeforeTransform = view1.frame
        //Apply transform
        self.view1.transform = .init(scaleX: transformScaleX, y: transformScaleY)
        //Frame after transform.
        //NOTE: When a non-identity transform is applied to a view, its frame is undefined, so we would not get proper frame info. of the view anywhere other than after applying the tranform.
        self.rectAfterTransform = view1.frame
    }

    override func updateViewConstraints() {
        DispatchQueue.main.async {
            let constraintOffset = abs(self.rectAfterTransform.bottomY - self.rectBeforeTransform.bottomY)
            self.topConstraint.constant = self.originalTopConstraintConstant + constraintOffset
        }
        super.updateViewConstraints()
    }

}

extension CGRect {
    var bottomY:CGFloat {
        get {
            return self.height + self.origin.y
        }
    }
}

PS:当前答案仅适用于顶部约束,我将尝试更新所有leading/trailing/top/bottom 约束的答案,可能还有更优雅的解决方案。至于动画,不幸的是我无法提供一种方式。如果我找到一些会更新。谢谢。


关于ios - 如何将 AutoLayout 与 transform 一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53111763/

相关文章:

ios - iOS Kamcord SDK可以用于录制游戏以外的应用程序的屏幕吗?

ios - 对隐藏 View 使用自动布局

cocoa - 动画自动布局更改与 NSPopover contentSize 更改同时发生

ios - 在 UITableViewCell 中自动布局两个标签?

ios - 具有自定义行的 UIActivity View Controller (就像应用程序在编辑照片中所做的那样)

iphone - 从 NSDictionary 中检索值

ios - 如何通过 swift 包管理器在 swift 项目中添加 Alamofire

ios - CLLocationManager授权消息

ios - 约束变化不起作用

ios - 将多个布局约束数组附加到单个 Swift 数组中?