为什么角半径仅应用于自定义视图的左上角和右上角?
Why is corner radius applied only to top left and right corner for a custom view?
我有自己的自定义视图,我将其用作加载栏。它有一个不透明度为 0.1 的底层和一个不透明度为 1 的顶层。
设置方法如下:
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
func configure() {
animation.delegate = self
bottomProgressBar.strokeColor = UIColor.white.cgColor
bottomProgressBar.opacity = 0.1
bottomProgressBar.lineWidth = self.frame.height
bottomProgressBar.strokeStart = 0
bottomProgressBar.strokeEnd = 1
self.layer.addSublayer(bottomProgressBar)
topProgressBar.strokeColor = UIColor.white.cgColor
topProgressBar.lineWidth = self.frame.height
topProgressBar.strokeStart = 0
topProgressBar.strokeEnd = 1
self.layer.addSublayer(topProgressBar)
animate(fromValue: 0, toValue: 0)
}
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: self.frame.width, y: 0))
bottomProgressBar.lineWidth = self.frame.height // Update lineWidth
topProgressBar.lineWidth = self.frame.height // Update lineWidth
bottomProgressBar.path = path.cgPath
topProgressBar.path = path.cgPath
self.clipsToBounds = true
self.layer.cornerRadius = 4
}
现在我也想应用圆角半径,所以我在layoutSubviews
的底部添加了代码:
self.clipsToBounds = true
self.layer.cornerRadius = 4
这只将圆角半径应用于左上角和右上角部分,仅此而已。我不明白为什么。
我还尝试在 bottomProgressBar
和 topProgressBar
层的 configure
方法中设置它,但根本不起作用。
有人可以指出这里的问题吗?
编辑:
这是完整的 class 代码:
class ProgressBar: UIView {
var bottomProgressBar = CAShapeLayer()
var topProgressBar = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
func configure() {
bottomProgressBar.strokeColor = UIColor.white.cgColor
bottomProgressBar.opacity = 0.1
bottomProgressBar.lineWidth = self.frame.height
bottomProgressBar.strokeStart = 0
bottomProgressBar.strokeEnd = 1
self.layer.addSublayer(bottomProgressBar)
topProgressBar.strokeColor = UIColor.white.cgColor
topProgressBar.lineWidth = self.frame.height
topProgressBar.strokeStart = 0
topProgressBar.strokeEnd = 1
self.layer.addSublayer(topProgressBar)
animate(toValue: 1)
}
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: self.frame.width, y: 0))
bottomProgressBar.lineWidth = self.frame.height // Update lineWidth
topProgressBar.lineWidth = self.frame.height // Update lineWidth
bottomProgressBar.path = path.cgPath
topProgressBar.path = path.cgPath
}
func animate(toValue: Double) {
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = 1.0
animation.fromValue = 0
animation.toValue = toValue
topProgressBar.strokeEnd = CGFloat(toValue)
topProgressBar.animation(forKey: "animate")
}
}
编辑 2:
添加了更多有关正在发生的事情的屏幕截图:
这没有应用圆角半径 - 简单的矩形
这就是我应用圆角半径时发生的情况 - 下半部分被切掉(矩形显然被切掉一半)
这就是我想要的 - 简单的矩形,所有 4 个角的角半径为:
编辑: 明确最终目标后,这里有一个更简单的方法。您可以 运行 直接在游乐场页面中...
import UIKit
import PlaygroundSupport
class ProgressBar: UIView {
var progressView: UIView = {
let v = UIView()
v.backgroundColor = .white
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
func configure() {
backgroundColor = UIColor(white: 1.0, alpha: 0.1)
layer.cornerRadius = 8.0
clipsToBounds = true
progressView.frame = bounds
progressView.frame.size.width = 0.0
addSubview(progressView)
}
func animate(_ pct: CGFloat) -> Void {
UIView.animate(withDuration: 1.0, animations: {
self.progressView.frame.size.width = self.bounds.size.width * pct
})
}
}
class MyViewController : UIViewController {
var theButton: UIButton = {
let b = UIButton()
b.setTitle("Tap Me", for: .normal)
b.translatesAutoresizingMaskIntoConstraints = false
b.backgroundColor = .red
return b
}()
var myProgressBar = ProgressBar(frame: CGRect(x: 40, y: 300, width: 300, height: 30))
// on button tap, set the progress bar to 80%
@objc func didTap(_ sender: Any?) -> Void {
myProgressBar.animate(0.8)
}
override func loadView() {
let view = UIView()
self.view = view
view.backgroundColor = UIColor.purple
view.addSubview(theButton)
// constrain button to Top: 32 and centerX
NSLayoutConstraint.activate([
theButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 32.0),
theButton.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0.0),
])
// add an action for the button tap
theButton.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)
view.addSubview(myProgressBar)
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
点击按钮将使 "progress bar" 从零变为 80%
结果:
原回答:
首先:您要添加一个从 0,0
到 width,0
的 "line" ... 并且您要使它的 lineWidth 成为您视图的高度。
这给你这个结果:
黄色矩形是您的视图,白色矩形是您的 "line"。如您所见,直线的 center 沿着视图的 top 运行ning,因此上半部分线实际上在 视图框架之外。因此,当您对视图的角进行圆角处理时,看起来好像只有顶角被圆角化了。如果为视图设置背景颜色,您会看到底角也变圆了,但那里没有 "line"。
如果这是您真正想要的(顶部栏为白色,底部栏为绿色):
您想将线宽设置为视图高度的 1/2,并将线中心设置为 1/4 和 3/4:
class ProgressBar: UIView {
var bottomProgressBar = CAShapeLayer()
var topProgressBar = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
func configure() {
bottomProgressBar.strokeColor = UIColor.green.cgColor
bottomProgressBar.opacity = 1
bottomProgressBar.lineWidth = self.frame.height
bottomProgressBar.strokeStart = 0
bottomProgressBar.strokeEnd = 1
self.layer.addSublayer(bottomProgressBar)
topProgressBar.strokeColor = UIColor.white.cgColor
topProgressBar.lineWidth = self.frame.height
topProgressBar.strokeStart = 0
topProgressBar.strokeEnd = 1
self.layer.addSublayer(topProgressBar)
// animate(toValue: 1)
}
override func layoutSubviews() {
super.layoutSubviews()
var path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: self.frame.height * 0.25))
path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height * 0.25))
topProgressBar.path = path.cgPath
path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: self.frame.height * 0.75))
path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height * 0.75))
bottomProgressBar.path = path.cgPath
bottomProgressBar.lineWidth = self.frame.height / 2.0 // Update lineWidth
topProgressBar.lineWidth = self.frame.height / 2.0 // Update lineWidth
self.clipsToBounds = true
self.layer.cornerRadius = 8
}
func animate(toValue: Double) {
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = 3.0
animation.fromValue = 0
animation.toValue = toValue
topProgressBar.strokeEnd = CGFloat(toValue)
topProgressBar.animation(forKey: "animate")
}
}
当然,如果您只需要一个带圆角的矩形,那么您不需要任何子层...只需创建一个白色背景的视图,并设置其层的角半径...
我有自己的自定义视图,我将其用作加载栏。它有一个不透明度为 0.1 的底层和一个不透明度为 1 的顶层。
设置方法如下:
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
func configure() {
animation.delegate = self
bottomProgressBar.strokeColor = UIColor.white.cgColor
bottomProgressBar.opacity = 0.1
bottomProgressBar.lineWidth = self.frame.height
bottomProgressBar.strokeStart = 0
bottomProgressBar.strokeEnd = 1
self.layer.addSublayer(bottomProgressBar)
topProgressBar.strokeColor = UIColor.white.cgColor
topProgressBar.lineWidth = self.frame.height
topProgressBar.strokeStart = 0
topProgressBar.strokeEnd = 1
self.layer.addSublayer(topProgressBar)
animate(fromValue: 0, toValue: 0)
}
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: self.frame.width, y: 0))
bottomProgressBar.lineWidth = self.frame.height // Update lineWidth
topProgressBar.lineWidth = self.frame.height // Update lineWidth
bottomProgressBar.path = path.cgPath
topProgressBar.path = path.cgPath
self.clipsToBounds = true
self.layer.cornerRadius = 4
}
现在我也想应用圆角半径,所以我在layoutSubviews
的底部添加了代码:
self.clipsToBounds = true
self.layer.cornerRadius = 4
这只将圆角半径应用于左上角和右上角部分,仅此而已。我不明白为什么。
我还尝试在 bottomProgressBar
和 topProgressBar
层的 configure
方法中设置它,但根本不起作用。
有人可以指出这里的问题吗?
编辑:
这是完整的 class 代码:
class ProgressBar: UIView {
var bottomProgressBar = CAShapeLayer()
var topProgressBar = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
func configure() {
bottomProgressBar.strokeColor = UIColor.white.cgColor
bottomProgressBar.opacity = 0.1
bottomProgressBar.lineWidth = self.frame.height
bottomProgressBar.strokeStart = 0
bottomProgressBar.strokeEnd = 1
self.layer.addSublayer(bottomProgressBar)
topProgressBar.strokeColor = UIColor.white.cgColor
topProgressBar.lineWidth = self.frame.height
topProgressBar.strokeStart = 0
topProgressBar.strokeEnd = 1
self.layer.addSublayer(topProgressBar)
animate(toValue: 1)
}
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: self.frame.width, y: 0))
bottomProgressBar.lineWidth = self.frame.height // Update lineWidth
topProgressBar.lineWidth = self.frame.height // Update lineWidth
bottomProgressBar.path = path.cgPath
topProgressBar.path = path.cgPath
}
func animate(toValue: Double) {
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = 1.0
animation.fromValue = 0
animation.toValue = toValue
topProgressBar.strokeEnd = CGFloat(toValue)
topProgressBar.animation(forKey: "animate")
}
}
编辑 2:
添加了更多有关正在发生的事情的屏幕截图:
这没有应用圆角半径 - 简单的矩形
这就是我应用圆角半径时发生的情况 - 下半部分被切掉(矩形显然被切掉一半)
这就是我想要的 - 简单的矩形,所有 4 个角的角半径为:
编辑: 明确最终目标后,这里有一个更简单的方法。您可以 运行 直接在游乐场页面中...
import UIKit
import PlaygroundSupport
class ProgressBar: UIView {
var progressView: UIView = {
let v = UIView()
v.backgroundColor = .white
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
func configure() {
backgroundColor = UIColor(white: 1.0, alpha: 0.1)
layer.cornerRadius = 8.0
clipsToBounds = true
progressView.frame = bounds
progressView.frame.size.width = 0.0
addSubview(progressView)
}
func animate(_ pct: CGFloat) -> Void {
UIView.animate(withDuration: 1.0, animations: {
self.progressView.frame.size.width = self.bounds.size.width * pct
})
}
}
class MyViewController : UIViewController {
var theButton: UIButton = {
let b = UIButton()
b.setTitle("Tap Me", for: .normal)
b.translatesAutoresizingMaskIntoConstraints = false
b.backgroundColor = .red
return b
}()
var myProgressBar = ProgressBar(frame: CGRect(x: 40, y: 300, width: 300, height: 30))
// on button tap, set the progress bar to 80%
@objc func didTap(_ sender: Any?) -> Void {
myProgressBar.animate(0.8)
}
override func loadView() {
let view = UIView()
self.view = view
view.backgroundColor = UIColor.purple
view.addSubview(theButton)
// constrain button to Top: 32 and centerX
NSLayoutConstraint.activate([
theButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 32.0),
theButton.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0.0),
])
// add an action for the button tap
theButton.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)
view.addSubview(myProgressBar)
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
点击按钮将使 "progress bar" 从零变为 80%
结果:
原回答:
首先:您要添加一个从 0,0
到 width,0
的 "line" ... 并且您要使它的 lineWidth 成为您视图的高度。
这给你这个结果:
黄色矩形是您的视图,白色矩形是您的 "line"。如您所见,直线的 center 沿着视图的 top 运行ning,因此上半部分线实际上在 视图框架之外。因此,当您对视图的角进行圆角处理时,看起来好像只有顶角被圆角化了。如果为视图设置背景颜色,您会看到底角也变圆了,但那里没有 "line"。
如果这是您真正想要的(顶部栏为白色,底部栏为绿色):
您想将线宽设置为视图高度的 1/2,并将线中心设置为 1/4 和 3/4:
class ProgressBar: UIView {
var bottomProgressBar = CAShapeLayer()
var topProgressBar = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
func configure() {
bottomProgressBar.strokeColor = UIColor.green.cgColor
bottomProgressBar.opacity = 1
bottomProgressBar.lineWidth = self.frame.height
bottomProgressBar.strokeStart = 0
bottomProgressBar.strokeEnd = 1
self.layer.addSublayer(bottomProgressBar)
topProgressBar.strokeColor = UIColor.white.cgColor
topProgressBar.lineWidth = self.frame.height
topProgressBar.strokeStart = 0
topProgressBar.strokeEnd = 1
self.layer.addSublayer(topProgressBar)
// animate(toValue: 1)
}
override func layoutSubviews() {
super.layoutSubviews()
var path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: self.frame.height * 0.25))
path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height * 0.25))
topProgressBar.path = path.cgPath
path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: self.frame.height * 0.75))
path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height * 0.75))
bottomProgressBar.path = path.cgPath
bottomProgressBar.lineWidth = self.frame.height / 2.0 // Update lineWidth
topProgressBar.lineWidth = self.frame.height / 2.0 // Update lineWidth
self.clipsToBounds = true
self.layer.cornerRadius = 8
}
func animate(toValue: Double) {
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = 3.0
animation.fromValue = 0
animation.toValue = toValue
topProgressBar.strokeEnd = CGFloat(toValue)
topProgressBar.animation(forKey: "animate")
}
}
当然,如果您只需要一个带圆角的矩形,那么您不需要任何子层...只需创建一个白色背景的视图,并设置其层的角半径...