2024-10-23 18:30:18
,某些文章具有时效性,若有错误或已失效,请在下方留言。视图修饰符总是把已经存在的视图包装到另⼀层中去:修饰符会变为它所作⽤的视图的⽗视图。
Padding
.padding
修饰符使⽤它收到的内边距值来修改建议尺⼨,它会从建议尺⼨的对应边上减去这个边距值。修改后的尺⼨将被提供给 .padding
的⼦视图 (也就是这个修饰符所作⽤的视图)。当⼦视图将其尺⼨报告回来时,padding 再将边距值加回到对应的边上,然后把扩展后的尺⼨作为⾃⼰的尺⼨进⾏报告。
.padding
有两个有⽤的变体:
.padding(_ inset:)
让我们能使⽤EdgeInset
值,在单次调⽤中就为不同的边指定不同的边距值。.padding(_ edges:_ length:)
让我们能够为⼀组边 (⽐如分别代表左右两边和上下两边的 .horizontal 或者 vertical) 指定⼀个边距值。
没有指定具体值,⽽是使⽤不带任何参数的 .padding() 时,则会使⽤当前平台的默认边距值。
固定框架
固定框架 (fixed frame) 修饰符 .frame(width:height:alignment)
所拥有的布局⾏为⾮常简单:它把所指定的尺⼨原封不动地提供给⼦视图,同时不管⼦视图所报告的尺⼨是多少,总是把这个指定的尺⼨作为⾃⼰的⼤⼩进⾏汇报。
如果只指定宽度
或者⾼度
,⽽把另⼀个参数设定为 nil
或者完全省略
掉它,那么固定框架将不会对该维度产⽣影响。向固定框架提供的建议尺⼨在该维度上将被直接转发给⼦视图,固定框架在这个维度上也会将⼦视图所报告的尺⼨作为⾃⼰的尺⼨。
灵活框架
灵活框架 (flexible frame
) 的 API ⽀持⾮常多的参数:我们不仅可以为框架的宽和⾼提供最⼩、最⼤和理想值,还可以为它指定对⻬⽅式。
最⼤值和最⼩值的边界会被灵活框架使⽤两次:⼀次在它提供尺⼨给⼦视图时,另⼀次在决定框架⾃⼰要报告的尺⼨时。
灵活框架会把所获得的建议尺⼨,按照指定的最⼩值和最⼤值进⾏限制:如果传⼊的尺⼨没有落在最⼩和最⼤值之间,则强制使⽤最⼩和最⼤值作为建议尺⼨并提供给框架的⼦视图。
当⼦视图的尺⼨被计算出来后,灵活框架将基于它所收到的建议尺⼨,再次应⽤设定边界值,来确定⾃⼰的尺⼨。
在实践中,这意味着如果我们只指定了 minWidth
,那么灵活框架的宽度⾄少会是 minWidth
,⽽最多会是它的⼦视图的宽度。如果我们只指定了 maxWidth
,那么灵活框架的宽度⾄少是它的⼦视图的宽度,⽽最多则为 maxWidth
。⾼度也同理。
Text("Hello, World!")
.frame(maxWidth: .infinity)
.background(.quaternary)

.frame(maxWidth: .in!nity)
模式确保了灵活框架的宽度⾄少和被建议的宽度相同,如果⼦视图的宽度要⽐框架接受的建议宽度还宽的话,则使⽤⼦视图的宽度。通常,这个模式被⽤来创建占据整个可⽤宽度的视图。
此模式添加为 View 的扩展
extension View {
func proposedWidthOrGreater() -> some View {
frame(maxWidth: .infinity)
}
}
另⼀种常⻅模式如下:
Text("Hello, World!")
.frame(minWidth: 0, maxWidth: .infinity)
.background(.teal)
这可以确保框架⽆视它的⼦视图的尺⼨,框架报告的宽度将总是它接受到的建议宽度。同样,我们可以为这个模式编写⼀个辅助函数:
extension View {
func acceptProposedWidth() -> some View {
frame(minWidth: 0, maxWidth: .infinity)
}
}

假设视图渲染到 320⨉480 的屏幕上,布局渲染的步骤如下所示:
- 系统向
padding
提出 320⨉480 作为建议尺⼨。 padding
向background
建议 300⨉460。background
将同样的 300⨉460 建议给它的主要⼦视图 (也就是frame
)。frame
把同样的 300⨉460 提供给它的⼦视图Text
。Text
汇报它的尺⼨为 76⨉17。frame
的宽度变为max(0, min(.infinity, 300)) = 300
。注意,这⾥的0
和.infinity
值都是灵活尺⼨的参数⾥指定的值。background
把灵活框架的尺⼨ (300⨉17) 提供给次要⼦视图 (Color
)。- 颜⾊视图接受这个建议,并将它作为⾃⼰的尺⼨汇报。
background
将它的主要⼦视图的尺⼨ (300⨉17) 进⾏汇报。padding
在每条边上加上 10,最终它的尺⼨为 320⨉37。
理想尺⼨,就是如果某个维度上收到的建议尺⼨为 nil
时将采⽤的尺⼨。如果指定了 idealWidth
或 idealHeight
参数,在灵活框架收到包含 nil
的建议尺⼨时 (通常来源于 fixedSize
修饰符),它会把指定的理想尺⼨提供给⼦视图,并且这个尺⼨也会被作为框架⾃身的尺⼨进⾏报告,⽽再不考虑其⼦视图的尺⼨。
宽高比
aspectRatio
修饰符在处理完全灵活的视图时⾮常有⽤。例如
Color.secondary
.aspectRatio(4/3, contentMode: .fit)

aspectRatio
修饰符将会计算出⼀个能够适配建议尺⼨的宽⾼⽐为 4/3 的矩形,然后将它作为建议尺⼨提供给⼦视图。对于⽗视图,它总是把⼦视图的尺⼨汇报上去,⽽不去理会建议尺⼨或者设定的⽐例。
假设⼀个 200×200 的建议尺⼨。aspectRatio
通过计算,得到在这个建议尺⼨下满⾜ 4/3 宽⾼⽐的最⼤矩形,此处是 200⨉150。它把这个尺⼨建议给 Color
,后者会直接接受建议。最后,aspectRatio
把 200⨉150 作为它⾃⼰的尺⼨进⾏汇报。
如果把 contentMode
从 .fit
替换为 .fill
时
Color.secondary
.aspectRatio(4/3, contentMode: .fill)
假设⼀个 200×200 的建议尺⼨,基于这个尺⼨,.aspectRatio
将会计算⼀个 4/3 宽⾼⽐,能够覆盖住整个建议尺⼨的最⼩矩形。本例中这个矩形的尺⼨约为 266.6⨉200。它把这个尺⼨建议给 Color
,后者会直接接受建议。最后,aspectRatio
把 266.6⨉200 这个⼦视图的尺⼨作为它⾃⼰的尺⼨进⾏汇报。
.aspectRatio
最常⻅的使⽤⽅式是配合图像。
Image("header")
.resizable()
.aspectRatio(contentMode: .fit)
因为我们没有指定具体的宽⾼⽐,.aspectRatio
将会 (通过提供 nil⨉nil 尺⼨) 探测⼦视图的理想尺⼨,并依据这个尺⼨计算宽⾼⽐。

假设建议尺寸为 200×200,布局渲染的步骤:
- 200⨉200 的建议尺⼨被提供给 aspectRatio。
- aspectRatio 向图像建议 nil⨉nil。
- 图像报告⾃⼰的理想尺⼨ 100⨉30。
- aspectRatio 将⼀个宽⾼⽐为 100/30 的矩形适配到 200⨉200 中,得到 200⨉60,并将这个尺⼨提供给图⽚。
- 图⽚将⾃⼰的尺⼨报告为 200⨉60。
- aspectRatio 将⼦视图的尺⼨ 200⨉60 作为⾃⼰的尺⼨进⾏报告。
覆盖层和背景
覆盖层 (overlay
) 和背景 (background
) 是 SwiftUI 中最有⽤的⼏个修饰符中的成员了。在布局⽅⾯,两者的⼯作⽅式完全相同。唯⼀的区别在于 overlay
会把次要视图绘制在主要视图的前⾯,⽽ background
则是把次要视图绘制在主要视图的后⽅。

background
⾸先确定它的主要⼦视图 (上例中的 Text
) ⼤⼩,然后将这个主要⼦视图的尺⼨作为建议尺⼨提供给次要⼦视图。这样,⻘绿⾊背景的⼤⼩就和 Text 视图⼤⼩⼀致了。我们也可以修改⼀下 Text
,让它包含⼀些边距:

背景和覆盖层都不会影响其主要⼦视图的布局,两者所报告的尺⼨始终都是主要⼦视图的尺⼨。
固定尺寸
fixedSize()
修饰符不考虑它⾃⼰收到的建议尺⼨,转⽽会向它的⼦视图建议 nil
的宽度和⾼度。这样,⼦视图将使⽤⾃⼰的理想尺⼨。它具有⼀个重载⽅法 fixedSize(horizontal:vertical:)
,可以允许我们使⼦视图只在某⼀个维度上变为其理想尺⼨ (例如,将宽度建议为 nil
,但不改变⾼度)。
extension View {
func badge<Badge: View>(@ViewBuilder contents: () -> Badge) -> some View {
self.overlay(alignment: .topTrailing) {
contents()
.padding(3)
.background {
RoundedRectangle(cornerRadius: 5)
.fill(.teal)
}
.fixedSize()
}
}
}
暂无评论内容