为什么这些 CAShapeLayers 没有到达预期的位置?
Why are these CAShapeLayers not going where expected?
我正在开发一个自定义加载指示器,我在使用 CAShapeLayers 时遇到了很多问题。
加载指示器将包含在自定义 UIView 中,以便任何人 viewController 都可以使用它。
第一期:
子视图的框架与边界不匹配。
当使用此代码在框架的每个角显示一个圆时,圆被放置在一个正方形中,但它不在视图附近。
导入 UIKit
视图控制器:
class MergingCicles: UIViewController, HolderViewDelegate {
func animateLabel() {
}
var holderView = HolderView(frame: CGRect.zero)
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
addHolderView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func addHolderView() {
let boxSize: CGFloat = 100.0
holderView.frame = CGRect(x: view.bounds.width / 2 - boxSize / 2,
y: view.bounds.height / 2 - boxSize / 2,
width: boxSize,
height: boxSize)
holderView.parentFrame = view.frame
holderView.delegate = self
holderView.center = self.view.center
view.addSubview(holderView)
holderView.addCircleLayer()
}
}
子视图:
Import UIKit
protocol HolderViewDelegate:class {
func animateLabel()
}
class HolderView: UIView {
let initalLayer = InitialLayer()
let triangleLayer = TriangleLayer()
let redRectangleLayer = RectangleLayer()
let blueRectangleLayer = RectangleLayer()
let arcLayer = ArcLayer()
var parentFrame :CGRect = CGRect.zero
weak var delegate:HolderViewDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.clear
}
required init(coder: NSCoder) {
super.init(coder: coder)!
}
func addCircleLayer() {
var circleLocations = [CGPoint]()
let offset = CircleLayer().maxSize / 2
circleLocations.append(CGPoint(x: self.frame.maxX - offset, y: self.frame.maxY - offset))
circleLocations.append(CGPoint(x: self.frame.minX + offset, y: self.frame.minY + offset))
circleLocations.append(CGPoint(x: self.frame.maxX - offset, y: self.frame.minY + offset))
circleLocations.append(CGPoint(x: self.frame.minX + offset, y: self.frame.maxY - offset))
circleLocations.append(layer.anchorPoint)
for point in circleLocations {
let circle = CircleLayer()
circle.updateLocation(Size: .medium, center: point)
self.layer.addSublayer(circle)
}
self.backgroundColor = .blue
// layer.anchorPoint = CGPoint(x: (self.bounds.maxX + self.bounds.maxX)/2, y: (self.bounds.maxY + self.bounds.minY)/2)
let rotationAnimation: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotationAnimation.toValue = CGFloat(Double.pi * 2)
rotationAnimation.duration = 0.45
rotationAnimation.isCumulative = true
//rotationAnimation.repeatCount = 1000
//rotationAnimation.isRemovedOnCompletion = true
// layer.add(rotationAnimation, forKey: nil)
}
}
圆形图层:
import Foundation
import UIKit
enum ShapeSize {
case medium
case small
case large
}
class CircleLayer: CAShapeLayer {
let animationDuration: CFTimeInterval = 0.3
let maxSize = CGFloat(50)
override init() {
super.init()
fillColor = UIColor.red.cgColor
}
func updateLocation(Size: ShapeSize, center: CGPoint){
switch Size {
case .medium:
path = UIBezierPath(ovalIn: CGRect(x: center.x, y: center.y, width: maxSize/3, height: maxSize/3)).cgPath
case .small:
path = UIBezierPath(ovalIn: CGRect(x: center.x, y: center.y, width: (2*maxSize)/3, height: (2*maxSize)/3)).cgPath
case .large:
path = UIBezierPath(ovalIn: CGRect(x: center.x, y: center.y, width: maxSize, height: maxSize)).cgPath
}
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
结果:
这确实表明框架不在 uiView 附近。
如果我将 addCircleLayer
改为使用边界,我会得到更接近的结果:
但圆圈仍然不在角落里(除了右下角,那个是正确的)。似乎在视图的左侧和顶部有一些额外的 space 未使用 self.bounds 捕获。
最终目标也是将圆圈围绕中心旋转 360 度,但如左上角的圆圈所示,图层锚点不在视图的中心,我将锚点更改为中心的圆圈,但随后屏幕上什么也没有出现。
我发现问题出在我使用 UIBezierPath(ovalIn:CGRect, width: CGFloat, height: CGFloat
绘制圆圈时,它使用圆圈左侧的 x 值。当我更改为 UIBezierPath(arcCenter: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)
时,该点用于圆心,并在使用 self.bounds 计算点时使其完全符合预期。
之后我不再需要更改锚点,因为它默认位于正确的位置。
我不明白为什么框架在一个完全不同的地方,但它不再影响项目。
你说的是
circleLocations.append(CGPoint(x: self.frame.maxX - offset, y: self.frame.maxY - offset))
但是self.frame
是视图在它自己的父视图中的位置。因此,形状层最终从视图偏移了与视图从它自己的父视图偏移的一样多。无论你在这里说 frame
,你的意思是 bounds
。
我正在开发一个自定义加载指示器,我在使用 CAShapeLayers 时遇到了很多问题。
加载指示器将包含在自定义 UIView 中,以便任何人 viewController 都可以使用它。
第一期:
子视图的框架与边界不匹配。
当使用此代码在框架的每个角显示一个圆时,圆被放置在一个正方形中,但它不在视图附近。 导入 UIKit
视图控制器:
class MergingCicles: UIViewController, HolderViewDelegate {
func animateLabel() {
}
var holderView = HolderView(frame: CGRect.zero)
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
addHolderView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func addHolderView() {
let boxSize: CGFloat = 100.0
holderView.frame = CGRect(x: view.bounds.width / 2 - boxSize / 2,
y: view.bounds.height / 2 - boxSize / 2,
width: boxSize,
height: boxSize)
holderView.parentFrame = view.frame
holderView.delegate = self
holderView.center = self.view.center
view.addSubview(holderView)
holderView.addCircleLayer()
}
}
子视图:
Import UIKit
protocol HolderViewDelegate:class {
func animateLabel()
}
class HolderView: UIView {
let initalLayer = InitialLayer()
let triangleLayer = TriangleLayer()
let redRectangleLayer = RectangleLayer()
let blueRectangleLayer = RectangleLayer()
let arcLayer = ArcLayer()
var parentFrame :CGRect = CGRect.zero
weak var delegate:HolderViewDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.clear
}
required init(coder: NSCoder) {
super.init(coder: coder)!
}
func addCircleLayer() {
var circleLocations = [CGPoint]()
let offset = CircleLayer().maxSize / 2
circleLocations.append(CGPoint(x: self.frame.maxX - offset, y: self.frame.maxY - offset))
circleLocations.append(CGPoint(x: self.frame.minX + offset, y: self.frame.minY + offset))
circleLocations.append(CGPoint(x: self.frame.maxX - offset, y: self.frame.minY + offset))
circleLocations.append(CGPoint(x: self.frame.minX + offset, y: self.frame.maxY - offset))
circleLocations.append(layer.anchorPoint)
for point in circleLocations {
let circle = CircleLayer()
circle.updateLocation(Size: .medium, center: point)
self.layer.addSublayer(circle)
}
self.backgroundColor = .blue
// layer.anchorPoint = CGPoint(x: (self.bounds.maxX + self.bounds.maxX)/2, y: (self.bounds.maxY + self.bounds.minY)/2)
let rotationAnimation: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotationAnimation.toValue = CGFloat(Double.pi * 2)
rotationAnimation.duration = 0.45
rotationAnimation.isCumulative = true
//rotationAnimation.repeatCount = 1000
//rotationAnimation.isRemovedOnCompletion = true
// layer.add(rotationAnimation, forKey: nil)
}
}
圆形图层:
import Foundation
import UIKit
enum ShapeSize {
case medium
case small
case large
}
class CircleLayer: CAShapeLayer {
let animationDuration: CFTimeInterval = 0.3
let maxSize = CGFloat(50)
override init() {
super.init()
fillColor = UIColor.red.cgColor
}
func updateLocation(Size: ShapeSize, center: CGPoint){
switch Size {
case .medium:
path = UIBezierPath(ovalIn: CGRect(x: center.x, y: center.y, width: maxSize/3, height: maxSize/3)).cgPath
case .small:
path = UIBezierPath(ovalIn: CGRect(x: center.x, y: center.y, width: (2*maxSize)/3, height: (2*maxSize)/3)).cgPath
case .large:
path = UIBezierPath(ovalIn: CGRect(x: center.x, y: center.y, width: maxSize, height: maxSize)).cgPath
}
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
结果:
这确实表明框架不在 uiView 附近。
如果我将 addCircleLayer
改为使用边界,我会得到更接近的结果:
但圆圈仍然不在角落里(除了右下角,那个是正确的)。似乎在视图的左侧和顶部有一些额外的 space 未使用 self.bounds 捕获。
最终目标也是将圆圈围绕中心旋转 360 度,但如左上角的圆圈所示,图层锚点不在视图的中心,我将锚点更改为中心的圆圈,但随后屏幕上什么也没有出现。
我发现问题出在我使用 UIBezierPath(ovalIn:CGRect, width: CGFloat, height: CGFloat
绘制圆圈时,它使用圆圈左侧的 x 值。当我更改为 UIBezierPath(arcCenter: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)
时,该点用于圆心,并在使用 self.bounds 计算点时使其完全符合预期。
之后我不再需要更改锚点,因为它默认位于正确的位置。
我不明白为什么框架在一个完全不同的地方,但它不再影响项目。
你说的是
circleLocations.append(CGPoint(x: self.frame.maxX - offset, y: self.frame.maxY - offset))
但是self.frame
是视图在它自己的父视图中的位置。因此,形状层最终从视图偏移了与视图从它自己的父视图偏移的一样多。无论你在这里说 frame
,你的意思是 bounds
。