ViewLayout润饰符

  • 怎么设置一个自定义View的frame
  • 怎么运用padding填充操控各个视图周围的间隔
  • 怎么运用GeometryReader供给相对尺度
  • 怎么将内容放置在safeArea之外
  • 怎么回来不同类型的视图
  • 怎么运用ForEach在循环中创立视图
  • 怎么运用layoutPriority()操控布局优先级
  • 怎么使两个视图具有相同的宽度或高度
  • 怎么运用forgoundStyle供给视觉结构
  • 怎么运用自定义内容刺进safeArea
  • 怎么躲藏主页指示器和其他体系UI
  • 怎么阻挠体系手势搅扰你自己的手势
  • 怎么在VStack和HStack之间动态切换
  • 怎么运用Layout Protocol创立自定义布局
  • 怎么运用ViewThatFits创立自适应布局
  • 怎么向safeArea增加额定的填充
  • 怎么依据视图巨细和方位动态调整视图的外观
  • 怎么调整一个视图相关于其container的巨细

概述

文章首要分享SwiftUI Modifier的学习过程,将运用事例的办法进行阐明。内容深入浅出,对成果大部分进行了截图展现,也有偷懒的部分,不过测试代码是彻底的。假如想要运转成果,能够移步Github下载code -> github事例链接

1、怎么设置一个自定义View的frame

默许状况下,SwiftUI的视图仅占用所需的空间,但假如更改,能够运用frame()来操控

1.1 .frame(minWidth: 0, maxWidth: 200, minHeight: 0, maxHeight: 200)

创立一个最大宽高200*200的Button

Button {
    print("Button tapped")
} label: {
    Text("Welcome")
        .frame(minWidth: 0, maxWidth: 200, minHeight: 0, maxHeight: 200)
        .font(.largeTitle)
}
.background(.black)

1.2、.infinity

指定View的最小宽度和最小高度,最大宽度和高度为无穷大,是文本视图填充整个可用空间

Text("蜀道难,难于上青天!")
    .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
    .font(.largeTitle)
    .foregroundStyle(.white)
    .background(.red)

调试成果:

SwiftUI基础篇ViewLayout

2、怎么运用padding填充操控各个视图周围的间隔

在SwiftUI中,能够运用润饰符在视图的周围单独填充padding(),从而使视图间隔相邻的视图更远

2.1、padding

不带任何参数运用,Text(“Hello, SwiftUI!”)的padding的设定是在上下左右一切的边上

VStack {
    Text("Hello, World!")
    Text("Hello, SwiftUI!")
        .padding()
    Text("Hello, FF!")
    Text("Hello, BBLv!")
}

2.2、padding(.bottom)

增加方位参数,只针对Text的bottom设置

VStack {
    Text("Hello, World!")
    Text("Hello, SwiftUI!")
        .padding(.bottom)
    Text("Hello, FF!")
}

2.3、padding(100)

增加间隔参数,设置目标到边的具体间隔,默许是一切边全部填充

VStack {
    Text("Hello, World!")
    Text("Hello, SwiftUI!")
        .padding(100)
    Text("Hello, FF!")
    Text("Hello, BBLv!")
}

2.4、padding(.bottom, 100)

一起增加方位与间隔参数

VStack {
    Text("Hello, World!")
    Text("Hello, SwiftUI!")
        .padding(.bottom, 100)
    Text("Hello, FF!")
}

调试成果:

SwiftUI基础篇ViewLayout
SwiftUI基础篇ViewLayout

3、怎么运用GeometryReader供给相对尺度

在SwiftUI中,最好的挑选是让体系自己主动布局,但也能够运用GeometryReader ,例如你期望两个View占有屏幕上可用宽度的一半,假如运用硬编码是不可能的,由于我事先不知道屏幕宽度是多少,为了处理这个问题GeometryReader为咱们供给了一个输入值,告知咱们能够运用的宽度和高度,然后咱们能够将其用于咱们的任何核算。

我现在创立两个视图,期望其间一个占有父视图的1/3,另一个占有2/3
GeometryReader(content: { geometry in
    HStack(spacing: 0, content: {
        Text("Left")
            .font(.largeTitle)
            .foregroundStyle(.black)
            .frame(width: geometry.size.width * 0.33)
            .background(.yellow)
        Text("Right")
            .font(.largeTitle)
            .foregroundStyle(.black)
            .frame(width: geometry.size.width * 0.67)
            .background(.orange)
    })
})
.frame(height: 50)

注意:GeometryReader 不考虑视图层次结构中的任何偏移或间隔,这便是 HStack 上没有间隔的原因 – 假如咱们答应其间的间隔,则视图关于可用空间来说会有点太大 。经过GeometryReader设置的就超出了本来视图frame

调试成果

SwiftUI基础篇ViewLayout

4、怎么将内容放置在safeArea之外

默许状况下,SwiftUI视图将大部分留在安全区域内,它们将抵达屏幕底部,但不会挨近设备顶部的任何凹口,假如想更改,设置真正的全屏,增加润饰符ignoresSafeArea()

Text("Hello, World!")
    .frame(minWidth: 100, maxWidth: .infinity, minHeight: 100, maxHeight: .infinity)
    .foregroundStyle(.white)
    .background(.orange)
    .ignoresSafeArea()

调试成果

SwiftUI基础篇ViewLayout

5、怎么回来不同类型的视图

凭借名为 @ViewBuilder 的特殊特点,任何 SwiftUI 的 body 特点都会主动取得回来不同视图的能力。 这是运用 Swift 的成果生成器体系完结的,它了解怎么依据咱们运用程序的状态呈现两个不同的视图。可是,这种相同的功用并不是主动无处不在,这意味着您创立的任何自定义特点都必须回来相同的视图类型。

5.1、Group

计划一:将输出包装在一个Group中,依据下面的比如,不管你回来图画仍是文本视图,它们都会回来到一个组中

var tossResult: some View {
    Group {
        if Bool.random() {
            Image("chincoteague")
                .resizable()
                .scaledToFit()
        } else {
            Text("Better luck next time")
                .font(.title)
        }
    }
    .frame(width: 400,height: 150)
}

5.2、AnyView

计划二:运用类型擦除的包装器,回来AnyView

var tossResult2: some View {
    if Bool.random() {
        return AnyView(Image("chincoteague")
            .resizable()
            .scaledToFit()
        )
    } else {
        return AnyView( Text("Better luck next time")
            .font(.title)
        )
    }
}

5.3、@ViewBuilder

计划三:为特点主动增加@ViewBuilder特点包装器,将特点符号,使其主动取得回来不同视图的能力

@ViewBuilder var tossResult3: some View {
    if Bool.random() {
        Image("chincoteague")
            .resizable()
            .scaledToFit()
    } else {
        Text("Better luck next time")
            .font(.title)
    }
}

5.4、创立一个Struct

计划四:大多数状况下最有用的处理计划,是将视图分化为更小的视图,然后依据需求组合在一起

struct TossResult4: View {
    var body: some View {
        if Bool.random() {
            Image("chincoteague")
                .resizable()
                .scaledToFit()
        } else {
            Text("Better luck next time")
                .font(.title)
        }
    }
}

关于body内的调试代码

var body: some View {
    List {
        Section {
            VStack {
                Text("Coin Flip")
                    .font(.largeTitle)
                tossResult
            }
        }
        Section {
            VStack {
                Text("Coin Flip")
                    .font(.largeTitle)
                tossResult2
                    .frame(width: 400, height: 150)
            }
        }
        Section {
            VStack {
                Text("Coin Flip")
                    .font(.largeTitle)
                tossResult3
                    .frame(width: 400, height: 150)
            }
        }
        Section {
            VStack {
                Text("Coin Flip")
                    .font(.largeTitle)
                TossResult4()
                    .frame(width: 400, height: 150)
            }
        }
    }
}

关于回来type的总结:

  1. 关于类型擦除,他会有用地迫使Swift忘记内部的具体类型,从而使他们看起来像是相同的东西,不过,这会带来功能成本,因而不要经常运用它。
  2. GroupAnyView都完结了相同的成果,两之间更推荐Group,关于SwiftUI来讲更加的高效
  3. 自定义struct这特别有助于分化逻辑和布局,而且还有一个优点是使你的视图在app的其他的当地能够重用。SwiftUI会主动折叠你的视图层次结构,因而当你分化视图时,不会产生有异议的功能差异。

调试成果

SwiftUI基础篇ViewLayout

6、怎么运用ForEach在循环中创立视图

你通常会发现需求循环序列来创立视图,而在 SwiftUI 中这是运用 ForEach 完结的。 重要提示:很容易看到 ForEach 并以为它与 Swift 序列上的 forEach() 办法相同,但状况并非如此。

SwiftUI 中的 ForEach 自身便是一个视图结构,这意味着假如需求,您能够直接从视图主体回来它。 你为其供给一个项目数组,而且您可能还需求告知 SwiftUI 怎么仅有地识别每个项目,以便它知道怎么在值更改时更新它们。 您还能够向其传递一个要运转的函数,以便为循环中的每个项目创立视图。

6.1、倒计时

关于规模内的简略循环,你将规模直接传递到 ForEach 中,并告知 Swift 运用每个数字作为项意图仅有标识符。 例如,从 10 计数到 1,然后在结尾增加一条音讯:倒计时完毕。

id: .self 部分是必需的,以便 SwiftUI 能够仅有地标识数组中的每个元素 – 这意味着假如您增加或删除一个项目,SwiftUI 确切地知道是哪一个。

ForEach((1...10).reversed(), id: \.self) {
    Text("\($0)...")
}
Text("Ready or not, here I come!")

6.2、经过colors数组遍历创立

创立一个包括三种色彩的数组,循环遍历一切色彩,并运用每种色彩的称号和色彩值来创立文本View

let colors: [Color] = [.red, .green, .blue]
ForEach(colors, id: \.self) { color in
    Text(color.description.capitalized)
        .padding()
        .background(color)
}

6.3、经过id特点来区分

假如数组中有自定义类型,则应运用id类型中的任何特点来仅有表明它。例如,下面创立了一个结构体,其间id特点为UUID,这意味着它保证是仅有的。

struct SimpleGameResult {
    let id = UUID()
    let score: Int
}

ForEach告知SwiftUI能够经过查看特点来区分内部的视图

let results = [
    SimpleGameResult(score: 8),
    SimpleGameResult(score: 5),
    SimpleGameResult(score: 10)
]
ForEach(results, id: \.id) { result in
    Text("Result: \(result.score)")
}

6.4、Identifiable

SwiftUI 中的 Identifiable 是一个协议(protocol),用于为调集类型中的元素供给仅有标识符。它要求类型拥有一个名为 id 的特点,该特点用于仅有标识调集中的每个元素。 作为代替计划,能够创立一个恪守Identifiable协议的结构体,恪守此协议意味着必定要增加一个id成员变量,用来符号每个对象

struct IdentifiableGameResult: Identifiable {
    var id = UUID()
    var score: Int
}

能够省略id: \.id参数

let results1 = [
    IdentifiableGameResult(score: 8),
    IdentifiableGameResult(score: 5),
    IdentifiableGameResult(score: 10)
]
ForEach(results1) { result in
    Text("Result: \(result.score)")
}

调试成果

SwiftUI基础篇ViewLayout
SwiftUI基础篇ViewLayout

7、怎么运用layoutPriority()操控布局优先级

SwiftUI 的layoutPriority() 润饰符让咱们能够恳求在空间有限时在屏幕上为某些视图供给更多空间。 默许状况下,一切视图的布局优先级均为 0,但假如你发现某个特定视图被压缩,你能够运用layoutPriority() 要求为其赋予更高的优先级。

//由于两个字符串都很长,在iPhone上会主动换行,而且SwiftUI会尝试公正的调整它们的巨细,
//以便它们依据自己的长度取得更大的空间
HStack{
    Text("蜀道难,难于上青天!")
    Text("蚕丛及鱼凫,开国何茫然!")
}
//这里运用layoutPriority润饰符来改变两个字符串的优先级,SwiftUI将核算低优先级文本视图所需的最小空间,
//然后将剩下空间供给给高优先级的文本,以便它能够占有更大的空间
HStack{
    Text("蜀道难,难于上青天!")
    Text("蚕丛及鱼凫,开国何茫然!")
        .layoutPriority(1)
}

调试成果

SwiftUI基础篇ViewLayout

8、怎么使两个视图具有相同的宽度或高度

SwiftUI能够轻松创立两个相同巨细的视图,不管你想要相同的高度仍是宽度,经过组合润饰符fixedSize

iOS上,要害时为每个要调整巨细的视图供给无限量的最大宽或高度,这将主动使其拉伸以填充一切的可用空间。然后,你将其运用于fixedSize它们地点的容器,这告知SwiftUI这些视图应该只占用它们所需求的空间。

8.1、fixedSize(horizontal: false, vertical: true)

此比如展现了两个文本视图具有相同的高度,即使文本视图长度差异很大,由于frame和fixedSize组合运用,两个文本视图都以相同巨细布局的

HStack{
    Text("蜀道难.")
        .foregroundStyle(.white)
        .padding()
        .frame(maxHeight: .infinity)
        .background(.red)
    Text("蜀道之难,难于上青天!蚕丛及鱼凫,开国何茫然!")
        .foregroundStyle(.white)
        .padding()
        .frame(maxHeight: .infinity)
        .background(.green)
}
.fixedSize(horizontal: false, vertical: true)
.frame(maxHeight: 200)

8.2、fixedSize(horizontal: true, vertical: false)

两个视图宽度相同

VStack {
    Button("Log in") { }
        .foregroundStyle(.white)
        .padding()
        .frame(maxWidth: .infinity)
        .background(.red)
        .clipShape(Capsule())
    Button(" Reset Password") { }
        .foregroundStyle(.white)
        .padding()
        .frame(maxHeight: .infinity)
        .background(.green)
        .clipShape(Capsule())
}
.fixedSize(horizontal: true, vertical: false)

调试成果

SwiftUI基础篇ViewLayout

9、怎么运用forgoundStyle供给视觉结构

SwiftUI 供给了 foregroundStyle() 润饰符来一起操控文本、图画和形状的款式设置办法。 最简略的办法类似于将 foregroundStyle() 与 .secondary 一起运用,但它不仅解锁了更多语义色彩 – .tertiary.quaternary,还增加了对任何契合 ShapeStyle 的支撑。

9.1、quaternary

HStack {
    Image(systemName: "clock.fill")
    Text("Set the time")
}
.font(.largeTitle.bold())
.foregroundStyle(.quaternary)

9.2、ShapeStyle

foregroundStyle恪守ShapeStyle协议,这就意味这任何恪守ShapeStyle协议的View都能够润饰,此事例设置Hsatck的线性突变

HStack {
    Image(systemName: "clock.fill")
    Text("Set the time")
}
.font(.largeTitle.bold())
.foregroundStyle(
    .linearGradient(colors: [.red, .black], startPoint: .top, endPoint: .bottom)
)

调试成果

SwiftUI基础篇ViewLayout

10、怎么运用自定义内容刺进safeArea

SwiftUI供给了一个safeAreaInset润饰符,答应咱们将内容放置在安全区域之外,一起还能够让其他视图调整其布局,使其内容坚持可见,以保证一切的内容都能够依照预期显现在屏幕上。这与ignoresSafeArea不同,后者仅仅扩展视图边际。

10.1、safeAreaInset(edge: .bottom, spacing: 100)

在iOS15.2之前,这只适用于ScrollView,在15.2之后,List和Form也能够用

NavigationStack {
    List(0..<100) { i in
        Text("Row \(i)")
    }
    .navigationTitle("Select a row")
    .safeAreaInset(edge: .bottom, spacing: 100) {
        Text("Outside Safe Area")
            .font(.largeTitle)
            .foregroundStyle(.white)
            .frame(maxWidth: .infinity)
            .padding()
            .background(.indigo)
    }
}

10.2、alignment

NavigationStack {
    List(0..<100) { i in
        Text("Row \(i)")
    }
    .navigationTitle("Select a row")
    .safeAreaInset(edge: .bottom, alignment: .trailing) {
        Button {
            print("Show help")
        } label: {
            Image(systemName: "info.circle.fill")
                .font(.largeTitle)
                .symbolRenderingMode(.multicolor)
                .padding(.trailing)
        }
        .accessibilityLabel("Show help")
    }
}

调试成果

此个事例code由于自己偷懒,没有做两个View,假如想看到这两种调试成果,请用最愚蠢的办法,CV大法~

SwiftUI基础篇ViewLayout
SwiftUI基础篇ViewLayout
SwiftUI基础篇ViewLayout

11、怎么躲藏主页指示器和其他体系UI

SwiftUI 的 persistenceSystemOverlays() 润饰符让咱们能够显现或躲藏所谓的“非瞬态体系视图”,这些视图主动放置在咱们的 UI 上 – 苹果对主页指示器的称号、iPad 上的多任务指示器等等。

Text("This needs to take up lots of space")
    .frame(maxWidth: .infinity,maxHeight: .infinity)
    .background(.yellow)
    .persistentSystemOverlays(.hidden)

调试成果

这个润饰符我也没太理解,有高手能给具体解说解说吗?

SwiftUI基础篇ViewLayout

12、怎么阻挠体系手势搅扰你自己的手势

SwiftUI 的 defersSystemGestures() 润饰符答应咱们恳求咱们的手势优先于体系自己的内置手势。 这在许多当地都很重要,例如用户可能会频繁滑动的游戏,或者当您将自己的手势放在屏幕边际时。

Text("Current value: \(input)")
    .frame(maxWidth: .infinity, maxHeight: .infinity)
    .contentShape(Rectangle())
    .gesture(
        DragGesture().onChanged({ value in
            input = value.location.y - value.startLocation.y
        })
    )
    .defersSystemGestures(on: .vertical)

在 iOS 上,它做了三件不同的工作:

  1. 假如用户从顶部向下拉,他们会看到一个需求再次拉动的小选项卡,而不是立即呈现操控中心 – 他们更难意外激活操控中心。
  2. 主页指示器将淡出至较低的不透明度,假如用户直接拖动该淡出的主页指示器,则主页指示器将淡入。然后,他们能够再次向上滑动以进入任务切换器或主屏幕。
  3. 假如用户从底部向上滑动到主页指示器的恣意一侧,则会产生咱们的拖动手势。

调试成果

SwiftUI基础篇ViewLayout
SwiftUI基础篇ViewLayout

13、怎么在VStack和HStack之间动态切换

SwiftUI 的 AnyLayout 结构答应咱们依据咱们想要考虑的任何环境上下文在 HStack 和 VStack 之间自在切换 – 只需记住运用每个的契合布局的变体即可。

与运用AnyView不同,AnyLayout不会产生任何功能影响,而且不会丢弃其子视图的任何状态。

13.1、horizontalSizeClass

@Environment(\.horizontalSizeClass) var horizontalSizeClass
let layout = horizontalSizeClass == .regular ? AnyLayout(HStackLayout()) : AnyLayout(VStackLayout())
layout {
    Image(systemName: "1.circle")
    Image(systemName: "2.circle")
    Image(systemName: "3.circle")
}
.font(.largeTitle)

13.2、dynamicTypeSize

@Environment(\.dynamicTypeSize) **var** dynamicTypeSize
let layout = dynamicTypeSize <= .xxxLarge ? AnyLayout(HStackLayout()) : AnyLayout(VStackLayout())
layout {
    Image(systemName: "1.circle")
    Image(systemName: "2.circle")
    Image(systemName: "3.circle")
}
.font(.largeTitle)

除了VStackLayout和HStackLayout之外,还能够运用ZStackLayout和GridLayout

调试成果

SwiftUI基础篇ViewLayout

14、怎么运用Layout Protocol创立自定义布局

SwiftUI 答应咱们运用布局协议为视图创立彻底自定义的布局,而且咱们的自定义布局能够像 HStack、VStack 或任何其他内置布局类型相同运用。

采用Layout协议只要两个要求:

  • 回来布局为其子视图需求多少空间的办法。 这将得到一个巨细主张,即父视图有多少可用空间。 这可能会被屡次调用,以便 SwiftUI 能够了解您的容器的灵活性。
  • 另一种办法实践大将这些子视图放置在您想要的方位。 这将被给予与第一种办法相同的巨细主张,但也会被给予特定的规模来运用——这将是

假如你做的函数很杂乱,执行起来特别慢,你也能够挑选让这些办法缓存它们的核算,这是一种猜测,现在没遇到过。 当你给出一个frame时,他的宽度或高度可能包括nil值,因而,通常在天上调用repalcingUnspecifiedDimensions,以便将任何nil值替换为非nil值


struct RadialLayout: Layout {
    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        //接受完整的主张空间,用合理的默许值替换任何nil值
        proposal.replacingUnspecifiedDimensions()
    }
    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
        //核算边界的半径
        let radius = min(bounds.size.width, bounds.size.height) / 2
        //求出圆上每个子视图之间的夹角
        let angle = Angle.degrees(360 / Double(subviews.count)).radians
        for (index, subView) in subviews.enumerated() {
            //问询这个视图的理想尺度
            let viewSize = subView.sizeThatFits(.unspecified)
            //核算x、y的方位,时视图位于圆的边际内
            let xPos = cos(angle * Double(index) - .pi / 2) * (radius - viewSize.width / 2)
            let yPos = cos(angle * Double(index) - .pi / 2) * (radius - viewSize.height / 2)
            //运用其自然巨细(未指定),将此视图相关于咱们的中心方位。
            let point = CGPoint(x: bounds.midX + xPos, y: bounds.midY + yPos)
            subView.place(at: point, anchor: .center, proposal: .unspecified)
        }
    }
}
struct FFViewLayoutProtocol: View {
    @State private var count = 16
    var body: some View {
        //我现在能够像任何其他布局类型相同运用它。例如,我能够在周围放置一些形状,运用stepper来操控显现的数量
        RadialLayout {
            ForEach(0..<count, id: \.self) {_ in
                Circle()
                    .frame(width: 32, height: 32)
            }
        }
        .safeAreaInset(edge: .bottom) {
            Stepper("Count: \(count)",value: $count.animation(), in: 0...36)
                .padding()
        }
    }
}

调试成果

SwiftUI基础篇ViewLayout

15、怎么运用ViewThatFits创立自适应布局

SwiftUI 为咱们供给了 ViewThatFits,以便咱们能够依据合适可用屏幕空间的布局从几种可能的布局中挑选一种。 这使其成为保证您的运用程序从最大的 tvOS 屏幕到最小的 Apple Watch 都具有超卓外观的绝佳办法。

15.1、ViewThatFits根底办法

在最简略的办法中,你应该从最首选到最不首选列出您想要的一切布局代替计划,SwiftUI 将尝试一切这些,直到找到合适该空间的一个。比如挑选顺序为:大文本长标题 -> 大文本短标题 -> 小文本短标题

ViewThatFits {
    Label("Wecome to AwsoemApp", systemImage: "bolt.shield")
        .font(.largeTitle)
    Label("Wecome", systemImage: "bolt.shield")
        .font(.largeTitle)
    Label("Wecome", systemImage: "bolt.shield")
}

15.2、多个按钮的横/纵主动布局

创立一个具有四个不同按钮的button,然后能够依据空间巨细决定水平仍是笔直排列它们,这是非常有用的自定义布局的一种办法。

struct OptionsView: View {
    var body: some View {
        Button("Log in") { }
            .buttonStyle(.borderedProminent)
        Button("Create Account") { }
            .buttonStyle(.bordered)
        Button("Settings") { }
            .buttonStyle(.bordered)
        Spacer().frame(width: 50, height: 50)
        Button("Need Help?") {}
    }
}
ViewThatFits {
    HStack {
        OptionsView()
    }
    VStack {
        OptionsView()
    }
}

15.3、ViewThatFits处理文本布局的办法

在SwiftUI中,文本更喜爱放在一行上,默许状况下ViewTahtFits更喜爱避免布局导致文本换行。因而,当空间有限时,像这样的代码会默许为VStack,而不是运用带有换行文本的HStack

ViewThatFits {
    HStack {
        Text("The rain")
        Text("in Spain")
        Text("falls mainly")
        Text("on the Spaniards")
    }
    VStack {
        Text("The rain")
        Text("in Spain")
        Text("falls mainly")
        Text("on the Spaniards")
    }
}
.font(.title)

这里产生的是ViewThatFits正在水平缓笔直测量咱们的文本,并试图找到合适这两个维度的文本的状况–文本合适一行的状况,而不被切断笔直。这有时会导致问题,但幸运的事,我能够告知ViewThatFits只关心一个维度,这样就能够取得更多的操控。

15.4、ViewThatFits在ScrollView的运用

假设你想要向用户显现一些条款和条件,假如能够在有限的空间内展现,那么将其作为固定文本,否者将其显现为翻滚文本

let terms = String(repeating: "abcde", count: 100)
ViewThatFits() {
    Text(terms)
    ScrollView {
        Text(terms)
    }
}

15.5、ViewThatFits(in: .vertical)

除非有一个巨大的屏幕,否则将始终挑选翻滚版本,由于咱们要求ViewThatFits关心文本的水平轴和笔直轴,这意味着一旦文本超越一行便是这个成果。SwiftUI更倾向于这种布局,为了处理这个问题,咱们需求限制ViewThatFits为仅测量笔直轴(.vertical)

ViewThatFits(in: .vertical) {
    Text(terms)
    ScrollView {
        Text(terms)
    }
}

调试成果

15.4和15.5这两个比如不应该放在List中,产生了搅扰,永远都是全部文本展现,不会cell内翻滚,能够将其逐一放在body中查看成果

SwiftUI基础篇ViewLayout
SwiftUI基础篇ViewLayout

16、怎么向safeArea增加额定的填充

SwiftUI 中的 safeAreaPadding() 润饰符能够挑选性地将安全区域内部缩进必定的间隔,能够挑选在一切边际或子集上进行缩进。在处理翻滚内容时,它与运用普通的 padding() 润饰符的行为不同,由于它会将翻滚视图的内容缩进,而不是缩进翻滚视图自身。

简而言之,safeAreaPadding() 润饰符是用于处理安全区域内部内容的缩进。安全区域是指屏幕上不被刘海、下巴、圆角等设备特定元素所覆盖的区域。

经过运用 safeAreaPadding() 润饰符,您能够保证内容不会与设备的安全区域堆叠,以供给更好的用户体验和可读性。

16.1 safeAreaPadding根底用法

//绘制一个红色圆,安全区域设置为50
Circle()
    .fill(.red)
    .safeAreaPadding(50)
//只操控横向 50
Circle()
    .fill(.red)
    .safeAreaPadding(.horizontal, 50)
// Edgeinsets,四边别离操控
Circle()
    .fill(.red)
    .safeAreaPadding(.init(top: 20, leading: 50, bottom: 20, trailing: 50))

16.2、关于ScrollView

当我对ScrollView运用safeAreaPadding时,工作就变得有趣了,由于它保证咱们的内容从屏幕的边际开始。

ScrollView(.horizontal) {
    HStack {
        ForEach(0..<10) {i in
            Circle()
                .frame(width: 50, height: 50)
        }
    }
}
.safeAreaPadding(50)

调试成果

SwiftUI基础篇ViewLayout
SwiftUI基础篇ViewLayout
SwiftUI基础篇ViewLayout

17、怎么依据视图巨细和方位动态调整视图的外观

SwiftUI 的 VisualEffect() 润饰符答应咱们在不运用 GeometryReader 的状况下读取视图的几何署理,这意味着咱们能够查看视图的巨细和方位,而不影响其布局行为。

重要提示:此修改器专门用于运用视觉作用,例如调整色彩或增加含糊,而且无法调整出于布局意图核算内容结构的办法。 它能够调整类似结构的内容,例如视图的偏移和份额,由于它们不会影响布局。

17.1、含糊作用

代码将翻滚视图中的每个视图含糊必定的含糊量,该含糊量是依据视图距其翻滚视图中心的间隔核算的。 这意味着笔直中心附近的视图很少或没有含糊,而外部的视图则严重含糊:

ScrollView {
    ForEach(0..<100) { i in
        Text("Row \(i)")
            .font(.largeTitle)
            .frame(maxWidth: .infinity)
            .visualEffect { content, proxy in
                content.blur(radius: blurAmount(for: proxy))
            }
    }
}
func blurAmount(for proxy: GeometryProxy) -> Double {
    let scrollViewHeight = proxy.bounds(of: .scrollView)?.height ?? 100
    let ourCenter = proxy.frame(in: .scrollView).midY
    let distanceFromCenter = abs(scrollViewHeight / 2 - ourCenter)
    return Double(distanceFromCenter) / 100
}

调用proxy.frame(in: .scrollView)在包括该视图的最内层翻滚视图中查找该视图的巨细

17.2、rotationEffect

这些视觉作用适用于任何类型的方位,包括经过动画生成的方位。 例如,这使得网格中的一系列圆圈旋转,每个圆圈依据色彩旋转动态重新上色。

@State private var rotationAmount = 0.0
var body: some View {
    Grid {
        ForEach(0..<3) {_ in
            GridRow{
                ForEach(0..<3) {_ in
                    Circle()
                        .fill(.green)
                        .frame(width: 100, height: 100)
                        .visualEffect { content, proxy in
                            content.hueRotation(.degrees(proxy.frame(in: .global).midY / 2))
                        }
                }
            }
        }
    }
    .rotationEffect(.degrees(rotationAmount))
    .onAppear {
        withAnimation(.linear(duration: 5).repeatForever(autoreverses: false)) {
            rotationAmount = 360
        }
    }
}

调试成果

SwiftUI基础篇ViewLayout
SwiftUI基础篇ViewLayout
SwiftUI基础篇ViewLayout

18、怎么调整一个视图相关于其container的巨细

SwiftUI 的 containerRelativeFrame() 是一种简略但功用强大的办法,能够使视图具有相关于其容器的巨细,容器可能是它们的整个窗口、它们地点的翻滚视图,甚至仅仅布局中的一列。

您需求供给三个中心值:您尝试设置哪个轴,您想要将空间划分为多少部分,以及应该为每个视图分配多少部分。

ScrollView(.horizontal, showsIndicators: false) {
    HStack {
        ForEach(0..<10) { i in
            Text("Item \(i)")
                .foregroundStyle(.white)
                .containerRelativeFrame(.horizontal, count: 5, span: 2, spacing: 10)
                .background(.blue)
        }
    }
}

比如针对ScrollView中的视图,它们应该是其容器宽度的2/5,需求明确的是:

  • count参数是指ScrollView的水平空间应该分红多少部分,
  • span参数指的是应该为每个文本视图分配多少部分,

我运用5表明count,这意味着scrollView的水平空间被分为5,然后用2表明span,这意味着每个文本视图将分配2/5的空间

这种不均匀的跨度意味着当app运转时,用户将看到2.5个视图,让用户知道scrollview后面还有视图 怎么划分取决你的参数

关于运用容器相对结构有两个点:

  1. 假如需求,能够运用多个轴 [. horizontal, .vertical]
  2. 默许的对齐办法为.center,可是你能够用任何你想要的来指定自定义的对齐办法参数

调试成果:

SwiftUI基础篇ViewLayout