在 SwiftUI 应用程序中,我使用 UIViewControllerRepresentable 封装了 UIKit 的 UIPageViewController,并将封装的类命名为 PagedInfiniteScrollView。该组件会导致导航栏元素(标题和按钮)消失。 此问题仅在物理设备上的低功耗模式下发生。
在物理设备上启用低功耗模式并打开应用程序的主页。
从主页中,打开包含 PagedInfiniteScrollView 的详细信息表。此详细信息页面应在右上角包含导航标题和工具栏按钮。 PagedInfiniteScrollView 支持水平滑动切换页面。
点击详细信息页面右上角的工具栏按钮可打开编辑表。 不进行任何更改,关闭编辑表并返回到详细信息页面。 在详情页面,在PagedInfiniteScrollView上左右滑动。
import SwiftUI
@main
struct CalendarApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
import SwiftUI
struct ContentView: View {
@State private var showDetailSheet = false
@State private var currentPage: Int = 0
var body: some View {
NavigationStack {
Button {
showDetailSheet = true
} label: {
Text("show Calendar sheet")
}
.sheet(isPresented: $showDetailSheet) {
DetailSheet(currentPage: $currentPage)
}
}
}
}
struct DetailSheet: View {
@Binding var currentPage: Int
@State private var showEditSheet = false
var body: some View {
NavigationStack {
PagedInfiniteScrollView(content: { pageIndex in
Text("\(pageIndex)")
.frame(width: 200, height: 200)
.background(Color.blue)
},
currentPage: $currentPage)
.sheet(isPresented: $showEditSheet, content: {
Text("edit")
})
.navigationTitle("Detail")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItemGroup(placement: .topBarTrailing) {
Button {
showEditSheet = true
} label: {
Text("Edit")
}
}
}
}
}
}
import SwiftUI
import UIKit
struct PagedInfiniteScrollView<Content: View>: UIViewControllerRepresentable {
typealias UIViewControllerType = UIPageViewController
let content: (Int) -> Content
@Binding var currentPage: Int
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
pageViewController.dataSource = context.coordinator
pageViewController.delegate = context.coordinator
let initialViewController = UIHostingController(rootView: IdentifiableContent(index: currentPage, content: { content(currentPage) }))
pageViewController.setViewControllers([initialViewController], direction: .forward, animated: false, completion: nil)
return pageViewController
}
func updateUIViewController(_ uiViewController: UIPageViewController, context: Context) {
let currentViewController = uiViewController.viewControllers?.first as? UIHostingController<IdentifiableContent<Content>>
let currentIndex = currentViewController?.rootView.index ?? 0
if currentPage != currentIndex {
let direction: UIPageViewController.NavigationDirection = currentPage > currentIndex ? .forward : .reverse
let newViewController = UIHostingController(rootView: IdentifiableContent(index: currentPage, content: { content(currentPage) }))
uiViewController.setViewControllers([newViewController], direction: direction, animated: true, completion: nil)
}
}
class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var parent: PagedInfiniteScrollView
init(_ parent: PagedInfiniteScrollView) {
self.parent = parent
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let currentView = viewController as? UIHostingController<IdentifiableContent<Content>>, let currentIndex = currentView.rootView.index as Int? else {
return nil
}
let previousIndex = currentIndex - 1
return UIHostingController(rootView: IdentifiableContent(index: previousIndex, content: { parent.content(previousIndex) }))
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let currentView = viewController as? UIHostingController<IdentifiableContent<Content>>, let currentIndex = currentView.rootView.index as Int? else {
return nil
}
let nextIndex = currentIndex + 1
return UIHostingController(rootView: IdentifiableContent(index: nextIndex, content: { parent.content(nextIndex) }))
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed,
let currentView = pageViewController.viewControllers?.first as? UIHostingController<IdentifiableContent<Content>>,
let currentIndex = currentView.rootView.index as Int? {
parent.currentPage = currentIndex
}
}
}
}
extension PagedInfiniteScrollView {
struct IdentifiableContent<Content: View>: View {
let index: Int
let content: Content
init(index: Int, @ViewBuilder content: () -> Content) {
self.index = index
self.content = content()
}
var body: some View {
content
}
}
}
滑动 PagedInfiniteScrollView 时,详细信息页面的导航标题和右上角的工具栏按钮应保持可见。
滑动PagedInfiniteScrollView时,详情页的导航标题和右上角工具栏按钮消失。
🎊恭喜💐😋 立即向我们获取您的优惠券。点击链接,提交您的 Gmail 和邮政编码以获取您的优惠券。 赶快在这里注册吧🤫👇👇👇
在 SwiftUI 环境中使用
UIPageViewController
时,您有时可能会遇到导航栏元素消失的意外行为,尤其是在低功耗模式等条件下。这是因为低功耗模式会影响动画和视图更新,导致不一致。
🎊恭喜💐😋 立即向我们获取您的优惠券。点击链接,提交您的 Gmail 和邮政编码以获取您的优惠券。 赶快在这里注册吧🤫👇👇👇 https://cutt.ly/5eWF0Ffn
当
UIPageViewController
嵌入到 SwiftUI UIViewControllerRepresentable
包装器中时,导航栏可能会在视图更新期间丢失其状态。在低功耗模式下,某些系统优化可能会干扰视图的渲染和生命周期。这可能会导致导航项目消失或无法正确设置。
要解决此问题,您需要确保每次
UIPageViewController
更新或显示新视图时都显式设置导航栏元素。这可以通过以下方式完成:
显式设置导航栏项目: 以不受
UIPageViewController
内状态更改影响的方式添加导航栏项目。一种常见的方法是在托管 .navigationBarItems
的 SwiftUI 视图上使用 SwiftUI 的 UIPageViewController
修饰符。
正确处理状态: 如果导航栏项目在
UIPageViewController
内进行管理,请确保页面转换等状态更改不会干扰导航栏配置。
以下是解决此问题的一般结构:
创建
UIViewControllerRepresentable
包装:
import SwiftUI
import UIKit
struct PageViewController: UIViewControllerRepresentable {
var viewControllers: [UIViewController]
func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(
transitionStyle: .scroll,
navigationOrientation: .horizontal
)
return pageViewController
}
func updateUIViewController(_ uiViewController: UIPageViewController, context: Context) {
uiViewController.setViewControllers(
[viewControllers[0]], direction: .forward, animated: true
)
}
}
将其包装在 SwiftUI 视图中并配置导航:
在父 SwiftUI 视图中,即使在省电状态下,您也可以确保正确设置和显示导航项:
struct PageView: View {
var body: some View {
NavigationView {
PageViewController(viewControllers: [
UIHostingController(rootView: Text("Page 1")),
UIHostingController(rootView: Text("Page 2"))
])
.navigationBarTitle("Page View", displayMode: .inline)
.navigationBarItems(trailing: Button("Action") {
print("Action tapped")
})
}
}
}
避免大量动画: SwiftUI 和 UIKit 动画在低功耗模式下可能会受到影响。使用更简单的动画或有条件地禁用它们。
检查查看生命周期事件: 监控视图的生命周期(
onAppear
、onDisappear
)以确保导航项设置正确。
调试状态更改: 使用
print
语句或 Xcode 的调试工具来查看导航栏项目何时以及在什么情况下消失。
通过显式设置导航栏项目并确保它们不依赖于
UIPageViewController
内的状态更改,您应该能够克服低功耗模式的不一致问题。
🎊恭喜💐😋 立即向我们获取您的优惠券。点击链接,提交您的 Gmail 和邮政编码以获取您的优惠券。 赶快在这里注册吧🤫👇👇👇 https://cutt.ly/5eWF0Ffn