Swift协议,IBOutlet属性不能具有非对象类型

18

我想在IB中连接自定义的Swift委托。该委托是一个用Swift实现特定协议的对象。

protocol ThumbnailTableViewCellDelegate {
    func cellWasTouched(thumbnail: Bool, cell: UITableViewCell)
}

class ThumbnailTableViewCell: UITableViewCell {
    @IBOutlet var thumbnailTableViewCellDelegate: ThumbnailTableViewCellDelegate?
}

不幸的是,编译器报错:

error: 'IBOutlet' property cannot have non-object type 'ThumbnailTableViewCellDelegate'
    @IBOutlet var thumbnailTableViewCellDelegate: ThumbnailTableViewCellDelegate?
    ^~~~~~~~~

3
没问题 - 你说的是一个符合该协议的对象。 - Nate Cook
7个回答

38
你需要将你的ThumbnailTableViewCellDelegate协议声明为@objc:
@objc protocol ThumbnailTableViewCellDelegate {
    func cellWasTouched(thumbnail: Bool, cell: UITableViewCell)
}

这是因为@IBOutlet将变量声明为weak,只能与对象一起使用。我不确定为什么不能只说协议符合AnyObject,也许这是Swift的一个bug。


1
这个方法对我不再起作用了(使用Xcode 6的GM种子版本)。 - Ash
9
这段代码可以编译通过,但我在尝试在Storyboard中连接插座时出现了问题。 - Adam Waite
是的,Xcode 6.2上也是这样...有人解决了吗? - Alex Bush
您可以使用Morten J提供的解决方法,或者在代码中手动设置委托代理,而不是在故事板中设置。如果您还没有对ThumbnailTableViewCell的引用,您需要首先创建一个outlet。 - Phocacius
十年后,注意这现在是正确且唯一的解决方案。它已经很普遍,并且百分之百地有效。 - Fattie
显示剩余2条评论

13

您可以通过以下方法使用此解决方法将自己的协议连接到IB。这是Xcode已知的问题,所以有一天可能会解决。在那之前:

  1. 将代理声明为 AnyObject - @IBOutlet var delegate:AnyObject!
  2. 在Interface Builder中连接代理
  3. 更改输出口的类型为您的协议,例如@IBOutlet var delegate:MyDelegate

这对我很有效。


这个问题有没有已知的雷达或发布说明文档记录? - Victor Jalencas
注意,十年后不要这样做 - Fattie

1

虽然不是理想的选择,但可以尝试类似以下的做法:

@IBOutlet var objectType: NSObject!

private var conformingObject: SomeProtocol {
  return objectType as SomeProtocol
}

一定要确保你的objectType符合SomeProtocol,否则会出现问题。


0
一个协议类型的变量可能不是一个对象,因为结构体和枚举也可以符合协议。为了确保协议只能由类来符合,您可以使用@class_protocol声明协议。

0

IB 要求使用 AnyObject,而不是你特定的协议,这有点说得通。你想要连接的对象可能符合该协议,但也可能不符合,而且该协议可能包含可选项 - 因此:

将你的协议定义为如下形式:

@objc public protocol HexViewDataSource: NSObjectProtocol {
    @objc optional func dataAtOffset (_ hexView: HexView, offset: UInt64, length: Int)-> Data?
    @objc optional func dataLength (_ hexView: HexView) -> UInt64
}

例如,在您的类中这样声明:

@IBOutlet weak open var dataSource: AnyObject?

当你使用它时,请检查它是否符合协议并且可选项存在 - 就像这样:

if let dataSource = dataSource as? HexViewDataSource, let dfr = dataSource.dataAtOffset {
    setRowData(offset: offset, data: dfr (self, offset, bytesPerRow))
}

0

关于2023年...

您只需在协议定义之前添加@objc即可。

不需要对视图控制器进行任何更改

示例:

@objc protocol VideoControls {
    var playButton: UIButton! { get }
    var pauseButton: UIButton! { get }
    var timeText: UILabel! { get }

然后在视图控制器中...

class CompactPlayer: UIViewController, VideoControls {
    @IBOutlet var playButton: UIButton!
    @IBOutlet var pauseButton: UIButton!
    @IBOutlet var timeText: UILabel!

并且

class FullScreenPlayer: UIViewController, VideoControls {
    @IBOutlet var playButton: UIButton!
    @IBOutlet var pauseButton: UIButton!
    @IBOutlet var timeText: UILabel!

就是这样。


-2

IBOutlets用于指示指向存储在nib(或storyboard)文件中的对象的指针。协议不是一个对象,因此您不能在nib文件中拥有一个协议。将IBOutlet变量的类型设置为您在nib中实际拥有的对象的类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接