UIButton的IBAction及其自己的.xib文件

时间:2018-09-06 16:18:49

标签: ios swift

我想在我的整个应用程序上创建一个可重复使用的按钮,并打算使用自己的.xib文件进行设计。问题是我无法将IBAction连接到使用它的控制器中的自定义按钮。

我创建了一个名为.xib的新SampleButton.xib文件,并添加了一个按钮。这是层次结构和视图的样子:

enter image description here

然后,我创建了一个名为SampleButton.swift的新快速文件,该文件名为SampleButton,该类是UIButton的子类,并将其分配为我的SampleButton.xib文件中的文件所有者。

SampleButton.swift的内容如下:

import Foundation
import UIKit

@IBDesignable
class SampleButton: UIButton {

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

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    func setup() {
        guard let view = loadViewFromNib() as? UIButton else {
            return
        }
        view.frame = bounds
        view.autoresizingMask = [UIView.AutoresizingMask.flexibleWidth,
                                 UIView.AutoresizingMask.flexibleHeight]
        addSubview(view)

        view.layer.borderWidth = 2
        view.layer.borderColor = UIColor.white.cgColor
    }

    func loadViewFromNib() -> UIView? {
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
        return nib.instantiate(withOwner: self, options: nil).first as? UIButton
    }

    @IBAction func pressed(_ sender: Any) {
        print("Called in here")
    }
}

然后,我可以在故事板中创建一个新按钮,并将其设置为custom,将类设置为SampleButton。但是现在,如果我按住Ctrl键并从我的按钮拖动到相应的View Controller来为该按钮创建IBAction,则不会调用它。 SampleButton.swift文件中的一个是。即使我删除了IBAction文件中的SampleButton,它仍然没有被调用。

这里有帮助吗?我希望能够分别设计按钮,然后在使用它们的控制器中为它们设置IBactions

4 个答案:

答案 0 :(得分:1)

我在一些自定义xib视图中遇到了相同的问题,最初的想法是我可以将xib设置为IBDesignable,然后从视图控制器中的故事板渲染按钮连接插座。

那没有用。

因此,我在自定义视图中使用委托回调设置了一些解决方法。我使用它们为视图控制器创建了用于视图的IBOutlet,然后在viewDidLoad中设置委托并处理视图控制器中的按钮点击

import UIKit

// defines a callback protocol for the SampleButtonView
protocol SampleButtonViewDelegate: class {
    func sampleButtonTapped(_ button: SampleButton)
}

@IBDesignable
class SampleButton: UIView, NibLoadable {

    // create IBOutlet to button if you want to register a target/action directly
    @IBOutlet var button: UIButton!

    // set delegate if you want to handle button taps via delegate
    weak var delegate: SampleButtonViewDelegate?

    // initializers to make it so this class renders in view controllers
    // when using IBDesignable
    convenience init() {
        self.init(frame: .zero)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        loadFromNib(owner: self)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        loadFromNib(owner: self)
    }

    @IBAction func buttonTapped(_ sender: Any) {
        delegate?.sampleButtonTapped(_ button: self)
    }
}

// here's a sample ViewController using this view and the delegate callback method
class ViewController: UIViewController {

    @IBOutlet var sampleButtonView: SampleButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        sampleButtonView.delegate = self
    }
}

extension ViewController: SampleButtonViewDelegate {
    func sampleButtonTapped(_ button: SampleButton) {
        // TODO: run logic for button tap here
    }
}

出于完整性考虑,我还将在此处添加此NibLoadable协议定义。

// I used this for the @IBDesignable functionality to work and actually render 
// my xib layouts in the storyboard view controller layouts using this class
import UIKit

/// Defines an interface for UIViews defined in .xib files.
public protocol NibLoadable {

    // the name of the associated nib file
    static var nibName: String { get }

    // loads the view from the nib
    func loadFromNib(owner: Any?)
}

public extension NibLoadable where Self: UIView {

    /// Specifies the name of the associated .xib file.
    /// Defaults to the name of the class implementing this protocol.
    /// Provide an override in your custom class if your .xib file has a different name than it's associated class.
    static var nibName: String {
        return String(describing: Self.self)
    }

    /// Provides an instance of the UINib for the conforming class.
    /// Uses the bundle for the conforming class and generates the UINib using the name of the .xib file specified in the nibName property.
    static var nib: UINib {
        let bundle = Bundle(for: Self.self)
        return UINib(nibName: Self.nibName, bundle: bundle)
    }

    /// Tries to instantiate the UIView class from the .xib file associated with the UIView subclass conforming to this protocol using the owner specified in the function call.
    /// The xib views frame is set to the size of the parent classes view and constraints are set to make the xib view the same size as the parent view. The loaded xib view is then added as a subview.
    /// This should be called from the UIView's initializers "init(frame: CGRect)" for instantiation in code, and "init?(coder aDecoder: NSCoder)" for use in storyboards.
    ///
    /// - Parameter owner: The file owner. Is usually an instance of the class associated with the .xib.
    func loadFromNib(owner: Any? = nil) {
        guard let view = Self.nib.instantiate(withOwner: owner, options: nil).first as? UIView else {
            fatalError("Error loading \(Self.nibName) from nib")
        }
        view.frame = self.bounds
        view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        addSubview(view)
    }
}

您还可以简单地将在视图控制器中定义的功能注册为自定义视图中按钮的目标/动作功能。

override func viewDidLoad() {
    super.viewDidLoad()

    mySampleButtonView.button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
}

@objc func buttonTapped(_ sender: UIButton) {
    // handle button tap action in view controller here...
}

答案 1 :(得分:1)

  1. 在笔尖类中创建按钮的iboutlet。
  2. 在所需的视图控制器中添加笔尖视图。
  3. 为按钮插座添加目标。

尝试以下代码:

override func viewDidLoad() {
    super.viewDidLoad()
let myButton = Bundle.main.loadNibNamed("myButtonxibName", owner: self, options: nil)?[0] as? myButtonxibClassName
myButton.button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
self.view.addsubview(myButton)
}
@objc func buttonTapped() {}

答案 2 :(得分:0)

您不需要Xib即可完成操作。从上面的类中删除loadViewFromNib()pressed(_ sender: Any)函数。更改您的setup()方法以自定义按钮。我看到您想为其添加边框。做这样的事情:

func setup() {
    self.layer.borderWidth = 2
    self.layer.borderColor = UIColor.white.cgColor
    // * Any other UI customization you want to do can be done here * //
}

在情节提要中,将常规UIButton拖放到任何要使用的地方,将属性检查器中的类设置为SampleButton,连接IBOutlet和{{1} },并且应该很好。

答案 3 :(得分:0)

我认为不可能做到这一点。比较简单的方法是在视图控制器中设置目标和操作。像这样:

class VC: UIViewController {
    func viewDidLoad() {
       sampleButton.addTarget(self, action: #selector(didClickOnSampleButton))
    }
}
相关问题