JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
15.86k stars 1.15k forks source link

ComposeUIViewController don't Update View #4781

Open FelixSubject211 opened 4 months ago

FelixSubject211 commented 4 months ago

Hello I have

fun SelectAppointmentViewController( bookAppointmentScreenModel: BookAppointmentScreenModel, appointmentBody: AppointmentBody ) = ComposeUIViewController { MaterialTheme(colors = ColorSystem.ColorPalette()) { SelectAppointmentCompose(bookAppointmentScreenModel, appointmentBody) } }

When I start the app in Android the SelectAppointmentCompose is updated. When the app starts from MainViewController in IOS also.

The problem occurs as soon as I call the SelectAppointmentCompose via the SelectAppointmentViewController in Swiftui. Then the compose is not redrawn with new calls, although SelectAppointmentViewController is called with new parameters

FelixSubject211 commented 3 months ago

I found a solution. It may well be that this isn't a bug at all. If so, I'm very sorry for the disruption!

You have to use the updateUIViewController in the calling UIViewControllerRepresentable.

I did it like this:

func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        let newViewController = SelectAppointmentViewControllerKt.SelectAppointmentViewController(
            bookAppointmentScreenModel: bookAppointmentScreenModel,
            appointmentBody: appointmentBody
        )

        uiViewController.willMove(toParent: nil)
        uiViewController.view.removeFromSuperview()
        uiViewController.removeFromParent()

        uiViewController.addChild(newViewController)
        newViewController.view.frame = uiViewController.view.frame
        uiViewController.view.addSubview(newViewController.view)
        newViewController.didMove(toParent: uiViewController)
 }

Felt a bit hacky but it worked

elijah-semyonov commented 3 months ago

Sorry, I'm not exactly following what you are trying to achieve. Can you create a repro and describe what is the expected behavior?

There is too much information missing. Your updateUIViewController will recreate SelectAppointmentViewControllerKt.SelectAppointmentViewController every time the value of UIViewControllerRepresentable structure changes, which I don't think is something desired. Plus creating a UIViewController inside a UIViewControllerRepresentable context is a bit novel? Why not just use another UIViewControllerRepresentable?

The problem occurs as soon as I call the SelectAppointmentCompose via the SelectAppointmentViewController in SwiftUI.

This function creates a new UIViewController, that draws Composable content inside. In your case the Composable content is SelectAppointmentCompose(bookAppointmentScreenModel, appointmentBody). It will recalculate itself using the rules of Compose whenever values that affect the compose and are coming from bookAppointmentScreenModel and appointmentBody change (and the implementation actually reads or writes it). The state in there must be represented as compose.runtime.State (using mutableStateOf APIs and similar) otherwise Compose will not see them changing and hence won't be reacting.

Then the compose is not redrawn with new calls, although SelectAppointmentViewController is called with new parameters

It's not exactly clear what's happening in here. Do you mean, that parameters used by UIViewControllerRepresentable structure changed, but the ComposeUIViewController did not redraw? SwiftUI will try not to call makeUIViewController of UIViewControllerRepresentable unless it indeeds needs to make a new one and will only call updateUIViewController so you can update your data. In your case models should be created on Kotlin side and changed from within this callback, this way Compose will see the change in State and redraw accordingly.

okushnikov commented 1 month ago

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.