我有一个协议扩展,它曾经在swift 2.2之前完美地工作。
现在我有一个警告告诉我使用新的#selector
,但如果我添加它
没有使用Objective-C Selector声明的方法。
我尝试在这几行代码中重现这个问题,可以很容易地复制并粘贴到游乐场中
protocol Tappable {
func addTapGestureRecognizer()
func tapGestureDetected(gesture:UITapGestureRecognizer)
}
extension Tappable where Self: UIView {
func addTapGestureRecognizer() {
let gesture = UITapGestureRecognizer(target: self, action:#selector(Tappable.tapGestureDetected(_:)))
addGestureRecognizer(gesture)
}
}
class TapView: UIView, Tappable {
func tapGestureDetected(gesture:UITapGestureRecognizer) {
print("Tapped")
}
}
还有一个建议在协议@objc
中附加到该方法,但是如果我这样做也要求我将它添加到实现它的类中,但是一旦我添加了类就不符合到协议了,因为它似乎没有看到协议扩展中的实现。
我该如何正确实现?
答案 0 :(得分:24)
我有类似的问题。这就是我所做的。
然后使用Self。在#selector。
@objc public protocol UpdatableUserInterfaceType {
optional func startUpdateUITimer()
optional var updateInterval: NSTimeInterval { get }
func updateUI(notif: NSTimer)
}
public extension UpdatableUserInterfaceType where Self: ViewController {
var updateUITimer: NSTimer {
return NSTimer.scheduledTimerWithTimeInterval(updateInterval, target: self, selector: #selector(Self.updateUI(_:)), userInfo: nil, repeats: true)
}
func startUpdateUITimer() {
print(updateUITimer)
}
var updateInterval: NSTimeInterval {
return 60.0
}
}
答案 1 :(得分:16)
您可以创建一个属性作为选择器...示例:
protocol Tappable {
var selector: Selector { get }
func addTapGestureRecognizer()
}
extension Tappable where Self: UIView {
func addTapGestureRecognizer() {
let gesture = UITapGestureRecognizer(target: self, action: selector)
addGestureRecognizer(gesture)
}
}
class TapView: UIView, Tappable {
var selector = #selector(TapView.tapGestureDetected(_:))
func tapGestureDetected(gesture:UITapGestureRecognizer) {
print("Tapped")
}
}
错误停止显示,并且不需要使用@objc装饰器设置协议和类。
这个解决方案不是最优雅的,但直到现在看起来还不错。
答案 2 :(得分:7)
这个答案与Bruno Hecktheuers完全相似,但不是让每个想要符合“Tappable”协议的人都实现变量“selector”,我们选择将它作为参数传递给addTapGestureRecognizer函数:
protocol Tappable {
func addTapGestureRecognizer(selector selector: Selector)
func tapGestureDetected(gesture:UITapGestureRecognizer)
}
extension Tappable where Self: UIView {
func addTapGestureRecognizer(selector selector: Selector)
let gesture = UITapGestureRecognizer(target: self, action: selector)
addGestureRecognizer(gesture)
}
}
class TapView: UIView, Tappable {
func tapGestureDetected(gesture:UITapGestureRecognizer) {
print("Tapped")
}
}
然后只需将选择器传递到任何地方:
addTapGestureRecognizer(selector: #selector(self.tapGestureDetected(_:)))
通过这种方式,我们避免让实现此协议的人必须实现选择器变量,并且我们也避免使用“@objc”将此协议标记为每个人。像这种方法的感觉不那么臃肿。
答案 3 :(得分:4)
这是一个使用Swift 3的工作示例。它使用标准的Swift协议,无需任何@objc
装饰和私有扩展来定义回调函数。
protocol PlayButtonPlayable {
// be sure to call addPlayButtonRecognizer from viewDidLoad or later in the display cycle
func addPlayButtonRecognizer()
func handlePlayButton(_ sender: UITapGestureRecognizer)
}
fileprivate extension UIViewController {
@objc func _handlePlayButton(_ sender: UITapGestureRecognizer) {
if let playable = self as? PlayButtonPlayable {
playable.handlePlayButton(sender)
}
}
}
fileprivate extension Selector {
static let playTapped =
#selector(UIViewController._handlePlayButton(_:))
}
extension PlayButtonPlayable where Self: UIViewController {
func addPlayButtonRecognizer() {
let playButtonRecognizer = UITapGestureRecognizer(target: self, action: .playTapped)
playButtonRecognizer.allowedPressTypes = [ NSNumber(value: UIPressType.playPause.rawValue as Int) ]
view.addGestureRecognizer(playButtonRecognizer)
}
}
答案 4 :(得分:0)
我碰巧在侧栏看到这个,我最近遇到了同样的问题..不幸的是,由于Objective-C运行时限制你不能在协议扩展上使用@objc,我相信这个问题在今年年初就已经关闭了。 / p>
问题出现是因为在协议一致性之后添加了扩展,因此无法保证满足协议的一致性。也就是说,有可能从一个子类NSObject并符合协议的任何东西中调用方法作为选择器。这通常是通过授权完成的。
这意味着您可以创建一个符合协议的空包装子类,并使用包装器从包装器中定义的协议调用其方法,协议中的任何其他未定义方法都可以传递给委托。还有其他类似的解决方案使用具体类的私有扩展,如UIViewController,并定义一个调用协议方法的方法,但这些方法也绑定到一个特定的类,而不是特定类的默认实现,恰好发生在符合协议。
意识到您正在尝试实现协议函数的默认实现,该函数使用其自己的协议函数来为其自己的实现定义值。噢!
协定:强>
public protocol CustomViewDelegate {
func update()
func nonDelegatedMethod()
}
查看:强>
使用委托,并定义一个包装器方法来安全地解包委托的方法。
class CustomView: UIView {
let updateButton: UIButton = {
let button = UIButton(frame: CGRect(origin: CGPoint(x: 50, y: 50), size: CGSize(width: 150, height: 50)))
button.backgroundColor = UIColor.lightGray
button.addTarget(self, action: #selector(doDelegateMethod), for: .touchUpInside)
return button
}()
var delegate:CustomViewDelegate?
required init?(coder aDecoder: NSCoder) {
fatalError("Pew pew, Aghh!")
}
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(updateButton)
}
@objc func doDelegateMethod() {
if delegate != nil {
delegate!.update()
} else {
print("Gottfried: I wanted to be a brain surgeon, but I had a bad habit of dropping things")
}
}
}
<强>的ViewController:强>
使视图控制器符合视图的委托:并实现协议的方法。
class ViewController: UIViewController, CustomViewDelegate {
let customView = CustomView(frame: CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 200, height: 200)))
override func viewDidLoad() {
super.viewDidLoad()
customView.backgroundColor = UIColor.red
customView.delegate = self //if delegate is not set, the app will not crash
self.view.addSubview(customView)
}
// Protocol -> UIView Button Action -> View Controller's Method
func update() {
print("Delegating work from View that Conforms to CustomViewDelegate to View Controller")
}
//Protocol > View Controller's Required Implementation
func nonDelegatedMethod() {
//Do something else
}
}
请注意,视图控制器只需要符合委托,并且没有设置视图的某些属性的选择器,这会将视图(及其协议)与视图控制器分开。
您已经拥有一个名为TapView的UIView,它继承自UIView和Tappable,因此您的实现可能是:
协定:强>
protocol TappableViewDelegate {
func tapGestureDetected(gesture:UITapGestureRecognizer)
}
<强> TappableView:强>
class TappableView: UIView {
var delegate:TappableViewDelegate?
required init?(coder aDecoder: NSCoder) {
fatalError("Pew pew, Aghh!")
}
override init(frame: CGRect) {
super.init(frame: frame)
let gesture = UITapGestureRecognizer(target: self, action: #selector(doDelegateMethod(gesture:)))
addGestureRecognizer(gesture)
}
@objc func doDelegateMethod(gesture:UITapGestureRecognizer) {
if delegate != nil {
delegate!.tapGestureDetected(gesture: gesture)
} else {
print("Gottfried: I wanted to be a brain surgeon, but I had a bad habit of dropping things")
}
}
}
<强>的ViewController:强>
class ViewController: UIViewController, TappableViewDelegate {
let tapView = TappableView(frame: CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 200, height: 200)))
override func viewDidLoad() {
super.viewDidLoad()
tapView.backgroundColor = UIColor.red
tapView.delegate = self
self.view.addSubview(tapView)
}
func tapGestureDetected(gesture: UITapGestureRecognizer) {
print("User did tap")
}
}