Делегирование для просмотра SwiftUI из контроллера представления хостинга

#ios #swift #swiftui #uikit #uihostingcontroller

#iOS #быстрый #свифтуи #уикит #контроллер uihostingcontroller

Вопрос:

Я добавляю UIHostingViewController в свою раскадровку и создаю класс для этого. Загрузите mySwiftUIView в инициализацию контроллера. Все работает хорошо.

Но я хочу сделать хостинг-контроллер похожим на делегат для mySwiftUIView, потому что я хочу обрабатывать нажатие кнопки в представлении.

 required init?(coder: NSCoder) {
    var mySwiftView = MySwiftUIView()
    mySwiftView.delegate = self //self used before super.init call
    super.init(coder: coder,rootView: mySwiftView)
}
 

Идентификатор не работает, потому что я передаю просмотр до полной инициализации контроллера хостинга. Swift показывает сообщение «self используется перед вызовом super.init»

Если я буду использовать обычный UIViewController и помещу HostingController внутрь — это работает. Но это мне не подходит, потому что вызывает проблемы с размерами и навигацией.

Как я могу это сделать с помощью отдельного UIHostingController. Спасибо

Полный код

 import UIKit
import SwiftUI

protocol MySwiftUIViewDelegate{
    func buttonPressed()
}

struct MySwiftUIView: View {
    var delegate: MySwiftUIViewDelegate?
    
    var body: some View {
        Button(action: {
            delegate?.buttonPressed()
        }) {
            Text("My button")
        }
    }
}

class MyHostingViewController: UIHostingController<MySwiftUIView>, MySwiftUIViewDelegate {
        
required init?(coder: NSCoder) {
    var mySwiftView = MySwiftUIView()
    //mySwiftView.delegate = self //self used before super.init call
    super.init(coder: coder,rootView: mySwiftView)
}

override func viewDidLoad() {
    super.viewDidLoad()
}

func buttonPressed() {
    performSegue(withIdentifier: "GoToOtherScreenSegue", sender: self)
}
}
 

Ответ №1:

Поскольку MyHostingViewController известно, что rootView это MySwiftUIView так, вы можете впоследствии назначить контроллер представления в качестве делегата:

 required init?(coder: NSCoder) {
    var mySwiftView = MySwiftUIView()
    super.init(coder: coder, rootView: mySwiftView)
    rootView.delegate = self
}
 

Комментарии:

1. Обратите внимание, что вы не можете этого сделать mySwiftView.delegate = self , потому что это структура, скопированная по значению, поэтому для «реального» view ( rootView ) не будет установлен делегат. Также mySwiftView может быть let здесь.

Ответ №2:

Вы можете обернуть делегат в класс

 import UIKit
import SwiftUI

protocol MySwiftUIViewDelegate{
    func buttonPressed()
}

struct MySwiftUIView: View {
    let configuration: Configuration
    
    var body: some View {
        Button(action: {
            configuration.delegate?.buttonPressed()
        }) {
            Text("My button")
        }
    }
}

extension MySwiftUIView {
    final class Configuration {
        unowned var delegate: MySwiftUIViewDelegate?
    }
}

class MyHostingViewController: UIHostingController<MySwiftUIView>, MySwiftUIViewDelegate {
        
    required init?(coder: NSCoder) {
        let configuration = MySwiftUIView.Configuration()
        let mySwiftView = MySwiftUIView(configuration: configuration)
        super.init(coder: coder,rootView: mySwiftView)
        configuration.delegate = self
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    func buttonPressed() {
        performSegue(withIdentifier: "GoToOtherScreenSegue", sender: self)
    }
}