Ios 如何在SwiftUI中在大型导航栏标题旁边显示配置文件图标?
我正在开发一个支持多个配置文件的应用程序。我真的很喜欢苹果在其所有应用程序中显示Ios 如何在SwiftUI中在大型导航栏标题旁边显示配置文件图标?,ios,swift,image,swiftui,profile,Ios,Swift,Image,Swiftui,Profile,我正在开发一个支持多个配置文件的应用程序。我真的很喜欢苹果在其所有应用程序中显示大导航栏标题旁边的配置文件图标的方式。请参见下面的屏幕截图: 我的问题如下: 在SwiftUI中是否可以实现这一点?如果是,怎么做 如果在纯SwiftUI中无法实现,我如何实现它,包括UIKit代码 谢谢您的帮助。无导航视图 我用的是纯迅捷。您必须用自己的映像替换映像(“Profile”)行(可能来自资产,也可能来自带有UIImage的base64数据) 这将产生以下结果: 使用NavigationView
大导航栏标题旁边的配置文件图标的方式。请参见下面的屏幕截图:
我的问题如下:
- 在
SwiftUI
中是否可以实现这一点?如果是,怎么做
- 如果在纯
SwiftUI
中无法实现,我如何实现它,包括UIKit
代码
谢谢您的帮助。无导航视图
我用的是纯迅捷。您必须用自己的映像替换映像(“Profile”)
行(可能来自资产,也可能来自带有UIImage的base64数据)
这将产生以下结果:
使用NavigationView
假设您有NavigationView
,其中只有ScrollView
和.navigationTitle
。您可以使用覆盖在那里添加该配置文件图像
NavigationView {
ScrollView {
//your content here
}
.overlay(
ProfileView()
.padding(.trailing, 20)
.offset(x: 0, y: -50)
, alignment: .topTrailing)
.navigationTitle(Text("Apps"))
}
其中ProfileView可以是这样的:
struct ProfileView: View {
var body: some View {
Image("Profile")
.resizable()
.scaledToFit()
.frame(width: 40, height: 40)
.clipShape(Circle())
}
}
结果会是这样的
…离应用商店很近:
这里有一个可能的解决方案。
我添加了一个HStack
视图,其中包含标题和图像作为navigationBarItems
我通过使用“反思SwiftUI的底层UIKit组件”解决了这个问题
以下是一个视图示例:
struct ContentView: View {
@State private var lastHostingView: UIView!
var body: some View {
NavigationView {
ScrollView {
ForEach(1 ... 50, id: \.self) { index in
Text("Index: \(index)")
}
.frame(maxWidth: .infinity)
}
.navigationTitle("Large title")
.introspectNavigationController { navController in
let bar = navController.navigationBar
let hosting = UIHostingController(rootView: BarContent())
guard let hostingView = hosting.view else { return }
// bar.addSubview(hostingView) // <--- OPTION 1
// bar.subviews.first(where: \.clipsToBounds)?.addSubview(hostingView) // <--- OPTION 2
hostingView.backgroundColor = .clear
lastHostingView?.removeFromSuperview()
lastHostingView = hostingView
hostingView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
hostingView.trailingAnchor.constraint(equalTo: bar.trailingAnchor),
hostingView.bottomAnchor.constraint(equalTo: bar.bottomAnchor, constant: -8)
])
}
}
}
}
.frame(宽:40,高:40)
&hostingView.bottomAnchor
常量
需要根据您的需要进行调整
以及每个选项的结果(在代码中注释):
选择1
选择2
滚动时,视图将保持不变
在滚动条下面消失的视图
谢谢你的回答。然而,这并不是我想要的。现在滚动时,标题将从顶部边缘消失,而不会在带有内联标题的导航栏中转换。有什么办法可以保留这个功能吗?@christophriepe哦,我的错。我编辑了我的答案,以便更好地满足您的需要。请看一看,太棒了!基本上,这正是我想要的。你知道如何让图标像导航栏后面的标题一样消失而不消失吗?更重要的是:配置文件图标需要是一个按钮。但是,当添加填充和偏移时,按钮不再可单击。我怎样才能解决这个问题?谢谢你的回答。但是,我正在寻找一个类似AppStore应用程序中的默认行为为导航栏
/导航栏标题
的解决方案。感谢您提供了这个令人惊叹的答案,这正是我所要寻找的。但是,我在选项2
中遇到了一个问题(选项2是我想要使用的行为):当导航到第二个也使用大标题的视图时,一切都很顺利,但是当导航到带有内联标题的视图时,然后返回,纵断面图标显示在原始视图的大标题上,不再显示在右端。如何修复此错误?那么,您的解决方案就是完美的;)@christophriepe我已经试了好几个小时试图解决这个问题-希望我能尽快解决它!那太好了。这个溶液很干净。这是唯一的问题。@christophriepe,所以似乎没有一个很好的解决方案,它不是真正的黑客,看起来对选项2非常糟糕。选项#1是最好的方式,我不认为滚动时重叠应该是一个太大的问题。您可以使用目标内容中的.onAppear
和onDisappear
临时隐藏BarContent()。我认为最好的解决方案是简单地使用选项1,因为它工作得更好。毕竟,我们正在尝试访问一个私有视图,因此选项2的解决方案有点粗糙。虽然它仍然不是一个理想的解决方案,但您已经赢得了赏金。如果你将来能想到别的东西,我很高兴你能在这里分享。
struct ContentView: View {
var body: some View {
GeometryReader { geo in
NavigationView{
VStack{
ScrollView{
Text("Contents")
Spacer()
Text("Contents2")
Spacer()
Text("Contents3")
}
}.navigationBarItems(leading: HStack {
Text("Apps")
.font(.largeTitle)
.fontWeight(.bold)
Spacer().frame(width: geo.size.width * 0.6)
Image(systemName: "person.crop.circle")
.resizable()
.frame(width: 40, height: 40)
})
}
}
}
}
struct ContentView: View {
@State private var lastHostingView: UIView!
var body: some View {
NavigationView {
ScrollView {
ForEach(1 ... 50, id: \.self) { index in
Text("Index: \(index)")
}
.frame(maxWidth: .infinity)
}
.navigationTitle("Large title")
.introspectNavigationController { navController in
let bar = navController.navigationBar
let hosting = UIHostingController(rootView: BarContent())
guard let hostingView = hosting.view else { return }
// bar.addSubview(hostingView) // <--- OPTION 1
// bar.subviews.first(where: \.clipsToBounds)?.addSubview(hostingView) // <--- OPTION 2
hostingView.backgroundColor = .clear
lastHostingView?.removeFromSuperview()
lastHostingView = hostingView
hostingView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
hostingView.trailingAnchor.constraint(equalTo: bar.trailingAnchor),
hostingView.bottomAnchor.constraint(equalTo: bar.bottomAnchor, constant: -8)
])
}
}
}
}
struct BarContent: View {
var body: some View {
Button {
print("Profile tapped")
} label: {
ProfilePicture()
}
}
}
struct ProfilePicture: View {
var body: some View {
Circle()
.fill(
LinearGradient(
gradient: Gradient(colors: [.red, .blue]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.frame(width: 40, height: 40)
.padding(.horizontal)
}
}