查看布局 分层内容 在覆盖或背景中定义视图以使其布局适应主要内容。
分层内容的设计通常会指定某些内容保持在其他内容的边界内,或在该内容周围保持特定的边距。您可以使用叠加层和背景修饰符来定义视图之间的这些关系。例如,如果您的设计包含一个图形,该图形在某些文本后面提供对比,您可以定义一个布局,使图形随着文本的更新而调整其大小和位置。您可以通过将文本定义在另一个视图的背景或叠加层中,使文本换行以适应该视图的宽度。
要试验代码,请下载项目文件并在 Xcode 中打开示例。
第 1 部分
定义覆盖 在 z 轴上排列内容时,可以使用 ZStack 或叠加层或背景修饰符,例如 overlay(alignment: content:) 或 background(_: in: fill Style:) 。 ZStack 根据可用空间调整每个视图的大小,而不考虑堆栈中的其他视图。要指定某些内容的大小取决于其他内容的大小,请在叠加层或背景修饰符中定义此辅助内容。
此示例展示了一张照片,照片下方覆盖着一段文字。为了提高文本的可读性,同时又不完全遮挡照片的下半部分,文本后方采用了近乎透明的背景。文本会换行以适应照片的宽度。文本的背景大小会根据文本的大小进行调整。Captioned Captioned Photo 视图会排列图片,并将文本显示在图片 overlay(alignment: content:) 中的 Caption 视图中。
步骤 1
Captioned Photo 是一个自定义 View ,它定义照片的布局,另一个自定义 Caption 视图则定义出现在图像顶部的标题文本的布局。
import SwiftUI
struct CaptionedPhoto: View {
let assetName: String
let captionText: String
var body: some View {
Image(assetName)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Caption(text: captionText)
}
.clipShape(RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct Caption: View {
let text: String
var body: some View {
Text(text)
.padding()
.background(Color("TextContrast").opacity(0.75),
in: RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct CaptionedPhoto_Previews: PreviewProvider {
static let landscapeName = "Landscape.jpeg"
static let landscapeCaption = "This photo is wider than it is tall."
static let portraitName = "Portrait.jpeg"
static let portraitCaption = "This photo is taller than it is wide."
static var previews: some View {
CaptionedPhoto(assetName: portraitName,
captionText: portraitCaption)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.dark)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.light)
}
}
第 2 步
此视图定义了 asset Name 属性来保存图像资产的名称。
import SwiftUI
struct CaptionedPhoto: View {
let assetName: String
let captionText: String
var body: some View {
Image(assetName)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Caption(text: captionText)
}
.clipShape(RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct Caption: View {
let text: String
var body: some View {
Text(text)
.padding()
.background(Color("TextContrast").opacity(0.75),
in: RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct CaptionedPhoto_Previews: PreviewProvider {
static let landscapeName = "Landscape.jpeg"
static let landscapeCaption = "This photo is wider than it is tall."
static let portraitName = "Portrait.jpeg"
static let portraitCaption = "This photo is taller than it is wide."
static var previews: some View {
CaptionedPhoto(assetName: portraitName,
captionText: portraitCaption)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.dark)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.light)
}
}
步骤4
此 Image 视图初始化程序从您的应用中按名称检索照片或图形并显示它。
import SwiftUI
struct CaptionedPhoto: View {
let assetName: String
let captionText: String
var body: some View {
Image(assetName)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Caption(text: captionText)
}
.clipShape(RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct Caption: View {
let text: String
var body: some View {
Text(text)
.padding()
.background(Color("TextContrast").opacity(0.75),
in: RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct CaptionedPhoto_Previews: PreviewProvider {
static let landscapeName = "Landscape.jpeg"
static let landscapeCaption = "This photo is wider than it is tall."
static let portraitName = "Portrait.jpeg"
static let portraitCaption = "This photo is taller than it is wide."
static var previews: some View {
CaptionedPhoto(assetName: portraitName,
captionText: portraitCaption)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.dark)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.light)
}
}
步骤6
Caption 自定义视图定义文本及其背景。
import SwiftUI
struct CaptionedPhoto: View {
let assetName: String
let captionText: String
var body: some View {
Image(assetName)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Caption(text: captionText)
}
.clipShape(RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct Caption: View {
let text: String
var body: some View {
Text(text)
.padding()
.background(Color("TextContrast").opacity(0.75),
in: RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct CaptionedPhoto_Previews: PreviewProvider {
static let landscapeName = "Landscape.jpeg"
static let landscapeCaption = "This photo is wider than it is tall."
static let portraitName = "Portrait.jpeg"
static let portraitCaption = "This photo is taller than it is wide."
static var previews: some View {
CaptionedPhoto(assetName: portraitName,
captionText: portraitCaption)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.dark)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.light)
}
}
步骤7
在 overlay(alignment: content:) 中定义标题,表明该标题位于图片前方。主视图的大小限制了用于修改主视图 overlay(alignment: content:) 的大小。
指定 bottom 对齐会将 overlay(alignment: content:) 修饰符的内容推到主视图的底部中心。
笔记 覆盖层可以小于它所修改的视图。
步骤8
将视图剪裁为 Rounded Rectangle 会使图像的角变圆,而不会改变其大小或位置。
实验 将 corner Radius 值更改为更大的数字,以查看其对照片角落的影响。
import SwiftUI
struct CaptionedPhoto: View {
let assetName: String
let captionText: String
var body: some View {
Image(assetName)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Caption(text: captionText)
}
.clipShape(RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct Caption: View {
let text: String
var body: some View {
Text(text)
.padding()
.background(Color("TextContrast").opacity(0.75),
in: RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct CaptionedPhoto_Previews: PreviewProvider {
static let landscapeName = "Landscape.jpeg"
static let landscapeCaption = "This photo is wider than it is tall."
static let portraitName = "Portrait.jpeg"
static let portraitCaption = "This photo is taller than it is wide."
static var previews: some View {
CaptionedPhoto(assetName: portraitName,
captionText: portraitCaption)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.dark)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.light)
}
}
步骤9
此 padding(_: _😃 在照片的所有四个边缘及其包含视图之间留出一些空间。
实验 删除 padding(_: _😃 修饰符,看看布局有何变化。padding 修饰符在 Caption 视图中也出现了两次。也删除这两个修饰符,看看它们的缺失对布局有何影响。
import SwiftUI
struct CaptionedPhoto: View {
let assetName: String
let captionText: String
var body: some View {
Image(assetName)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Caption(text: captionText)
}
.clipShape(RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct Caption: View {
let text: String
var body: some View {
Text(text)
.padding()
.background(Color("TextContrast").opacity(0.75),
in: RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct CaptionedPhoto_Previews: PreviewProvider {
static let landscapeName = "Landscape.jpeg"
static let landscapeCaption = "This photo is wider than it is tall."
static let portraitName = "Portrait.jpeg"
static let portraitCaption = "This photo is taller than it is wide."
static var previews: some View {
CaptionedPhoto(assetName: portraitName,
captionText: portraitCaption)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.dark)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.light)
}
}
第 2 节
定义背景 Captioned Photo 上的 overlay(alignment: content:) 包含一个用于显示标题文本的 Caption 视图。
Caption 视图使用 background(_: in: fill Style:) 修饰符在文本后面放置一个形状,该形状部分遮挡其后面的任何内容(在此示例中为照片),以便为文本提供更高的对比度。
步骤 1
这个 padding(_: _😃 修饰符在单词和下方对比背景的边缘之间添加了空间。代码的结构与视图的视觉外观相匹配——填充位于文本和背景之间。
重要的 谨慎选择如何将填充与叠加层或背景修饰符组合使用。如果您在使用叠加层或背景修饰符修改主视图之前先填充主视图,系统会使用填充的主视图的大小来计算次要视图的位置。在叠加层或背景之后应用填充,以便在包含两个图层的视图周围留出一些空间。
import SwiftUI
struct CaptionedPhoto: View {
let assetName: String
let captionText: String
var body: some View {
Image(assetName)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Caption(text: captionText)
}
.clipShape(RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct Caption: View {
let text: String
var body: some View {
Text(text)
.padding()
.background(Color("TextContrast").opacity(0.75),
in: RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct CaptionedPhoto_Previews: PreviewProvider {
static let landscapeName = "Landscape.jpeg"
static let landscapeCaption = "This photo is wider than it is tall."
static let portraitName = "Portrait.jpeg"
static let portraitCaption = "This photo is taller than it is wide."
static var previews: some View {
CaptionedPhoto(assetName: portraitName,
captionText: portraitCaption)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.dark)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.light)
}
}
第 2 步
类似 background(_: in: fill Style:) 背景修饰符与叠加修饰符类似,其内容的大小取决于其所修饰视图的大小。然而,背景修饰符将其内容置于所修饰视图的后方,而不是前方。
import SwiftUI
struct CaptionedPhoto: View {
let assetName: String
let captionText: String
var body: some View {
Image(assetName)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Caption(text: captionText)
}
.clipShape(RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct Caption: View {
let text: String
var body: some View {
Text(text)
.padding()
.background(Color("TextContrast").opacity(0.75),
in: RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct CaptionedPhoto_Previews: PreviewProvider {
static let landscapeName = "Landscape.jpeg"
static let landscapeCaption = "This photo is wider than it is tall."
static let portraitName = "Portrait.jpeg"
static let portraitCaption = "This photo is taller than it is wide."
static var previews: some View {
CaptionedPhoto(assetName: portraitName,
captionText: portraitCaption)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.dark)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.light)
}
}
步骤3
为了无论人们是否使用黑暗模式都能提供高对比度,此 background(_: in: fillstyle:) 修饰符使用具有部分不透明度的自定义颜色。
该项目资产目录中设置的 Text Contrast 颜色为明暗外观定义了单独的颜色值。
import SwiftUI
struct CaptionedPhoto: View {
let assetName: String
let captionText: String
var body: some View {
Image(assetName)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Caption(text: captionText)
}
.clipShape(RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct Caption: View {
let text: String
var body: some View {
Text(text)
.padding()
.background(Color("TextContrast").opacity(0.75),
in: RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct CaptionedPhoto_Previews: PreviewProvider {
static let landscapeName = "Landscape.jpeg"
static let landscapeCaption = "This photo is wider than it is tall."
static let portraitName = "Portrait.jpeg"
static let portraitCaption = "This photo is taller than it is wide."
static var previews: some View {
CaptionedPhoto(assetName: portraitName,
captionText: portraitCaption)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.dark)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.light)
}
}
步骤4
Rounded Rectangle ,作为一种 Shape ,接受其包含的视图所建议的任何尺寸。
在这种情况下, background(_: in: fillstyle:) 修饰符会创建包含视图,并且任何背景修饰符都会根据其修饰的视图确定其大小。这将生成一个 Rounded Rectangle ,其大小与 Text 视图周围的 padding 大小相同。
import SwiftUI
struct CaptionedPhoto: View {
let assetName: String
let captionText: String
var body: some View {
Image(assetName)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Caption(text: captionText)
}
.clipShape(RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct Caption: View {
let text: String
var body: some View {
Text(text)
.padding()
.background(Color("TextContrast").opacity(0.75),
in: RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct CaptionedPhoto_Previews: PreviewProvider {
static let landscapeName = "Landscape.jpeg"
static let landscapeCaption = "This photo is wider than it is tall."
static let portraitName = "Portrait.jpeg"
static let portraitCaption = "This photo is taller than it is wide."
static var previews: some View {
CaptionedPhoto(assetName: portraitName,
captionText: portraitCaption)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.dark)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.light)
}
}
步骤5
背景周围的额外 padding(_: _😃 Caption 视图外部和其内部容器之间添加了空间;在本例中, Captioned Photo 是包含视图。
mport SwiftUI
struct CaptionedPhoto: View {
let assetName: String
let captionText: String
var body: some View {
Image(assetName)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Caption(text: captionText)
}
.clipShape(RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct Caption: View {
let text: String
var body: some View {
Text(text)
.padding()
.background(Color("TextContrast").opacity(0.75),
in: RoundedRectangle(cornerRadius: 10.0, style: .continuous))
.padding()
}
}
struct CaptionedPhoto_Previews: PreviewProvider {
static let landscapeName = "Landscape.jpeg"
static let landscapeCaption = "This photo is wider than it is tall."
static let portraitName = "Portrait.jpeg"
static let portraitCaption = "This photo is taller than it is wide."
static var previews: some View {
CaptionedPhoto(assetName: portraitName,
captionText: portraitCaption)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.dark)
CaptionedPhoto(assetName: landscapeName,
captionText: landscapeCaption)
.preferredColorScheme(.light)
}
}