diff --git a/proposals/0000-hover-style-ipad-api.md b/proposals/0000-hover-style-ipad-api.md new file mode 100644 index 00000000..ab3bd94f --- /dev/null +++ b/proposals/0000-hover-style-ipad-api.md @@ -0,0 +1,102 @@ +--- +title: Hover style API for iPad / Apple Vision Pro +author: + - Oskar Kwaśniewski + - Saad Najmi +date: 08.01.2023 +--- + +# RFC0000: Hover style API for iPad / Apple Vision Pro + +## Summary + +iPad supports hover style effects when using an external mouse/trackpad. This effect illuminates the UIView in two configurable ways: lift or highlight. This proposal intends to add support for this API in React Native. This API is also used by visionOS to display the hover effect when a user looks at the view. + +The native API this feature is basing on: https://developer.apple.com/documentation/uikit/uihoverstyle (iOS 17+). There is an older version of this API supporting iOS 13.4+ https://developer.apple.com/documentation/uikit/uipointerstyle + +## Basic example + +This API would introduce an additional prop to UIView (which is used to render touchable/pressable elements). + +```jsx + + I'm pressable! + +``` + +The hover effect automatically adapts to view’s corner radius. + +Example of highlight effect: + +https://github.com/react-native-community/discussions-and-proposals/assets/52801365/a1ace4ac-44c1-45e6-afbe-6e0b0f05f74d + +## Motivation + +Controlling hover style allows React Native developers to provide a much better user experience on iPad and Apple Vision Pro, allowing them to seamlessly interact with React Native applications using the mouse. + +Additionally, We have lots of React Native experiences in brownfield contexts at Microsoft. The value add of React Native over something like a webview is that we can access the system APIs, and provide the best UX for the platform. This includes iPadOS / visionOS, where hover events are now a part of the UX language and built into UIKit controls. In order for React Native experiences to thrive living side by side with native experiences, we would live to have as close UX as possible. + +This API can work side by side with the Pointer Events API. Users can choose to either spin up their own custom interactions with the pointer events API, or use Apple OS default. Adding a hover effect shouldn’t effect pointer events being fired or not. https://reactnative.dev/blog/2022/12/13/pointer-events-in-react-native + +## Detailed design + +_Disclaimer: I will discuss the design for New Architecture, but the API can be easily backported._ + +The API should be opt-in to avoid breaking current iPad apps. The implementation bases on extending `RCTView` with a new prop of type `std::string`. + +Next inside of `RCTViewComponentView` `- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps`` method: + +```objc +if (oldViewProps.hoverEffect != newViewProps.hoverEffect) { + [self updateHoverEffect:[NSString stringWithUTF8String:newViewProps.hoverEffect.c_str()]]; + } + +In the code above we are detecting props update, and calling updateHoverEffect method: + +- (void) updateHoverEffect:(NSString*)hoverEffect { + if (hoverEffect == nil || [hoverEffect isEqualToString:@""]) { + self.hoverStyle = nil; + return; + } + + UIShape *shape = [UIShape rectShapeWithCornerRadius:self.layer.cornerRadius]; + id effect; + + if ([hoverEffect isEqualToString:@"lift"]) { + effect = [UIHoverLiftEffect effect]; + } else if ([hoverEffect isEqualToString:@"highlight"]) { + effect = [UIHoverHighlightEffect effect]; + } + + if (hoverEffect != nil) { + self.hoverStyle = [UIHoverStyle styleWithEffect:effect shape:shape]; + } +} +``` + +Here we initialize a new `UIShape` which reassembles the current’s view layer `cornerRadius`. Next we are creating a `UIHoverEffect` instance based on the value passed by user. + +The current API implementation might need some polish to correctly retrieve corner radius. + +## Drawbacks + +- Apple only API + - Only Android equivalent I found is: https://developer.android.com/reference/android/view/MotionEvent which doesn’t provide any built-in effect but allows to create custom ones. +- There might be different plan for the Pointer Events API. + +## Alternatives + +- This API might be also implemented using Pointer Events API by detecting hover and applying some custom styling to the view but this would be less performant than using native solution. +- This feature could be also implemented in user space by creating their own subclass of UIView and wrapping every element with it. + +## Adoption strategy + +Users can opt-in without any breaking changes. + +## How we teach this + +Describe this prop in the documentation. Add section to docs about iPad development. + +## Unresolved questions + +- How does it fit with Pointer Events API?