如何在SwiftUI的NavigationView中放置一个logo?

25

我试图在应用程序顶部的导航视图标题处使用标志图像,而不是标题文本。找不到有关在NavigationView内使用图像的任何文档。


不清楚您需要什么。请添加一些图片以澄清您期望的结果。 - DenFav
8
我将尝试使用标志图片代替应用程序顶部导航视图标题。这是需要的非常清晰明了的解释。 - NRitH
8个回答

35

iOS 14+

从iOS 14开始,您可以创建一个具有“principal”放置的ToolbarItem

struct ContentView: View {
    var body: some View {
        NavigationView {
            Text("Test")
                .toolbar {
                    ToolbarItem(placement: .principal) {
                        Image(systemName: "ellipsis.circle")
                    }
                }
        }
    }
}

查看ToolbarItemPlacement文档以获取更多放置位置。


2
如果您可以使用iOS 14+,那么这绝对是最好的答案。 - Paolo

17

NavigationView.navigationBarTitle()目前只能使用Text()参数。您可以使用 .navigationBarItems()Image 作为trailingleading参数设置,但这相当于SwiftUI中的 UINavigationItem.leftBarButtonItem[s]UINavigationItem.rightBarButtonItem[s],这意味着您受限于导航栏按钮的尺寸。但是如果您可以接受这个,您可能想要设置一个空白标题,以便指定标准高度的导航栏。

硬编码定位

如果您能够容忍自己,您可以通过在图像周围硬编码填充来伪造居中的导航栏项,例如:

.padding(.trailing, 125),

这里输入图片描述

(注意,我故意将其偏移,以便您可以看到它是硬编码的。)

略微减少硬编码定位

如果您知道正在使用的图像的确切宽度,则更好的方法是将整个内容包装在GeometryReader { geometry in ... }块中,使用屏幕尺寸计算精确位置:

GeometryReader { geometry in
    NavigationView {
        ...
    }
        .navigationBarTitle(Text(""), displayMode: .inline)
        .navigationBarItems(trailing:
            PresentationButton(
                Image(systemName: "person.crop.circle")
                    .imageScale(.large)
                    .padding(.trailing, (geometry.size.width / 2.0) + -30), // image width = 60
                destination: ProfileHost()
            )
        )

输入图像描述

如果您不想进行黑客攻击,您可以这样做:

标准导航栏高度,左侧按钮项

.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarItems(leading:
    PresentationButton(
        Image(systemName: "person.crop.circle")
            .imageScale(.large)
            .padding(),
        destination: ProfileHost()
    )
)

图片描述

标准导航栏高度,右侧按钮项

.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarItems(trailing:
    PresentationButton(
        Image(systemName: "person.crop.circle")
            .imageScale(.large)
            .padding(),
        destination: ProfileHost()
    )
)

输入图像描述

展开的导航栏高度,无标题,左按钮项

.navigationBarItems(leading:
    PresentationButton(
        Image(systemName: "person.crop.circle")
            .imageScale(.large)
            .padding(),
        destination: ProfileHost()
    )
)

输入图片描述


谢谢回复。我也做了同样的事情,只允许将图像放置在导航视图的前沿或后沿。也许在以后的版本中,他们会包括在导航视图中放置更多项目的功能。 - Baisampayan Saha
你好 @NRitH,我尝试了你的解决方案,但是出现了问题。当我使用“.padding(.trailing, 125)”来水平对齐标志中心时,在所有屏幕尺寸上都不能完美地工作。例如,在iPad上,它甚至不接近中心。你有解决办法吗?如何在每个可能的屏幕尺寸下将其对齐到中心。 - C.Aglar
完全没有问题。我的解决方案并不是为了适应所有屏幕尺寸而设计的。需要你自己计算每个屏幕尺寸所需的填充值。 - NRitH
我假设PresentationButton不是系统对象。此外,只有当我将.navigationBarTitle和.navigationBarItems放置在现有对象之后的NavigationView范围内时,它才对我有效。 - Repose

9

使用这个:

NavigationView {
    Text("Hello, SwiftUI!")
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .principal) {
                HStack {
                    Image(systemName: "sun.min.fill")
                    Text("Title").font(.headline)
                }
            }
        }
}

来源:https://sarunw.com/posts/custom-navigation-bar-title-view-in-swiftui/

本文将介绍如何在SwiftUI中自定义导航栏标题视图。


6

使用 SwiftUIX,你可以使用navigationBarTitleView(View)方法:

NavigationView() {
    NavigationLink(destination:YourView().navigationBarTitleView(Image(systemName: "message.fill")))
}

enter image description here


2
兔子洞导致了 UIViewControllerRepresentable :(. 不过SwiftUIX是个不错的发现,可以借鉴和贡献想法。 - TruMan1
@andrei 请查看他们的文档,看看是否有变化,可以在Github上找到SwiftUIX。 - Zorayr
@andrei 你好! 我是SwiftUIX的开发者,navigationBarTitleView(_:)仍然非常有效。 如果您无法使用它,请提供一个可重现的问题报告? - Vatsal Manot
1
@VatsalManot 这只是我自己犯傻,没有在我的当前文件中导入库。navigationBarTitleView(_:) 可用并且运行良好。 - andrei
1
@andrei 哈哈哈,没关系,最好的人也会犯错。 - Vatsal Manot
显示剩余2条评论

5

我不想声称标题图片的位置百分之百准确,但在视觉上看起来是居中的。请您自行判断并调整填充(padding) :)

在此输入图片描述

以下是代码:

            .navigationBarTitle(
            Text("")
            , displayMode: .inline)
            .navigationBarItems(leading:
                HStack {
                    Button(action: {
                    }) {
                        Image(systemName: "arrow.left")
                    }.foregroundColor(Color.oceanWhite)
                    Image("oceanview-logo")
                        .resizable()
                        .foregroundColor(.white)
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 60, height: 40, alignment: .center)
                    .padding(UIScreen.main.bounds.size.width/4+30)
                }
                ,trailing:

                HStack {

                    Button(action: {
                    }) {
                        Image(systemName: "magnifyingglass")
                        }.foregroundColor(Color.oceanWhite)
                }
        )

3

在NRitH的回答基础上,把你的标志放在不同的组件中(借用React的方式)可能有助于任何想要理解概念的人。

实际的图像可以包装在任何容器视图中,例如VStack等。以下是设置结构体作为导航项中使用的组件的示例:

struct NavLogo: View {

    var body: some View {
            VStack {
                Image("app-logo")
                    .resizable()
                    .aspectRatio(2, contentMode: .fit)
                    .imageScale(.large)
            }
            .frame(width: 200)
            .background(Color.clear)
    }
}

当设置了宽高比时,容器视图上的框架只需要设置宽度。我们也可以在 NavLogo 中设置属性来从属性依赖注入中设置宽度和/或高度。不管怎样,我们的navigationBarItems变得非常简单直观,更易读。

NavigationView {
    Text("Home View")
        .navigationBarItems(
            leading: NavLogo()
            trailing: ProfileButton()
        )
    }

这个做得还不错,但是不要硬编码200,可以使用完全居中的方式:.frame(width: .infinity) - TruMan1
实际上必须使用geometry.size.width。请参考@NRitH的答案。 - TruMan1

0
在iOS 13上,实现这个的一个小技巧:
private var logo: some View {
    Image("logo-image")
}

var body: some View {
    GeometryReader { g in
        content()
            .navigationBarTitle("")
            .navigationBarItems(leading:
                                    ZStack(alignment: .leading) {
                                        logo.frame(width: g.size.width).padding(.trailing, 8)
                                        HStack {
                                            leadingItems().padding(.leading, 10)
                                            Spacer()
                                            trailingItems().padding(.trailing, 10)
                                        }
                                        .frame(width: g.size.width)
                                    }
                                )
    }
}

-3

请尝试以下方法。

struct ContainerView: View {
    var body: some View {
        VStack {
            Image(systemName: "person.crop.square")
            ContentView()
        }
    }
}

这对我来说有效。

在运行模拟器或设备之前,请确保将 SceneDelegate.swift 中的 ContentView 更改为 ContainerView。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接