简体   繁体   中英

Custom PageControl image - Swift

I am trying to set an image for UIPageControl dots.

I need to change the default dots, instead of that I need an image. I used the below code

self.pageCtrl.currentPageIndicatorTintColor = UIColor.init(patternImage: UIImage(named: "Page Indicator_Selected")!)
self.pageCtrl.pageIndicatorTintColor = UIColor.init(patternImage: UIImage(named: "Page Indicator_Normal")!)

I could set the image in Pagecontrol but, it is overlapping with default dots.

The code Politta suggested is not working on iOS 14 as Apple had introduced some new APIs for UIPageControll.

Take look here for details UIPageControls New APIs

Here is the updated code supporting iOS 14

class CustomPageControl: UIPageControl {

@IBInspectable var currentPageImage: UIImage?

@IBInspectable var otherPagesImage: UIImage?

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

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

override func awakeFromNib() {
    super.awakeFromNib()
    if #available(iOS 14.0, *) {
        defaultConfigurationForiOS14AndAbove()
    } else {
        pageIndicatorTintColor = .clear
        currentPageIndicatorTintColor = .clear
        clipsToBounds = false
    }
}

private func defaultConfigurationForiOS14AndAbove() {
    if #available(iOS 14.0, *) {
        for index in 0..<numberOfPages {
            let image = index == currentPage ? currentPageImage : otherPagesImage
            setIndicatorImage(image, forPage: index)
        }

        // give the same color as "otherPagesImage" color.
        pageIndicatorTintColor = .gray
        
        // give the same color as "currentPageImage" color.
        currentPageIndicatorTintColor = .red 
        /*
         Note: If Tint color set to default, Indicator image is not showing. So, give the same tint color based on your Custome Image.
        */
    }
}

private func updateDots() {
    if #available(iOS 14.0, *) {
        defaultConfigurationForiOS14AndAbove()
    } else {
        for (index, subview) in subviews.enumerated() {
            let imageView: UIImageView
            if let existingImageview = getImageView(forSubview: subview) {
                imageView = existingImageview
            } else {
                imageView = UIImageView(image: otherPagesImage)
                
                imageView.center = subview.center
                subview.addSubview(imageView)
                subview.clipsToBounds = false
            }
            imageView.image = currentPage == index ? currentPageImage : otherPagesImage
        }
    }
}

private func getImageView(forSubview view: UIView) -> UIImageView? {
    if let imageView = view as? UIImageView {
        return imageView
    } else {
        let view = view.subviews.first { (view) -> Bool in
            return view is UIImageView
        } as? UIImageView

        return view
    }
}
}

I improved Agent Smith's answer to work with any number of pages. You can customize the images on xibs/storyboards.

class CustomPageControl: UIPageControl {

@IBInspectable var currentPageImage: UIImage?

@IBInspectable var otherPagesImage: UIImage?

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

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

override func awakeFromNib() {
    super.awakeFromNib()
    pageIndicatorTintColor = .clear
    currentPageIndicatorTintColor = .clear
    clipsToBounds = false
}

private func updateDots() {

    for (index, subview) in subviews.enumerated() {
        let imageView: UIImageView
        if let existingImageview = getImageView(forSubview: subview) {
            imageView = existingImageview
        } else {
            imageView = UIImageView(image: otherPagesImage)

            imageView.center = subview.center
            subview.addSubview(imageView)
            subview.clipsToBounds = false
        }
        imageView.image = currentPage == index ? currentPageImage : otherPagesImage
    }
}

private func getImageView(forSubview view: UIView) -> UIImageView? {
    if let imageView = view as? UIImageView {
        return imageView
    } else {
        let view = view.subviews.first { (view) -> Bool in
            return view is UIImageView
        } as? UIImageView

        return view
    }
}
}

This approach works for me. I create a custom pageControl class and change the images for dots to be any custom image.

In the code below I changed the image for the first dot in pageControl with any custom image.

You can change updateDots() method as per your needs.

Hope it helps!!!

override func viewDidLoad() {
    super.viewDidLoad()

    let pageControl = CustomPageControl(frame: CGRect(x: 100, y: 100, width: 104, height: 40))
    pageControl.numberOfPages = 5
    pageControl.currentPage = 0
    self.view.addSubview(pageControl)
}


class CustomPageControl: UIPageControl {


var imageToBeReplacedByDot: UIImage {
    return // Image you want to replace with dots
}
var circleImage: UIImage {

    return //Default Image
}


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

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

override func awakeFromNib() {
    super.awakeFromNib()
    self.pageIndicatorTintColor = UIColor.clear
    self.currentPageIndicatorTintColor = UIColor.clear
    self.clipsToBounds = false
}

func updateDots() {
    var i = 0
    for view in self.subviews {
        var imageView = self.imageView(forSubview: view)
        if imageView == nil {
            if i == 0 {
                imageView = UIImageView(image: imageToBeReplacedByDot)
            } else {
                imageView = UIImageView(image: circleImage)
            }
            imageView!.center = view.center
            view.addSubview(imageView!)
            view.clipsToBounds = false
        }
        if i == self.currentPage {
            imageView!.alpha = 1.0
        } else {
            imageView!.alpha = 0.5
        }
        i += 1
    }
}

fileprivate func imageView(forSubview view: UIView) -> UIImageView? {
    var dot: UIImageView?
    if let dotImageView = view as? UIImageView {
        dot = dotImageView
    } else {
        for foundView in view.subviews {
            if let imageView = foundView as? UIImageView {
                dot = imageView
                break
            }
        }
    }
    return dot
}
}

Supporting iOS 14 , our PageControl subclass, simply we can do this:

    override var numberOfPages: Int {
        didSet {
            self._updateIndicators()
        }
    }
    
    override var currentPage: Int {
        didSet {
            self._updateIndicators()
        }
    }

    private func _updateIndicators() {
        var indicators: [UIView] = []
        
        if #available(iOS 14.0, *) {
            indicators = self.subviews.first?.subviews.first?.subviews ?? []
        } else {
            indicators = self.subviews
        }
        
        for (index, indicator) in indicators.enumerated() {
            let image = self.currentPage == index ? UIImage.init(named: "Page Indicator_Normal") : UIImage.init(named: "Page Indicator_Selected")
            if let dot = indicator as? UIImageView {
                dot.image = image
                
            } else {
                let imageView = UIImageView.init(image: image)
                indicator.addSubview(imageView)
                // here you can add some constraints to fix the imageview to his superview
            }
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM