You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何修改Page Control中当前页面指示点的半径,其余点保持默认大小?

嘿,这个需求我之前帮朋友实现过,给你分享几种实用的方案,根据你的iOS版本和需求来选就行~

先看你想要的效果:
当前指示点放大的PageControl效果

实现方案

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

火山引擎 最新活动