简体   繁体   English

SwiftUI - 水平扩展主网格将我的其他视图推到屏幕外

[英]SwiftUI - Expanding the main grid horizontally pushes my other views offscreen

I am working on an iPhone app using SwiftUI.我正在使用 SwiftUI 开发 iPhone 应用程序。 The main grid uses HStacks nested inside a VStack.主网格使用嵌套在 VStack 中的 HStack。 The number of columns will be variable on initialization, but fixed thereafter.列数在初始化时可变,但之后固定。 The number of rows is completely dynamic.行数是完全动态的。 When the grid expands beyond the width or height of the screen it pushes the other views out of the way.当网格扩展到超出屏幕的宽度或高度时,它会将其他视图推开。

I would like it to just expand rightward (on initialization) and downward (dynamically) offscreen and behind the other views.我希望它向右(在初始化时)和向下(动态)在屏幕外和其他视图后面扩展。 I tried putting everything in a ZStack and setting their zindices, but that didn't work.我尝试将所有内容放入 ZStack 并设置它们的 zindice,但这不起作用。 Is there any way to do this or do I need a new approach?有什么办法可以做到这一点还是我需要一种新的方法?

//
//  GameView.swift
//  Scoreboard
//
//  Created by user926153 on 8/21/20.
//  Copyright © 2020 user926153. All rights reserved.
//
//
//

/*
 This uses the TrackableScrollView as created by Max Natchanon here:
https://medium.com/@maxnatchanon/swiftui-how-to-get-content-offset-from-scrollview-5ce1f84603ec
 */

import SwiftUI
import UIKit

struct ScrollOffsetPreferenceKey: PreferenceKey {
    typealias Value = [CGFloat]
    
    static var defaultValue: [CGFloat] = [0]
    
    static func reduce(value: inout [CGFloat], nextValue: () -> [CGFloat]) {
        value.append(contentsOf: nextValue())
    }
}

struct TrackableScrollView<Content>: View where Content: View {
    let axes: Axis.Set
    let showIndicators: Bool
    @Binding var contentOffset: CGFloat
    let content: Content
    
    init(_ axes: Axis.Set = .vertical, showIndicators: Bool = true, contentOffset: Binding<CGFloat>, @ViewBuilder content: () -> Content) {
        self.axes = axes
        self.showIndicators = showIndicators
        self._contentOffset = contentOffset
        self.content = content()
    }
    
    var body: some View {
    GeometryReader { outsideProxy in
        ScrollView(self.axes, showsIndicators: self.showIndicators) {
            ZStack(alignment: self.axes == .vertical ? .top : .leading) {
                GeometryReader { insideProxy in
                    Color.clear
                        .preference(key: ScrollOffsetPreferenceKey.self, value: [self.calculateContentOffset(fromOutsideProxy: outsideProxy, insideProxy: insideProxy)])
                        // Send value to the parent
                }
                VStack {
                    self.content
                }
            }
        }
        .onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in
            self.contentOffset = value[0]
        }
        // Get the value then assign to offset binding
    }
}
    
    private func calculateContentOffset(fromOutsideProxy outsideProxy: GeometryProxy, insideProxy: GeometryProxy) -> CGFloat {
        if axes == .vertical {
            return (outsideProxy.frame(in: .global).minY - insideProxy.frame(in: .global).minY) * -1
        } else {
            return (outsideProxy.frame(in: .global).minX - insideProxy.frame(in: .global).minX) * -1
        }
    }
}

struct GameView: View {
    let row_label_offset: CGFloat = 80
    let col_width: CGFloat = 75
    let row_height: CGFloat = 50
    
    @ObservedObject var settings: GameSettings
    @State var round_number: Int = 1
    @State var scores: [[Int]] = [[0, 0, 0, 0]]
    @State var column_offset: CGFloat = 0
    @State var row_offset: CGFloat = 0
    
    private func AddRound() {
        self.round_number += 1
        self.scores.append([0, 0, 0, 0])
    }
    
    private func DeleteRound(at offsets: IndexSet) {
        // NOTE: also have to delete round from score array
        self.round_number -= 1
        self.scores.remove(atOffsets: offsets)
    }
    
    var body: some View {
        VStack {
            VStack(alignment: .center, spacing: 10) {
                Text("Title")
                    .font(.title)
                Text("subtitle")
            }
            .border(Color.black)
            
            VStack(alignment: .leading, spacing: 10) {
                HStack {
                    Button(action: {
                        self.AddRound()
                    }) {
                        Text("New round +")
                        .fixedSize(horizontal: false, vertical: true)
                    }
                    .frame(width: self.row_label_offset, height: self.row_height)
                    
                    TrackableScrollView(.horizontal, showIndicators: false, contentOffset: $column_offset) {
                        HStack {
                            ForEach((1...7), id: \.self) {
                                Text("Player \($0)\n  total")
                                .frame(width: self.col_width, height: self.row_height)
                            }
                        }
                    }
                    .frame(height: 50)
                    .border(Color.red)
                }
                
                
                HStack {
                    TrackableScrollView(.vertical, showIndicators: false, contentOffset: $row_offset) {
                        
                        ForEach((1...self.round_number), id: \.self) { round in
                            Text("Round \(round)")
                            .frame(width: self.col_width, height: self.row_height)
                        }
                    }
                    .border(Color.black)
                    .frame(width: self.row_label_offset)
                    
                    VStack {
                        ForEach(self.scores, id: \.self) { round_score in
                            HStack {
                                ForEach(round_score, id: \.self) { score in
                                    Text("\(score)")
                                    .frame(width: self.col_width, height: self.row_height)
                                }
                                
                            }
                        }
                        
                        Spacer()
                    }
                    .offset(x: self.column_offset, y: self.row_offset)
                    
                    Spacer()
                    
                }
                .border(Color.blue)
            }
            
            Button(action: {
                
            }) {
                Text("Finish Game")
            }
            Spacer()
        }
    }
}

#if DEBUG

struct GameView_Previews: PreviewProvider {
    
    static var previews: some View {
        GameView(settings: GameSettings())
    }
}
#endif

Here is fixed part - to avoid layout corruption you have to move dynamic part out-of-current-layout , eg.这是固定部分 - 为避免布局损坏,您必须将动态部分移出当前布局,例如。 in overlay of empty space area.在空白区域的叠加中。 Also some minor fixes added, like clipping and alignment.还添加了一些小修复,例如裁剪和对齐。

Tested with Xcode 12 / iOS 14使用 Xcode 12 / iOS 14 测试

演示

    HStack {
        TrackableScrollView(.vertical, showIndicators: false, contentOffset: $row_offset) {

            ForEach((1...self.round_number), id: \.self) { round in
                Text("Round \(round)")
                .frame(width: self.col_width, height: self.row_height)
            }
        }
        .border(Color.black)
        .frame(width: self.row_label_offset)

        Color.clear.overlay (      // << from here !!
            VStack {
                ForEach(self.scores, id: \.self) { round_score in
                    HStack {
                        ForEach(round_score, id: \.self) { score in
                            Text("\(score)")
                            .frame(width: self.col_width, height: self.row_height)
                        }

                    }
                }

                Spacer()
            }
            .offset(x: self.column_offset, y: self.row_offset)
        , alignment: .topLeading)
        .clipped()
    }
    .border(Color.blue)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM