有人用 swift 框架理解这个悖论吗?
does anybody understand this paradox with swift frames?
在 UIKIT 中,我有两个 uiview 的主视图和 uiview,在顶部安装了故事板,主视图的 1/3 高。
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var TopView: UIView!
@IBOutlet weak var MiddleView: UIView!
@IBOutlet weak var BottomView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let t = Vvp(inView: TopView)
TopView.addSubview(t)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: TopView.frame.maxX, y: 0))
bezierPath.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = UIColor.red.cgColor
shapeLayer.lineWidth = 1.0
TopView.layer.addSublayer(shapeLayer)
}
}
第二个视图:
func Vvp(inView: UIView)-> UIView {
let viewWithBeizer = UIView(frame: inView.frame)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: inView.frame.maxX, y: 0))
bezierPath.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
// apply other properties related to the path
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.blue.cgColor
shapeLayer.lineWidth = 1.0
viewWithBeizer.layer.addSublayer(shapeLayer)
return viewWithBeizer
}
两个视图都使用相同的框架,在情节提要中所有边框都为零
为什么线条不一样?
我认为这是 iPod touch 7' 模拟器的错误 - 另一个模拟器代码运行良好。您可以在下面看到我的问题中的代码,其中我将 2px 添加到红线。
问题与画线的位置无关...
问题是当您应该使用 bounds
、 和 时,您指的是 frame
您在自动布局配置视图之前设置框架。
根据您的屏幕截图,您正在基于 iPhone 带有缺口的模型在 Storyboard 中布置您的视图...因此,在 viewDidLoad()
中,您的 TopView
具有在情节提要中设置的框架。
这是在 Storyboard 中使用 iPhone 13 Pro 时的样子:
如您所见,即使黄色的TopView被限制在安全区域的顶部,它的Y
位置却是44
。因此,您 func Vvp(inView: UIView)
中的代码将 Frame Y 位置设置为 44
,而不是零。
如果在viewDidLoad()
末尾添加这4行:
TopView.layer.addSublayer(shapeLayer)
// move t (from Vvp(inView: TopView))
// 40-pts to the right
t.frame.origin.x += 40.0
// give it an orange background color
t.backgroundColor = .orange
// allow it to show outside the bounds of TopView
TopView.clipsToBounds = false
// bring TopView to the front of the view hierarchy
view.bringSubviewToFront(TopView)
iPad Touch 7th Gen 的输出如下所示:
如您所见,TopView 的子视图(橙色视图)比 TopView 大得多,并且显示在您指定的位置:距离 TopView 顶部 44 磅。
要按照您编写的方式使用代码,您需要调用该函数 - 以及 TopView 的 shapeLayer 代码 - 在控制器生命周期的后期......例如 viewDidLayoutSubviews()
。但是,如果你这样做,你需要记住它会被多次调用(任何时候主视图发生变化,例如设备旋转),所以你要确保你不会重复添加新的子视图和图层.
这里是对您的代码的快速修改:
class ViewController: UIViewController {
@IBOutlet weak var TopView: UIView!
@IBOutlet weak var MiddleView: UIView!
@IBOutlet weak var BottomView: UIView!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if TopView.subviews.count == 0 {
// we haven't added the subview or shape layer,
// so let's do that here
let t = Vvp(inView: TopView)
TopView.addSubview(t)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: TopView.frame.maxX, y: 0))
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = UIColor.red.cgColor
shapeLayer.lineWidth = 1.0
TopView.layer.addSublayer(shapeLayer)
}
}
func Vvp(inView: UIView)-> UIView {
let viewWithBeizer = UIView(frame: inView.bounds)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: inView.bounds.maxX, y: 0))
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
// apply other properties related to the path
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.blue.cgColor
shapeLayer.lineWidth = 1.0
viewWithBeizer.layer.addSublayer(shapeLayer)
return viewWithBeizer
}
}
结果(蓝线不可见,因为我们在上面添加了红线):
不过,更好的方法是 A) 使用自动布局约束,以及 B) 在自定义 UIView
子类中处理 shapeLayer 逻辑——但这是另一个话题。
在 UIKIT 中,我有两个 uiview 的主视图和 uiview,在顶部安装了故事板,主视图的 1/3 高。
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var TopView: UIView!
@IBOutlet weak var MiddleView: UIView!
@IBOutlet weak var BottomView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let t = Vvp(inView: TopView)
TopView.addSubview(t)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: TopView.frame.maxX, y: 0))
bezierPath.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = UIColor.red.cgColor
shapeLayer.lineWidth = 1.0
TopView.layer.addSublayer(shapeLayer)
}
}
第二个视图:
func Vvp(inView: UIView)-> UIView {
let viewWithBeizer = UIView(frame: inView.frame)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: inView.frame.maxX, y: 0))
bezierPath.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
// apply other properties related to the path
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.blue.cgColor
shapeLayer.lineWidth = 1.0
viewWithBeizer.layer.addSublayer(shapeLayer)
return viewWithBeizer
}
两个视图都使用相同的框架,在情节提要中所有边框都为零
为什么线条不一样?
我认为这是 iPod touch 7' 模拟器的错误 - 另一个模拟器代码运行良好。您可以在下面看到我的问题中的代码,其中我将 2px 添加到红线。
问题与画线的位置无关...
问题是当您应该使用 bounds
、 和 时,您指的是 frame
您在自动布局配置视图之前设置框架。
根据您的屏幕截图,您正在基于 iPhone 带有缺口的模型在 Storyboard 中布置您的视图...因此,在 viewDidLoad()
中,您的 TopView
具有在情节提要中设置的框架。
这是在 Storyboard 中使用 iPhone 13 Pro 时的样子:
如您所见,即使黄色的TopView被限制在安全区域的顶部,它的Y
位置却是44
。因此,您 func Vvp(inView: UIView)
中的代码将 Frame Y 位置设置为 44
,而不是零。
如果在viewDidLoad()
末尾添加这4行:
TopView.layer.addSublayer(shapeLayer)
// move t (from Vvp(inView: TopView))
// 40-pts to the right
t.frame.origin.x += 40.0
// give it an orange background color
t.backgroundColor = .orange
// allow it to show outside the bounds of TopView
TopView.clipsToBounds = false
// bring TopView to the front of the view hierarchy
view.bringSubviewToFront(TopView)
iPad Touch 7th Gen 的输出如下所示:
如您所见,TopView 的子视图(橙色视图)比 TopView 大得多,并且显示在您指定的位置:距离 TopView 顶部 44 磅。
要按照您编写的方式使用代码,您需要调用该函数 - 以及 TopView 的 shapeLayer 代码 - 在控制器生命周期的后期......例如 viewDidLayoutSubviews()
。但是,如果你这样做,你需要记住它会被多次调用(任何时候主视图发生变化,例如设备旋转),所以你要确保你不会重复添加新的子视图和图层.
这里是对您的代码的快速修改:
class ViewController: UIViewController {
@IBOutlet weak var TopView: UIView!
@IBOutlet weak var MiddleView: UIView!
@IBOutlet weak var BottomView: UIView!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if TopView.subviews.count == 0 {
// we haven't added the subview or shape layer,
// so let's do that here
let t = Vvp(inView: TopView)
TopView.addSubview(t)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: TopView.frame.maxX, y: 0))
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = UIColor.red.cgColor
shapeLayer.lineWidth = 1.0
TopView.layer.addSublayer(shapeLayer)
}
}
func Vvp(inView: UIView)-> UIView {
let viewWithBeizer = UIView(frame: inView.bounds)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: inView.bounds.maxX, y: 0))
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
// apply other properties related to the path
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.blue.cgColor
shapeLayer.lineWidth = 1.0
viewWithBeizer.layer.addSublayer(shapeLayer)
return viewWithBeizer
}
}
结果(蓝线不可见,因为我们在上面添加了红线):
不过,更好的方法是 A) 使用自动布局约束,以及 B) 在自定义 UIView
子类中处理 shapeLayer 逻辑——但这是另一个话题。