У меня есть протокол:
@objc protocol SomeProtocol { }
что я распространяю на экземпляры UIViewController. В этом расширении я хочу создать и добавить кнопку, селектор которой также определен в протоколе:
extension SomeProtocol where Self: UIViewController {
func addSomeButton() {
let someButton = UIButton()
someButton.addTarget(self, #selector(someButtonPressed), for: .touchUpInside)
view.addSubview(someButton)
}
@objc func someButtonPressed() {
}
}
Однако я получаю ошибку @objc можно использовать только с членами классов, протоколами @objc и конкретными расширениями классов. при определении someButtonPressed.
Есть ли способ добиться этого с помощью протоколов?
Спасибо заранее за любые предложения!
@Sh_Khan Привет. Это был протокол @objc (опечатка исправлена). Я пытаюсь перейти на протоколы Swift для таких вещей, чтобы у меня не было очень длинного UIViewController с большим количеством расширений. Вы знаете, как это сделать, пожалуйста?





В протоколе необходимо указать требование Selector. Это потому, что вы можете применить атрибут @objc только к NSObject. Единственная причина пометить протокол @objc - это необязательные методы.
@objc protocol SomeProtocol {
var action: Selector { get }
}
Измените расширение на это:
extension SomeProtocol where Self: UIViewController {
func addSomeButton() {
let someButton = UIButton()
someButton.addTarget(self, action: action, for: .touchUpInside)
view.addSubview(someButton)
}
}
Теперь это работает:
extension UIViewController: SomeProtocol {
@objc func buttonPressed(sender: UIButton) {
print("Button pressed")
}
var action: Selector {
return #selector(buttonPressed(sender:))
}
}
Использование:
let vc: myViewController: MyViewController!
func doSomething() {
vc.addSomeButton()
}
Спасибо за предложение. Я бы хотел сохранить реализацию buttonPressedвнутри как расширение протокола, пожалуйста. Это сделано для минимизации длины UIViewController, поэтому я не добавляю много расширений.
Вам не придется. Просто определите его в UIViewController, и все подклассы получат его бесплатно.
Спасибо. То есть нет возможности включить это только в протокол?
Нет. Как я сказал в своем ответе, атрибут @objc требует наличия NSObject. Невозможно гарантировать, что протокол Swift предназначен только для NSObject. :(
@ImportAccelerate Да, вы можете с новейшим набором инструментов. Однако я попробовал, и это не работает. Атрибуты objc просто не разрешены в расширениях протокола.
Я знаю это. В чем ваше замешательство?
@ j-doe есть ли другой способ сделать это, возможно, без расширения UIViewControllers?
@ImportAccelerate Я прочитал этот фрагмент, но спасибо за дальнейшие объяснения.
Другого пути нет
@ Import-Accelerate Смотрите мой ответ, если вам интересно.
Интересное решение, но кажется хакерским. Мне нравится чистый Свифт.
Обходной путь заключается в добавлении закрывающей втулки к UIButton вместо целевого действия, как показано здесь https://stackoverflow.com/a/41438789/5058116 и скопировано ниже для удобства.
typealias Closure = () -> ()
///
class ClosureSleeve {
let closure: Closure
init(_ closure: @escaping Closure) {
self.closure = closure
}
@objc func invoke () {
closure()
}
}
extension UIControl {
func addAction(for controlEvents: UIControl.Event = .touchUpInside, _ closure: @escaping Closure) {
let sleeve = ClosureSleeve(closure)
addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
objc_setAssociatedObject(self, String(format: "[%d]", arc4random()), sleeve, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
Затем просто замените:
someButton.addTarget(self, #selector(someButtonPressed), for: .touchUpInside)
с участием:
someButton.addAction { [weak self] in
self?.someButtonPressed()
}
и привет.
почему не
extension UIViewController?? пробовал@objc protocol