在 UIView 中居中 CAShapeLayer Swift
Centering CAShapeLayer within UIView Swift
我无法在 UIView 中将 CAShapeLayer 居中。我搜索过,大多数解决方案来自 2015 年前的 Obj C 或尚未解决。
附件是图片的样子。当我检查它时,它在红色视图内,但我不知道为什么它不居中。我试过调整它的大小,但还是不行。
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
}
您在 viewDidLoad()
中呼叫 setupCircleLayers()
。当时,progressView.frame
还没有从约束中计算出来。
尝试
let centerpoint = CGPoint(x: 100, y: 100)
而不是从 progressView.frame
计算值
你可以在你的 playground 上试试这个:
import UIKit
import PlaygroundSupport
class MyVC: UIViewController {
let progressView: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
var layer: CAShapeLayer?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(progressView)
progressView.translatesAutoresizingMaskIntoConstraints = false
progressView.backgroundColor = .red
progressView.widthAnchor.constraint(equalToConstant: 200).isActive = true
progressView.heightAnchor.constraint(equalToConstant: 200).isActive = true
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: .red, 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: 100, y: 100)
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
return layer
}
}
let containerView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 375.0, height: 667.0))
let vc = MyVC()
PlaygroundPage.current.liveView = vc.view
正如@ukim 所说,您的问题是您试图根据视图及其在有限之前的大小来确定图层的位置。
当您处于 viewDidLoad
时,您还不知道视图的大小和最终位置。您可以添加 progressView
好,但您不能确定它的大小或位置是否正确,直到 viewDidLayoutSubviews
(已记录 here)。
因此,如果我将您对 setupCircleLayers
的呼叫移动到 viewDidLayoutSubviews
并将中心点更改为 CGPoint.zero
并将您的 layer.position
的计算更改为:
layer.position = CGPoint(x: progressView.frame.size.width / 2, y: progressView.frame.size.height / 2)
然后我看到这个:
我希望这更多是你的目标。
这是完整的清单(请注意,我不得不更改您的一些方法,因为我无法访问 anchor
或 UIColor.rgb
等,但您可能可以自己解决那:))
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({ [=11=].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
}
}
希望对您有所帮助。
警告
当您执行上述操作时,这也意味着 每次 viewDidLayoutSubviews
被调用时,您都在添加一个新层。为避免这种情况,您可以使用图层
的 name
属性
layer.name = "myLayer"
然后检查您是否已经添加了图层。这样的事情应该有效:
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({ [=13=].name == "myLayer"}).count == 0
}
然后你在这里使用:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if shouldAddSublayer {
setupCircleLayers()
}
}
我更新了列表。
我无法在 UIView 中将 CAShapeLayer 居中。我搜索过,大多数解决方案来自 2015 年前的 Obj C 或尚未解决。
附件是图片的样子。当我检查它时,它在红色视图内,但我不知道为什么它不居中。我试过调整它的大小,但还是不行。
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
}
您在 viewDidLoad()
中呼叫 setupCircleLayers()
。当时,progressView.frame
还没有从约束中计算出来。
尝试
let centerpoint = CGPoint(x: 100, y: 100)
而不是从 progressView.frame
你可以在你的 playground 上试试这个:
import UIKit
import PlaygroundSupport
class MyVC: UIViewController {
let progressView: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
var layer: CAShapeLayer?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(progressView)
progressView.translatesAutoresizingMaskIntoConstraints = false
progressView.backgroundColor = .red
progressView.widthAnchor.constraint(equalToConstant: 200).isActive = true
progressView.heightAnchor.constraint(equalToConstant: 200).isActive = true
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: .red, 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: 100, y: 100)
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
return layer
}
}
let containerView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 375.0, height: 667.0))
let vc = MyVC()
PlaygroundPage.current.liveView = vc.view
正如@ukim 所说,您的问题是您试图根据视图及其在有限之前的大小来确定图层的位置。
当您处于 viewDidLoad
时,您还不知道视图的大小和最终位置。您可以添加 progressView
好,但您不能确定它的大小或位置是否正确,直到 viewDidLayoutSubviews
(已记录 here)。
因此,如果我将您对 setupCircleLayers
的呼叫移动到 viewDidLayoutSubviews
并将中心点更改为 CGPoint.zero
并将您的 layer.position
的计算更改为:
layer.position = CGPoint(x: progressView.frame.size.width / 2, y: progressView.frame.size.height / 2)
然后我看到这个:
我希望这更多是你的目标。
这是完整的清单(请注意,我不得不更改您的一些方法,因为我无法访问 anchor
或 UIColor.rgb
等,但您可能可以自己解决那:))
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({ [=11=].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
}
}
希望对您有所帮助。
警告
当您执行上述操作时,这也意味着 每次 viewDidLayoutSubviews
被调用时,您都在添加一个新层。为避免这种情况,您可以使用图层
name
属性
layer.name = "myLayer"
然后检查您是否已经添加了图层。这样的事情应该有效:
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({ [=13=].name == "myLayer"}).count == 0
}
然后你在这里使用:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if shouldAddSublayer {
setupCircleLayers()
}
}
我更新了列表。