1

我正在尝试遵循为应用程序创建的设计,该应用程序在屏幕中间放置了一些对象。

对象的大小和填充应与设备的屏幕尺寸成比例,这意味着如果屏幕大于我们在设计中作为基础的屏幕(在本例中为 iPhone 11 屏幕),它们应该看起来更大。另外,这些物体里面的物体比较多,也应该和屏幕大小成正比。例如:放置在 RoundedRectangle 边框内的 Text 视图,如果屏幕大于用作基础的屏幕,则字体应该增长;或另一个图像中的图像。在这些示例中,对象及其内部的对象都应与屏幕尺寸成正比。

到目前为止,我们正在使用 GeometryReader 来完成这项工作。我们这样做的方式需要我们在为屏幕及其视图定义的每个文件中使用 GeometryReader。一旦我们有了 GeometryReader 数据,我们就使用 Scale 结构来获取对象的正确比例。

这是示例代码:

GeometryReaderSampleView.swift

import SwiftUI

struct GeometryReaderSampleView: View {
    var body: some View {
        NavigationView {
            GeometryReader { metrics in
                ZStack {
                    VStack {
                        LoginMainDecorationView(Scale(geometry: metrics))
                        Spacer()
                    }
                    
                    VStack {
                        HStack {
                            GreenSquareView(Scale(geometry: metrics))
                            Spacer()
                        }
                        .offset(x: 29, y: Scale(geometry: metrics).vertical(300.0))
                        Spacer()
                    }
                }
            }
        }
    }
}

struct GreenSquareView: View {
    let scale:Scale
    
    init (_ scale:Scale) {
        self.scale = scale
    }
    
    var body: some View {
        ZStack(alignment: .topLeading) {
            RoundedRectangle(cornerRadius: scale.horizontal(30))
                .fill(Color.green)
                .frame(width: scale.horizontal(157), height: scale.horizontal(146))
            
            Text("Here goes\nsome text")
                .font(.custom("TimesNewRomanPS-ItalicMT", size: scale.horizontal(20)))
                .padding(.top, scale.horizontal(29))
                .padding(.leading, scale.horizontal(19))
            
            VStack {
                Spacer()
                HStack {
                    Spacer()
                    Image(systemName: "heart.circle")
                        .resizable()
                        .frame(width: scale.horizontal(20), height: scale.horizontal(20))
                        .offset(x: scale.horizontal(-20), y: scale.vertical(-17.0))
                }
            }.frame(width: scale.horizontal(157), height: scale.horizontal(146))
        }
    }
}

struct LoginMainDecorationView: View {
    let scale:Scale
    
    init (_ scale:Scale) {
        self.scale = scale
    }
    
    var body: some View {
            HStack {
                Image(systemName: "cloud.rain")
                    .resizable()
                    .frame(width: scale.horizontal(84), height: scale.horizontal(68), alignment: .leading)
                    .offset(x: 0, y: scale.vertical(200.0))
                Spacer()
                Image(systemName: "cloud.snow")
                    .resizable()
                    .frame(width: scale.horizontal(119), height: scale.horizontal(91), alignment: .trailing)
                    .offset(x: scale.horizontal(-20.0), y: scale.vertical(330.0))
            }
    }
}

struct GeometryReaderSampleView_Previews: PreviewProvider {
    static var previews: some View {
        GeometryReaderSampleView()
    }
}

规模.swift

import SwiftUI

struct Scale {
    // Size of iPhone 11 Pro
    let originalWidth:CGFloat = 375.0
    let originalHeight:CGFloat = 734.0
    
    let horizontalProportion:CGFloat
    let verticalProportion:CGFloat
    
    init(screenWidth:CGFloat, screenHeight:CGFloat) {
        horizontalProportion =  screenWidth / originalWidth
        verticalProportion = screenHeight / originalHeight
    }
    
    init(geometry: GeometryProxy) {
        self.init(screenWidth: geometry.size.width, screenHeight: geometry.size.height)
    }
    
    func horizontal(_ value:CGFloat) -> CGFloat {
        return value * horizontalProportion
    }
    
    func vertical(_ value:CGFloat) -> CGFloat {
        return value * verticalProportion
    }
}

问题/要求

我想简化此代码并将 GeometryReader 数据(及其信息的 Scale 结构)存储在 ObservedObject 或 EnvironmentObject 中,以便我们可以在整个项目的不同视图和文件中使用它。这样做的问题是,在加载视图之前我们无法获取 GeometryReader 数据,一旦加载了视图,我相信我们不能再声明 ObservedObject 或 EnvironmentObject(正确吗?)。

我知道有一种方法可以在不使用 GeometryReader 的情况下获取屏幕尺寸,如下所示:如何在 SwiftUI 中获取 iPhone 的屏幕宽度?. 但是,如果我使用 GeometryReader 来获取另一个视图内的视图的大小,我希望也存储它的信息。

目标是不在需要使用比例的每个视图中使用此代码:

let scale:Scale
        
init (_ scale:Scale) {
    self.scale = scale
}

而是使用 ObservedObject 或 EnvironmentObject 从需要它的视图中获取比例数据。因此,如何使用 ObservedObject 或 EnvironmentObject 来存储 GeometryReader 数据?

4

1 回答 1

2

我倾向于认为您这样做是在与 SwiftUI 的一般原则作斗争(即基于屏幕尺寸而不是使用与屏幕尺寸无关的内置 SwiftUI 布局原则padding)。不过,假设您想继续执行该计划,我建议您使用一个@Envrionment值。我认为它不需要是 a @EnvironmentObject,因为Scale是 astruct并且没有令人信服的理由让引用类型来装箱值。

这是一个简单的例子:

private struct ScaleKey: EnvironmentKey {
  static let defaultValue = Scale(screenWidth: -1, screenHeight: -1)
}

extension EnvironmentValues {
  var scale: Scale {
    get { self[ScaleKey.self] }
    set { self[ScaleKey.self] = newValue }
  }
}

struct ContentView: View {
    
    var body: some View {
        GeometryReader { metrics in
            SubView()
                .environment(\.scale, Scale(geometry: metrics))
        }
    }
}

struct SubView : View {
    @Environment(\.scale) private var scale : Scale
    
    var body: some View {
        Text("Scale: \(scale.horizontal(1)) x \(scale.vertical(1))")
    }
}
于 2021-09-17T05:44:41.693 回答