0

enter image description here

I want customize page control like a image.
I've already search that, but there are only deal scale.
I want change width, height, spacing.
How can I do that?

I tried this

class DefaultPageControl: UIPageControl {

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

    func updateDots() {
        let currentDot = subviews[currentPage]

        subviews.forEach {
            $0.frame.size = ($0 == currentDot) ? CGSize(width: 16, height: 4) : CGSize(width: 8, height: 4)
            $0.layer.cornerRadius = 2
        }
    }
}

But how to change distance??

oddK
  • 261
  • 4
  • 14
  • Can you try this - https://stackoverflow.com/a/42439839/3683408 - I mean the third answer - `viewDidLayoutSubviews` – Ram Mar 18 '20 at 09:06
  • @Ram Aren't you dealing with scale like I said? I don't want to deal with scale. – oddK Mar 18 '20 at 09:20
  • Cool! Mostly I can see it with the solution by using scaling. So that's the reason I was suggested to you. It's okay you can ignore it. – Ram Mar 18 '20 at 09:51

3 Answers3

1

The default UIPageControll is not flexible.

class ExtendedpageControll: UIView{
 var numberOfPage: Int
 var currentpage : Int                  = 0{didSet{reloadView()}}
 var currentIndicatorColor: UIColor     = .black
 var indicatorColor: UIColor            = UIColor(white: 0.9, alpha: 1)
 var circleIndicator: Bool              = false
 private var dotView                    = [UIView]()
 private let spacing: CGFloat           = 6
 private lazy var  extraWidth: CGFloat  = circleIndicator ? 6 : 4

 init(numberOfPages: Int,currentPage: Int,isCircular: Bool){
    self.numberOfPage    = numberOfPages
    self.currentpage     = currentPage
    self.circleIndicator = isCircular
    super.init(frame: .zero)
    configView()
}
 required init?(coder: NSCoder) {fatalError("not implemented")}

 private func configView(){
    backgroundColor = .clear
    (0..<numberOfPage).forEach { _ in
        let view = UIView()
        addSubview(view)
        dotView.append(view)
    }
 }

 private func reloadView(){
    dotView.forEach{$0.backgroundColor = indicatorColor}
    dotView[currentpage].backgroundColor = currentIndicatorColor
    UIView.animate(withDuration: 0.2) {
        self.dotView[self.currentpage].frame.origin.x   = self.dotView[self.currentpage].frame.origin.x - self.extraWidth
        self.dotView[self.currentpage].frame.size.width = self.dotView[self.currentpage].frame.size.width + (self.extraWidth * 2)
    }
}

 override func layoutSubviews() {
    super.layoutSubviews()
    for (i,view) in dotView.enumerated(){
        view.clipsToBounds      = true
        view.layer.cornerRadius = bounds.height / 2
        let width: CGFloat      = circleIndicator ? self.bounds.height : CGFloat(self.bounds.width / CGFloat(self.numberOfPage) - self.spacing) - self.extraWidth
        UIView.animate(withDuration: 0.2) {
          view.frame = CGRect(x: ((self.bounds.width / CGFloat(self.numberOfPage)) * CGFloat(i)) + self.spacing, y: 0, width: width , height: self.bounds.height)
        }
    }
    reloadView()
}
}

Usage: If you want to link ExtendedpageControll to a View Such as CollectionView Just Do like this: (item is your Datamodel)

class SampleViewController: UIViewController{

let colectionView = UICollectionView()

lazy var pageControll: ExtendedpageControll = {
    let pc                   = ExtendedpageControll(numberOfPages: items.count, currentPage: 0,isCircular: true)
    pc.currentIndicatorColor = .black
    return pc
}()

func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    
        if pageControll.currentpage == indexPath.row {
            guard let visible = self.collectionView.visibleCells.first else { return }
            guard let index = self.collectionView.indexPath(for: visible)?.row else { return }
            pageControll.currentpage = index
        }

   } 
}

inside init, you can set the shape of the indicator to be circular or extended via isCircular.

1

I was looking for a similar solution but in SwiftUI, I managed to achieve it with few lines of code.

struct CustomPageControl: View {
   @Binding var currentPage: Int
   var numberOfPages: Int
    
   var body: some View {
     HStack {
       ForEach(0..<numberOfPages) { currentIndex in
         Capsule()
           .frame(width: currentPage == currentIndex + 1 ? 54 : 20, height: 8)
           .foregroundColor(currentPage == currentIndex + 1 ? .goldNeom : .white)
           .animation(.easeInOut, value: currentPage)
       }
     }
   }
 }

CustomPageControl takes currentPage and numberOfPages as the dependency, I have used a simple Capsule shape to create a single page control dot, you can customize it with different parameters (example: foreground Color, width, height etc.) If needed this custom view can be used in the UIKit using UIHostingController.

I hope this would be helpful for other users who are looking for SwiftUI solutions.

Jarvis The Avenger
  • 2,750
  • 1
  • 19
  • 37
0

@oddK Can you try with this below answer. It's my assumption.

    class DefaultPageControl: UIPageControl {

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

    func updateDots() {
        let currentDot = subviews[currentPage]

        let spacing = 5.0

        subviews.forEach {
            $0.frame = ($0 == currentDot) ? CGRect(x: 0, y: 0, width: 16, height: 4) : CGRect(x: spacing, y: 0, width: 8, height: 4)
            //$0.frame.size = ($0 == currentDot) ? CGSize(width: 16, height: 4) : CGSize(width: 8, height: 4)
            $0.layer.cornerRadius = 2
        }
    }
}
Ram
  • 764
  • 1
  • 7
  • 20
  • did you check how to show? uhm...It is slightly different. – oddK Mar 18 '20 at 10:02
  • @oddK Yes Checked it. Seems it's distance and dot width were ok, but facing `x` position issue based on currentPage index. Why you can try the following libraries - https://material.io/develop/ios/components/page-controls/ and https://medium.com/@Pelaez/recreating-instagrams-page-control-ebc2103b8a39 Hope it will give some logical things to do your required output. I mean get some logical things you need to go through the second link. – Ram Mar 18 '20 at 11:10