swift - 如何确保 SwiftUI 视图不会滚动到导航栏之外?
问题描述
我有一个 SwiftUI 视图,其中包含一个可拉伸的标题、一些选项卡和一个滚动视图。
向上滚动时,无论标题内容大小如何,我都希望标签始终停在导航栏上。标题应与标签一致地向上滚动。
我已经能够使用固定值来实现这一点,但这不适用于不同的屏幕尺寸等。
无论屏幕大小如何,我怎么能做到这一点?
我在这里附上了一个显示行为的 gif,正如您在 iPhone 8 上看到的那样,它与导航栏不对齐。
import SwiftUI
struct ContentView: View {
@State private var safeArea: EdgeInsets = EdgeInsets(.zero)
@State private var offset: CGFloat = 0
@State private var tabBarOffset: CGFloat = 0
@State var currentTab = "Tab #1"
@Namespace var animation
var body: some View {
ScrollView {
VStack(spacing: 0) {
makeHeader()
VStack(spacing: 0) {
makeTabs()
makeBody()
}
}
}
}
@ViewBuilder func makeHeader() -> some View {
GeometryReader { proxy -> AnyView in
// Header Observer
DispatchQueue.main.async {
offset = proxy.frame(in: .global).minY
safeArea = proxy.safeAreaInsets
}
return AnyView(
ZStack {
// Cover
Color.gray
.frame(maxWidth: .infinity)
.frame(height: offset > 0 ? 180 + offset : 180)
VStack {
Color.white
.clipShape(Circle())
.frame(width: 60, height: 60)
Text("Some Name")
.font(.system(size: 24, weight: .medium, design: .rounded))
HStack {
Text("Some Text #1")
.font(.caption)
Circle()
.frame(width: 3, height: 3)
Text("Some Text #2")
.font(.caption)
}
}
}
.clipped()
// Stretchy Effect
.frame(height: offset > 0 ? 180 + offset : nil)
.offset(y: offset > 0 ? -offset : -offset < 90 ? 0 : -offset - 90)
)
}
.frame(height: 180)
.zIndex(1)
}
@ViewBuilder func makeTabs() -> some View {
VStack(spacing: 0) {
HStack(spacing: 0) {
TabButton(title: "Tab #1", currentTab: $currentTab, animation: animation)
.frame(maxWidth: .infinity)
TabButton(title: "Tab #2", currentTab: $currentTab, animation: animation)
.frame(maxWidth: .infinity)
TabButton(title: "Tab #3", currentTab: $currentTab, animation: animation)
.frame(maxWidth: .infinity)
}
Text("tabBarOffset: \(tabBarOffset) offset: \(offset)")
.font(.caption)
Divider()
}
.padding(.top, 20)
.background(Color.white)
.offset(y: tabBarOffset < 90 ? -tabBarOffset + 90 : 0)
.overlay(
GeometryReader { proxy -> Color in
DispatchQueue.main.async {
tabBarOffset = proxy.frame(in: .global).minY
}
return Color.clear
}
.frame(width: 0, height: 0),
alignment: .top
)
.zIndex(1)
}
@ViewBuilder func makeBody() -> some View {
VStack {
ForEach((0..<50)) {
Text("Row #\($0)")
Divider()
}
}
.padding(.top)
.zIndex(0)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct TabButton: View {
var title: String
@Binding var currentTab: String
var animation: Namespace.ID
var body: some View {
Button(
action: {
withAnimation { currentTab = title }
},
label: {
LazyVStack(spacing: 12) {
Text(title)
.fontWeight(.semibold)
.foregroundColor(currentTab == title ? .blue : .gray)
.padding(.horizontal)
if currentTab == title {
Capsule()
.fill(Color.blue)
.frame(height: 1.2)
.matchedGeometryEffect(id: "TAB", in: animation)
} else {
Capsule()
.fill(Color.clear)
.frame(height: 1.2)
}
}
}
)
}
}
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = ContentView()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UINavigationController(rootViewController: ViewController(rootView: contentView))
self.window = window
window.makeKeyAndVisible()
}
}
}
class ViewController<Content: View>: UIHostingController<Content> {
override public func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController?.navigationBar.barTintColor = .clear
navigationController?.navigationBar.tintColor = .white
navigationController?.navigationBar.backgroundColor = .clear
navigationController?.navigationBar.barStyle = .black
}
override public func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.navigationBar.setBackgroundImage(nil, for: .default)
navigationController?.navigationBar.barTintColor = UINavigationBar.appearance().barTintColor
navigationController?.navigationBar.backgroundColor = UINavigationBar.appearance().backgroundColor
navigationController?.navigationBar.barStyle = .default
navigationController?.navigationBar.tintColor = UINavigationBar.appearance().tintColor
}
}
解决方案
推荐阅读
- node.js - 如何在 NodeJS 中测试服务器发送事件 (SSE) 路由?
- xml - Visual Basic XMLTextWriter -> WriteRaw 后没有换行符
- java - springboot中REST api的多个端点
- php - 无法在 Debian8 上安装 php5-curl
- javascript - 承诺中的处理程序是否首先执行(异步执行代码)
- regex - PHP,preg_replace (RegEx) 的不一致结果
- sql - 向 SQL 窗口函数添加条件语句
- git - 如何将第三方代码标记到特定目录
- python - 使用 xlsxwriter merge_row 错误
- c# - listBox 按周和年排序