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

时间:2016-12-16 14:11:47

标签: ios objective-c swift swift3

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

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

因此UILabelUIControl的协议实现就像这样简单:

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

这很好用,我可以访问并将Swift中的highlighted属性设置为Highlightable个实例。但是,当我尝试在我的Swift类上实现协议时,即使是最简单的实现,如下所示:

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

我收到此编译错误:

  

Objective-C方法'突出显示'由getter提供以突出显示'与要求的选择器不匹配(' 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
}

2 个答案:

答案 0 :(得分:1)

您的UILabelUIControl扩展程序符合您创建协议的方式,因为它们已经有一个名为highlighted的属性,其getter访问器方法为isHighlighted

您的HighlightableView无法满足您Highlightable协议的要求,因为您对获取者有@objc(isHighlighted)要求。

您必须使用计算属性来满足此要求。但是,这意味着您还需要highlighted属性的后备存储。类似于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中进行一些测试),UILabelUIControl的{​​{1}}属性以前被命名为isHighlighted。在链接iOS SDK 9.3或更低版本时使用上面的代码将导致编译时错误。

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

在第二个示例中,可以通过将label.highlighted的所有实例重命名为isHighlighted来修复这些错误(highlighted属性括号内除外)。

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

答案 1 :(得分:0)

我有一个解决方案,但它比你想要的要大,但我认为它会对你有帮助。

您应该执行swift协议并确定所需类的默认实现

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

extension Highlightable where Self: UILabel {
    var highlighted: Bool {
        get {
            return isHighlighted
        }
        set {
            isHighlighted = newValue
        }
    }
}

extension Highlightable where Self: UIControl {
    var highlighted: Bool {
        get {
            return isHighlighted
        }
        set {
            isHighlighted = newValue
        }
    }
}

所以你可以实现它

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

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

我希望它可以帮到你。