我想在我的整个应用程序上创建一个可重复使用的按钮,并打算使用自己的.xib
文件进行设计。问题是我无法将IBAction
连接到使用它的控制器中的自定义按钮。
我创建了一个名为.xib
的新SampleButton.xib
文件,并添加了一个按钮。这是层次结构和视图的样子:
然后,我创建了一个名为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
。
答案 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)
尝试以下代码:
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))
}
}