You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中PDF当前页面保存与返回定位实现问题咨询

解决SwiftUI中PDF页码保存与自动跳转问题

你当前的核心问题是在updateUIView中直接修改页码存储会触发视图无限更新循环,同时缺少对PDF页码变化的有效监听机制。下面是修复后的完整代码,同时保留你原有的tab切换逻辑:

import SwiftUI
import PDFKit
import SlidingTabView

struct PDFKitRepresentedView: UIViewRepresentable {
    let url: URL
    @Binding var pageNum: [String: Int]

    init(_ url: URL, _ pageNum: Binding<[String: Int]>) {
        self.url = url
        self._pageNum = pageNum
    }

    // 定义Coordinator监听PDF页码变化
    class Coordinator: NSObject, PDFViewDelegate {
        var parent: PDFKitRepresentedView
        
        init(parent: PDFKitRepresentedView) {
            self.parent = parent
        }
        
        // 页码变化时触发,更新字典存储
        func pdfViewDidChangeCurrentPage(_ pdfView: PDFView) {
            guard let currentPage = pdfView.currentPage,
                  let document = pdfView.document else { return }
            let pageIndex = document.index(for: currentPage)
            parent.pageNum[parent.url.lastPathComponent] = pageIndex
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }

    func makeUIView(context: UIViewRepresentableContext<PDFKitRepresentedView>) -> UIView {
        guard let document = PDFDocument(url: url) else { return UIView() }
        let pdfView = PDFView()
        pdfView.document = document
        pdfView.autoScales = true
        pdfView.delegate = context.coordinator
        
        // 初始化时跳转到保存的页码,默认0(第一页)
        let savedPageIndex = pageNum[url.lastPathComponent] ?? 0
        if let targetPage = document.page(at: savedPageIndex) {
            pdfView.go(to: targetPage)
        }
        
        return pdfView
    }
    
    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PDFKitRepresentedView>) {
        // 仅在必要时更新,这里不需要修改状态,避免循环
        guard let pdfView = uiView as? PDFView,
              let document = pdfView.document else { return }
        
        // 处理切换tab后可能的页码同步(确保视图切换后页码正确)
        let savedPageIndex = pageNum[url.lastPathComponent] ?? 0
        if let targetPage = document.page(at: savedPageIndex),
           pdfView.currentPage != targetPage {
            pdfView.go(to: targetPage)
        }
    }
}

struct PDFKitView: View {
    var url: URL
    @Binding var pageNum: [String: Int]

    var body: some View {
        PDFKitRepresentedView(url, $pageNum)
    }
}

struct ContentView: View {
    @State private var showPicker = false
    @State private var fileURLs: [URL] = []
    @State private var tabs: [String] = []
    @State private var selectedTabIndex = 0
    @State private var pageNums: [String: Int] = [:]
    
    @ViewBuilder
    var barItems: some View {
        if !fileURLs.isEmpty && selectedTabIndex < fileURLs.count {
            PDFKitView(url: fileURLs[selectedTabIndex], pageNum: $pageNums)
                .id(selectedTabIndex)
        } else {
            Text("No File Selected")
        }
    }
    
    var body: some View {
        NavigationView {
            VStack(alignment: .leading) {
                if !tabs.isEmpty {
                    SlidingTabView(selection: $selectedTabIndex, tabs: tabs)
                }
                barItems.padding()
                Spacer()
            }
            .animation(.none, value: selectedTabIndex)
            .fileImporter(isPresented: $showPicker, allowedContentTypes: [.pdf]) { result in
                switch result {
                case .success(let fileURL):
                    var fileName = fileURL.lastPathComponent
                    // 处理重复文件名
                    let duplicateCount = tabs.filter { $0.hasPrefix(fileName) }.count
                    if duplicateCount > 0 {
                        fileName += " - \(duplicateCount)"
                    }
                    
                    // 更新tab列表
                    if tabs.isEmpty {
                        tabs.append(fileName)
                        tabs.append("Open a new file")
                    } else {
                        if let newFileIndex = tabs.lastIndex(of: "Open a new file") {
                            tabs[newFileIndex] = fileName
                            tabs.append("Open a new file")
                        }
                    }
                    
                    fileURLs.append(fileURL)
                    // 初始化当前PDF的页码为0(第一页)
                    pageNums[fileName] = 0
                    
                case .failure(let error):
                    print(error.localizedDescription)
                }
            }
            .navigationBarTitle("Tinjin", displayMode: .inline)
            .toolbar {
                Button {
                    showPicker.toggle()
                } label: {
                    Image(systemName: "folder")
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

关键修改说明

  • Coordinator监听页码变化:通过实现PDFViewDelegate的pdfViewDidChangeCurrentPage方法,仅在用户手动翻页时更新页码字典,完全避免了updateUIView中的循环问题。
  • 初始化时加载页码:在makeUIView中直接读取字典里的保存值并跳转,确保PDF加载后直接显示上次浏览的位置。
  • 优化文件名处理:简化了重复文件名的判断逻辑,同时限制文件选择器仅接受PDF类型,更符合需求。
  • 初始化页码默认值:添加新PDF时自动给pageNums字典设置初始页码0,避免字典中无对应键导致的崩溃。

内容的提问来源于stack exchange,提问作者toast

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 12:05:15