To expand on matt's comment to watch the app orientation instead of the device orientation, you can modify your approach to observe changes in the interface orientation.
That would focus on the orientation of the app's user interface, which might change less frequently than the device's physical orientation, thus providing a less sensitive detection mechanism.
修改你的DeviceRotationViewModifier
以观察应用程序窗口场景的statusBarOrientation
的变化.这需要访问当前的UIWindowScene
来观察其interfaceOrientation
.
确保你的onRotate
修饰符现在可以根据应用程序的界面方向触发动作.
import SwiftUI
import UIKit
struct TopVideo: View {
var body: some View {
ZStack {
// Your ZStack content
}
.onRotate { newOrientation in
withAnimation(.easeInOut(duration: 0.2)) {
// Update your UI based on the newOrientation
// orien = newOrientation
}
}
}
}
struct AppOrientationViewModifier: ViewModifier {
let action: (UIInterfaceOrientation) -> Void
func body(content: Content) -> some View {
content
.onAppear()
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didChangeStatusBarOrientationNotification)) { _ in
if let windowScene = UIApplication.shared.windows.first?.windowScene {
action(windowScene.interfaceOrientation)
}
}
}
}
extension View {
func onRotate(perform action: @escaping (UIInterfaceOrientation) -> Void) -> some View {
self.modifier(AppOrientationViewModifier(action: action))
}
}
The AppOrientationViewModifier
now listens for the UIApplication.didChangeStatusBarOrientationNotification
notification. When the orientation changes, it retrieves the current UIInterfaceOrientation
from the UIWindowScene
and triggers the provided action.
The extension method onRotate
is adapted to accept a closure with a UIInterfaceOrientation
parameter, reflecting the change to app interface orientation detection.
同样,这假设您的应用程序使用UIWindowScene
(这是针对iOS 13及更高版本的应用程序的标准).如果您支持较旧版本的iOS,则可能需要调整实现以确保兼容性.
如the comments中的lorem ipsum所示,您还可以使用:
ViewThatFits
,它会根据最适合可用空间的视图自动在多个视图之间进行 Select .这在布局需要在纵向和横向之间显著调整而不显式跟踪方向状态的情况下特别有用.
struct AdaptiveVideoLayout: View {
var body: some View {
ViewThatFits {
PortraitVideoPlayer() // A custom view optimized for portrait orientation
LandscapeVideoPlayer() // A custom view optimized for landscape orientation
}
}
}
SwiftUI.Layout
,在iOS 16中引入,您可以定义更复杂的定制布局,以适应容器的大小.它提供了一种更强大、更灵活的方式来创建自适应用户界面,可以响应方向、屏幕大小和其他因素的变化.
struct CustomVideoLayout: View {
var body: some View {
Layout {
// Define custom layout logic that adapts to the container size
}
}
}
我不想用UIViewControllerRepresentable
.
正确,不推荐使用UIApplication.shared.windows
和UIApplication.didChangeStatusBarOrientationNotification
.
另一种方法(除了UIViewControllerRepresentable
之外)应该留在SwiftUI框架中,而不是恢复到UIKit机制,这些机制在纯SwiftUI应用程序中要么不推荐使用,要么被认为不太惯用.
You might consider creating a custom EnvironmentKey
to propagate orientation changes through the SwiftUI view hierarchy. That would involve using a combination of SwiftUI and UIKit under the hood, but expose the functionality in a way that is idiomatic to SwiftUI.
This is inspired from "Exploring SwiftUI: Orientation Property Wrapper" by Rudrank Riyam.
import SwiftUI
// Define an EnvironmentKey for orientation
struct OrientationKey: EnvironmentKey {
static let defaultValue: UIDeviceOrientation = .unknown
}
extension EnvironmentValues {
var orientation: UIDeviceOrientation {
get { self[OrientationKey.self] }
set { self[OrientationKey.self] = newValue }
}
}
// A ViewModifier that updates the orientation environment value
struct OrientationModifier: ViewModifier {
@Environment(\.orientation) var orientation
let action: (UIDeviceOrientation) -> Void
func body(content: Content) -> some View {
content
.onAppear()
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
let newOrientation = UIDevice.current.orientation
action(newOrientation)
}
}
}
extension View {
func onOrientationChange(action: @escaping (UIDeviceOrientation) -> Void) -> some View {
self.modifier(OrientationModifier(action: action))
}
}
您可以在视图中使用此修饰符来获得方向更改的通知,而无需直接与UIViewControllerRepresentable
交互或处理过时的API:
struct ContentView: View {
@Environment(\.orientation) var orientation
var body: some View {
Text("Current orientation: \(orientation.isLandscape ? "Landscape" : "Portrait")")
.onOrientationChange { newOrientation in
// Handle orientation change
print("Orientation changed to: \(newOrientation)")
}
}
}
通过将UIKit交互抽象为可重用的ViewModifier
,您可以避免直接使用UIViewControllerRepresentable
,同时仍然可以实现您想要的.