侧边栏壁纸
  • 累计撰写 28 篇文章
  • 累计创建 10 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

使用导航链接显示详细界面

Jserv
2025-04-28 / 0 评论 / 0 点赞 / 5 阅读 / 6723 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2025-04-28,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

[本文翻译自hackingwithswift,点击链接阅读原文]

当点击菜单项时,我们希望引入一个显示更多信息的详细信息视图。我们已经将ContentView放在导航堆栈中,所以现在我们可以使用一种名为NavigationLink的新视图类型。我们需要给它一个目的地——它应该显示什么样的东西——以及在屏幕上显示的链接。

在实践中,这看起来就像我们迄今为止使用过的所有其他容器一样,所以让我们用一个整洁的快捷方式来试试:虽然我们将在一分钟内显示一个详细视图,但我们可以使用常规文本视图作为占位符。

因此,将此直接放在ContentView.swift中的ItemRow代码周围:

NavigationLink {
    Text(item.name)
} label: {
    // existing contents…
}

这意味着整行是一个导航链接,其中包含项目名称的目的地。

如果您现在运行该应用程序,您将看到两个重要的区别:

  1. 我们所有的行现在在右侧边缘都有一个灰色的披露指示器,因为SwiftUI默认为我们提供了正确的行为。

  2. 当您点击任何项目时,一个新的屏幕将滑入,说出您选择的任何项目的名称。

能够像这样呈现文本视图是构建用户界面时的绝佳节省时间!

当然,我们想要更多——我们想要一个漂亮的大局,一些关于食物的细节,等等。因此,按Cmd+N来制作另一个新的SwiftUI视图,这次称为ItemDetail.swift。

ItemRow一样,这需要将菜单项传递并存储为属性,因此现在将此添加到ItemDetail

let item: MenuItem

我们还需要更新其预览代码来使用我们的示例项目,这样我们就可以看到我们正在做什么:

static var previews: some View {
    ItemDetail(item: MenuItem.example)
}

与我们的列表行一样,我们将从简单和迭代开始,直到我们得到一些效果好的东西。

首先,我们的ItemDetail视图的简单版本,其中包含项目的图像和描述,以及顶部的标题:

struct ItemDetail : View {
    let item: MenuItem

    var body: some View {
        VStack {
            Image(item.mainImage)
            Text(item.description)
        }
        .navigationTitle(item.name)
    }
}

为了让您能够立即开始看到操作,让我们在ContentView.swift中更新我们的NavigationLink,以便它显示包含所选项目的ItemDetail视图。有两种方法可以这样做,最简单的方法就是将ItemDetail代码放在我们有Text(item.name)的位置,像这样:

NavigationLink {
    ItemDetail(item: item)
} label: {

这有用,但在幕后,它导致SwiftUI做比你想象的更多的工作——每次它在我们的List创建一行时,它也会创建NavigationLink,作为其中的一部分,它还将为每个可见行创建ItemDetail

这远非理想,所以SwiftUI为我们提供了一个更快、更简单的替代方案:我们可以将任何Hashable对象直接附加到NavigationLink作为其值,然后使用navigationDestination()修饰符告诉SwiftUI“当您被要求导航到MenuItem时,加载具有该值的ItemDetail视图。

这需要两个步骤。首先,我们需要将NavigationLink代码更改为:

NavigationLink(value: item) {
    ItemRow(item: item)
}

现在我们需要将此修饰符添加到List——在navigationTitle()它很好,但这其实并不重要:

.navigationDestination(for: MenuItem.self) { item in
    ItemDetail(item: item)
}

现在,您可以随着进度运行代码,看到操作中的详细屏幕。

您不会在顶部看到标题,因为预览不知道它在导航堆栈中。为了解决这个问题,我们可以像这样更改预览:

struct ItemDetail_Previews: PreviewProvider {
    static var previews: some View {
        NavigationStack {
            ItemDetail(item: MenuItem.example)
        }
    }
}

这实际上并没有改变我们的代码在运行时的作用——只是预览发生了变化。

你可以看到我们的详细视图有一些布局问题,所以让我们纠正它们。

首先,导航栏标题不应该很大,因为苹果建议仅将该样式用于用户界面中的顶级屏幕。我们可以通过添加另一个修饰符belownavigationTitlenavigationTitle()来解决这个问题,如下所示:

.navigationBarTitleDisplayMode(.inline)

其次,虽然图像非常宽,但描述文本从边缘到边缘看起来就不太好了。我们可以通过添加像这样的padding()修饰符来解决这个问题:

Text(item.description)
    .padding()

padding()修饰符允许我们指定想要填充的边以及使用多少,但如果没有任何参数,它将对所有边缘应用填充。它的适用取决于上下文——正在使用什么设备等——但总体上看起来不错。

第三,我们的内容垂直居中看起来很奇怪,因为我们的眼睛习惯于信息对齐到顶部。为了解决这个问题我们可以直接在项目描述之后使用另一个Spacer()

Image(item.mainImage)
Text(item.description)
    .padding()
Spacer()

我们的详细信息屏幕在顶部显示了枫叶法式吐司的大图片,以及下面的描述文本。

这开始看起来不错了,但我们也需要想办法展示给我们食物拍照的人的名字。我们可以把它放在图片下方或警报内,但更好的主意是放在图像的右下角。

您已经遇到了水平和垂直堆栈(HStackVStack),但SwiftUI为我们提供了第三个名为ZStack的选项来处理重叠视图。要在这里使用一个,请用这个替换我们现有的图像:

ZStack {
    Image(item.mainImage)
    Text("Photo: \(item.photoCredit)")
}

这创建了图像,然后在上面叠加了一些文本。很有可能你很难看到那个文本,所以让我们应用一些修饰符来让它更清晰:

Text("Photo: \(item.photoCredit)")
    .padding(4)
    .background(.black)
    .font(.caption)
    .foregroundStyle(.white)

提示:如果您交换padding()background()修饰符的顺序,结果会有所不同。秩序很重要!

它现在更明显了,但这只是意味着我们可以看到它看起来不是很好——它不应该真的在我们的食物上!

为了解决这个问题,我们可以在我们的ZStack中添加一些对齐方式,以便标签在右下角:

ZStack(alignment: .bottomTrailing) {

我们甚至可以对文本应用一些自定义偏移量,将其拉起,从边缘稍微离开:

Text("Photo: \(item.photoCredit)")
    .padding(4)
    .background(.black)
    .font(.caption)
    .foregroundStyle(.white)
    .offset(x: -5, y: -5)

不错!

还有另一个布局问题,但根据您的Xcode配置,您可能还没有注意到它:我们用户界面的某些部分挂在屏幕上!

到目前为止,我一直在用iPhone 14 Pro Max设备做画布,效果很好,因为它有一个巨大的屏幕。然而,如果我换成小型设备——例如iPhone SE(转到产品>目的地>iPhone SE(第3代))——屏幕要小得多,现在您应该会看到照片信用区域现在从屏幕的右边缘运行。

发生这种情况是因为SwiftUI默认以自然尺寸显示图像,这意味着它们在屏幕上的宽度和高度与像素相同。我们的主要图像对于iPhone SE屏幕来说太大了,因此SwiftUI不是把它挤在屏幕外溢出,而是让它溢出——图像会挂起来,这样做允许其他一切都在增长。

为了解决这个问题,我们需要为我们的图像添加两个新的修饰符:一个是使图像调整大小,一个是使其缩放以适应可用空间。

因此,将您的图像修改为:

Image(item.mainImage)
    .resizable()
    .scaledToFit()

有了这个小小的改变,我们的图像将在所有iPhone屏幕尺寸上从边缘到边缘运行,这要好得多。除了scaledToFit()还有一个scaledToFill()修饰符——前者将确保整个图像可见,即使这意味着留出一点空的空间,而后者永远不会留下任何空位,即使这意味着裁剪一些图片。两者都会自动保留它们所应用的图像的自然宽高比。

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区