修改 endcap .round 样式,使其看起来更圆,与此处显示的示例图像不同
Modify endcap .round style, to be more apparently round, unlike the example image shown here
我刚刚了解到 标准 Apple .round
endcap
let l = CAShapeLayer()
l.path = .. just a straight line, say 50 long
l.lineWidth = say "3.1" in the example shown here
l.lineCap = .round
在某些情况下不是很明显圆,请看图片。
如果我想要明显“精确的半圆”端盖,该怎么办?
我很感激我可以建立自己的线并填充它。
但是有没有办法以某种方式将“endcap”子类化?
如果你能说 = .myHappyEndcapMode
就好了
我们如何解决这个问题?
你可以在这里看到它有多远:
更多信息:
在以下情况下出现非圆形视觉外观:
- 细线
- 可能当宽度为非整数时
- 在手头的情况下,我的渐变和渐变无处不在,这可能无济于事
无法对帽形进行“子类化”。如果您想要不同的帽子形状,您将必须手动构建形状的轮廓。
圆帽已经和 CGPath(ellipseIn:)
创建的圆完全一样了。
这是在半径稍大的圆上绘制的圆顶线,放大了 8 倍:
线帽的曲率正好从圆的水平中心开始。
代码如下:
import UIKit
import PlaygroundSupport
let lineWidth: CGFloat = 20
let circleRadius = lineWidth / 2 + 1
let lineLayer = CAShapeLayer()
lineLayer.position = .init(x: 30, y: 30)
let path = CGMutablePath()
path.move(to: .zero)
path.addLine(to: .init(x: 30, y: 0))
lineLayer.path = path
lineLayer.lineWidth = lineWidth
lineLayer.lineCap = .round
lineLayer.strokeColor = UIColor.gray.cgColor
lineLayer.fillColor = nil
let circleLayer = CAShapeLayer()
circleLayer.position = lineLayer.position
circleLayer.path = CGPath(ellipseIn: CGRect.zero.insetBy(dx: -circleRadius, dy: -circleRadius), transform: nil)
circleLayer.fillColor = UIColor.blue.cgColor
let view = UIView(frame: .init(x: 0, y: 0, width: 100, height: 80))
view.layer.backgroundColor = UIColor.white.cgColor
view.layer.addSublayer(circleLayer)
view.layer.addSublayer(lineLayer)
PlaygroundPage.current.liveView = view
我想知道你是否看到了我们没有看到的东西......
这是一个线端,8 磅宽(在 iPhone 8 上以 16 像素呈现),放大到 3200%
如您所见,圆形端盖的半径正好 8 像素。
这是我用来获取上述放大图像的实际屏幕截图。线宽从 1 到 10:
编辑
Hmmm... 似乎是 A) 它是光学的,并且您没有计算放大图像中的所有抗锯齿像素(即,虽然结束-cap 是 round,只是look round to you),或者B) 也许您正在缩放/拉伸图像/图层?
这里有几个示例图像 - 这次,我使用 CAShapeLayer
作为覆盖在 UIImageView
上的 CAGradientLayer
的遮罩:
放大图片使用的屏幕截图:
以及生成该视图的源代码,如果您想使用它:
class GradientView: UIView {
override open class var layerClass: AnyClass {
return CAGradientLayer.classForCoder()
}
lazy var gradientLayer: CAGradientLayer = {
return self.layer as! CAGradientLayer
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
if let l = layer as? CAGradientLayer {
let c1 = UIColor(rgb: 0x6119D2)
let c2 = UIColor(rgb: 0x7E1CC6)
l.colors = [c1.cgColor, c2.cgColor]
l.startPoint = CGPoint(x: 0.0, y: 0.0)
l.endPoint = CGPoint(x: 1.0, y: 0.0)
}
}
override func layoutSubviews() {
super.layoutSubviews()
let l = CAShapeLayer()
let bez = UIBezierPath()
let xOffset = bounds.size.height * 0.5 + 4
bez.move(to: CGPoint(x: xOffset, y: bounds.size.height * 0.5))
bez.addLine(to: CGPoint(x: bounds.size.width - xOffset, y: bounds.size.height * 0.5))
l.lineCap = .round
l.lineWidth = bounds.size.height
l.strokeColor = UIColor.black.cgColor
l.path = bez.cgPath
self.layer.mask = l
}
}
class EndCapViewController: UIViewController {
let gView: GradientView = {
let v = GradientView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let imgView: UIImageView = {
let v = UIImageView()
v.translatesAutoresizingMaskIntoConstraints = false
v.contentMode = .scaleAspectFit
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
if let img = UIImage(named: "clouds") {
imgView.image = img
}
view.addSubview(imgView)
view.addSubview(gView)
let lineThickness: CGFloat = 8.0
NSLayoutConstraint.activate([
imgView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
imgView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
imgView.widthAnchor.constraint(equalToConstant: 128.0),
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: 1.0),
gView.widthAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: 1.0),
gView.heightAnchor.constraint(equalToConstant: lineThickness),
gView.centerXAnchor.constraint(equalTo: imgView.centerXAnchor),
gView.centerYAnchor.constraint(equalTo: imgView.centerYAnchor),
])
}
}
extension UIColor {
convenience init(red: Int, green: Int, blue: Int) {
assert(red >= 0 && red <= 255, "Invalid red component")
assert(green >= 0 && green <= 255, "Invalid green component")
assert(blue >= 0 && blue <= 255, "Invalid blue component")
self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0)
}
convenience init(rgb: Int) {
self.init(
red: (rgb >> 16) & 0xFF,
green: (rgb >> 8) & 0xFF,
blue: rgb & 0xFF
)
}
}
我刚刚了解到 标准 Apple .round
endcap
let l = CAShapeLayer()
l.path = .. just a straight line, say 50 long
l.lineWidth = say "3.1" in the example shown here
l.lineCap = .round
在某些情况下不是很明显圆,请看图片。
如果我想要明显“精确的半圆”端盖,该怎么办?
我很感激我可以建立自己的线并填充它。
但是有没有办法以某种方式将“endcap”子类化?
如果你能说 = .myHappyEndcapMode
我们如何解决这个问题?
你可以在这里看到它有多远:
更多信息:
在以下情况下出现非圆形视觉外观:
- 细线
- 可能当宽度为非整数时
- 在手头的情况下,我的渐变和渐变无处不在,这可能无济于事
无法对帽形进行“子类化”。如果您想要不同的帽子形状,您将必须手动构建形状的轮廓。
圆帽已经和 CGPath(ellipseIn:)
创建的圆完全一样了。
这是在半径稍大的圆上绘制的圆顶线,放大了 8 倍:
线帽的曲率正好从圆的水平中心开始。
代码如下:
import UIKit
import PlaygroundSupport
let lineWidth: CGFloat = 20
let circleRadius = lineWidth / 2 + 1
let lineLayer = CAShapeLayer()
lineLayer.position = .init(x: 30, y: 30)
let path = CGMutablePath()
path.move(to: .zero)
path.addLine(to: .init(x: 30, y: 0))
lineLayer.path = path
lineLayer.lineWidth = lineWidth
lineLayer.lineCap = .round
lineLayer.strokeColor = UIColor.gray.cgColor
lineLayer.fillColor = nil
let circleLayer = CAShapeLayer()
circleLayer.position = lineLayer.position
circleLayer.path = CGPath(ellipseIn: CGRect.zero.insetBy(dx: -circleRadius, dy: -circleRadius), transform: nil)
circleLayer.fillColor = UIColor.blue.cgColor
let view = UIView(frame: .init(x: 0, y: 0, width: 100, height: 80))
view.layer.backgroundColor = UIColor.white.cgColor
view.layer.addSublayer(circleLayer)
view.layer.addSublayer(lineLayer)
PlaygroundPage.current.liveView = view
我想知道你是否看到了我们没有看到的东西......
这是一个线端,8 磅宽(在 iPhone 8 上以 16 像素呈现),放大到 3200%
如您所见,圆形端盖的半径正好 8 像素。
这是我用来获取上述放大图像的实际屏幕截图。线宽从 1 到 10:
编辑
Hmmm... 似乎是 A) 它是光学的,并且您没有计算放大图像中的所有抗锯齿像素(即,虽然结束-cap 是 round,只是look round to you),或者B) 也许您正在缩放/拉伸图像/图层?
这里有几个示例图像 - 这次,我使用 CAShapeLayer
作为覆盖在 UIImageView
上的 CAGradientLayer
的遮罩:
放大图片使用的屏幕截图:
以及生成该视图的源代码,如果您想使用它:
class GradientView: UIView {
override open class var layerClass: AnyClass {
return CAGradientLayer.classForCoder()
}
lazy var gradientLayer: CAGradientLayer = {
return self.layer as! CAGradientLayer
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
if let l = layer as? CAGradientLayer {
let c1 = UIColor(rgb: 0x6119D2)
let c2 = UIColor(rgb: 0x7E1CC6)
l.colors = [c1.cgColor, c2.cgColor]
l.startPoint = CGPoint(x: 0.0, y: 0.0)
l.endPoint = CGPoint(x: 1.0, y: 0.0)
}
}
override func layoutSubviews() {
super.layoutSubviews()
let l = CAShapeLayer()
let bez = UIBezierPath()
let xOffset = bounds.size.height * 0.5 + 4
bez.move(to: CGPoint(x: xOffset, y: bounds.size.height * 0.5))
bez.addLine(to: CGPoint(x: bounds.size.width - xOffset, y: bounds.size.height * 0.5))
l.lineCap = .round
l.lineWidth = bounds.size.height
l.strokeColor = UIColor.black.cgColor
l.path = bez.cgPath
self.layer.mask = l
}
}
class EndCapViewController: UIViewController {
let gView: GradientView = {
let v = GradientView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let imgView: UIImageView = {
let v = UIImageView()
v.translatesAutoresizingMaskIntoConstraints = false
v.contentMode = .scaleAspectFit
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
if let img = UIImage(named: "clouds") {
imgView.image = img
}
view.addSubview(imgView)
view.addSubview(gView)
let lineThickness: CGFloat = 8.0
NSLayoutConstraint.activate([
imgView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
imgView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
imgView.widthAnchor.constraint(equalToConstant: 128.0),
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: 1.0),
gView.widthAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: 1.0),
gView.heightAnchor.constraint(equalToConstant: lineThickness),
gView.centerXAnchor.constraint(equalTo: imgView.centerXAnchor),
gView.centerYAnchor.constraint(equalTo: imgView.centerYAnchor),
])
}
}
extension UIColor {
convenience init(red: Int, green: Int, blue: Int) {
assert(red >= 0 && red <= 255, "Invalid red component")
assert(green >= 0 && green <= 255, "Invalid green component")
assert(blue >= 0 && blue <= 255, "Invalid blue component")
self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0)
}
convenience init(rgb: Int) {
self.init(
red: (rgb >> 16) & 0xFF,
green: (rgb >> 8) & 0xFF,
blue: rgb & 0xFF
)
}
}