Skip to content

查看布局 分层内容 在覆盖或背景中定义视图以使其布局适应主要内容。

分层内容的设计通常会指定某些内容保持在其他内容的边界内,或在该内容周围保持特定的边距。您可以使用叠加层和背景修饰符来定义视图之间的这些关系。例如,如果您的设计包含一个图形,该图形在某些文本后面提供对比,您可以定义一个布局,使图形随着文本的更新而调整其大小和位置。您可以通过将文本定义在另一个视图的背景或叠加层中,使文本换行以适应该视图的宽度。

要试验代码,请下载项目文件并在 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)
    }
}