1

通常,当您想到父视图-子视图的关系时,人们会倾向于期望子视图的原点将包含在父视图中(通常是父视图的原点)。然而,在UIKitiOS 上,情况似乎并非如此。为什么子视图的原点 (0,0) 的UIWindow

为了演示,下面的代码生成了两个 UIView,一个是另一个的子视图。由于某种原因,innerView原点似乎是整个UIWindow. 我希望原点innerView是原点outerView(其原点由其超级视图的中心点决定)。

我得到什么:

self.edgesForExtendedLayout = []

let superView = self.view!
superView.backgroundColor = .white

let outerView = UIView()
outerView.backgroundColor = .cyan
superView.addSubview(outerView)

outerView.snp.makeConstraints { make in
    make.width.equalToSuperview().inset(20)
    make.height.equalTo(superView.snp.height).dividedBy(3)
    make.center.equalToSuperview()
 }

let innerView = UIView()
innerView.backgroundColor = .purple
outerView.addSubview(innerView)
innerView.snp.makeConstraints { (make) in
    make.height.width.equalToSuperview().dividedBy(2)
}

实际的

我想要的是:

添加额外的SnapKit约束线将innerView原点设置为原点,outerView但我发现这一步是无关紧要的。

self.edgesForExtendedLayout = []

let superView = self.view!
superView.backgroundColor = .white

let outerView = UIView()
outerView.backgroundColor = .cyan
superView.addSubview(outerView)

outerView.snp.makeConstraints { make in
    make.width.equalToSuperview().inset(20)
    make.height.equalTo(superView.snp.height).dividedBy(3)
    make.center.equalToSuperview()
 }

let innerView = UIView()
innerView.backgroundColor = .purple
outerView.addSubview(innerView)
innerView.snp.makeConstraints { (make) in
    make.top.left.equalToSuperview() // Set origins equal
    make.height.width.equalToSuperview().dividedBy(2)
}

预期的

这可能是对 iOS 中视图层次结构的根本误解,因为我一直依赖 Storyboards。这是我第一次涉足 iOS 上的程序化 UI,所以我仍在思考所有的细微差别。现在,证据使我相信每个视图——无论它存在于视图层次结构中的哪个位置——都从 key 的位置 (0,0) 开始UIWindow

4

1 回答 1

1

每个视图内部都有一个新的坐标空间。窗口不能确定没有原点的子视图的原点。关于你的例子最有趣的是你没有给你的紫色视图一个框架,因此没有起源。如果要打印紫色的原点,它将是紫色高度和宽度的一半的负值,作为相对于超级视图的原点。最主要的是通过自动布局或分配一个点来给它一个原点。我想我们很幸运,甚至可以建立一个起源。这是在坐标系中添加视图的示例,带有屏幕截图描述。

import UIKit

class MeasureViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        //view will start at 0,0 of superview which is the main view and be the same size

        let green = UIView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height))
        green.backgroundColor = .green
        self.view.addSubview(green)

        //first inside view  half of outers width and height
        let blue = UIView(frame: CGRect(x: 0, y: 0, width: green.bounds.midX, height: green.bounds.midY))
        blue.backgroundColor = .blue
        // in this case it is self.view.center and outer.center but won't always be just works because coordinate space matches
        blue.center = green.center
        green.addSubview(blue)

        let white = UIView(frame: CGRect(x: 0, y: 0, width: blue.bounds.midX, height: blue.bounds.midY))
        //will not be in the center of firstInside because it is using the coordinate derived from outer
        white.center = blue.center
        white.backgroundColor = .white
        blue.addSubview(white)

        let redDot = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 10))
        redDot.layer.cornerRadius = 5
        redDot.backgroundColor = .red
        //will be the center of blue center because it is half the width and height and be correct
        redDot.center = CGPoint(x: blue.bounds.midX, y: blue.bounds.midY)
        blue.addSubview(redDot)

        //same size as white view above but no origin.  this is bad
        let noOriginView = UIView()
        noOriginView.backgroundColor = .cyan
        noOriginView.bounds.size = CGSize(width: blue.bounds.midX, height: blue.bounds.midY)

        blue.addSubview(noOriginView)
        print("Test withoutFrame \(noOriginView.frame.origin)")
    }

}

说明和截图 截屏

这些基本原理适用于手动布局、自动布局和 Snapkit。使用自动布局和 Snapkit,您不会直接设置点和边界,但需要有做同样事情的约束。

使用 Autolayout:在使用 Autolayout 进行大量测试后,我确实认为如果视图缺少位置约束,它将转换为窗口的 0,0,这意味着实际原点将是 x,y 的负值,上面的示例调整为转换为原点到窗口坐标空间。

于 2017-07-30T14:03:51.933 回答