我正在尝试遵循为应用程序创建的设计,其中有一些对象放置在屏幕中间。
对象的大小和填充应该与设备的屏幕大小成比例,这意味着如果屏幕比我们在设计中作为基础的屏幕大(在这种情况下,基础是iPhone 11的屏幕),它们应该看起来更大。此外,这些对象内部有更多的对象,也应该与屏幕大小成比例。例如:放置在圆角矩形边框内的文本视图,如果屏幕比用作基础的屏幕大,则字体应该增长;或者是一个图像中的另一个图像。在这些例子中,对象和其中的对象都应该与屏幕大小成比例。
到目前为止,我们正在使用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 goesnsome 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()
}
}
Scale.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
}
}
问题/请求
我想简化这段代码和存储的几何阅读器数据(规模结构与它的信息)在一个ObservedObject或一个EnvironmentObject,以便我们可以在不同的视图和文件在整个项目中使用它。问题是,我们不能得到GeometryReader数据,直到视图加载,一旦视图加载,我相信我们不能再声明ObservedObject或EnvironmentObject(这是正确的吗?)。
我知道可能有一种方法来获得屏幕尺寸,而不使用这里所示的几何阅读器:如何获得iPhone的屏幕宽度在SwiftUI?但是,如果我使用GeometryReader来获取另一个视图内的视图的大小,我希望将其信息也存储起来。
我们的目标是不要在每个需要使用scale的视图中使用这段代码:let scale:Scale
init (_ scale:Scale) {
self.scale = scale
}
,而不是使用ObservedObject或EnvironmentObject从需要它的视图中获取比例数据。因此,我如何使用observedoobject或EnvironmentObject来存储GeometryReader数据?
我倾向于认为你这样做有点违背了SwiftUI的一般原则(即基于屏幕大小的东西,而不是使用内置的SwiftUI布局原则,像padding
一样与屏幕大小无关)。但是,假设您想继续执行该计划,我建议使用@Envrionment
值。我不认为它需要是@EnvironmentObject
,因为Scale
是struct
,并且没有令人信服的理由使用引用类型来框值。
下面是一个简单的例子:
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))")
}
}