-
Notifications
You must be signed in to change notification settings - Fork 81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Revisit iOS interop API #1494
Revisit iOS interop API #1494
Conversation
* @see Modifier.semantics | ||
*/ | ||
@Deprecated( | ||
message = "Use androidx.compose.ui.viewinterop.UIKitView instead" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Restore the doc and add replaceWith
as separate parameter instead of writing it in the message
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
// Despite the name, onResize actually contains the logic for default resizing strategy. | ||
// Since this strategy is already implied, changes to this argument can't be processed in a | ||
// sane manner | ||
require(onResize == DefaultViewResize) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like a breaking change if it was used previously
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but it didn't make sense because it basically required user to set a proper frame of interop view, which was already laid out by compose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't matter. It can be used in libraries that the user doesn't control, so it will become a blocker for Compose update
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When did we stabilise this API in the first place?
I mean, I just don't want to carry a trail of compatibility code on API, that was experimental due to target being experimental.
compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/viewinterop/UIKitInteropCallbacks.kt
Outdated
Show resolved
Hide resolved
callbacks?.run { | ||
newUserComponentSize?.let { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: just regular if
? double lambda wrapping seems weird
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's a side effect of my experimentation with reassignable callbacks/properties and callbacks being var
. Changed it.
/** | ||
* Check that [group] doesn't entirely clip a child view with a [cgRect] | ||
*/ | ||
private fun isCgRectEntirelyClippedByGroup(cgRect: CValue<CGRect>): Boolean = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems semantic of this is simpler than name isVisible
probably?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, refactored it.
@Immutable | ||
data class UIKitInteropProperties @ExperimentalComposeUiApi constructor( | ||
internal val interactionMode: UIKitInteropInteractionMode? = UIKitInteropInteractionMode.Cooperative(), | ||
internal val isNativeAccessibilityEnabled: Boolean = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are getters internal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it's only needed for us.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not so sure. This is kind of an object where you just wrap a few booleans/enums. The expectation here is full creating + reading for the ability to create a new instance based on the some values from the existing one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, let's make it public
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unresolved
compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/viewinterop/UIKitView.uikit.kt
Outdated
Show resolved
Hide resolved
compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/viewinterop/UIKitViewController.uikit.kt
Outdated
Show resolved
Hide resolved
* All the changes are executed synchronously right after the state change on a main thread | ||
* in sync with a CATransaction batching UIKit objects changes to sync it with Compose rendering. | ||
*/ | ||
interface UIKitInteropCallbacks<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UIKitInteropListener
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, sounds better
* - View receives touches with 150ms delay, allowing compose to intercept them. | ||
* - Native accessibility resolution is disabled | ||
*/ | ||
val Default = UIKitInteropProperties() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure that we want this as part of a public API
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would you like to do here?
I think we'd still expand this constructor/argument types, so want to keep at least a stable part without having a constructor overloads combinatory explosion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean this particular default value. Let's make it internal for now (still can be used as default arg)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made it internal
17081a3
to
cdd5ae3
Compare
0f4508e
to
bdae9c4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, such new APIs require a second reviewer
* @property delay Indicates how much time in milliseconds is given for Compose to intercept | ||
* the touches before delivering them to the interop view. The default value is [DEFAULT_DELAY]. | ||
*/ | ||
class Cooperative( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's explicitly mark it as @ExperimentalComposeUiApi
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
...se/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/viewinterop/UIKitInteropInteractionMode.kt
Outdated
Show resolved
Hide resolved
* main thread in sync with a CATransaction batching UIKit objects changes to sync it with Compose | ||
* rendering. | ||
*/ | ||
interface UIKitInteropListener<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the final shape of this API? public-stable is intended here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I've requested some insight from our dev advocates, so it could be not
@Immutable | ||
data class UIKitInteropProperties @ExperimentalComposeUiApi constructor( | ||
internal val interactionMode: UIKitInteropInteractionMode? = UIKitInteropInteractionMode.Cooperative(), | ||
internal val isNativeAccessibilityEnabled: Boolean = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unresolved
...e/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/viewinterop/UIKitInteropProperties.uikit.kt
Show resolved
Hide resolved
...e/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/viewinterop/UIKitInteropProperties.uikit.kt
Outdated
Show resolved
Hide resolved
compose/mpp/demo/src/uikitMain/kotlin/androidx/compose/mpp/demo/UIKitInteropExample.kt
Outdated
Show resolved
Hide resolved
beea29e
to
a80338b
Compare
I rolled back additional APIs, only |
Hope you can review and merge this PR as soon as possible. The change in touch strategy is having a big impact on our project, and we really need to push out a version that behaves correctly for our clients. Thanks! 😊 |
You mean new touches strategy brought in recent alpha? |
Yes, in youtrack is this. |
No worries, it's in its presumably final state, so it will be merged after reviewed. |
Understand, but because we are happy to conduct active exploration, we will provide customers with some experimental versions. |
.trackInteropPlacement(this) | ||
.onGloballyPositioned { layoutCoordinates -> | ||
layoutAccordingTo(layoutCoordinates) | ||
// TODO: Should be the same as [Owner.onInteropViewLayoutChange]? | ||
// container.onInteropViewLayoutChange(this) | ||
// container.onInteropViewLayoutChange(this) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a reminder for investigation after upstream merge, ask @MatkovIvan for more details.
...i/ui/src/uikitMain/kotlin/androidx/compose/ui/viewinterop/UIKitInteropElementHolder.uikit.kt
Show resolved
Hide resolved
...ui/src/uikitMain/kotlin/androidx/compose/ui/viewinterop/UIKitInteropInteractionMode.uikit.kt
Outdated
Show resolved
Hide resolved
compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/window/InteractionUIView.uikit.kt
Outdated
Show resolved
Hide resolved
71b6e10
to
8bbcb64
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the current shape of it, thanks!
(aside of InteropPlatformDetails
in shared code 🙃 )
Also, could you please update PR description/release notes (no breaking changes here)
* chain needs to be changed within ComposeNode Updater | ||
*/ | ||
internal interface InteropPlatformDetails { | ||
fun platformModifier(holder: InteropViewHolder): Modifier |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why isn't it a part of InteropContainer
? Those 2 booleans don't seem as platform-specific and can be mentioned in the common interface as parameters in this function.
or. it might be converted to something like
internal fun interface InteropViewModifierFactory {
fun create(holder: InteropViewHolder): Modifier
}
and probably make some default implementation(s) in shared code (outside of this PR).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Native platform accessibility resolution is absolutely platform specific, touch interaction mode is also platform specific.
The point of InteropPlatformDetails
is an ability to change something in the implementation of specific InteropViewHolder
when it's reused. It can cover basically anything what happens here (in case of iOS - not only change of a modifier, but also - reconfiguration of InteropWrappingView
which serves as a group
. In future it could be used for expanding gesture recogniser failure requirements behavior and forwarding it to the user.
interopWrappingView.interactionMode = null | ||
} else { | ||
val uiKitInteropPlatformDetails = | ||
checkNotNull(platformDetails as? UIKitInteropPlatformDetails) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolving the previous comment should remove this cast, I guess
1.7.0-dev1783 properties = UIKitInteropProperties(
interactionMode= UIKitInteropInteractionMode.Cooperative(
delayMillis = 1,
),
isNativeAccessibilityEnabled = false
) I still don't quite understand the purpose of delaying touch events. Why is delay necessary? In most cases, delayMillis doesn't make much sense, as in scrollable scenarios, no one would hold down and wait for the page to become scrollable. |
|
Changes
Revisit iOS interop API to build upon new implementation of
InteropView
.Main functional additions are:
Support of
onReset
, fine-grain touches strategy control (cooperative with explicit time delay, eager, none)юFixes 5719, 5876
Testing
TBD, new/updated demo pages.
This should be tested by QA
Release Notes
Breaking changes - iOS
UIKitView
andUIKitViewController
inpackage androidx.compose.ui.interop
are deprecated. New API are mentioned in deprecation message. Deprecated invocations should work fine unless customonResize
is used, it is disallowed now and will print a warning.Features - iOS
UIKitView
andUIKitViewController
API inpackage androidx.compose.ui.viewinterop
. Support ofonReset
to reuse the interop composable emitted node and avoid excessive native views reallocations, fine-grain touches strategy control (cooperative with explicit time delay, non-cooperative where no touches are received by Compose, ignoring touches).