自定义 UIButton 的边缘在与它交互后看起来像素化
Custom UIButton's edges look pixelated after interacting with it
我创建了一个带有圆角、渐变背景和阴影的自定义 UIButton 子class。启动应用程序后,按钮看起来正常并且立即抗锯齿,但如果我按下一个按钮,它的边缘就会像素化。
我尝试了很多方法,例如在按钮层上设置 .allowsEdgeAntialiasing = true 或从 "Highlighted" setter 中删除缩放变换动画等,但没有任何帮助:(
这是我的按钮 class:
@IBDesignable class CircleTintedButton: UIButton {
@IBInspectable var cornerRadius : CGFloat = 1.0
@IBInspectable var shadowOffsetWidth: CGFloat = 0.0
@IBInspectable var shadowOffsetHeight: CGFloat = 2.0
@IBInspectable var shadowColor : UIColor = UIColor.gray
@IBInspectable var shadowOpacity: CGFloat = 0.3
@IBInspectable var startColor: UIColor = .blue {
didSet {
setNeedsLayout()
}
}
@IBInspectable var endColor: UIColor = .green {
didSet {
setNeedsLayout()
}
}
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = cornerRadius
layer.shadowColor = shadowColor.cgColor
layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight)
let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
layer.shadowPath = shadowPath.cgPath
layer.shadowOpacity = Float(shadowOpacity)
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.6)
gradientLayer.locations = [0, 1]
gradientLayer.frame = bounds
gradientLayer.cornerRadius = cornerRadius
gradientLayer.masksToBounds = true
layer.insertSublayer(gradientLayer, below: self.titleLabel?.layer)
}
override var isHighlighted: Bool {
get {
return super.isHighlighted
}
set {
let xScale : CGFloat = newValue ? 1.025 : 1.0
let yScale : CGFloat = newValue ? 1.1 : 1.0
UIView.animate(withDuration: 0.1) {
let transformation = CGAffineTransform(scaleX: xScale, y: yScale)
self.transform = transformation
}
super.isHighlighted = newValue
}
}
}
我的测试设备的一些屏幕截图 (iPhone 7 @ 12.1.2):
应用启动后:
https://vinishko.party/files/ok.jpg
按下此按钮后:
https://vinishko.party/files/aliased.jpg
已经花了一整天的时间来解决这个问题,请帮助我:D
谢谢
每次调用 layoutSubviews()
时,您都在添加 另一个 渐变层。您可以通过为按钮添加 @IBAction
来确认这一点:
@IBAction func didTap(_ sender: Any) {
if let b = sender as? CircleTintedButton {
let n = b.layer.sublayers?.count
print("nunLayers: \(String(describing: n))")
}
}
您会看到子层数随着每次点击而增加。
添加渐变层作为自定义按钮的 var / 属性,然后只添加一次:
@IBDesignable class CircleTintedButton: UIButton {
@IBInspectable var cornerRadius : CGFloat = 1.0
@IBInspectable var shadowOffsetWidth: CGFloat = 0.0
@IBInspectable var shadowOffsetHeight: CGFloat = 2.0
@IBInspectable var shadowColor : UIColor = UIColor.gray
@IBInspectable var shadowOpacity: CGFloat = 0.3
// add this var / property
private var gradLayer: CAGradientLayer?
@IBInspectable var startColor: UIColor = .blue {
didSet {
setNeedsLayout()
}
}
@IBInspectable var endColor: UIColor = .green {
didSet {
setNeedsLayout()
}
}
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = cornerRadius
layer.shadowColor = shadowColor.cgColor
layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight)
let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
layer.shadowPath = shadowPath.cgPath
layer.shadowOpacity = Float(shadowOpacity)
// only create / add the gradient layer once
if gradLayer == nil {
let gradientLayer = CAGradientLayer()
gradientLayer.locations = [0, 1]
gradientLayer.masksToBounds = true
layer.insertSublayer(gradientLayer, below: self.titleLabel?.layer)
self.gradLayer = gradientLayer
}
gradLayer?.colors = [startColor.cgColor, endColor.cgColor]
gradLayer?.startPoint = CGPoint(x: 0.0, y: 0.0)
gradLayer?.endPoint = CGPoint(x: 1.0, y: 0.6)
gradLayer?.frame = bounds
gradLayer?.cornerRadius = cornerRadius
// original code
// let gradientLayer = CAGradientLayer()
// gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
// gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
// gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.6)
//
// gradientLayer.locations = [0, 1]
// gradientLayer.frame = bounds
// gradientLayer.cornerRadius = cornerRadius
// gradientLayer.masksToBounds = true
//
// layer.insertSublayer(gradientLayer, below: self.titleLabel?.layer)
}
override var isHighlighted: Bool {
get {
return super.isHighlighted
}
set {
let xScale : CGFloat = newValue ? 1.025 : 1.0
let yScale : CGFloat = newValue ? 1.1 : 1.0
UIView.animate(withDuration: 0.1) {
let transformation = CGAffineTransform(scaleX: xScale, y: yScale)
self.transform = transformation
}
super.isHighlighted = newValue
}
}
}
我创建了一个带有圆角、渐变背景和阴影的自定义 UIButton 子class。启动应用程序后,按钮看起来正常并且立即抗锯齿,但如果我按下一个按钮,它的边缘就会像素化。
我尝试了很多方法,例如在按钮层上设置 .allowsEdgeAntialiasing = true 或从 "Highlighted" setter 中删除缩放变换动画等,但没有任何帮助:(
这是我的按钮 class:
@IBDesignable class CircleTintedButton: UIButton {
@IBInspectable var cornerRadius : CGFloat = 1.0
@IBInspectable var shadowOffsetWidth: CGFloat = 0.0
@IBInspectable var shadowOffsetHeight: CGFloat = 2.0
@IBInspectable var shadowColor : UIColor = UIColor.gray
@IBInspectable var shadowOpacity: CGFloat = 0.3
@IBInspectable var startColor: UIColor = .blue {
didSet {
setNeedsLayout()
}
}
@IBInspectable var endColor: UIColor = .green {
didSet {
setNeedsLayout()
}
}
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = cornerRadius
layer.shadowColor = shadowColor.cgColor
layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight)
let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
layer.shadowPath = shadowPath.cgPath
layer.shadowOpacity = Float(shadowOpacity)
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.6)
gradientLayer.locations = [0, 1]
gradientLayer.frame = bounds
gradientLayer.cornerRadius = cornerRadius
gradientLayer.masksToBounds = true
layer.insertSublayer(gradientLayer, below: self.titleLabel?.layer)
}
override var isHighlighted: Bool {
get {
return super.isHighlighted
}
set {
let xScale : CGFloat = newValue ? 1.025 : 1.0
let yScale : CGFloat = newValue ? 1.1 : 1.0
UIView.animate(withDuration: 0.1) {
let transformation = CGAffineTransform(scaleX: xScale, y: yScale)
self.transform = transformation
}
super.isHighlighted = newValue
}
}
}
我的测试设备的一些屏幕截图 (iPhone 7 @ 12.1.2):
应用启动后: https://vinishko.party/files/ok.jpg
按下此按钮后: https://vinishko.party/files/aliased.jpg
已经花了一整天的时间来解决这个问题,请帮助我:D 谢谢
每次调用 layoutSubviews()
时,您都在添加 另一个 渐变层。您可以通过为按钮添加 @IBAction
来确认这一点:
@IBAction func didTap(_ sender: Any) {
if let b = sender as? CircleTintedButton {
let n = b.layer.sublayers?.count
print("nunLayers: \(String(describing: n))")
}
}
您会看到子层数随着每次点击而增加。
添加渐变层作为自定义按钮的 var / 属性,然后只添加一次:
@IBDesignable class CircleTintedButton: UIButton {
@IBInspectable var cornerRadius : CGFloat = 1.0
@IBInspectable var shadowOffsetWidth: CGFloat = 0.0
@IBInspectable var shadowOffsetHeight: CGFloat = 2.0
@IBInspectable var shadowColor : UIColor = UIColor.gray
@IBInspectable var shadowOpacity: CGFloat = 0.3
// add this var / property
private var gradLayer: CAGradientLayer?
@IBInspectable var startColor: UIColor = .blue {
didSet {
setNeedsLayout()
}
}
@IBInspectable var endColor: UIColor = .green {
didSet {
setNeedsLayout()
}
}
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = cornerRadius
layer.shadowColor = shadowColor.cgColor
layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight)
let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
layer.shadowPath = shadowPath.cgPath
layer.shadowOpacity = Float(shadowOpacity)
// only create / add the gradient layer once
if gradLayer == nil {
let gradientLayer = CAGradientLayer()
gradientLayer.locations = [0, 1]
gradientLayer.masksToBounds = true
layer.insertSublayer(gradientLayer, below: self.titleLabel?.layer)
self.gradLayer = gradientLayer
}
gradLayer?.colors = [startColor.cgColor, endColor.cgColor]
gradLayer?.startPoint = CGPoint(x: 0.0, y: 0.0)
gradLayer?.endPoint = CGPoint(x: 1.0, y: 0.6)
gradLayer?.frame = bounds
gradLayer?.cornerRadius = cornerRadius
// original code
// let gradientLayer = CAGradientLayer()
// gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
// gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
// gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.6)
//
// gradientLayer.locations = [0, 1]
// gradientLayer.frame = bounds
// gradientLayer.cornerRadius = cornerRadius
// gradientLayer.masksToBounds = true
//
// layer.insertSublayer(gradientLayer, below: self.titleLabel?.layer)
}
override var isHighlighted: Bool {
get {
return super.isHighlighted
}
set {
let xScale : CGFloat = newValue ? 1.025 : 1.0
let yScale : CGFloat = newValue ? 1.1 : 1.0
UIView.animate(withDuration: 0.1) {
let transformation = CGAffineTransform(scaleX: xScale, y: yScale)
self.transform = transformation
}
super.isHighlighted = newValue
}
}
}