Mann
Mann

Reputation: 5507

ios Changing UIScrollView scrollbar color to different colors

How can we change color of UIScrollview's scroll indicator to something like blue, green etc.

I know we can change it to white, black. But other then these colors.

Many Thanks

Upvotes: 35

Views: 41038

Answers (17)

Vineet Choudhary
Vineet Choudhary

Reputation: 7633

Both UIScrollView indicator are sub view of UIScrollView. So, we can access subview of UIScrollView and change the property of subview.

1 .Add UIScrollViewDelegate

@interface ViewController : UIViewController<UIScrollViewDelegate>
@end

2. Add scrollViewDidScroll in implementation section

-(void)scrollViewDidScroll:(UIScrollView *)scrollView1
{
    //get refrence of vertical indicator
    UIImageView *verticalIndicator = ((UIImageView *)[scrollView.subviews objectAtIndex:(scrollView.subviews.count-1)]);
    //set color to vertical indicator
    [verticalIndicator setBackgroundColor:[UIColor redColor]];


    //get refrence of horizontal indicator
    UIImageView *horizontalIndicator = ((UIImageView *)[scrollView.subviews objectAtIndex:(scrollView.subviews.count-2)]);
    //set color to horizontal indicator
    [horizontalIndicator setBackgroundColor:[UIColor blueColor]];
}

Note:- Because these indicator update every time when you scroll (means reset to default). SO, we put this code in scrollViewDidScroll delegate method.

enter image description here Demo available on GitHub - https://github.com/developerinsider/UIScrollViewIndicatorColor

Upvotes: 11

j_gonfer
j_gonfer

Reputation: 1567

Based on the answer of @Alex (https://stackoverflow.com/a/58415249/3876285), I'm posting just a little improvement to change the color of scroll indicators.

extension UIScrollView {

    var scrollIndicators: (horizontal: UIView?, vertical: UIView?) {

        guard self.subviews.count >= 2 else {
            return (horizontal: nil, vertical: nil)
        }

        func viewCanBeScrollIndicator(view: UIView) -> Bool {
            let viewClassName = NSStringFromClass(type(of: view))
            if viewClassName == "_UIScrollViewScrollIndicator" || viewClassName == "UIImageView" {
                return true
            }
            return false
        }

        let horizontalScrollViewIndicatorPosition = self.subviews.count - 2
        let verticalScrollViewIndicatorPosition = self.subviews.count - 1

        var horizontalScrollIndicator: UIView?
        var verticalScrollIndicator: UIView?

        let viewForHorizontalScrollViewIndicator = self.subviews[horizontalScrollViewIndicatorPosition]
        if viewCanBeScrollIndicator(view: viewForHorizontalScrollViewIndicator) {
            horizontalScrollIndicator = viewForHorizontalScrollViewIndicator.subviews[0]
        }

        let viewForVerticalScrollViewIndicator = self.subviews[verticalScrollViewIndicatorPosition]
        if viewCanBeScrollIndicator(view: viewForVerticalScrollViewIndicator) {
            verticalScrollIndicator = viewForVerticalScrollViewIndicator.subviews[0]
        }
        return (horizontal: horizontalScrollIndicator, vertical: verticalScrollIndicator)
    }

}

If you don't add .subviews[0], you will get the deeper view and when you try to change the color of the indicator, this will appear with a weird white effect. That's because there is another view in front of it:

enter image description here

By adding .subviews[0] to each indicator view, once you try to change the color by calling:

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    DispatchQueue.main.async() {
        scrollView.scrollIndicators.vertical?.backgroundColor = UIColor.yourcolor
    }
}

You will access to the first view and change the color properly:

enter image description here

Kudos to @Alex who posted a great solution 👍

Upvotes: 11

Melany
Melany

Reputation: 466

As for iOS 13 subviews changed so adding simple if, solved this issues.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 13.0) {
    UIView *verticalIndicator = [scrollView.subviews lastObject];
    verticalIndicator.backgroundColor = [UIColor redColor];
} else {
    UIImageView *verticalIndicator = [scrollView.subviews lastObject];
    verticalIndicator.backgroundColor = [UIColor redColor];
}
}

Upvotes: 0

Alex
Alex

Reputation: 2150

in IOS 13

Since iOS13 scroll indicators have class _UIScrollViewScrollIndicator, not UIImageView.

Many people used code like

let verticalIndicator: UIImageView = (scrollView.subviews[(scrollView.subviews.count - 1)] as! UIImageView)

It's not good idea, because they promised that last subview will be UIImageView :). Now it's not and they can get crash.

You can try following code to get scrollView indicators:

extension UIScrollView {

    var scrollIndicators: (horizontal: UIView?, vertical: UIView?) {

        guard self.subviews.count >= 2 else {
            return (horizontal: nil, vertical: nil)
        }

        func viewCanBeScrollIndicator(view: UIView) -> Bool {
            let viewClassName = NSStringFromClass(type(of: view))
            if viewClassName == "_UIScrollViewScrollIndicator" || viewClassName == "UIImageView" {
                return true
            }
            return false
        }

        let horizontalScrollViewIndicatorPosition = self.subviews.count - 2
        let verticalScrollViewIndicatorPosition = self.subviews.count - 1

        var horizontalScrollIndicator: UIView?
        var verticalScrollIndicator: UIView?

        let viewForHorizontalScrollViewIndicator = self.subviews[horizontalScrollViewIndicatorPosition]
        if viewCanBeScrollIndicator(view: viewForHorizontalScrollViewIndicator) {
            horizontalScrollIndicator = viewForHorizontalScrollViewIndicator
        }

        let viewForVerticalScrollViewIndicator = self.subviews[verticalScrollViewIndicatorPosition]
        if viewCanBeScrollIndicator(view: viewForVerticalScrollViewIndicator) {
            verticalScrollIndicator = viewForVerticalScrollViewIndicator
        }
        return (horizontal: horizontalScrollIndicator, vertical: verticalScrollIndicator)
    }

}

If you need only one (h or v indicator) - it's better to cut this func and keep only one you need (to improve perfomance).

Also it would be good to call update func inside of DispatchQueue, to keep smoothness of scrolling.

func scrollViewDidScroll(_ scrollView: UIScrollView) {
   DispatchQueue.main.async {
       scrollView.updateCustomScrollIndicatorView()
   }
}

Upvotes: 2

Berlin
Berlin

Reputation: 2201

in IOS 13

Try this one

func scrollViewDidScroll(_ scrollView: UIScrollView){


        if #available(iOS 13, *) {
            (scrollView.subviews[(scrollView.subviews.count - 1)].subviews[0]).backgroundColor = UIColor.themeColor(1.0) //verticalIndicator
            (scrollView.subviews[(scrollView.subviews.count - 2)].subviews[0]).backgroundColor = UIColor.themeColor(1.0) //horizontalIndicator
        } else {
            if let verticalIndicator: UIImageView = (scrollView.subviews[(scrollView.subviews.count - 1)] as? UIImageView) {
                verticalIndicator.backgroundColor = UIColor.themeColor(1.0)
            }

            if let horizontalIndicator: UIImageView = (scrollView.subviews[(scrollView.subviews.count - 2)] as? UIImageView) {
                horizontalIndicator.backgroundColor = UIColor.themeColor(1.0)
            }
        }
    }

Upvotes: 8

Rahul Tripathi
Rahul Tripathi

Reputation: 1

please use below code on iOS Renderer

 private bool _layouted;
 public override void LayoutSubviews()
 {
       base.LayoutSubviews();
       if (!_layouted)
       {
            this.Layer.BorderColor = UIColor.Red.CGColor;
            var Verticalbar = (UIImageView)this.Subviews[this.Subviews.Length - 1];

            Verticalbar.BackgroundColor = Color.FromHex("#0099ff").ToUIColor();
            var Horizontlebar = (UIImageView)this.Subviews[this.Subviews.Length - 2];

            Horizontlebar.BackgroundColor = Color.FromHex("#0099ff").ToUIColor();
            _layouted = true;
       }
}

Upvotes: 0

DevAndArtist
DevAndArtist

Reputation: 5149

Here is what I did in Swift 4, similar to previous answers. In my case I'm recoloring the image to be invisible, set correct corner radius and only execute this process once.

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let color = UIColor.red
    guard
        let verticalIndicator = scrollView.subviews.last as? UIImageView,
        verticalIndicator.backgroundColor != color,
        verticalIndicator.image?.renderingMode != .alwaysTemplate
    else { return }
    verticalIndicator.layer.masksToBounds = true
    verticalIndicator.layer.cornerRadius = verticalIndicator.frame.width / 2
    verticalIndicator.backgroundColor = color
    verticalIndicator.image = verticalIndicator.image?.withRenderingMode(.alwaysTemplate)
    verticalIndicator.tintColor = .clear
}

Upvotes: 0

bpolat
bpolat

Reputation: 3908

If you wish to add image as well, here is the code for Swift 3

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let verticalIndicator = scrollView.subviews.last as? UIImageView
    verticalIndicator?.image = UIImage(named: "imageName")
}

This works for UITableView and UICollectionView as well.

Upvotes: 1

Andrey Gordeev
Andrey Gordeev

Reputation: 32449

Here's more safe Swift 3 method:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let verticalIndicator = scrollView.subviews.last as? UIImageView
    verticalIndicator?.backgroundColor = UIColor.green
}

Upvotes: 18

akaDuality
akaDuality

Reputation: 408

You can change an image of indicator, but you should do this repeadeatly

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    self.chageScrollIndicator()
}

func chageScrollIndicator (){
    if let indicator = self.collection.subviews.last as? UIImageView {
        let edge = UIEdgeInsets(top: 1.25,
                                left: 0,
                                bottom: 1.25,
                                right: 0)
        indicator.image = UIImage(named: "ScrollIndicator")?.withRenderingMode(.alwaysTemplate).resizableImage(withCapInsets: edge)
        indicator.tintColor = UIConfiguration.textColor
    }
}

You can use this 2 image as template: Use this image for @2x Use this image for @3x

Upvotes: 2

Alvin George
Alvin George

Reputation: 14296

Swift 2.0 :

Add UIScrollView Delegate.

func scrollViewDidScroll(scrollView: UIScrollView){
let verticalIndicator: UIImageView = (scrollView.subviews[(scrollView.subviews.count - 1)] as! UIImageView)
verticalIndicator.backgroundColor = UIColor.greenColor()

let horizontalIndicator: UIImageView = (scrollView.subviews[(scrollView.subviews.count - 2)] as! UIImageView)
horizontalIndicator.backgroundColor = UIColor.blueColor()
}

Upvotes: 3

Purushottam Padhya
Purushottam Padhya

Reputation: 408

This is how the color of the scroll bar is changed:

    //scroll view  
    UIScrollView *scView = [[UIScrollView alloc] init];  
    scView.frame = self.view.bounds; //scroll view occupies full parent views  
    scView.contentSize = CGSizeMake(400, 800);  
    scView.backgroundColor = [UIColor lightGrayColor];  
    scView.indicatorStyle = UIScrollViewIndicatorStyleBlack;  
    scView.showsHorizontalScrollIndicator = NO;  
    scView.showsVerticalScrollIndicator = YES;  
    scView.scrollEnabled = YES;  
    [self.view addSubview: scView];

Upvotes: 1

karthik
karthik

Reputation: 71

Try this it would certainly help you

    for ( UIView *view in scrollBar.subviews ) {

       if (view.tag == 0 && [view isKindOfClass:UIImageView.class])
       {
        UIImageView *imageView      = (UIImageView *)view;
        imageView.backgroundColor   = [UIColor yellowColor];
       }
    }

Explanation: UIScrollBar is a collection of subviews. Here scrollBar indicator(vertical/horizontal) is the one of the subviews and it's an UIImageView.So if we set custom color to the UIImageView it effects scrollBar Indicator.

Upvotes: 2

Stefan
Stefan

Reputation: 518

I ran into the same problem recently so I decided to write a category for it.

https://github.com/stefanceriu/UIScrollView-ScrollerAdditions

[someScrollView setVerticalScrollerTintColor:someColor]; 
[someScrollView setHorizontalScrollerTintColor:someColor];`

It blends it with the original image so only the color will change. On the other hand, it can also be modified to provide a custom image for the scrollers to use.

Upvotes: 0

Nikita Leonov
Nikita Leonov

Reputation: 5694

I wrote an article about this not so far ago. Unfortunately color of this bars defined by pre-defined images, so if you are going to change the color of bars some extra work will be required. Take a look to following link, you will definitely find an answer here since I tried to solve the same issue.

http://leonov.co/2011/04/uiscrollviews-scrollbars-customization/

Upvotes: 0

Kaan Dedeoglu
Kaan Dedeoglu

Reputation: 14841

Unfortunately you can't, of course you can always roll your own. These are your options:

UIScrollViewIndicatorStyleDefault:

The default style of scroll indicator, which is black with a white border. This style is good against any content background.

UIScrollViewIndicatorStyleBlack:

A style of indicator which is black and smaller than the default style. This style is good against a white content background.

UIScrollViewIndicatorStyleWhite:

A style of indicator is white and smaller than the default style. This style is good against a black content background.

Upvotes: 38

Sumanth
Sumanth

Reputation: 4921

You can use custom UIScrollView scrollBars to implement color in scrollbars. For more details look here

Upvotes: -2

Related Questions