ios – 使用Objective-C属性getter实现Swift协议

我想为
Swift中所有可突出显示的视图创建一个共同的祖先.我希望现有的UIKit类实现突出显示的属性是开箱即用的,所以在阅读
this answer并检查
the Objective-C getter is defined as isHighlighted之后,我将协议定义更改为:

@objc protocol Highlightable {
    var highlighted: Bool { @objc(isHighlighted) get set }
}

所以UILabel和UIControl的协议实现就像这样简单:

extension UILabel: Highlightable {}
extension UIControl: Highlightable {}

这很好用,我可以从Swift访问并设置突出显示的属性作为Highlightable实例.但是,当我尝试在我的Swift类上实现协议时,即使是最简单的实现,如下所示:

class HighlightableView: UIView, Highlightable {
    var highlighted: Bool = false
}

我得到这个编译错误:

Objective-C method ‘highlighted’ provided by getter for ‘highlighted’ does not match the requirement’s selector (‘isHighlighted’)

我能让它工作的唯一方法是使用计算属性,但这不是我想要的.

class HighlightableView: UIView, Highlightable {
    var highlighted: Bool { @objc(isHighlighted) get { return true } set {} }
}

我的环境是Xcode 8.0和Swift 3.更新到XCode 8.2并且错误仍然存​​在.

我目前的解决方法是完全避免Objective-C和Swift之间的任何命名冲突.但这远非理想:

@objc protocol Highlightable {
    var _highlighted: Bool { get set }
}

extension UILabel: Highlightable {
    var _highlighted: Bool {
        get { return isHighlighted }
        set { isHighlighted = newValue }
    }
}
extension UIControl: Highlightable {
    var _highlighted: Bool {
        get { return isHighlighted }
        set { isHighlighted = newValue }
    }
}
class HighlightableView: UIView, Highlightable {
    var _highlighted: Bool = false
}

最佳答案 您的UILabel和UIControl扩展符合您创建协议的方式,因为它们已经有一个名为highlight的属性,其getter访问器方法是突出显示的.

您的HighlightableView不满足您的Highlightable协议的采用,因为您的getter上有@objc(isHighlighted)要求.

您必须使用计算属性来满足此要求.但是,这意味着您还需要突出显示的属性的后备存储.像private var _highlighted = false之类的东西.

在您的情况下,由于这是不合需要的,您可以删除协议上的@objc属性.

protocol Highlightable: class {
    var highlighted: Bool { get set }
}

extension UILabel: Highlightable { }
extension UIControl: Highlightable { }

class HighlightableView: UIView, Highlightable {
    var highlighted = false
}

let label = UILabel()
label.isHighlighted // Prior to iOS 10, this property is called "highlighted"

let view = HighlightableView()
view.highlighted

let highlightables: [Highlightable] = [ label, view ]

for highlightable in highlightables {
    print(highlightable.highlighted)
}

// Prints:
// false
// false

但是,属性名称在具体类型中不一致.

这是另一种方法:

@objc protocol Highlightable: class {
    var isHighlighted: Bool { @objc(isHighlighted)get @objc(setHighlighted:)set }
}

extension UILabel: Highlightable { }
extension UIControl: Highlightable { }

class HighlightableView: UIView, Highlightable {
    private var _isHighlighted = false
    var isHighlighted: Bool {
        @objc(isHighlighted) get {
            return _isHighlighted
        }
        @objc(setHighlighted:) set {
            _isHighlighted = newValue
        }
    }
}

let label = UILabel()
label.isHighlighted = true

let view = HighlightableView()
view.isHighlighted

let highlightables: [Highlightable] = [ label, view ]

for highlightable in highlightables {
    print(highlightable.isHighlighted)
}

// Prints:
// false
// false

这会在所有具体类型中公开一致的isHighlighted属性,同时仍符合Highlightable.这里的缺点是@objc属性在不需要的上下文中更为普遍.也就是说,@ objc属性不用于将Swift协议暴露给Objective-C代码.

编辑:

查看Swift的iOS 10 API差异(并在Xcode 7.2中进行一些测试),UILabel和UIControl的isHighlighted属性以前被命名为突出显示.在链接iOS SDK 9.3或更低版本时使用上面的代码将导致编译时错误.

在第一个示例中,可以通过将label.isHighlighted行重命名为label.highlighted来修复这些错误.

在第二个示例中,可以通过将isHighlighted的所有实例重命名为突出显示来修复这些错误(除了@objc属性括号内的那些实例).

9.3到iOS 10.0 API差异:https://developer.apple.com/library/content/releasenotes/General/iOS10APIDiffs/index.html

点赞