Быстрый протокол, свойство IBOutlet не может иметь не-объектный тип

Я хотел бы подключить пользовательский быстрый делегат в IB. Делегат - это объект, который реализует определенный протокол в 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?
    ^~~~~~~~~

7 ответов

Решение

Вы должны объявить свой ThumbnailTableViewCellDelegate протокол как @objc:

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

Это потому что @IBOutlet объявляет переменную как weak, который работает только с объектами. Я не уверен, почему вы не можете просто сказать, что протокол соответствует AnyObjectвозможно это ошибка Swift.

Вы можете подключить свои собственные протоколы в IB с этим обходным путем. Это известная проблема с Xcode, поэтому, вероятно, будет решена однажды. До тех пор:

  1. Объявите делегата как AnyObject - @IBOutlet var делегат:AnyObject!
  2. Подключите делегата в Интерфейсном Разработчике
  3. Измените тип розетки на ваш протокол, например, @IBOutlet var делегат:MyDelegate

Это работает для меня.

Не идеально, но есть вариант сделать что-то вроде этого:

@IBOutlet var objectType: NSObject!

private var conformingObject: SomeProtocol {
  return objectType as SomeProtocol
}

Должен убедиться, что ваш objectType соответствует SomeProtocol или вещи взорвутся

Переменная типа протокола может не быть объектом, потому что структуры и перечисления также могут соответствовать протоколам. Чтобы протокол мог соответствовать только классам, вы можете объявить протокол с помощью @class_protocol,

На 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!

Вот и все.

В некотором смысле имеет смысл, что 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))
}

IBOutlets должны указывать указатель на объект, сохраненный в файле пера (или раскадровки). Протокол не является объектом, поэтому вы не можете иметь его в файле пера. Сделайте тип IBOutlet var типом реального объекта, который у вас есть в наконечнике.

Другие вопросы по тегам