Ios IBInspectable和从xib为Interface Builder加载视图时出现问题

Ios IBInspectable和从xib为Interface Builder加载视图时出现问题,ios,interface-builder,ibdesignable,ibinspectable,Ios,Interface Builder,Ibdesignable,Ibinspectable,我有一个自定义类,它有自己的.xib。我正在尝试合并IBInspectable,以便在IB中更改其背景和子视图背景的颜色 在IB中,当我将'Custom Class'留空,并添加用户定义的运行时属性'bgColor',然后尝试运行该应用程序时,我得到“…该类与键bgColor的键值编码不兼容。”该应用程序运行时不会崩溃,但不会将该颜色应用于视图 当我将'Custom Class'设置为的“AxesView”并运行时,应用程序会因线程1:EXC_BAD_ACCESS(code=2,address)

我有一个自定义类,它有自己的.xib。我正在尝试合并IBInspectable,以便在IB中更改其背景和子视图背景的颜色

在IB中,当我将
'Custom Class'
留空,并添加用户定义的运行时属性
'bgColor'
,然后尝试运行该应用程序时,我得到
“…该类与键bgColor的键值编码不兼容。”
该应用程序运行时不会崩溃,但不会将该颜色应用于视图

当我将
'Custom Class'
设置为
的“AxesView”
并运行时,应用程序会因
线程1:EXC_BAD_ACCESS(code=2,address)
而崩溃,并在NibLoadableExtension.swift的
函数setupFromNib()
中突出显示行
“guard let view=Self.nib.instantiate…”

有没有办法让这些东西一起工作,或者这是一种非此即彼的情况

视图中的控制器:

var axesView = AxesView()
override func viewDidLoad() {
    super.viewDidLoad()
    axesView.frame = self.view.bounds
    self.view.addSubview(axesView)
}
全视图控制器。swift

var axesView = AxesView()

override func viewDidLoad() {
    super.viewDidLoad()
    axesView.frame = self.view.bounds

    axesView.bgColor = .clear
    axesView.lineColor = .red
    self.view.addSubview(axesView)
    axesView.contentView.translatesAutoresizingMaskIntoConstraints = false

    axesView.contentView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0).isActive = true
    axesView.contentView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0).isActive = true
    axesView.contentView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0).isActive = true
    axesView.contentView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touch = touches.first! as UITouch
    let p = touch.location(in: self.view)
    axesView.vLine.center.x = p.x
    axesView.hLine.center.y = p.y
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touch = touches.first! as UITouch
    let p = touch.location(in: self.view)
    axesView.vLine.center.x = p.x
    axesView.hLine.center.y = p.y
    self.view.setNeedsDisplay()
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    touchesMoved(touches, with: event)
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
    touchesEnded(touches, with: event)
}
@IBDesignable class AxesView: UIView, NibLoadable {

    @IBOutlet var contentView: UIView!
    @IBOutlet weak var hLine: UIView!
    @IBOutlet weak var vLine: UIView!

    @IBInspectable var bgColor: UIColor = UIColor.white {
        didSet {
            contentView.backgroundColor = bgColor
        }
    }

    @IBInspectable var lineColor: UIColor = UIColor.cyan {
        didSet {
            hLine.backgroundColor = lineColor
            vLine.backgroundColor = lineColor
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupFromNib()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupFromNib()
    }
}
public protocol NibLoadable {
    static var nibName: String { get }
}

public extension NibLoadable where Self: UIView {

    static var nibName: String {
        return String(describing: Self.self) // defaults to the name of the class implementing this protocol.
    }

    static var nib: UINib {
        let bundle = Bundle(for: Self.self)
        return UINib(nibName: Self.nibName, bundle: bundle)
    }

    func setupFromNib() {
        guard let view = Self.nib.instantiate(withOwner: self, options: nil).first as? UIView else { fatalError("Error loading \(self) from nib") }

        addSubview(view)
    }
}
NibLoadableExtension.swift

var axesView = AxesView()

override func viewDidLoad() {
    super.viewDidLoad()
    axesView.frame = self.view.bounds

    axesView.bgColor = .clear
    axesView.lineColor = .red
    self.view.addSubview(axesView)
    axesView.contentView.translatesAutoresizingMaskIntoConstraints = false

    axesView.contentView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0).isActive = true
    axesView.contentView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0).isActive = true
    axesView.contentView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0).isActive = true
    axesView.contentView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touch = touches.first! as UITouch
    let p = touch.location(in: self.view)
    axesView.vLine.center.x = p.x
    axesView.hLine.center.y = p.y
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touch = touches.first! as UITouch
    let p = touch.location(in: self.view)
    axesView.vLine.center.x = p.x
    axesView.hLine.center.y = p.y
    self.view.setNeedsDisplay()
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    touchesMoved(touches, with: event)
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
    touchesEnded(touches, with: event)
}
@IBDesignable class AxesView: UIView, NibLoadable {

    @IBOutlet var contentView: UIView!
    @IBOutlet weak var hLine: UIView!
    @IBOutlet weak var vLine: UIView!

    @IBInspectable var bgColor: UIColor = UIColor.white {
        didSet {
            contentView.backgroundColor = bgColor
        }
    }

    @IBInspectable var lineColor: UIColor = UIColor.cyan {
        didSet {
            hLine.backgroundColor = lineColor
            vLine.backgroundColor = lineColor
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupFromNib()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupFromNib()
    }
}
public protocol NibLoadable {
    static var nibName: String { get }
}

public extension NibLoadable where Self: UIView {

    static var nibName: String {
        return String(describing: Self.self) // defaults to the name of the class implementing this protocol.
    }

    static var nib: UINib {
        let bundle = Bundle(for: Self.self)
        return UINib(nibName: Self.nibName, bundle: bundle)
    }

    func setupFromNib() {
        guard let view = Self.nib.instantiate(withOwner: self, options: nil).first as? UIView else { fatalError("Error loading \(self) from nib") }

        addSubview(view)
    }
}

您的代码运行良好(大部分情况下)

确保在正确的对象上设置了类:

setupFromNib()
func更改为:

func setupFromNib() {
    guard let view = Self.nib.instantiate(withOwner: self, options: nil).first as? UIView else { fatalError("Error loading \(self) from nib") }

    addSubview(view)
    view.frame = self.bounds
    view.autoresizingMask = [.flexibleWidth, .flexibleHeight]

    // layout code...
}
以下是在IB中添加到视图控制器的外观:

下面是通过代码显示的情况:

class AxesTestViewController: UIViewController {

    var axesView = AxesView()

    override func viewDidLoad() {
        super.viewDidLoad()
        axesView.frame = self.view.bounds
        axesView.bgColor = .blue
        axesView.lineColor = .red
        self.view.addSubview(axesView)
    }

}


编辑

在评论中讨论后,这里是一种使用“可拖动”交叉线的方法来实现此
@IBDesignable
xib

这将使用约束并修改centerXcenterY上的
.constant
,以移动线。我还将您的
touch…
funcs移动到自定义视图中,以使事情更加有序

下面是完整的示例代码,包括
nibloable
代码(为了比较,我将控件重命名为
TapAxesView
):


编辑2

也许值得一试。。。仅通过代码查看自定义
@IBDesignable
视图。。。无需
xib
文件(或nib加载)。此外,它使用
CALayer
作为“交叉线”,而不是子视图。让它更轻一点

@IBDesignable
类LayerAxesView:UIView{
var hLine:CALayer=CALayer()
变量vLine:CALayer=CALayer()
var curX:CGFloat=-1.0
变量curY:CGFloat=-1.0
让线宽:CGFloat=4.0
@IBInspectable var bgColor:UIColor=UIColor.white{
迪塞特{
self.backgroundColor=bgColor
}
}
@IBInspectable var lineColor:UIColor=UIColor.cyan{
迪塞特{
hLine.backgroundColor=lineColor.cgColor
vLine.backgroundColor=lineColor.cgColor
}
}
重写初始化(帧:CGRect){
super.init(frame:frame)
commonInit()
}
必需的初始化?(编码器aDecoder:NSCoder){
super.init(编码者:aDecoder)
commonInit()
}
重写func prepareForInterfaceBuilder(){
commonInit()
}
func commonInit()->Void{
如果hLine.superlayer==nil{
layer.addSublayer(hLine)
图层。添加子图层(vLine)
hLine.backgroundColor=lineColor.cgColor
vLine.backgroundColor=lineColor.cgColor
背景颜色=背景颜色
}
}
覆盖func布局子视图(){
//如果curX和curY尚未设置,
//例如在init上或在故事板/IB中使用时,
//初始化到视图中心
如果curX==-1{
curX=bounds.midX
curY=bounds.midY
}
hLine.frame=CGRect(x:bounds.minX,y:curY-lineWidth*0.5,width:bounds.maxX,height:lineWidth)
vLine.frame=CGRect(x:curX-lineWidth*0.5,y:bounds.minY,width:lineWidth,height:bounds.maxY)
}
func updateCenter(u点:CGPoint)->Void{
//防止中心移动到边界之外
curX=max(最小值(bounds.maxX,point.x),bounds.minX)
curY=max(min(bounds.maxY,point.y),bounds.minY)
//禁用CALayer的内置动画
CATransaction.begin()
CATTransaction.setDisableActions(true)
setNeedsLayout()
layoutIfNeeded()
CATransaction.commit()
}
覆盖func TouchesBegind(Touchs:Set,带有事件:UIEvent?){
让touch=touch.first!作为UITouch
让p=触摸位置(in:self)
更新中心(p)
}
覆盖功能触摸移动(touchs:Set,带有事件:UIEvent?){
让touch=touch.first!作为UITouch
让p=触摸位置(in:self)
更新中心(p)
}
覆盖函数touchesend(touchs:Set,带有事件:UIEvent?){
触摸移动(触摸,带有:事件)
}
}
类TapAxesTestViewController:UIViewController{
var axesView:LayerAxesView={
设v=LayerAxesView()
v、 translatesAutoresizingMaskIntoConstraints=false
v、 lineColor=.red
v、 bgColor=UIColor.blue.带字母组件(0.5)
返回v
}()
重写func viewDidLoad(){
super.viewDidLoad()
self.view.addSubview(axesView)
//尊重安全区
设g=视图。安全区域布局指南
//用40分“填充”将axesView约束到安全区域
NSLayoutConstraint.activate([
AxeView.topAnchor.constraint(等式:g.topAnchor,常数:40.0),
AxeView.leadingAnchor.constraint(等式:g.leadingAnchor,常数:40.0),
AxeView.trailingAnchor.constraint(等式:g.trailingAnchor,常数:-40.0),
AxeView.bottomAnchor.constraint(等式:g.bottomAnchor,常数:-40.0),
])
}
}

我明白你的意思了。但是,当我把这个想法放在更大的范围内时,我遇到了很多不同的问题。我想我可能需要把我的问题扩大一点。开始比较好吗