如何在 CAShapLayer 中设置正确的三角形路径和笔划?
How to set correct path for triangle along with stroke in CAShapLayer?
我必须创建带有适合另一个视图的边框的三角形。但问题是,一旦我设置了描边,三角形就会比容器大。所以,我决定计算内三角大小
应用边框后,我们将有两个三角形,如 Homothetic Transformation。我只想计算小三角形的大小。因此,边框将适合容器。
我试图设置它,但我无法为三角形完成它。因为内三角中心不在容器中心。
Please note that this is first try with equilateral Triangle.
Originally I want solve with same solution with any shape of Polygons.
我附上完整的 demo xcode project at here.
请帮我解决这个问题。谢谢...
ShapeView.swift
import UIKit
class ShapeView: UIView {
var path: CGPath? {
didSet{
setNeedsDisplay(bounds)
}
}
var cornerRadius: CGFloat = .zero
var borderWidth: CGFloat{
set{
shapeLayer.lineWidth = newValue
makeTriangle()
}
get{
return shapeLayer.lineWidth
}
}
var borderColor: UIColor{
set{
shapeLayer.strokeColor = newValue.cgColor
}
get{
return UIColor(cgColor: shapeLayer.strokeColor ?? UIColor.clear.cgColor)
}
}
var background: UIColor{
set{
shapeLayer.fillColor = newValue.cgColor
setNeedsDisplay(bounds)
}
get{
return UIColor(cgColor: shapeLayer.fillColor ?? UIColor.clear.cgColor)
}
}
private lazy var shapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.lineWidth = .zero
layer.addSublayer(shapeLayer)
return shapeLayer
}()
func makeTriangle(){
// Homothetic Transformation
// https://math.stackexchange.com/questions/3952129/calculate-bounds-of-the-inner-rectangle-of-the-polygon-based-on-its-constant-bo
//
let lineWidth: CGFloat = borderWidth/2
let size = (frame.width)-lineWidth
let rect = CGRect(x: lineWidth, y: lineWidth , width:size, height: size)
var cornerRadius: CGFloat = self.cornerRadius-lineWidth
cornerRadius = max(cornerRadius, 0)
let path = CGMutablePath()
path.move(to: CGPoint(x: rect.width/2.0, y: lineWidth))
path.addLine(to: CGPoint(x: lineWidth, y: rect.height - lineWidth))
path.addLine(to: CGPoint(x: rect.width - lineWidth, y: rect.height - lineWidth))
path.closeSubpath()
self.path = path
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let path = path else{
return
}
shapeLayer.path = path
}
}
当前输出:
请注意,您链接的 Math.SE post 中内三角形边界坐标的表达式在这里不是很有用,因为 post 假设 等边 三角形。但是,您的三角形不是等边的。它的底部长度与其高度相同。它所在的视图毕竟是正方形。
在做了一些三角函数之后,我发现内三角形边界矩形的顶部被 lineWidth
除以 sin(atan(0.5))
插入。显然,底部由 lineWidth
插入。并且由于内部边界矩形也必须是正方形,左右插图都是顶部和底部插图之和的一半。
这是一张 GeoGebra 屏幕截图,说明了 sin(atan(0.5))
的来源(我对 GeoGebra 相当不满意,所以请原谅我的混乱):
外三角形右半边的底边是它高的一半,所以 beta = atan(0.5)。然后考虑三角形 E'EA2,注意 sin(beta) = lineWidth / EE',EE' 是我们想要的顶部插图。
// makeTriangle
let lineWidth: CGFloat = borderWidth / 2
let top = lineWidth / sin(atan(0.5))
let bottom = lineWidth
let horizontal = (top + bottom) / 2
let rect = bounds.inset(by:
UIEdgeInsets(
top: top,
left: horizontal,
bottom: bottom,
right: horizontal
)
)
let path = CGMutablePath()
path.move(to: CGPoint(x: rect.midX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
path.closeSubpath()()
self.path = path
现在外三角形应该整齐地放在正方形内。但请注意,默认情况下,CALayer
属性 变化是动画的,如果滑动滑块太快,外三角形可能会超出正方形的边界,动画跟不上。您可以通过将设置 CALayer
属性的代码包装到 CATransaction
和 setDisableActions(true)
中来禁用动画。例如在 borderWidth
的 setter 中:
set{
CATransaction.begin()
CATransaction.setDisableActions(true)
shapeLayer.lineWidth = newValue
makeTriangle()
CATransaction.commit()
}
我必须创建带有适合另一个视图的边框的三角形。但问题是,一旦我设置了描边,三角形就会比容器大。所以,我决定计算内三角大小
应用边框后,我们将有两个三角形,如 Homothetic Transformation。我只想计算小三角形的大小。因此,边框将适合容器。
我试图设置它,但我无法为三角形完成它。因为内三角中心不在容器中心。
Please note that this is first try with equilateral Triangle. Originally I want solve with same solution with any shape of Polygons.
我附上完整的 demo xcode project at here.
请帮我解决这个问题。谢谢...
ShapeView.swift
import UIKit
class ShapeView: UIView {
var path: CGPath? {
didSet{
setNeedsDisplay(bounds)
}
}
var cornerRadius: CGFloat = .zero
var borderWidth: CGFloat{
set{
shapeLayer.lineWidth = newValue
makeTriangle()
}
get{
return shapeLayer.lineWidth
}
}
var borderColor: UIColor{
set{
shapeLayer.strokeColor = newValue.cgColor
}
get{
return UIColor(cgColor: shapeLayer.strokeColor ?? UIColor.clear.cgColor)
}
}
var background: UIColor{
set{
shapeLayer.fillColor = newValue.cgColor
setNeedsDisplay(bounds)
}
get{
return UIColor(cgColor: shapeLayer.fillColor ?? UIColor.clear.cgColor)
}
}
private lazy var shapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.lineWidth = .zero
layer.addSublayer(shapeLayer)
return shapeLayer
}()
func makeTriangle(){
// Homothetic Transformation
// https://math.stackexchange.com/questions/3952129/calculate-bounds-of-the-inner-rectangle-of-the-polygon-based-on-its-constant-bo
//
let lineWidth: CGFloat = borderWidth/2
let size = (frame.width)-lineWidth
let rect = CGRect(x: lineWidth, y: lineWidth , width:size, height: size)
var cornerRadius: CGFloat = self.cornerRadius-lineWidth
cornerRadius = max(cornerRadius, 0)
let path = CGMutablePath()
path.move(to: CGPoint(x: rect.width/2.0, y: lineWidth))
path.addLine(to: CGPoint(x: lineWidth, y: rect.height - lineWidth))
path.addLine(to: CGPoint(x: rect.width - lineWidth, y: rect.height - lineWidth))
path.closeSubpath()
self.path = path
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let path = path else{
return
}
shapeLayer.path = path
}
}
当前输出:
请注意,您链接的 Math.SE post 中内三角形边界坐标的表达式在这里不是很有用,因为 post 假设 等边 三角形。但是,您的三角形不是等边的。它的底部长度与其高度相同。它所在的视图毕竟是正方形。
在做了一些三角函数之后,我发现内三角形边界矩形的顶部被 lineWidth
除以 sin(atan(0.5))
插入。显然,底部由 lineWidth
插入。并且由于内部边界矩形也必须是正方形,左右插图都是顶部和底部插图之和的一半。
这是一张 GeoGebra 屏幕截图,说明了 sin(atan(0.5))
的来源(我对 GeoGebra 相当不满意,所以请原谅我的混乱):
外三角形右半边的底边是它高的一半,所以 beta = atan(0.5)。然后考虑三角形 E'EA2,注意 sin(beta) = lineWidth / EE',EE' 是我们想要的顶部插图。
// makeTriangle
let lineWidth: CGFloat = borderWidth / 2
let top = lineWidth / sin(atan(0.5))
let bottom = lineWidth
let horizontal = (top + bottom) / 2
let rect = bounds.inset(by:
UIEdgeInsets(
top: top,
left: horizontal,
bottom: bottom,
right: horizontal
)
)
let path = CGMutablePath()
path.move(to: CGPoint(x: rect.midX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
path.closeSubpath()()
self.path = path
现在外三角形应该整齐地放在正方形内。但请注意,默认情况下,CALayer
属性 变化是动画的,如果滑动滑块太快,外三角形可能会超出正方形的边界,动画跟不上。您可以通过将设置 CALayer
属性的代码包装到 CATransaction
和 setDisableActions(true)
中来禁用动画。例如在 borderWidth
的 setter 中:
set{
CATransaction.begin()
CATransaction.setDisableActions(true)
shapeLayer.lineWidth = newValue
makeTriangle()
CATransaction.commit()
}