编辑:虽然我接受了一个帮助我在运行时正确绘制按钮的答案,但我还有其他问题。我怀疑,其根源是的问题,为什么给我的自定义按钮一个出口干扰它的绘制方式。我仍然需要知道为什么会这样。(见下面的答案)
我有自己的按钮类,它扩展了UIButton(见下文)并且有几个IBInspectable属性,如边框宽度/颜色,角半径,甚至渐变背景的开始/结束颜色。我还使用这些属性以编程方式设置图像和标题的插图,以便我可以考虑各种屏幕尺寸。
以前我遇到过一个问题,如果我改变故事板中的“查看为”设备,让我们说从iPhone SE到iPhone 7,然后刷新视图并在物理iPhone SE上运行它将部署的内容iPhone 7,而不是计算物理设备自己的屏幕大小的插图。但是,我几乎通过覆盖我的自定义按钮类中的draw
来解决此问题。
我现在的问题是被覆盖的draw
方法只被调用(或者似乎仅在有效时才有效)该按钮没有插入其所在的ViewController
类
例如:
* img是占位符;插图适当地适合真正的imgs。
右下方的按钮有正确绘制的渐变和角落,而其他3则没有。 我已经确认在此按钮上添加一个插座使其行为与其余按钮相同,并且从其他3个插槽中移除插座会导致其正确绘制。
作为参考,出口只是
@IBOutlet weak var button1: UIButton!
@IBOutlet weak var button2: UIButton!
@IBOutlet weak var button3: UIButton!
立即在class MyViewController: UIViewController {
声明。
另外,我没有忘记在每个按钮的界面构建器中设置自定义类。
以下是按钮类:
import UIKit
@IBDesignable
class ActionButton: UIButton {
override init(frame: CGRect){
super.init(frame: frame)
setupView()
}
required init(coder aDecoder: NSCoder){
super.init(coder: aDecoder)!
setupView()
}
@IBInspectable var cornerRadius: CGFloat = 0{
didSet{
setupView()
}
}
@IBInspectable var startColor: UIColor = UIColor.white{
didSet{
setupView()
}
}
@IBInspectable var endColor: UIColor = UIColor.black{
didSet{
setupView()
}
}
@IBInspectable var borderWidth: CGFloat = 0{
didSet{
self.layer.borderWidth = borderWidth
}
}
@IBInspectable var borderColor: UIColor = UIColor.clear{
didSet{
self.layer.borderColor = borderColor.cgColor
}
}
private func setupView(){
let colors:Array = [startColor.cgColor, endColor.cgColor]
gradientLayer.colors = colors
gradientLayer.cornerRadius = cornerRadius
gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
self.setNeedsDisplay()
}
var gradientLayer: CAGradientLayer{
return layer as! CAGradientLayer
}
override func draw(_ rect: CGRect) {
//Draw image
setupView()
let btnW = self.frame.size.width
let btnH = self.frame.size.height
//Compute and set image insets
let topBtnInset = btnH * (-5/81)
let bottomBtnInset = -4 * topBtnInset
let leftBtnInset = btnW / 4 //btnW * (35/141)
let rightBtnInset = leftBtnInset
self.imageEdgeInsets = UIEdgeInsets(top: topBtnInset, left: leftBtnInset, bottom: bottomBtnInset, right: rightBtnInset)
//Compute and set title insets
let topTitleInset = btnH * (47/81)
let bottomTitleInset = CGFloat(0)
let leftTitleInset = CGFloat(-256) //Image width
let rightTitleInset = CGFloat(0)
self.titleEdgeInsets = UIEdgeInsets(top: topTitleInset, left: leftTitleInset, bottom: bottomTitleInset, right: rightTitleInset)
//Draw text
//Default font size
self.titleLabel?.font = UIFont.boldSystemFont(ofSize: 15)
if(btnH > 81){
self.titleLabel?.font = UIFont.boldSystemFont(ofSize: 17)
}else if(btnH > 97){
self.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
}
}
override class var layerClass: AnyClass{
return CAGradientLayer.self
}
}
请忽略我处理插图,字体大小等方法。
有谁知道为什么指定按钮的插座使得它不能正确地绘制图层?
答案 0 :(得分:1)
有几点想法:
draw(_:)
方法用于描边路径,绘制图像等。它用于在给定时刻渲染视图。您不应该更新图层,UIKit子视图等。
在您的代码段中,您的draw(_:)
正在调用setupView
,然后调用setNeedsDisplay
(理论上可以触发draw(_:)
再次调用)。我建议仅为那些需要手动绘制的内容保留draw(_:)
。 (就像现在一样,你可能完全可以完全消除这种方法,因为这里没有任何东西可以吸引任何东西。)
对于子视图(其框架等)的任何手动配置,应移至layoutSubviews
,只要操作系统确定子视图可能需要frame
,就会调用该ActionButton
}值已调整。
如果您对可设计的视图有任何疑问,那么确定您将这些视图放在一个单独的目标中是值得的。它最大限度地减少了在IB中渲染时需要重新编译的内容。它还可以防止主要目标中的问题影响在IB中呈现这些可设计的能力。 (当Apple引入可设计的视图时,它们非常具体,应该将它们放在一个单独的目标中。)
我在Xcode中也有可设计视图的间歇性问题,但这些问题通常通过退出Xcode,清空派生数据文件夹并重新启动来解决。
我必须承认,我不知道为什么在可设计视图中添加插座会影响视图的呈现方式。我知道,当我做上述事情时,我无法重现你的问题。
无论如何,我这样调整你的@import UIKit
@IBDesignable
public class ActionButton: UIButton {
public override init(frame: CGRect = .zero) {
super.init(frame: frame)
setupView()
}
public required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
setupView()
}
@IBInspectable public var cornerRadius: CGFloat = 0 { didSet { setupView() } }
@IBInspectable public var startColor: UIColor = .white { didSet { setupView() } }
@IBInspectable public var endColor: UIColor = .black { didSet { setupView() } }
@IBInspectable public var borderWidth: CGFloat = 0 { didSet { layer.borderWidth = borderWidth } }
@IBInspectable public var borderColor: UIColor = .clear { didSet { layer.borderColor = borderColor.cgColor } }
private func setupView() {
let colors = [startColor.cgColor, endColor.cgColor]
gradientLayer.colors = colors
gradientLayer.cornerRadius = cornerRadius
gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
}
private var gradientLayer: CAGradientLayer {
return layer as! CAGradientLayer
}
public override func layoutSubviews() {
super.layoutSubviews()
let btnW = frame.width
let btnH = frame.height
//Compute and set image insets
let topBtnInset = btnH * (-5/81)
let bottomBtnInset = -4 * topBtnInset
let leftBtnInset = btnW / 4 //btnW * (35/141)
let rightBtnInset = leftBtnInset
imageEdgeInsets = UIEdgeInsets(top: topBtnInset, left: leftBtnInset, bottom: bottomBtnInset, right: rightBtnInset)
//Compute and set title insets
let topTitleInset = btnH * (47/81)
let bottomTitleInset = CGFloat(0)
let leftTitleInset = CGFloat(-256) //THIS MUST BE EQUAL TO -IMAGE WIDTH
let rightTitleInset = CGFloat(0)
titleEdgeInsets = UIEdgeInsets(top: topTitleInset, left: leftTitleInset, bottom: bottomTitleInset, right: rightTitleInset)
//Adjust text font
if btnH > 81 {
titleLabel?.font = .boldSystemFont(ofSize: 17)
} else if btnH > 97 {
titleLabel?.font = .boldSystemFont(ofSize: 20)
} else {
//Default font size
titleLabel?.font = .boldSystemFont(ofSize: 15)
}
}
public override class var layerClass: AnyClass {
return CAGradientLayer.self
}
}
,它似乎对我有用:
def f(x):
....
y = g(x)
z = tf.matmul(y, w) + b
return z
答案 1 :(得分:0)
我已经接受了Rob的回答,因为它更有可能被其他人使用,但我发现了我的问题,特别是有人遇到类似问题。强>
显然在我放置这些按钮的View Controller的代码中,我有一个我忘记的功能,它启用/禁用这些自定义按钮,以及设置它们的背景颜色(为纯色)。这就是为什么那些按钮看起来错了。第4个按钮从未受到此功能的影响。这不是其他3个有插座的事实,但当我移除插座时,我评论了对该功能的调用。
然而,在这个问题上解决了其他问题!
- 我有一个非常草率的UIButton实现,它覆盖并且不正确地调用UI函数,导致像旋转时冻结等问题。这些也在Rob的(已接受的)答案中得到了解决。
- Interface Builder中的“刷新所有视图”挂在“签名产品”上,这是由调用self.titleLabel?.font
引起的。根据Apple开发人员论坛上的some answers,在layoutSubViews()
中执行此操作很糟糕!