From 65263979176051f0657118576f9ba57528204b60 Mon Sep 17 00:00:00 2001 From: "dima.avdeev" <dima.avdeev@jetbrains.com> Date: Mon, 22 Jan 2024 17:24:55 +0300 Subject: [PATCH] fix UIKitView z-order (#965) Fix Z-order issue with UIKitView https://github.com/JetBrains/compose-multiplatform/issues/4004 --- .../src/uikitMain/kotlin/UIKitViewOrder.kt | 52 +++++++++++++++++++ .../androidx/compose/mpp/demo/main.uikit.kt | 2 + ...ikit.kt => LocalInteropContainer.uikit.kt} | 2 +- .../compose/ui/interop/UIKitView.uikit.kt | 7 ++- .../ui/scene/ComposeSceneMediator.uikit.kt | 8 +++ .../ui/window/ComposeContainer.uikit.kt | 2 - 6 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 compose/mpp/demo/src/uikitMain/kotlin/UIKitViewOrder.kt rename compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/interop/{LocalLayerContainer.uikit.kt => LocalInteropContainer.uikit.kt} (91%) diff --git a/compose/mpp/demo/src/uikitMain/kotlin/UIKitViewOrder.kt b/compose/mpp/demo/src/uikitMain/kotlin/UIKitViewOrder.kt new file mode 100644 index 0000000000000..81c5c24386010 --- /dev/null +++ b/compose/mpp/demo/src/uikitMain/kotlin/UIKitViewOrder.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +import androidx.compose.mpp.demo.Screen +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.interop.UIKitView +import androidx.compose.ui.unit.dp +import platform.UIKit.UIColor +import platform.UIKit.UIView + +/** + * Issue https://github.com/JetBrains/compose-multiplatform/issues/4004 + */ +val UIKitViewOrder = Screen.Example("UIKitViewOrder") { + Box(modifier = Modifier.fillMaxSize().background(Color.Yellow)) { + UIKitView( + factory = { + UIView().apply { + backgroundColor = UIColor.blueColor + } + }, + modifier = Modifier.size(100.dp), + ) + UIKitView( + factory = { + UIView().apply { + backgroundColor = UIColor.redColor + } + }, + modifier = Modifier.size(100.dp).offset(50.dp, 50.dp), + ) + } +} diff --git a/compose/mpp/demo/src/uikitMain/kotlin/androidx/compose/mpp/demo/main.uikit.kt b/compose/mpp/demo/src/uikitMain/kotlin/androidx/compose/mpp/demo/main.uikit.kt index 6b8f507a5dc13..1e99262590969 100644 --- a/compose/mpp/demo/src/uikitMain/kotlin/androidx/compose/mpp/demo/main.uikit.kt +++ b/compose/mpp/demo/src/uikitMain/kotlin/androidx/compose/mpp/demo/main.uikit.kt @@ -3,6 +3,7 @@ package androidx.compose.mpp.demo import NativeModalWithNaviationExample import SwiftUIInteropExample +import UIKitViewOrder import androidx.compose.runtime.* import androidx.compose.ui.main.defaultUIKitMain import androidx.compose.ui.window.ComposeUIViewController @@ -25,6 +26,7 @@ fun IosDemo(arg: String, makeHostingController: ((Int) -> UIViewController)? = n extraScreens = listOf( IosBugs, NativeModalWithNaviationExample, + UIKitViewOrder, ) + listOf(makeHostingController).mapNotNull { it?.let { SwiftUIInteropExample(it) diff --git a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/interop/LocalLayerContainer.uikit.kt b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/interop/LocalInteropContainer.uikit.kt similarity index 91% rename from compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/interop/LocalLayerContainer.uikit.kt rename to compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/interop/LocalInteropContainer.uikit.kt index aef9b7417a233..839d7841b4cef 100644 --- a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/interop/LocalLayerContainer.uikit.kt +++ b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/interop/LocalInteropContainer.uikit.kt @@ -19,6 +19,6 @@ package androidx.compose.ui.interop import androidx.compose.runtime.staticCompositionLocalOf import platform.UIKit.UIView -internal val LocalLayerContainer = staticCompositionLocalOf<UIView> { +internal val LocalInteropContainer = staticCompositionLocalOf<UIView> { error("CompositionLocal LayerContainer not provided") } diff --git a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/interop/UIKitView.uikit.kt b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/interop/UIKitView.uikit.kt index 04f465aa113c5..72379907ee8a6 100644 --- a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/interop/UIKitView.uikit.kt +++ b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/interop/UIKitView.uikit.kt @@ -27,7 +27,6 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshots.SnapshotStateObserver import androidx.compose.ui.* import androidx.compose.ui.draw.drawBehind -import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.InteropViewCatchPointerModifier @@ -83,7 +82,7 @@ fun <T : UIView> UIKitView( ) { // TODO: adapt UIKitView to reuse inside LazyColumn like in AndroidView: // https://developer.android.com/reference/kotlin/androidx/compose/ui/viewinterop/package-summary#AndroidView(kotlin.Function1,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1,kotlin.Function1) - val rootView = LocalLayerContainer.current + val rootView = LocalInteropContainer.current val embeddedInteropComponent = remember { EmbeddedInteropView( rootView = rootView, @@ -179,7 +178,7 @@ fun <T : UIViewController> UIKitViewController( ) { // TODO: adapt UIKitViewController to reuse inside LazyColumn like in AndroidView: // https://developer.android.com/reference/kotlin/androidx/compose/ui/viewinterop/package-summary#AndroidView(kotlin.Function1,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1,kotlin.Function1) - val rootView = LocalLayerContainer.current + val rootView = LocalInteropContainer.current val rootViewController = LocalUIViewController.current val embeddedInteropComponent = remember { EmbeddedInteropViewController( @@ -296,7 +295,7 @@ private abstract class EmbeddedInteropComponent<T : Any>( wrappingView = UIView().apply { addSubview(view) } - rootView.insertSubview(wrappingView, 0) + rootView.addSubview(wrappingView) } protected fun removeViewFromHierarchy(view: UIView) { diff --git a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.uikit.kt b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.uikit.kt index b6dc4999ffac1..333ab21995498 100644 --- a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.uikit.kt +++ b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.uikit.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.input.pointer.HistoricalChange import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.PointerId import androidx.compose.ui.input.pointer.PointerType +import androidx.compose.ui.interop.LocalInteropContainer import androidx.compose.ui.interop.LocalUIKitInteropContext import androidx.compose.ui.interop.UIKitInteropContext import androidx.compose.ui.interop.UIKitInteropTransaction @@ -146,6 +147,11 @@ internal class ComposeSceneMediator( renderingUIViewFactory(renderDelegate) } + /** + * Container for UIKitView and UIKitViewController + */ + private val interopViewContainer = UIView() + private val interactionView by lazy { InteractionUIView( keyboardEventHandler = keyboardEventHandler, @@ -331,6 +337,7 @@ internal class ComposeSceneMediator( this.onAttachedToWindow?.invoke() focusStack?.pushAndFocus(interactionView) } + container.addSubview(interopViewContainer) container.addSubview(interactionView) interactionView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activateConstraints( @@ -382,6 +389,7 @@ internal class ComposeSceneMediator( LocalKeyboardOverlapHeight provides keyboardOverlapHeightState.value, LocalSafeArea provides safeAreaState.value, LocalLayoutMargins provides layoutMarginsState.value, + LocalInteropContainer provides interopViewContainer, content = content ) diff --git a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/window/ComposeContainer.uikit.kt b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/window/ComposeContainer.uikit.kt index 54c252f3bf50a..db7f0ff1d0e81 100644 --- a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/window/ComposeContainer.uikit.kt +++ b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/window/ComposeContainer.uikit.kt @@ -25,7 +25,6 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.LocalSystemTheme import androidx.compose.ui.SystemTheme -import androidx.compose.ui.interop.LocalLayerContainer import androidx.compose.ui.interop.LocalUIViewController import androidx.compose.ui.platform.PlatformContext import androidx.compose.ui.platform.WindowInfoImpl @@ -419,7 +418,6 @@ internal fun ProvideContainerCompositionLocals( ) = with(composeContainer) { CompositionLocalProvider( LocalUIViewController provides this, - LocalLayerContainer provides view, LocalInterfaceOrientation provides interfaceOrientationState.value, LocalSystemTheme provides systemThemeState.value, content = content