使选项卡视图垂直将视图向右移动

问题描述 投票:0回答:1

我通过旋转选项卡视图和内部视图制作了垂直滚动选项卡视图。这样做时,视图会向右移动约 10.5 点(在更大和更小的设备上或多或少)。我可以简单地添加一个偏移量,将视图向右移动 10.5 点,但这在所有设备上看起来并不好。

单独显示视图而不使用任何选项卡视图看起来不错。就在我将视图放入垂直 tabView 后,它看起来向右移动了。我希望了解一种更有效的居中方法,而不是基本的偏移修改器。您可以忽略除 AllVideoView 中的代码之外的所有代码。

import SwiftUI
import WebKit
import UIKit

struct AllVideoView: View {
    @State private var selected = ""
    @State private var arr = ["-q6-DxWZnlQ", "Bp3iu47RRJQ", "lXJdgDjw1Ks", "It3ecCpuzgc", "7WNJjr8QM1w"]
    var body: some View {
        ZStack {
            Color.black.edgesIgnoringSafeArea([.bottom, .top])
            TabView(selection: $selected){
                ForEach(arr, id: \.self){ id in
                    SingleVideoView(link: id).tag(id)
                }
                .rotationEffect(.init(degrees: -90))
                .frame(width: widthOrHeight(width: true), height: widthOrHeight(width: false))
            }
            //.offset(x: -10.5)       // this makes it align right 
            .frame(width: widthOrHeight(width: false), height: widthOrHeight(width: true))
            .rotationEffect(.init(degrees: 90))
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
        }
    }
}

struct SingleVideoView: View {
    let link: String
    @State private var viewIsShowing = false
    @State private var isVideoPlaying = false
    var body: some View {
        ZStack {
            Color.black
            
            SmartReelView(link: link, isPlaying: $isVideoPlaying, viewIsShowing: $viewIsShowing)

            Button("", action: {}).disabled(true)
            
            Color.gray.opacity(0.001)
                .onTapGesture {
                    isVideoPlaying.toggle()
                }
            
        }
        .ignoresSafeArea()
        .onDisappear {
            isVideoPlaying = false
            viewIsShowing = false
        }
        .onAppear {
            viewIsShowing = true
            isVideoPlaying = true
        }
    }
}

struct SmartReelView: UIViewRepresentable {
    let link: String
    @Binding var isPlaying: Bool
    @Binding var viewIsShowing: Bool
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIView(context: Context) -> WKWebView {
        let webConfiguration = WKWebViewConfiguration()
        webConfiguration.allowsInlineMediaPlayback = true
        let webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.navigationDelegate = context.coordinator

        let userContentController = WKUserContentController()
        
        webView.configuration.userContentController = userContentController

        loadInitialContent(in: webView)
        
        return webView
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        var jsString = """
                isPlaying = \((isPlaying) ? "true" : "false");
                watchPlayingState();
            """
        uiView.evaluateJavaScript(jsString, completionHandler: nil)
    }
    
    class Coordinator: NSObject, WKNavigationDelegate {
        var parent: SmartReelView

        init(_ parent: SmartReelView) {
            self.parent = parent
        }
        
        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            if self.parent.viewIsShowing {
                webView.evaluateJavaScript("clickReady()", completionHandler: nil)
            }
        }
    }
    
    private func loadInitialContent(in webView: WKWebView) {
        let embedHTML = """
        <style>
            body {
                margin: 0;
                background-color: black;
            }
            .iframe-container iframe {
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
            }
        </style>
        <div class="iframe-container">
            <div id="player"></div>
        </div>
        <script>
            var tag = document.createElement('script');
            tag.src = "https://www.youtube.com/iframe_api";
            var firstScriptTag = document.getElementsByTagName('script')[0];
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

            var player;
            var isPlaying = false;
            function onYouTubeIframeAPIReady() {
                player = new YT.Player('player', {
                    width: '100%',
                    videoId: '\(link)',
                    playerVars: { 'playsinline': 1, 'controls': 0},
                    events: {
                        'onStateChange': function(event) {
                            if (event.data === YT.PlayerState.ENDED) {
                                player.seekTo(0);
                                player.playVideo();
                            }
                        }
                    }
                });
            }
        
            function clickReady() {
                player.playVideo();
            }
            
            function watchPlayingState() {
                if (isPlaying) {
                    player.playVideo();
                } else {
                    player.pauseVideo();
                }
            }
        
        </script>
        """
        
        webView.scrollView.isScrollEnabled = false
        webView.loadHTMLString(embedHTML, baseURL: nil)
    }
}

func widthOrHeight(width: Bool) -> CGFloat {
    let scenes = UIApplication.shared.connectedScenes
    let windowScene = scenes.first as? UIWindowScene
    let window = windowScene?.windows.first
    
    if width {
        return window?.screen.bounds.width ?? 0
    } else {
        return window?.screen.bounds.height ?? 0
    }
}
swift xcode swiftui layout youtube
1个回答
1
投票

鉴于您已旋转

TabView
及其内部的视图来创建垂直滚动选项卡,旋转可能会导致一些空间调整,从而改变视图。这种转变就是您所经历的向右移动 10.5 点的情况。

为了使垂直

TabView
在不同屏幕尺寸上保持一致,请考虑以下替代方案:

使用

GeometryReader
获取父视图的尺寸。然后,您可以动态计算偏移以确保其居中。

GeometryReader { geometry in
    TabView(selection: $selected) {
        // your code
    }
    .rotationEffect(.init(degrees: 90))
    .offset(x: -(geometry.size.width - geometry.size.height) / 2)
}

另请参阅“如何在 SwiftUI 中使用 Geometry Reader”以获取说明。

此外,请确保内部视图和旋转的

TabView
的框架的定义方式不留有此类移动的空间。仔细检查您是否在旋转之后而不是之前定义框架。


您可以使用

.alignmentGuide
根据
TabView
的尺寸调整内部视图的水平对齐方式。

假设您想根据某些计算的偏移量移动视图 (

calculatedOffset
)。

首先,您将定义一个

HorizontalAlignment
:

extension HorizontalAlignment {
    private enum MyAlignment: AlignmentID {
        static func defaultValue(in d: ViewDimensions) -> CGFloat {
            return d[HorizontalAlignment.center]
        }
    }

    static let myAlignment = HorizontalAlignment(MyAlignment.self)
}

然后,您可以将此自定义对齐指南应用到您的

TabView
,如下所示:

TabView(selection: $selected) {
    // Your existing code
}
.rotationEffect(.init(degrees: 90))
.alignmentGuide(HorizontalAlignment.myAlignment) { d in
    // calculatedOffset would be the value you have determined you need to move your view by
    let calculatedOffset: CGFloat = 10.5  // replace this with your calculated value
    return d[HorizontalAlignment.center] - calculatedOffset
}

最后,您需要确保您的

ZStack
使用此自定义对齐方式来定位
TabView

ZStack(alignment: Alignment(horizontal: .myAlignment, vertical: .center)) {
    // your existing code, including the TabView
}

我使用了硬编码的

calculatedOffset
值 10.5 作为占位符。但您可以将其替换为动态计算的任何值,以根据需要调整视图的位置。

通过使用对齐参考线,您可以对视图布局实现非常细粒度的控制。这可能会帮助您解决遇到的问题。


作为替代方法,您可以将

TabView
包装在另一个视图中,然后操纵
TabView
在该容器内的位置。这样可以更好地控制其位置。

HStack {
    Spacer()
    TabView(selection: $selected){
        // your code
    }
    .rotationEffect(.init(degrees: 90))
    Spacer()
}

或者创建一个自定义的 SwiftUI 修饰符来封装旋转和平移逻辑,使其更容易维护和跨不同视图应用。

这个想法是根据实际尺寸动态调整视图布局,从而消除不适应不同屏幕尺寸的硬编码值。

© www.soinside.com 2019 - 2024. All rights reserved.