如何获取已使用自动布局设置的 UIView 的中心点(CGPoint)

Posted

技术标签:

【中文标题】如何获取已使用自动布局设置的 UIView 的中心点(CGPoint)【英文标题】:How to get center point (CGPoint) of a UIView that has been set using autolayout 【发布时间】:2020-05-21 06:10:57 【问题描述】:

所以我还没有看到任何其他关于此的 SO 问题。基本上,我以编程方式设置了一个带有自动布局的 UIView。

当我尝试创建 UIBezierPath 时出现问题:

let circularPath = UIBezierPath(arcCenter: myView.center, radius: (91*(1.2))/2 - (8/2), startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)

问题是 myView.center 打印出:0.0, 0.0,因为我没有为 myView 设置 CGRect .frame,而是使用了自动布局(如下所示):

NSLayoutConstraint.activate([
    myView.heightAnchor.constraint(equalToConstant: 91),
    myView.widthAnchor.constraint(equalToConstant: 91),
    myView.centerXAnchor.constraint(equalToSystemSpacingAfter: myButton.centerXAnchor, multiplier: 1),
    myView.centerYAnchor.constraint(equalToSystemSpacingBelow: myButton.centerYAnchor, multiplier: 1),
])

也许我应该注意到 myView 是作为 myButton 的子视图添加的(尽管这并不重要..)

那么我该如何做呢?我怎样才能让 .center(一种 CGPoint 类型)在 UIBezierPath 中使用?谢谢!

【问题讨论】:

【参考方案1】:

一些观察:

    center 属性是当前视图的中心在父视图的坐标系中的位置。这可能不是您想要的。您需要CGPoint(x: myView.bounds.midX, y: myView.bounds.midY),它是current 视图的中点。

    因为您正在使用约束,所以您需要将路径的创建推迟到应用约束之后,因为那是设置视图的 bounds 的时间。典型的解决方案是实现UIButton 子类,在该子类中您可以实现layoutSubviews,并在那里设置路径。

例如,一个简单的圆形按钮可能定义如下:

@IBDesignable
class CircleButton: UIButton 
    let normalColor: UIColor = .red
    let highlightedColor: UIColor = .init(red: 0.5, green: 0, blue: 0, alpha: 1)

    lazy var shapeLayer: CAShapeLayer = 
        let shapeLayer = CAShapeLayer()
        shapeLayer.fillColor = normalColor.cgColor
        return shapeLayer
    ()

    override init(frame: CGRect = .zero) 
        super.init(frame: frame)
        configure()
    

    required init?(coder: NSCoder) 
        super.init(coder: coder)
        configure()
    

    func configure() 
        layer.addSublayer(shapeLayer)
    

    override func layoutSubviews() 
        super.layoutSubviews()

        let center = CGPoint(x: bounds.midX, y: bounds.midY)
        let radius = min(bounds.height, bounds.width) / 2
        shapeLayer.path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: true).cgPath
    

    override var isHighlighted: Bool 
        didSet 
            if isHighlighted 
                shapeLayer.fillColor = highlightedColor.cgColor
             else 
                shapeLayer.fillColor = normalColor.cgColor
            
        
    

这种方法的优点是:

您的视图控制器没有大量用于渲染圆圈的代码。

无论渲染的大小如何,这都会起作用(如果您的约束不是针对固定大小,但可能会响应设备的方向、大小类等,这可能很有用)。

如果您愿意,因为这是@IBDesignable,您可以在情节提要/NIB 中呈现它。

【讨论】:

我明白了。我想我应该给你更多信息:我当然在做 myView.center 之前设置了 myView 自动布局。哦,好的,作为对2.)的回应,是不是不可能做一些像myView.center这样简单的事情来获得一个自动布局视图的CGPoint? @GEOG - 从理论上讲,如果您在设置约束后调用了layoutIfNeeded,那么应该设置bounds,然后bounds.midXbounds.midY 应该返回正确的值。但它有点脆弱,对尺寸变化没有反应(例如,如果你的圆形视图的约束与屏幕上的其他东西成比例,而不是固定的宽度和高度。但是有很多方法可以完成你想要的。我d 个人会把它放在一个按钮子类中,回复layoutSubviews,你就有了一个很好的、灵活的、通用的解决方案。 是的,是我没有执行 myView.layoutIfNeeded()。它现在起作用了!也感谢你的课堂方法!【参考方案2】:

您可以尝试使用边界而不是框架。它们可能会帮助您提供 UIVIew 的中心坐标。 center 属性是当前视图的中心在父视图坐标系中的位置。您可以尝试使用以下代码,它是当前视图的中点。

CGPoint(x: myView.bounds.midX, y: myView.bounds.midY),

【讨论】:

以上是关于如何获取已使用自动布局设置的 UIView 的中心点(CGPoint)的主要内容,如果未能解决你的问题,请参考以下文章

如何使用自动布局将 UIView 定位在其他两个 UIView 的中心?

如何获取使用自动布局的 UIView 的 x、y、宽度、高度

自动布局将uiview放在两个uiview的中心[重复]

如何以编程方式设置 UIView 的自动调整大小掩码?

如何使用自动布局在 UIScrollView 中为视图高度设置动画?

如何使第一行标签位于另一个 UIView 的同一中心位置