如何在所有屏幕尺寸下适配UIPageViewController的多页文本显示
解决UIPageViewController全屏文本分页的多设备适配问题
嘿,作为刚接触Swift/Xcode的新手,遇到这种跨设备的文本适配问题太正常了!你的核心痛点是固定文本量或固定字号无法适配不同屏幕尺寸,其实解决思路很简单:根据当前设备的屏幕可用空间,动态计算一页能容纳的文本量,再把整段文本分割成刚好填满每页的子内容。下面给你一步步拆解实现方法,都是适合新手的实操步骤:
一、核心思路:动态计算每页文本容量
我们需要一个工具方法,输入完整文本、字体样式、页面容器尺寸,输出分割好的每页文本数组。原理是利用NSString的boundingRect方法,计算文本在指定容器内的高度,从而反推一页能装下多少字符。
示例工具方法代码
import UIKit func splitTextIntoPages(fullText: String, font: UIFont, pageSize: CGSize) -> [String] { var pages = [String]() let baseAttributes: [NSAttributedString.Key: Any] = [.font: font] var currentStartIndex = fullText.startIndex while currentStartIndex < fullText.endIndex { var currentEndIndex = fullText.endIndex var currentText = String(fullText[currentStartIndex..<currentEndIndex]) var textSize = NSAttributedString(string: currentText, attributes: baseAttributes) .boundingRect(with: pageSize, options: [.usesLineFragmentOrigin], context: nil).size // 如果当前文本超出页面,逐步减少字符直到刚好容纳 while textSize.height > pageSize.height && currentEndIndex > currentStartIndex { currentEndIndex = fullText.index(before: currentEndIndex) currentText = String(fullText[currentStartIndex..<currentEndIndex]) textSize = NSAttributedString(string: currentText, attributes: baseAttributes) .boundingRect(with: pageSize, options: [.usesLineFragmentOrigin], context: nil).size } pages.append(currentText) currentStartIndex = currentEndIndex } return pages }
二、配置分页的子ViewController
每个分页页面用一个UITextView(禁用滚动)来显示文本,确保文本刚好填满整个页面:
class PageContentViewController: UIViewController { var pageText: String! let textView = UITextView() override func viewDidLoad() { super.viewDidLoad() // 配置UITextView:禁用滚动,铺满整个可用页面(适配安全区) textView.translatesAutoresizingMaskIntoConstraints = false textView.isScrollEnabled = false textView.font = UIFont.systemFont(ofSize: 17) // 可自定义字号 textView.text = pageText textView.textAlignment = .justified // 可选:设置两端对齐更美观 view.addSubview(textView) // 用AutoLayout绑定到安全区边缘 NSLayoutConstraint.activate([ textView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), textView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), textView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) ]) } }
三、在UIPageViewController中整合逻辑
在你的UIPageViewController里,先获取当前设备的页面尺寸,分割文本,再实现数据源方法:
class PageViewController: UIPageViewController, UIPageViewControllerDataSource { var pageTexts: [String] = [] override func viewDidLoad() { super.viewDidLoad() dataSource = self // 1. 获取当前页面的可用尺寸(去掉安全区后的大小) let pageSize = view.safeAreaLayoutGuide.layoutFrame.size // 2. 替换成你的完整长文本 let fullText = "这里放入你的长文本内容,比如书籍章节、文章段落等..." // 3. 分割文本为每页内容 pageTexts = splitTextIntoPages(fullText: fullText, font: UIFont.systemFont(ofSize: 17), pageSize: pageSize) // 初始化显示第一页 if let firstPageVC = createPageVC(at: 0) { setViewControllers([firstPageVC], direction: .forward, animated: false, completion: nil) } } // 创建对应索引的页面VC func createPageVC(at index: Int) -> PageContentViewController? { guard index >= 0 && index < pageTexts.count else { return nil } let vc = PageContentViewController() vc.pageText = pageTexts[index] return vc } // UIPageViewController数据源方法:上一页 func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { guard let currentVC = viewController as? PageContentViewController, let currentIndex = pageTexts.firstIndex(of: currentVC.pageText), currentIndex > 0 else { return nil } return createPageVC(at: currentIndex - 1) } // UIPageViewController数据源方法:下一页 func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { guard let currentVC = viewController as? PageContentViewController, let currentIndex = pageTexts.firstIndex(of: currentVC.pageText), currentIndex < pageTexts.count - 1 else { return nil } return createPageVC(at: currentIndex + 1) } }
四、关键细节说明
- 安全区适配:用
safeAreaLayoutGuide绑定布局,避免文本被状态栏、导航栏遮挡,完美适配从iPhone4到iPhone8 Plus的所有机型。 - 自定义样式兼容:如果需要设置行距、字体颜色等样式,只需在
baseAttributes里添加对应的属性(比如.lineSpacing),计算文本高度时会自动考虑这些样式。 - 性能优化:如果文本特别长,建议把分割逻辑放在后台线程执行,完成后再回到主线程更新UI,避免界面卡顿。
这样处理后,不管是小屏的iPhone4还是大屏的iPhone8 Plus,每一页的文本都会刚好填满整个屏幕,用户滑动切换时的体验就流畅自然啦!
内容的提问来源于stack exchange,提问作者user67478
相关产品推荐
相关产品推荐

