Программно созданный кадр UIView равен 0 - PullRequest
0 голосов
/ 06 декабря 2018

Невозможно получить доступ к фрейму uiview после настройки его макета.Рамка и центр задаются как 0,0 баллов.

Я должен также упомянуть, что в этом проекте нет раскадровок.Все представления и все создаются программно.

Я программно создал UIView resultView и добавил его как подпредставление в scrollView, которое также добавлено как подпредставление view, затем установил его ограниченияЯкоря в методе с именем setupLayout() Я вызываю setupLayout() в viewDidLoad(), а после этого метода я вызываю другой метод с именем configureShapeLayer().Внутри configureShapeLayer() я пытаюсь получить доступ к центру моего вида как:

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

Затем, используя это значение центра, я пытаюсь добавить два bezierPaths, чтобы иметь вид строки состояния.Но поскольку центр resultView не обновляется в этот момент, он выглядит как неуместное.Пожалуйста, смотрите картинку ниже:

misplaced status bar layer

Я также пытался позвонить setupLayout() в loadView(), затем позвонить configureShapeLayer() в viewDidLoad(), но ничего не изменилось.

Поэтому мне нужен способ убедиться, что все представления установлены в моем представлении, все ограничения и макеты применяются перед вызовом configureShapeLayer().Но как я могу это сделать?

Я также пытался вызвать configureShapeLayer() в обоих методах viewWillLayoutSubviews() и viewDidLayoutSubviews(), но это ухудшило ситуацию и не работало.

Whole View ControllerФайл приведен ниже: Сначала объявляются представления, затем они добавляются в представление в prepareUI(), в конце 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)

}
}

Ответы [ 2 ]

0 голосов
/ 06 декабря 2018

Вам будет гораздо лучше создать собственный вид.Это позволит вам «автоматически» обновлять пути Безье при изменении размера представления.

Это также позволяет вам сохранить код рисования вдали от кода контроллера.

Вот простой пример,Он добавляет кнопку над «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

0 голосов
/ 06 декабря 2018

Это не идеальное исправление, но попробуйте вызвать configureShapeLayer() func в главном потоке, например:

DispatchQueue.main.async {
    configureShapeLayer()
}

Однажды у меня была такая проблема, и что-то подобное.

...