ios - 以编程方式创建的 UIView 的框架为 0

标签 ios swift uiview calayer cashapelayer

设置布局后无法访问 uiview 的框架。框架,中心指定为 0,0 点。

我还应该提到这个项目中没有 Storyboard。所有 View 和所有内容都是以编程方式创建的。

我以编程方式创建了一个 UIView resultView 并将其添加为 scrollView 中的 subview ,它也添加为 view 的 subview ,然后在名为 setupLayout() 的方法中设置它的约束和 anchor ,我在 viewDidLoad() 中调用 setupLayout() ,在该方法之后,我调用另一种方法称为configureShapeLayer()。在 configureShapeLayer() 内部,我尝试访问 View 的中心:

let center = resultView.center // should give resultView's center but gives 0

然后,通过使用这个中心值,我尝试添加两个 bezierPaths 以获得状态栏类型的 View 。但由于 resultView 的中心此时尚未更新,因此它看起来放错了位置。请看下面的图片:

misplaced status bar layer

我还尝试在 loadView() 中调用 setupLayout(),然后在 viewDidLoad() 中调用 configureShapeLayer() > 但没有任何改变。

因此,我需要一种方法来确保在调用 configureShapeLayer() 之前在我的 View 中设置所有 View 、应用所有约束和布局。但我怎样才能做到这一点呢?

我还尝试在 viewWillLayoutSubviews()viewDidLayoutSubviews() 方法中调用 configureShapeLayer() ,但它使情况变得更糟,并且不起作用要么。

整个 View Controller 文件如下:首先声明 View ,然后将它们添加到 prepareUI() 的 View 中,在 prepareUI() 的末尾,调用另一个方法setupLayout()。完成布局设置后,从viewDidLoad可以看出,最终调用了configureShapeLayer()方法。

import UIKit

class TryViewController: UIViewController {

let score: CGFloat = 70

lazy var percentage: CGFloat = {
    return score / 100
}()

// MARK: View Declarations
private let scrollView: UIScrollView = {
    let scrollView = UIScrollView()
    scrollView.backgroundColor = .white
    return scrollView
}()

private let iconImageView: UIImageView = {
    let imageView = UIImageView()
    imageView.contentMode = .scaleAspectFit
    return imageView
}()

let scoreLayer = CAShapeLayer()
let trackLayer = CAShapeLayer()

let percentageLabel: UILabel = {
    let label = UILabel()
    label.text = ""
    label.textAlignment = .center
    label.font = TextStyle.systemFont(ofSize: 50.0)
    return label
}()

// This one is the one should have status bar at center.
private let resultView: UIView = {
    let view = UIView()
    view.backgroundColor = .purple
    return view
}()

override func viewDidLoad() {
    super.viewDidLoad()
    prepareUI()
    configureShapeLayer()
}

private func prepareUI() {

    resultView.addSubviews(views: percentageLabel)

    scrollView.addSubviews(views: iconImageView,
                           resultView)

    view.addSubviews(views: scrollView)
    setupLayout()
}

private func setupLayout() {
    scrollView.fillSuperview()
    iconImageView.anchor(top: scrollView.topAnchor,
                         padding: .init(topPadding: 26.0))
    iconImageView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, multiplier: 0.31).isActive = true
    iconImageView.heightAnchor.constraint(equalTo: iconImageView.widthAnchor, multiplier: 0.67).isActive = true
    iconImageView.anchorCenterXToSuperview()

    //percentageLabel.frame = CGRect(x: 0, y: 0, width: 105, height: 60)
    //percentageLabel.center = resultView.center

    percentageLabel.anchorCenterXToSuperview()
    percentageLabel.anchorCenterYToSuperview()

    let resultViewTopConstraintRatio: CGFloat = 0.104
    resultView.anchor(top: iconImageView.bottomAnchor,
                      padding: .init(topPadding: (view.frame.height * resultViewTopConstraintRatio)))

    resultView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, multiplier: 0.533).isActive = true
    resultView.heightAnchor.constraint(equalTo: resultView.widthAnchor, multiplier: 1.0).isActive = true
    resultView.anchorCenterXToSuperview()

    configureShapeLayer()
}

private func configureShapeLayer() {
    let endAngle = ((2 * percentage) * CGFloat.pi) - CGFloat.pi / 2

    let center = resultView.center // should give resultView's center but gives 0

    // Track Layer Part
    let trackPath = UIBezierPath(arcCenter: center, radius: 50, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)

    trackLayer.path = trackPath.cgPath
    trackLayer.strokeColor = UIColor.lightGray.cgColor // to make different
    trackLayer.lineWidth = 10
    trackLayer.fillColor = UIColor.clear.cgColor
    trackLayer.lineCap = .round

    resultView.layer.addSublayer(trackLayer)

    // Score Fill Part
    let scorePath = UIBezierPath(arcCenter: center, radius: 50, startAngle: -CGFloat.pi / 2, endAngle: endAngle, clockwise: true)

    scoreLayer.path = scorePath.cgPath
    scoreLayer.strokeColor = UIColor.red.cgColor
    scoreLayer.lineWidth = 10
    scoreLayer.fillColor = UIColor.clear.cgColor
    scoreLayer.lineCap = .round
    scoreLayer.strokeEnd = 0

    resultView.layer.addSublayer(scoreLayer)

}
}

最佳答案

创建自定义 View 会更好。这将允许您在 View 大小发生变化时“自动”更新贝塞尔曲线路径。

它还允许您使绘图代码远离 Controller 代码。

这是一个简单的例子。它在“resultView”上方添加了一个按钮 - 每次点击它都会将百分比增加 5(演示时百分比从 5 开始):

//
//  PCTViewController.swift
//
//  Created by Don Mag on 12/6/18.
//

import UIKit

class MyResultView: UIView {

    let scoreLayer = CAShapeLayer()
    let trackLayer = CAShapeLayer()

    var percentage = CGFloat(0.0) {
        didSet {
            self.percentageLabel.text = "\(Int(percentage * 100))%"
            self.setNeedsLayout()
        }
    }

    let percentageLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = ""
        label.textAlignment = .center
        label.font = UIFont.systemFont(ofSize: 40.0)
        return label
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    func commonInit() -> Void {

        layer.addSublayer(trackLayer)
        layer.addSublayer(scoreLayer)

        addSubview(percentageLabel)

        NSLayoutConstraint.activate([
            percentageLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
            percentageLabel.centerYAnchor.constraint(equalTo: centerYAnchor)
            ])

    }

    override func layoutSubviews() {
        super.layoutSubviews()

        let endAngle = ((2 * percentage) * CGFloat.pi) - CGFloat.pi / 2

        trackLayer.frame = self.bounds
        scoreLayer.frame = self.bounds

        let centerPoint = CGPoint(x: self.bounds.width / 2.0, y: self.bounds.height / 2.0)

        // Track Layer Part
        let trackPath = UIBezierPath(arcCenter: centerPoint, radius: 50, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)

        trackLayer.path = trackPath.cgPath
        trackLayer.strokeColor = UIColor.lightGray.cgColor // to make different
        trackLayer.lineWidth = 10
        trackLayer.fillColor = UIColor.clear.cgColor
        // pre-Swift 4.2
        trackLayer.lineCap = kCALineCapRound
//      trackLayer.lineCap = .round

        // Score Fill Part
        let scorePath = UIBezierPath(arcCenter: centerPoint, radius: 50, startAngle: -CGFloat.pi / 2, endAngle: endAngle, clockwise: true)

        scoreLayer.path = scorePath.cgPath
        scoreLayer.strokeColor = UIColor.red.cgColor
        scoreLayer.lineWidth = 10
        scoreLayer.fillColor = UIColor.clear.cgColor
        // pre-Swift 4.2
        scoreLayer.lineCap = kCALineCapRound
//      scoreLayer.lineCap = .round

    }

}

class PCTViewController: UIViewController {

    let resultView: MyResultView = {
        let v = MyResultView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .purple
        return v
    }()

    let btn: UIButton = {
        let b = UIButton()
        b.translatesAutoresizingMaskIntoConstraints = false
        b.setTitle("Add 5 percent", for: .normal)
        b.backgroundColor = .blue
        return b
    }()

    var pct = 5

    @objc func incrementPercent(_ sender: Any) {
        pct += 5
        resultView.percentage = CGFloat(pct) / 100.0
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(btn)
        view.addSubview(resultView)

        NSLayoutConstraint.activate([

            btn.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20.0),
            btn.centerXAnchor.constraint(equalTo: view.centerXAnchor),

            resultView.widthAnchor.constraint(equalToConstant: 300.0),
            resultView.heightAnchor.constraint(equalTo: resultView.widthAnchor),

            resultView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            resultView.centerYAnchor.constraint(equalTo: view.centerYAnchor),

            ])

        btn.addTarget(self, action: #selector(incrementPercent), for: .touchUpInside)

        resultView.percentage = CGFloat(pct) / 100.0

    }

}

结果:

enter image description here

关于ios - 以编程方式创建的 UIView 的框架为 0,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53655845/

相关文章:

ios - 使用 Xcode 5 更新应用

ios - 以编程方式打开 Apple Health

ios - 当 View 已向上移动键盘时,UINavigationBar BarButtonItems 不响应点击

swift - 当 UIView 完全加载时如何收到通知

ios - 如何识别 UIView subview 上的触摸

ios - UIView 在 UITableView header 中不可见

ios - 当其中一项被删除时如何调整TableView高度

iOS 人员选择器获取数组中的联系人电子邮件

ios - "fatal error: unavailable function can' 不被调用”

swift - opentok:发布者和订阅者视频显示相同