如何用自定义图片替换UIPageControl的指示点?
如何将UIPageControl的指示点替换为自定义图片?
我来帮你搞定UIPageControl自定义指示点的问题!看你已经在写PageViewController的代码了,这里给你两种实用的实现方案,分版本兼容和原生API两种情况:
一、iOS 14+ 直接用系统原生API(最推荐)
苹果在iOS14之后给UIPageControl新增了直接设置自定义图片的API,不用绕弯子,代码简洁还稳定:
// 假设你已经有了UIPageControl实例,比如叫pageControl pageControl.preferredIndicatorImage = UIImage(named: "你的选中状态图片") // 给每个未选中的page设置默认图片 for index in 0..<pageControl.numberOfPages { pageControl.setIndicatorImage(UIImage(named: "未选中状态图片"), forPage: index) }
这种方法最省心,系统会自动帮你管理选中和未选中状态的切换,不需要额外处理代理或者绘制逻辑,直接用就完事儿了。
二、兼容iOS 14以下版本的方案
如果你的项目需要支持更低版本的iOS,那可以用下面两种方法:
方法1:通过KVC替换私有视图
这种方法是通过访问UIPageControl的私有子视图来替换成自定义图片,目前大部分版本都能兼容,不过要注意苹果后续可能修改私有属性:
extension UIPageControl { func setCustomIndicatorImages(selectedImage: UIImage, unselectedImage: UIImage) { guard numberOfPages > 0 else { return } // 获取系统自带的指示器视图数组 if let indicators = self.value(forKey: "_indicators") as? [UIView] { for (index, indicator) in indicators.enumerated() { let imageView = UIImageView(image: index == currentPage ? selectedImage : unselectedImage) // 清空原有视图,添加自定义图片 indicator.subviews.forEach { $0.removeFromSuperview() } indicator.addSubview(imageView) imageView.frame = indicator.bounds } } } // 监听currentPage变化,自动更新图片状态 override open var currentPage: Int { didSet { setCustomIndicatorImages(selectedImage: UIImage(named: "选中图")!, unselectedImage: UIImage(named: "未选中图")!) } } }
使用的时候,在你的PageViewController里初始化UIPageControl后,调用setCustomIndicatorImages方法即可。
方法2:完全自定义PageControl控件
如果担心KVC的风险,或者需要更灵活的样式控制,完全可以自己写一个自定义的PageControl,用UIStackView来管理指示器:
class CustomPageControl: UIView { // 总页数 var numberOfPages = 0 { didSet { setupIndicators() } } // 当前选中页 var currentPage = 0 { didSet { updateIndicatorStates() } } // 自定义图片(根据你的需求替换) private let selectedImage = UIImage(named: "选中状态图片")! private let unselectedImage = UIImage(named: "未选中状态图片")! private var indicatorViews = [UIImageView]() override init(frame: CGRect) { super.init(frame: frame) setupStackView() } required init?(coder: NSCoder) { super.init(coder: coder) setupStackView() } // 创建栈视图来管理指示器 private func setupStackView() { let stackView = UIStackView() stackView.axis = .horizontal stackView.spacing = 8 // 指示器之间的间距,可自定义 stackView.alignment = .center stackView.translatesAutoresizingMaskIntoConstraints = false addSubview(stackView) NSLayoutConstraint.activate([ stackView.centerXAnchor.constraint(equalTo: centerXAnchor), stackView.centerYAnchor.constraint(equalTo: centerYAnchor), stackView.heightAnchor.constraint(equalToConstant: selectedImage.size.height) ]) } // 生成所有指示器视图 private func setupIndicators() { guard let stackView = subviews.first as? UIStackView else { return } // 先清除旧的指示器 indicatorViews.forEach { $0.removeFromSuperview() } indicatorViews.removeAll() for _ in 0..<numberOfPages { let imageView = UIImageView(image: unselectedImage) imageView.contentMode = .scaleAspectFit indicatorViews.append(imageView) stackView.addArrangedSubview(imageView) } updateIndicatorStates() } // 更新选中/未选中状态 private func updateIndicatorStates() { guard currentPage < indicatorViews.count else { return } for (index, imageView) in indicatorViews.enumerated() { imageView.image = index == currentPage ? selectedImage : unselectedImage } } }
然后在你的PageViewController里使用这个自定义控件:
class PageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource { // 自定义分页控件 let customPageControl = CustomPageControl() // 你的视图控制器数组 var orderedViewControllers: [UIViewController] = [] override func viewDidLoad() { super.viewDidLoad() delegate = self dataSource = self // 设置自定义分页控件的位置和页数 customPageControl.numberOfPages = orderedViewControllers.count customPageControl.translatesAutoresizingMaskIntoConstraints = false view.addSubview(customPageControl) NSLayoutConstraint.activate([ customPageControl.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20), customPageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor), customPageControl.heightAnchor.constraint(equalToConstant: 20) ]) } // 监听页面切换,更新当前选中页 func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { if completed, let currentVC = pageViewController.viewControllers?.first, let index = orderedViewControllers.index(of: currentVC) { customPageControl.currentPage = index } } // 你的数据源方法(补充完整你的代码) func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else { return nil } let previousIndex = viewControllerIndex - 1 guard previousIndex >= 0 else { return orderedViewControllers.last } guard orderedViewControllers.count > previousIndex else { return nil } return orderedViewControllers[previousIndex] } func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else { return nil } let nextIndex = viewControllerIndex + 1 guard orderedViewControllers.count != nextIndex else { return orderedViewControllers.first } guard orderedViewControllers.count > nextIndex else { return nil } return orderedViewControllers[nextIndex] } }
完全自定义的方式最稳定,不会受系统版本更新的影响,适合需要长期维护的项目。
内容的提问来源于stack exchange,提问作者Lama
相关产品推荐
相关产品推荐

