Swift自定义ExpandedCustomLoopedPageControl选中圆点遮挡问题求助
问题分析
你遇到的遮挡问题核心是视图层级顺序错误:当currentPage为0时,选中的大尺寸圆点层级高于第二个圆点,导致后者被遮挡。因为动画过程中调用了bringSubviewToFront(currentView)会把选中圆点带到上层,后续切换回第0页时未调整层级,最终引发遮挡。
解决方案
要确保选中的圆点始终处于视图层级的最底层,让未选中的小圆点在它上方显示,这样即使选中圆点宽度更大,也不会遮挡其他圆点。需要在两个关键位置调整层级:
- 初始化布局完成后,将当前选中的圆点发送到最底层
- 页面切换动画完成后,将新选中的圆点发送到最底层
修改后的完整代码
import UIKit class ExpandedCustomLoopedPageControl: UIView { // MARK: - start flow in pagenumber set public var numberOfPages: Int = 0 { didSet { dotViewArray.forEach { $0.removeFromSuperview() } dotViewArray.removeAll() for _ in 0..<numberOfPages { let dotView = UIView() dotView.layer.cornerRadius = cornerRadius addSubview(dotView) dotViewArray.append(dotView) } isInitialize = true setNeedsLayout() } } public var currentPage: Int { set { if newValue < 0 || newValue >= dotViewArray.count || dotViewArray.count == 0 || newValue == currentPage || inAnimating { return } var animationDuration = 0.1 if (( currentPage == (numberOfPages - 1) ) && ( newValue == 0)) || (currentPage == 0 && (newValue == (numberOfPages - 1))) { animationDuration = 0 } // MARK: - Shift to the right if newValue > currentPage { let currentView = dotViewArray[currentPage] bringSubviewToFront(currentView) inAnimating = true UIView.animate(withDuration: animationDuration, animations: { let x = currentView.frame.origin.x let y = currentView.frame.origin.y let w = self.currentDotWidth + (self.dotSpace + self.otherDotWidth) * CGFloat(newValue - self.currentPage) let h = currentView.frame.size.height currentView.frame = CGRect(x: x, y: y, width: w, height: h) }) { (_) in let endView = self.dotViewArray[newValue] endView.backgroundColor = currentView.backgroundColor endView.frame = currentView.frame self.bringSubviewToFront(endView) currentView.backgroundColor = self.otherDotColor let start_X = currentView.frame.origin.x for i in 0..<(newValue - self.currentPage) { let dotView = self.dotViewArray[self.currentPage + i] let x = start_X + (self.otherDotWidth + self.dotSpace) * CGFloat(i) let y = dotView.frame.origin.y let w = self.otherDotWidth let h = self.dotHeight dotView.frame = CGRect(x: x, y: y, width: w, height: h) } UIView.animate(withDuration: 0.1, animations: { let w = self.currentDotWidth let x = endView.frame.maxX - self.currentDotWidth let y = endView.frame.origin.y let h = endView.frame.size.height endView.frame = CGRect(x: x, y: y, width: w, height: h) }) { (_) in self.currentPageInner = newValue self.inAnimating = false // 将新选中的dot发送到最底层,避免遮挡其他dot self.sendSubviewToBack(endView) } } } // MARK: - Shift to the Left else { let currentView = self.dotViewArray[self.currentPage] bringSubviewToFront(currentView) inAnimating = true UIView.animate(withDuration: animationDuration, animations: { let x = currentView.frame.origin.x - (self.dotSpace + self.otherDotWidth) * CGFloat(self.currentPage - newValue) let y = currentView.frame.origin.y let w = self.currentDotWidth + (self.dotSpace + self.otherDotWidth) * CGFloat(self.currentPage - newValue) let h = currentView.frame.size.height currentView.frame = CGRect(x: x, y: y, width: w, height: h) }) { (_) in let endView = self.dotViewArray[newValue] endView.backgroundColor = currentView.backgroundColor endView.frame = currentView.frame self.bringSubviewToFront(endView) currentView.backgroundColor = self.otherDotColor let start_X = currentView.frame.maxX for i in 0..<(self.currentPage - newValue) { let dotView = self.dotViewArray[self.currentPage - i] let tempFrame = dotView.frame let x = start_X - self.otherDotWidth - (self.otherDotWidth + self.dotSpace) * CGFloat(i) let y = tempFrame.origin.y let w = self.otherDotWidth let h = tempFrame.size.height dotView.frame = CGRect(x: x, y: y, width: w, height: h) } UIView.animate(withDuration: 0.1, animations: { let x = endView.frame.origin.x let y = endView.frame.origin.y let w = self.currentDotWidth let h = endView.frame.size.height endView.frame = CGRect(x: x, y: y, width: w, height: h) }) { (_) in self.currentPageInner = newValue self.inAnimating = false // 将新选中的dot发送到最底层,避免遮挡其他dot self.sendSubviewToBack(endView) } } } } get { currentPageInner } } // MARK: - properties of pageControl public var currentDotWidth: CGFloat = 20 public var otherDotWidth: CGFloat = 5 public var dotHeight: CGFloat = 5 public var dotSpace: CGFloat = 7 public var cornerRadius: CGFloat = 2.5 public var currentDotColor: UIColor = Color.lines.color public var otherDotColor: UIColor = Color.slider_indicator_bubble.color private var currentPageInner: Int = 0 private var dotViewArray = [UIView]() private var isInitialize: Bool = false private var inAnimating: Bool = false // MARK: - init() override init(frame: CGRect) { super.init(frame: frame) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutSubviews() { super.layoutSubviews() setupUI() } // MARK: - Setup UI private func setupUI() { if dotViewArray.isEmpty || isInitialize == false { return } self.isInitialize = false let totalWidth = CGFloat(numberOfPages) * (currentDotWidth + dotSpace) - dotSpace var currentX = (frame.size.width - totalWidth) / 2 for (index, dotView) in dotViewArray.enumerated() { let width = (index == currentPage ? currentDotWidth : otherDotWidth) let height = dotHeight let y = (frame.size.height - height) / 2 dotView.frame = CGRect(x: currentX, y: y, width: width, height: height) dotView.backgroundColor = (index == currentPage) ? currentDotColor : otherDotColor currentX += width + dotSpace } // 初始化时将选中的dot发送到最底层,避免遮挡其他dot sendSubviewToBack(dotViewArray[currentPage]) } }
关键修改点
- 在
setupUI方法末尾添加sendSubviewToBack(dotViewArray[currentPage]),确保初始化时选中的圆点在最底层 - 在向右滑动和向左滑动的动画完成回调中,分别添加
self.sendSubviewToBack(endView),确保切换页面后新选中的圆点始终在最底层
调整后,选中的大圆点会被其他小圆点覆盖在下方,不会出现遮挡问题。
内容的提问来源于stack exchange,提问作者Arohi Khan
相关产品推荐
相关产品推荐

