在 UIView Swift 中将 CAShapeLayer 居中 [英] Centering CAShapeLayer within UIView Swift

查看:24
本文介绍了在 UIView Swift 中将 CAShapeLayer 居中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法将 CAShapeLayer 居中放置在 UIView 中.我已经搜索过,大多数解决方案都来自 2015 年之前的 Obj C 或尚未解决.

附上图片的样子.当我检查它时,它在红色视图内,但不知道为什么它不居中.我试过调整它的大小,但仍然不起作用.

let progressView: UIView = {让视图 = UIView()view.backgroundColor = .red返回视图}()//MARK: - ViewDidLoad覆盖 func viewDidLoad() {super.viewDidLoad()view.addSubview(progressView)progressView.anchor(top: nil, left: nil, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 200, height: 200)progressView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = trueprogressView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = truesetupCircleLayers()}var shapeLayer: CAShapeLayer!私有函数 setupCircleLayers() {让 trackLayer = createCircleShapeLayer(strokeColor: UIColor.rgb(red: 56, green: 25, blue: 49, alpha: 1), fillColor: #colorLiteral(red: 0.9686274529, green: 0.78039217, blue: 0.345109)8progressView.layer.addSublayer(trackLayer)}private func createCircleShapeLayer(strokeColor: UIColor, fillColor: UIColor) ->CAShapeLayer {让中心点 = CGPoint(x:progressView.frame.width/2, y:progressView.frame.height/2)let circlePath = UIBezierPath(arcCenter: centerpoint, radius: 100, startAngle: 0, endAngle: 2 * CGFloat.pi, 顺时针: true)让层 = CAShapeLayer()layer.path = circlePath.cgPathlayer.fillColor = fillColor.cgColorlayer.lineCap = kCALineCapRoundlayer.position = progressView.center返回层}

解决方案

正如@ukim 所说,您的问题是您正试图根据视图及其大小来确定图层的位置,然后这些视图是有限的.

>

当您在 viewDidLoad 中时,您还不知道视图的大小和最终位置.您可以添加 progressView 好吧,但在 viewDidLayoutSubviews (记录在

我希望这更符合您的目标.

这是完整的清单(请注意,我必须更改您的某些方法,因为我无法访问 anchorUIColor.rgb 例如,但是您可能可以解决这个问题:))

导入 UIKit类视图控制器:UIViewController {var shapeLayer: CAShapeLayer!让进度视图:UIView = {让视图 = UIView()view.backgroundColor = .red返回视图}()覆盖 func viewDidLoad() {super.viewDidLoad()view.addSubview(progressView)progressView.translatesAutoresizingMaskIntoConstraints = falseprogressView.heightAnchor.constraint(equalToConstant: 200).isActive = trueprogressView.widthAnchor.constraint(equalToConstant: 200).isActive = trueprogressView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = trueprogressView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true}覆盖 func viewDidLayoutSubviews() {super.viewDidLayoutSubviews()如果 shouldAddSublayer {setupCircleLayers()}}私有函数 setupCircleLayers() {让 trackLayer = createCircleShapeLayer(strokeColor: UIColor.init(red: 56/255, green: 25/255, blue: 49/255, alpha: 1), fillColor: #colorLiteral(red: 0.9686274529, green: 0.780392017, blue: 0.780392017, blue: 1), 阿尔法: 1))progressView.layer.addSublayer(trackLayer)}私有变量 shouldAddSublayer: Bool {/*检查是否:1.我们有任何子层,如果我们没有,那么添加一个新的是安全的,所以返回true2.如果有子层,查看我们的"层是否存在,如果没有,返回true*/守卫让子层 = progressView.layer.sublayers else { return true }return sublayers.filter({ $0.name == "myLayer"}).count == 0}private func createCircleShapeLayer(strokeColor: UIColor, fillColor: UIColor) ->CAShapeLayer {让中心点 = CGPoint.zerolet circlePath = UIBezierPath(arcCenter: centerpoint, radius: 100, startAngle: 0, endAngle: 2 * CGFloat.pi, 顺时针: true)让层 = CAShapeLayer()layer.path = circlePath.cgPathlayer.fillColor = fillColor.cgColorlayer.lineCap = kCALineCapRoundlayer.position = CGPoint(x: progressView.frame.size.width/2, y: progressView.frame.size.height/2)layer.name = "myLayer"返回层}}

希望有所帮助.

警告

当您执行上述操作时,也意味着每次 viewDidLayoutSubviews 被调用时,您都在添加一个新层.为了避免这种情况,您可以使用图层的 name 属性

layer.name = "myLayer"

然后检查您是否已经添加了图层.这样的事情应该可以工作:

私有变量 shouldAddSublayer: Bool {/*检查是否:1.我们有任何子层,如果我们没有,那么添加一个新的是安全的,所以返回true2.如果有子层,查看我们的"层是否存在,如果没有,返回true*/守卫让子层 = progressView.layer.sublayers else { return true }return sublayers.filter({ $0.name == "myLayer"}).count == 0}

然后你在这里使用:

override func viewDidLayoutSubviews() {super.viewDidLayoutSubviews()如果 shouldAddSublayer {setupCircleLayers()}}

我已经更新了列表.

I'm having trouble centering CAShapeLayer within a UIView. I've search and most solutions are from pre 2015 in Obj C or haven't been solved.

Attached is what the image looks like. When I inspect it, its inside the red view, but idk why its not centering. I've tried resizing it but still doesn't work.

let progressView: UIView = {
    let view = UIView()
    view.backgroundColor = .red
    return view
}()

//MARK: - ViewDidLoad
override func viewDidLoad() {
    super.viewDidLoad()

    view.addSubview(progressView)
progressView.anchor(top: nil, left: nil, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 200, height: 200)
    progressView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    progressView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

    setupCircleLayers()
}

var shapeLayer: CAShapeLayer!

private func setupCircleLayers() {
    let trackLayer = createCircleShapeLayer(strokeColor: UIColor.rgb(red: 56, green: 25, blue: 49, alpha: 1), fillColor: #colorLiteral(red: 0.9686274529, green: 0.78039217, blue: 0.3450980484, alpha: 1))
    progressView.layer.addSublayer(trackLayer)
}

    private func createCircleShapeLayer(strokeColor: UIColor, fillColor: UIColor) -> CAShapeLayer {

    let centerpoint = CGPoint(x: progressView.frame.width / 2, y: progressView.frame.height / 2)
    let circularPath = UIBezierPath(arcCenter: centerpoint, radius: 100, startAngle: 0, endAngle: 2 * CGFloat.pi, clockwise: true)
    let layer = CAShapeLayer()
    layer.path = circularPath.cgPath
    layer.fillColor = fillColor.cgColor
    layer.lineCap = kCALineCapRound
    layer.position = progressView.center
    return layer
}

解决方案

As @ukim says, your problem is that you are trying to determine the position of your layer, based on views and their size before these are finite.

When you are in viewDidLoad you don't know the size and final position of your views yet. You can add the progressView alright but you can not be sure that its size or position are correct until viewDidLayoutSubviews (documented here).

So, if I move your call to setupCircleLayers to viewDidLayoutSubviews and I change the centerpoint to CGPoint.zero and alter the calculation of your layer.position to this:

layer.position = CGPoint(x: progressView.frame.size.width / 2, y: progressView.frame.size.height / 2)

Then I see this:

Which I hope is more what you were aiming for.

Here is the complete listing (note that I had to change some of your methods as I didn't have access to anchor or UIColor.rgb for instance but you can probably work your way around that :))

import UIKit

class ViewController: UIViewController {

    var shapeLayer: CAShapeLayer!
    let progressView: UIView = {
        let view = UIView()
        view.backgroundColor = .red
        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(progressView)
        progressView.translatesAutoresizingMaskIntoConstraints = false
        progressView.heightAnchor.constraint(equalToConstant: 200).isActive = true
        progressView.widthAnchor.constraint(equalToConstant: 200).isActive = true
        progressView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        progressView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if shouldAddSublayer {
            setupCircleLayers()
        }
    }

    private func setupCircleLayers() {
        let trackLayer = createCircleShapeLayer(strokeColor: UIColor.init(red: 56/255, green: 25/255, blue: 49/255, alpha: 1), fillColor: #colorLiteral(red: 0.9686274529, green: 0.78039217, blue: 0.3450980484, alpha: 1))
        progressView.layer.addSublayer(trackLayer)
    }

    private var shouldAddSublayer: Bool {
        /*
         check if:
         1. we have any sublayers at all, if we don't then its safe to add a new, so return true
         2. if there are sublayers, see if "our" layer is there, if it is not, return true 
        */
        guard let sublayers = progressView.layer.sublayers else { return true }
        return sublayers.filter({ $0.name == "myLayer"}).count == 0
    }

    private func createCircleShapeLayer(strokeColor: UIColor, fillColor: UIColor) -> CAShapeLayer {
        let centerpoint = CGPoint.zero
        let circularPath = UIBezierPath(arcCenter: centerpoint, radius: 100, startAngle: 0, endAngle: 2 * CGFloat.pi, clockwise: true)
        let layer = CAShapeLayer()
        layer.path = circularPath.cgPath
        layer.fillColor = fillColor.cgColor
        layer.lineCap = kCALineCapRound
        layer.position = CGPoint(x: progressView.frame.size.width / 2, y: progressView.frame.size.height / 2)
        layer.name = "myLayer"
        return layer
    }
}

Hope that helps.

Caveat

When you do the above, that also means that every time viewDidLayoutSubviews is called, you are adding a new layer. To circumvent that, you can use the name property of a layer

layer.name = "myLayer"

and then check if you have already added your layer. Something like this should work:

private var shouldAddSublayer: Bool {
    /*
     check if:
     1. we have any sublayers at all, if we don't then its safe to add a new, so return true
     2. if there are sublayers, see if "our" layer is there, if it is not, return true

    */
    guard let sublayers = progressView.layer.sublayers else { return true }
    return sublayers.filter({ $0.name == "myLayer"}).count == 0
}

Which you then use here:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    if shouldAddSublayer {
        setupCircleLayers()
    }
}

I've updated the listing.

这篇关于在 UIView Swift 中将 CAShapeLayer 居中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆