我想在SWIFT中检测设备方向,以便对视频应用正确的方向.我有的更新设备方向的代码工作正常,但对微小的移动非常敏感.将手机向上拿着,几乎不倾斜,会导致设备方向更新为横向,即使手机基本上仍然是纵向的.有没有办法降低设备方向的敏感度,这样微小的移动就不会导致不必要的更新?

此外,我的应用程序不支持应用程序构建文件中的景观.因此,跟踪应用程序的定位可能不会起作用.我想要这个特定的视图来检测方向,这样我就可以调整视频播放器的方向了.

struct TopVideo: View {
    var body: some View {
        ZStack {
   if let vid = player {
                let portrait = (orien == .portrait || orien == .unknown || orien == .portraitUpsideDown)
                let width = widthOrHeight(width: true)
                let height = widthOrHeight(width: false)
                
                let vid_width = portrait ? width : (height * CGFloat(aspect.width / aspect.height))
                let vid_height = !portrait ? width : (height * CGFloat(aspect.height / aspect.width))
                
                VidPlayer(player: vid)
                    .rotationEffect(.degrees((orien == .portrait || orien == .unknown)  ? 0.0 : orien == .portraitUpsideDown ? 180.0 : orien == .landscapeLeft ? 90 : -90 ))
                    .frame(width: vid_width, height: vid_height)
            }
        }
        .onRotate { newOrientation in
            withAnimation(.easeInOut(duration: 0.2)){
                orien = newOrientation
            }
        }
    }
}

struct DeviceRotationViewModifier: ViewModifier {
    let action: (UIDeviceOrientation) -> Void

    func body(content: Content) -> some View {
        content
            .onAppear()
            .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
                action(UIDevice.current.orientation)
            }
    }
}

extension View {
    func onRotate(perform action: @escaping (UIDeviceOrientation) -> Void) -> some View {
        self.modifier(DeviceRotationViewModifier(action: action))
    }
}

推荐答案

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.windowsUIApplication.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,同时仍然可以实现您想要的.

Ios相关问答推荐

如何创建自定义组件来接受(和传递)所有可能的Textfield参数?

在iOS中禁用URLSession自动重试机制

IFrame内容拒绝仅在iOS浏览器上加载-即使发布了内容-安全-策略(CSP)框架-祖先指令

以编程方式更改VC&39;S导航栏按钮的属性,以响应另一个VC中的按钮按下

SwiftUI图表-默认情况下,X轴数据点的间距不相等

ITMS-90432:将应用上传到应用store 时出现问题

过渡动画在 iOS16 中不起作用,但在 iOS15 中起作用

小部件链接有效,但不执行任何操作

如何根据设备时间设置 Flutter 应用时间线?

.onChange(of: ) 一次用于多个 @State 属性?

UICollectionView - 水平滚动,水平布局?

Select UITableViewCell 时 UIView backgroundColor 消失

如何在导航栏右侧添加多个 UIBarButtonItem?

以编程方式获取故事板 ID?

从 NSData 或 UIImage 中查找图像类型

在 iPhone 中将 UIViewController 显示为弹出窗口

Xcode 8:函数类型不能有参数标签 destruct 我的构建

Xcode:此时无法安装此应用程序.

AVAudioRecorder 抛出错误

iTunes Connect 中的无限制 Web 访问是什么意思