2

I've subclassed UIPageControl in order to have its current dot bigger.

class CustomPageControl: UIPageControl {
    override var currentPage: Int {
        didSet {
            updateDots()
        }
    }

    func updateDots() {
        let currentDot = subviews[currentPage]
        let largeScaling = CGAffineTransform(scaleX: 3, y: 3)

        subviews.forEach {
            // apply the large scale of newly selected dot
            // restore the normal scale of previously selected dot
            $0.transform = $0 == currentDot ? largeScaling : .identity
        }
    }
}

But the result of the transform isn't centered (the red dot should be aligned with the others):
enter image description here

I've tried (on iOS 12):

  • changing the frame or center of currentDot has no effect.
  • changing the transform to include a translatedBy(x: CGFloat, y: CGFloat) has no effect.
  • changing the constraints like here is making the first dot jumping:

    currentDot.translatesAutoresizingMaskIntoConstraints = false
    currentDot.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0)
    currentDot.centerXAnchor.constraint(equalTo: self.centerXAnchor, constant: 0)
    

    enter image description here

Cœur
  • 37,241
  • 25
  • 195
  • 267

2 Answers2

5

I got it finally working by rewriting all the subviews constraints by myself.

// https://stackoverflow.com/a/55063316/1033581
class DefaultPageControl: UIPageControl {

    override var currentPage: Int {
        didSet {
            updateDots()
        }
    }

    override func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) {
        super.sendAction(action, to: target, for: event)
        updateDots()
    }

    private func updateDots() {
        let currentDot = subviews[currentPage]
        let largeScaling = CGAffineTransform(scaleX: 3.0, y: 3.0)
        let smallScaling = CGAffineTransform(scaleX: 1.0, y: 1.0)

        subviews.forEach {
            // Apply the large scale of newly selected dot.
            // Restore the small scale of previously selected dot.
            $0.transform = $0 == currentDot ? largeScaling : smallScaling
        }
    }

    override func updateConstraints() {
        super.updateConstraints()
        // We rewrite all the constraints
        rewriteConstraints()
    }

    private func rewriteConstraints() {
        let systemDotSize: CGFloat = 7.0
        let systemDotDistance: CGFloat = 16.0

        let halfCount = CGFloat(subviews.count) / 2
        subviews.enumerated().forEach {
            let dot = $0.element
            dot.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.deactivate(dot.constraints)
            NSLayoutConstraint.activate([
                dot.widthAnchor.constraint(equalToConstant: systemDotSize),
                dot.heightAnchor.constraint(equalToConstant: systemDotSize),
                dot.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0),
                dot.centerXAnchor.constraint(equalTo: centerXAnchor, constant: systemDotDistance * (CGFloat($0.offset) - halfCount))
            ])
        }
    }
}

System constants in the code (7.0 and 16.0) are respectively the size and the distance found for a default UIPageControl dot on iOS 12.

result

Cœur
  • 37,241
  • 25
  • 195
  • 267
  • I tried this, and while it works (although offset to the left), I get error messages on the console: "Unable to simultaneously satisfy constraints." with more than 1 page and there are two centerX NSLayoutConstraints listed. When I print the constraints on `dot` before removing them, there are only two - and none of them show the centerX constraint. My pageControl is added to the UI in storyboard, but I also tried adding one programmatically but still get the errors. the component looks like it's doing the right thing to the user, but the constraint violations it's throwing worry me. – N.W Jul 26 '20 at 06:51
  • Improvements welcome, @ChanchalRaj. My solution was good for iOS 12 and iOS 13. – Cœur Dec 11 '20 at 07:17
0

I tried the solution proposed by Cœur in Swift 5 and Xcode 11 and it works fine with a few notes:

  • The PageControl element in IB/Storyboard has to be positioned with constraints.
  • The dots are slightly off-center but it can be quickly fixed by changing the constant of the last constraint to systemDotDistance * ( CGFloat($0.offset) - (halfCount - 0.5)).
  • If the updateConstraints override is never called, you might need to call self.view.setNeedsUpdateConstraints() in the view controller.
testone
  • 11
  • 2