如何修改Page Control中当前页面指示点的半径,其余点保持默认大小?
嘿,这个需求我之前帮朋友实现过,给你分享几种实用的方案,根据你的iOS版本和需求来选就行~
先看你想要的效果:
实现方案
iOS 14及以上(推荐官方API)
苹果在iOS14之后给UIPageControl开放了自定义指示点的官方API,完全不用碰私有属性,安全又简单,优先选这个!
核心思路是给当前页和默认页分别设置不同大小的自定义圆形图片:
let pageControl = UIPageControl() pageControl.numberOfPages = 5 pageControl.currentPage = 0 pageControl.translatesAutoresizingMaskIntoConstraints = false // 封装一个生成圆形指示点图片的工具方法 private func createIndicatorImage(size: CGSize, color: UIColor) -> UIImage { UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale) color.setFill() UIBezierPath(ovalIn: CGRect(origin: .zero, size: size)).fill() let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image! } // 设置当前页的大尺寸指示点,默认页用小尺寸 pageControl.currentIndicatorImage = createIndicatorImage(size: CGSize(width: 16, height: 16), color: .systemBlue) pageControl.indicatorImage = createIndicatorImage(size: CGSize(width: 8, height: 8), color: .systemGray3)
iOS 13及以下(兼容方案)
如果要兼容iOS14之前的版本,只能通过KVC修改系统私有属性来实现,注意这种方式有一定的审核风险,但只要不滥用,大部分情况都能通过:
let pageControl = UIPageControl() pageControl.numberOfPages = 5 pageControl.currentPage = 0 // 同样用工具方法生成不同大小的图片 private func createIndicatorImage(size: CGSize, color: UIColor) -> UIImage { UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale) color.setFill() UIBezierPath(ovalIn: CGRect(origin: .zero, size: size)).fill() let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image! } // 通过KVC设置私有属性 pageControl.setValue(createIndicatorImage(size: CGSize(width: 16, height: 16), color: .systemBlue), forKey: "_currentPageIndicatorImage") pageControl.setValue(createIndicatorImage(size: CGSize(width: 8, height: 8), color: .systemGray3), forKey: "_pageIndicatorImage")
完全自定义PageControl(无风险全兼容)
如果担心KVC的审核风险,或者需要更复杂的定制(比如指示点形状、动画等),可以自己写一个完全自定义的PageControl,全版本兼容,没有任何风险:
import UIKit class CustomPageControl: UIControl { // 总页数 var numberOfPages = 0 { didSet { setNeedsDisplay() } } // 当前页 var currentPage = 0 { didSet { setNeedsDisplay() } } // 配置参数,可根据需求修改 private let normalDotSize = CGSize(width: 8, height: 8) private let currentDotSize = CGSize(width: 16, height: 16) private let dotSpacing: CGFloat = 8 private let normalDotColor = UIColor.systemGray3 private let currentDotColor = UIColor.systemBlue override func draw(_ rect: CGRect) { super.draw(rect) // 计算整体宽度,居中布局 let totalWidth = (CGFloat(numberOfPages) * normalDotSize.width) + (CGFloat(numberOfPages - 1) * dotSpacing) + (currentDotSize.width - normalDotSize.width) let startX = (bounds.width - totalWidth) / 2 let y = (bounds.height - max(normalDotSize.height, currentDotSize.height)) / 2 // 绘制每个指示点 for index in 0..<numberOfPages { let isCurrentPage = index == currentPage let dotSize = isCurrentPage ? currentDotSize : normalDotSize // 计算当前点的X偏移,保证整体居中 let xOffset = startX + (CGFloat(index) * (normalDotSize.width + dotSpacing)) + (isCurrentPage ? (normalDotSize.width - dotSize.width)/2 : 0) let dotRect = CGRect(x: xOffset, y: y, width: dotSize.width, height: dotSize.height) let dotPath = UIBezierPath(ovalIn: dotRect) (isCurrentPage ? currentDotColor : normalDotColor).setFill() dotPath.fill() } } // 可选:添加点击切换页面的交互逻辑 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } let touchPoint = touch.location(in: self) let totalWidth = (CGFloat(numberOfPages) * normalDotSize.width) + (CGFloat(numberOfPages - 1) * dotSpacing) + (currentDotSize.width - normalDotSize.width) let startX = (bounds.width - totalWidth) / 2 for index in 0..<numberOfPages { let dotWidth = index == currentPage ? currentDotSize.width : normalDotSize.width let dotX = startX + (CGFloat(index) * (normalDotSize.width + dotSpacing)) + (index == currentPage ? (normalDotSize.width - dotWidth)/2 : 0) let dotRect = CGRect(x: dotX, y: bounds.midY - dotWidth/2, width: dotWidth, height: dotWidth) if dotRect.contains(touchPoint) { currentPage = index sendActions(for: .valueChanged) break } } } } // 使用示例 let customPageControl = CustomPageControl() customPageControl.numberOfPages = 5 customPageControl.currentPage = 0 customPageControl.frame = CGRect(x: 0, y: 200, width: UIScreen.main.bounds.width, height: 30) // 如果用AutoLayout: // customPageControl.translatesAutoresizingMaskIntoConstraints = false // NSLayoutConstraint.activate([ // customPageControl.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50), // customPageControl.leadingAnchor.constraint(equalTo: view.leadingAnchor), // customPageControl.trailingAnchor.constraint(equalTo: view.trailingAnchor), // customPageControl.heightAnchor.constraint(equalToConstant: 30) // ])
方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| iOS14+官方API | 无审核风险、代码简单、官方支持 | 只兼容iOS14+ |
| KVC兼容方案 | 代码简单、兼容旧版本 | 有审核风险、可能随系统版本失效 |
| 完全自定义 | 全版本兼容、无审核风险、高度灵活 | 需要自己实现绘制和交互代码 |
内容的提问来源于stack exchange,提问作者Ilie




