#ios #swift #swift-protocols
#iOS #swift #swift-протоколы
Вопрос:
У меня есть a, UIViewController
в котором есть a UICollectionView
, внутри которого у меня есть ListSectionController
класс, который управляет a UICollectionViewCell
, и внутри этой ячейки у меня есть UIView
подкласс.
При нажатии кнопки мне нужно вызвать метод из UIViewController
. В настоящее время у меня есть цепочка методов делегирования, поскольку она работает, возвращаясь к контроллеру представления следующим образом:
class MyView {
// delegate is the cell that the view is contained in
@IBAction func buttonPress() {
delegate?.myDelegateMethod()
}
}
extension MyCell : MyViewDelegate {
// The delegate is the section controller
func myDelegateMethod() {
delegate?.myDelegateMethod()
}
}
... etc
Это кажется большим дублированием кода и пустой тратой места. Как я могу это улучшить?
Комментарии:
1. Делегатом
MyView
не обязательно должна быть ячейка, это может быть контроллер представления. Просто передайте это вплоть до представления при создании ячейки2. Вот как это должно работать для правильного разделения реализаций. Однако в настоящее время я бы использовал замыкания (блоки обратного вызова) практически для всего.
3. вы можете использовать центр уведомлений в качестве альтернативы делегированию.
Ответ №1:
При нажатии кнопки мне нужно вызвать метод из UIViewController
Один из способов: присвоите кнопке действие, нацеленное на ноль, и реализуйте метод action в UIViewController. Сообщение прибудет автоматически.
Например, мы присваиваем кнопке действие, нацеленное на ноль:
class Dummy {
@objc func buttonPressed(_:Any) {}
}
button.addTarget(nil,
action: #selector(Dummy.buttonPressed),
for: .touchUpInside)
И в контроллере представления мы имеем:
@objc func buttonPressed(_ sender: Any) {
Это сработает, потому что контроллер представления расположен вверх по цепочке ответчиков от кнопки. Это именно то, для чего предназначены действия, нацеленные на ноль.
Другой способ — использовать NotificationCenter и Notification. Я думаю, что это также идеально подходит в этой ситуации.
Комментарии:
1. Вау, никогда раньше не слышал о действиях, нацеленных на ноль!
Ответ №2:
Вы можете напрямую подключиться по цепочке ответчиков к любому родительскому представлению или контроллеру представления универсальным способом, безопасным для ввода:
extension UIResponder {
func firstParent<T: UIResponder>(ofType type: T.Type ) -> T? {
return next as? T ?? next.flatMap { $0.firstParent(ofType: type) }
}
}
guard let ListSectionController = firstParent(ofType: ListSectionController) else {
return // we aren't in a ListSectionController
}
//Call ListSectionController methods here
Комментарии:
1. И это определенно не очень хорошее решение, потому что оно нарушает инкапсуляцию.
2. @Sulthan мы должны согласиться не соглашаться с этим.
Ответ №3:
Я сталкивался с этим условием так много раз, поэтому сначала я хотел бы указать на очевидное:
«Если ваша ячейка (и представления внутри нее) отвечают за выполнение одного и только одного действия (возможно, это кнопка формы внутри нее)», вы можете использовать didSelectItemAtIndexPath
«. Создав свой вид с помощью UIImageView
. У этого подхода есть некоторые проблемы с UX, такие как выделение, и все, кроме них, также могут быть обработаны с помощью delegate.
Если это не так, и ваша ячейка выполняет более одного действия, ответ Mat дает наилучшие подходы.