将 NavigationView 的标题与 SwiftUI 中的 ContentView 对齐
Aligning the NavigationView's title with the ContentView in SwiftUI
我很难将导航标题与
我在 SwiftUI 中的内容视图。这个操作在 UIKit 中很简单,尤其是使用 collection 视图流布局。
n.b. 这是一个实验项目,我正在使用 iOS 15.
我尝试了很多不同的方法:
1。使用列表
这使我们在导航标题、header 和 iPhone 12 Max 上的单元格之间正确对齐,但在 iPhone 12
滚动视图不会像在 Apple Music 应用程序中那样延伸到边缘。
有没有办法修复滚动视图?
iPhone 12 最大截图
iPhone 12 截图
代码
var body: some View {
NavigationView {
GeometryReader { proxy in
let cellWidth = proxy.size.width * 0.4
List {
ForEach(0...10, id: \.self) { rowIndex in
Section(header: Text("Unknown").font(.title2).redacted(reason: .placeholder)) {
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(alignment: .top, spacing: 16) {
ForEach(0...10, id: \.self) { index in
VStack(alignment: .leading, spacing: 2) {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(.red)
.frame(width: cellWidth, height: cellWidth)
VStack(alignment: .leading, spacing: 2) {
Text("Example")
.foregroundColor(.primary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
Text("Example")
.foregroundColor(.secondary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
}
}
}
}
}
}
}
}
.listStyle(InsetListStyle())
.navigationBarTitle("Experimental")
}
}
}
2。使用 VStack 和填充 ...
- 似乎没有一种直接的方法可以将
VStack
和第一个单元格与标题对齐。例如,在 iPhone 12 Pro Max 上,20 的填充给出了正确的对齐方式,而在 iPhone 12 上,我们将使用相同的填充偏移 2 个像素左右。
- 现在水平滚动视图的行为符合预期,只填充第一个
细胞.
iPhone 12 最大截图
iPhone 12 截图
代码
var bodyUsingVStack: some View {
NavigationView {
GeometryReader { proxy in
let cellWidth = proxy.size.width * 0.4
ScrollView {
VStack(alignment: .leading, spacing: 16) {
ForEach(0...10, id: \.self) { rowIndex in
Section(
header: Text("Unknown")
.font(.title2)
.padding(.leading, 20)
// .redacted(reason: .placeholder)
) {
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(alignment: .top, spacing: 16) {
ForEach(0...10, id: \.self) { index in
VStack(alignment: .leading, spacing: 2) {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(.red)
.frame(width: cellWidth, height: cellWidth)
VStack(alignment: .leading, spacing: 2) {
Text("Example")
.foregroundColor(.primary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
Text("Example")
.foregroundColor(.secondary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
}
}
.padding(.leading, index == 0 ? 22 : 0)
}
}
}
}
}
}
}
.listStyle(InsetListStyle())
.navigationBarTitle("Experimental")
}
}
}
- 其他想法
- 使用对齐参考线似乎不起作用
- 试图扩展底层 UINavigation 视图并捕获标题的确切位置以计算正确的填充。
- 测试所有 iOS 设备并为每个设备预先确定正确的填充值...
必须有一种更简单的方法,SwiftUI 提供如此多的灵活性真是太疯狂了,但我们经常在尝试做一些琐碎的事情时陷入数小时或数天的困境。
我会通过读取填充来完成,然后使用它来扩展 ScrollView
.
的宽度
示例如下。变化:
Cells
是一个单独的视图,否则它无法在合理的时间内编译(对我而言)。会因机器而异。
- 已添加
background
,其中包含 GeometryReader
。
- 在 内添加了填充,在
ScrollView
外添加了 。
struct ContentView: View {
@State private var padding: CGFloat?
var body: some View {
NavigationView {
GeometryReader { proxy in
let cellWidth = proxy.size.width * 0.4
List {
ForEach(0...10, id: \.self) { rowIndex in
Section(header: Text("Unknown").font(.title2).redacted(reason: .placeholder)) {
ScrollView(.horizontal, showsIndicators: false) {
Cells(cellWidth: cellWidth)
.background {
if padding == nil {
GeometryReader { geo in
Color.clear.onAppear {
padding = geo.frame(in: .global).minX
}
}
}
}
.padding(.horizontal, padding ?? 0)
}
.padding(.horizontal, -(padding ?? 0))
}
}
}
.listStyle(InsetListStyle())
.navigationBarTitle("Experimental")
}
}
.navigationViewStyle(.stack)
}
}
struct Cells: View {
let cellWidth: CGFloat
var body: some View {
LazyHStack(alignment: .top, spacing: 16) {
ForEach(0...10, id: \.self) { index in
VStack(alignment: .leading, spacing: 2) {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(.red)
.frame(width: cellWidth, height: cellWidth)
VStack(alignment: .leading, spacing: 2) {
Text("Example")
.foregroundColor(.primary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
Text("Example")
.foregroundColor(.secondary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
}
}
}
}
}
}
结果:
这是我找到的最简单的解决方案,无论设备如何,一切都对齐(尚未在 iPad 上测试)。
它使用 Introspect 捕获大标题视图标签的框架。
.introspectNavigationController {
let padding = [=10=].navigationBar
.subviews.first { String(describing: type(of: [=10=])).hasSuffix("LargeTitleView") }?
.subviews.first { [=10=] is UILabel }?
.frame.origin.x ?? 0
if !padding.isZero, self.padding != padding {
self.padding = padding
}
}
代码
@State private var padding: CGFloat = 0
var body: some View {
NavigationView {
GeometryReader { proxy in
let cellWidth = proxy.size.width * 0.4
ScrollView {
VStack(alignment: .leading, spacing: 16) {
ForEach(0...10, id: \.self) { rowIndex in
Section(
header: Text("Unknown")
.font(.title2)
.padding(.leading, padding)
.redacted(reason: .placeholder)
) {
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(alignment: .top, spacing: 16) {
ForEach(0...10, id: \.self) { index in
VStack(alignment: .leading, spacing: 2) {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(.red)
.frame(width: cellWidth, height: cellWidth)
VStack(alignment: .leading, spacing: 2) {
Text("Example")
.foregroundColor(.primary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
Text("Example")
.foregroundColor(.secondary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
}
}
.padding(.leading, index == 0 ? padding : 0)
}
}
}
}
}
}
}
.listStyle(InsetListStyle())
.navigationBarTitle("Experimental")
.introspectNavigationController {
let padding = [=11=].navigationBar
.subviews.first { String(describing: type(of: [=11=])).hasSuffix("LargeTitleView") }?
.subviews.first { [=11=] is UILabel }?
.frame.origin.x ?? 0
if !padding.isZero, self.padding != padding {
self.padding = padding
}
}
}
}
}
截图
测量所有 iPhone 模型(和 iPad)的填充我意识到如果屏幕宽度小于 414,则填充为 16 和 24 否则。
import SwiftUI
public func margin(for width: Double) -> Double {
guard !width.isZero else { return 0 }
return width >= 414 ? 20 : 16
}
我很难将导航标题与 我在 SwiftUI 中的内容视图。这个操作在 UIKit 中很简单,尤其是使用 collection 视图流布局。
n.b. 这是一个实验项目,我正在使用 iOS 15.
我尝试了很多不同的方法:
1。使用列表
这使我们在导航标题、header 和 iPhone 12 Max 上的单元格之间正确对齐,但在 iPhone 12
滚动视图不会像在 Apple Music 应用程序中那样延伸到边缘。
有没有办法修复滚动视图?
iPhone 12 最大截图
iPhone 12 截图
代码
var body: some View {
NavigationView {
GeometryReader { proxy in
let cellWidth = proxy.size.width * 0.4
List {
ForEach(0...10, id: \.self) { rowIndex in
Section(header: Text("Unknown").font(.title2).redacted(reason: .placeholder)) {
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(alignment: .top, spacing: 16) {
ForEach(0...10, id: \.self) { index in
VStack(alignment: .leading, spacing: 2) {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(.red)
.frame(width: cellWidth, height: cellWidth)
VStack(alignment: .leading, spacing: 2) {
Text("Example")
.foregroundColor(.primary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
Text("Example")
.foregroundColor(.secondary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
}
}
}
}
}
}
}
}
.listStyle(InsetListStyle())
.navigationBarTitle("Experimental")
}
}
}
2。使用 VStack 和填充 ...
- 似乎没有一种直接的方法可以将
VStack
和第一个单元格与标题对齐。例如,在 iPhone 12 Pro Max 上,20 的填充给出了正确的对齐方式,而在 iPhone 12 上,我们将使用相同的填充偏移 2 个像素左右。 - 现在水平滚动视图的行为符合预期,只填充第一个 细胞.
iPhone 12 最大截图
iPhone 12 截图
代码
var bodyUsingVStack: some View {
NavigationView {
GeometryReader { proxy in
let cellWidth = proxy.size.width * 0.4
ScrollView {
VStack(alignment: .leading, spacing: 16) {
ForEach(0...10, id: \.self) { rowIndex in
Section(
header: Text("Unknown")
.font(.title2)
.padding(.leading, 20)
// .redacted(reason: .placeholder)
) {
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(alignment: .top, spacing: 16) {
ForEach(0...10, id: \.self) { index in
VStack(alignment: .leading, spacing: 2) {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(.red)
.frame(width: cellWidth, height: cellWidth)
VStack(alignment: .leading, spacing: 2) {
Text("Example")
.foregroundColor(.primary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
Text("Example")
.foregroundColor(.secondary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
}
}
.padding(.leading, index == 0 ? 22 : 0)
}
}
}
}
}
}
}
.listStyle(InsetListStyle())
.navigationBarTitle("Experimental")
}
}
}
- 其他想法
- 使用对齐参考线似乎不起作用
- 试图扩展底层 UINavigation 视图并捕获标题的确切位置以计算正确的填充。
- 测试所有 iOS 设备并为每个设备预先确定正确的填充值...
必须有一种更简单的方法,SwiftUI 提供如此多的灵活性真是太疯狂了,但我们经常在尝试做一些琐碎的事情时陷入数小时或数天的困境。
我会通过读取填充来完成,然后使用它来扩展 ScrollView
.
示例如下。变化:
Cells
是一个单独的视图,否则它无法在合理的时间内编译(对我而言)。会因机器而异。- 已添加
background
,其中包含GeometryReader
。 - 在 内添加了填充,在
ScrollView
外添加了 。
struct ContentView: View {
@State private var padding: CGFloat?
var body: some View {
NavigationView {
GeometryReader { proxy in
let cellWidth = proxy.size.width * 0.4
List {
ForEach(0...10, id: \.self) { rowIndex in
Section(header: Text("Unknown").font(.title2).redacted(reason: .placeholder)) {
ScrollView(.horizontal, showsIndicators: false) {
Cells(cellWidth: cellWidth)
.background {
if padding == nil {
GeometryReader { geo in
Color.clear.onAppear {
padding = geo.frame(in: .global).minX
}
}
}
}
.padding(.horizontal, padding ?? 0)
}
.padding(.horizontal, -(padding ?? 0))
}
}
}
.listStyle(InsetListStyle())
.navigationBarTitle("Experimental")
}
}
.navigationViewStyle(.stack)
}
}
struct Cells: View {
let cellWidth: CGFloat
var body: some View {
LazyHStack(alignment: .top, spacing: 16) {
ForEach(0...10, id: \.self) { index in
VStack(alignment: .leading, spacing: 2) {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(.red)
.frame(width: cellWidth, height: cellWidth)
VStack(alignment: .leading, spacing: 2) {
Text("Example")
.foregroundColor(.primary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
Text("Example")
.foregroundColor(.secondary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
}
}
}
}
}
}
结果:
这是我找到的最简单的解决方案,无论设备如何,一切都对齐(尚未在 iPad 上测试)。
它使用 Introspect 捕获大标题视图标签的框架。
.introspectNavigationController {
let padding = [=10=].navigationBar
.subviews.first { String(describing: type(of: [=10=])).hasSuffix("LargeTitleView") }?
.subviews.first { [=10=] is UILabel }?
.frame.origin.x ?? 0
if !padding.isZero, self.padding != padding {
self.padding = padding
}
}
代码
@State private var padding: CGFloat = 0
var body: some View {
NavigationView {
GeometryReader { proxy in
let cellWidth = proxy.size.width * 0.4
ScrollView {
VStack(alignment: .leading, spacing: 16) {
ForEach(0...10, id: \.self) { rowIndex in
Section(
header: Text("Unknown")
.font(.title2)
.padding(.leading, padding)
.redacted(reason: .placeholder)
) {
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(alignment: .top, spacing: 16) {
ForEach(0...10, id: \.self) { index in
VStack(alignment: .leading, spacing: 2) {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(.red)
.frame(width: cellWidth, height: cellWidth)
VStack(alignment: .leading, spacing: 2) {
Text("Example")
.foregroundColor(.primary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
Text("Example")
.foregroundColor(.secondary)
.font(.subheadline)
.lineLimit(1)
.redacted(reason: .placeholder)
}
}
.padding(.leading, index == 0 ? padding : 0)
}
}
}
}
}
}
}
.listStyle(InsetListStyle())
.navigationBarTitle("Experimental")
.introspectNavigationController {
let padding = [=11=].navigationBar
.subviews.first { String(describing: type(of: [=11=])).hasSuffix("LargeTitleView") }?
.subviews.first { [=11=] is UILabel }?
.frame.origin.x ?? 0
if !padding.isZero, self.padding != padding {
self.padding = padding
}
}
}
}
}
截图
测量所有 iPhone 模型(和 iPad)的填充我意识到如果屏幕宽度小于 414,则填充为 16 和 24 否则。
import SwiftUI
public func margin(for width: Double) -> Double {
guard !width.isZero else { return 0 }
return width >= 414 ? 20 : 16
}