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

时间:2020-08-24 21:24:25

标签: ios swiftui

我正在使用SwiftUI开发iPhone应用程序。主网格使用嵌套在VStack中的HStack。列数在初始化时是可变的,但此后固定。行数是完全动态的。当网格扩展到超出屏幕的宽度或高度时,它将其他视图推开。

我希望它向右(在初始化时)和向下(动态)在屏幕外以及其他视图后面扩展。我尝试将所有内容放入ZStack并设置其zindice,但这没有用。有什么办法可以做到?还是我需要一种新的方法?

//
//  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

1 个答案:

答案 0 :(得分:0)

这是固定的零件-为避免布局损坏,您必须将动态零件移出当前布局,例如。在空白区域的叠加中。还添加了一些较小的修复程序,例如剪切和对齐。

通过Xcode 12 / iOS 14测试

demo

    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)
}