我想在我的应用程序中创建一个可重复使用的按钮,并计划使用它自己的.xib
文件进行设计。问题是我无法将IBAction
连接到使用它的控制器中的自定义按钮。
我创建了一个名为.xib
的新SampleButton.xib
文件,并添加了一个按钮。这是层次结构和视图的样子:
然后我创建了一个名为SampleButton.swift
的新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")
}
}
然后我可以在我的故事板中创建一个新按钮并将其设置为自定义,并将该类设置为SampleButton
。但是现在如果我按住+从我的按钮拖动到相应的视图控制器为按钮创建一个IBAction
,它就不会被调用。 SampleButton.swift
文件中的那个是。即使我删除了IBAction
文件中的SampleButton
,它仍然没有被调用。
这里有什么帮助?我希望能够单独设计按钮,然后在控制器中使用IBactions
。
我的一些自定义xib视图遇到了同样的问题,我最初的想法是我可以将我的xib设置为IBDesignable,然后在视图控制器中连接我的按钮的故事板渲染中的插座。
那没用。
所以我使用自定义视图中的委托回调设置了一些变通方法。我使用它们为视图控制器创建了IBOutlets视图,然后在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...
}
尝试以下代码:
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() {}
您不需要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
和IBAction
s,这应该是好的。
我认为不可能这样做。更简单的方法是在视图控制器中设置目标和操作。就像是:
class VC: UIViewController {
func viewDidLoad() {
sampleButton.addTarget(self, action: #selector(didClickOnSampleButton))
}
}