33

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

Mann
  • 5,477
  • 6
  • 45
  • 57

17 Answers17

38

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.

Kaan Dedeoglu
  • 14,765
  • 5
  • 40
  • 41
18

Here's more safe Swift 3 method:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let verticalIndicator = scrollView.subviews.last as? UIImageView
    verticalIndicator?.backgroundColor = UIColor.green
}
Andrey Gordeev
  • 30,606
  • 13
  • 135
  • 162
  • can we make the scrolling indicator height constant ?? http://stackoverflow.com/questions/40783442/ios-can-we-make-the-uiscrolling-indicator-height-same-for-any-number-of-data?noredirect=1#comment68790724_40783442 – Bikram Thapa Nov 24 '16 at 10:57
  • This is not work – Ucdemir Sep 19 '21 at 08:23
11

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

Vineet Choudhary
  • 7,433
  • 4
  • 45
  • 72
  • may be!! not 100% sure. But what is wrong? We put stuff in scroll view indicator without breaking any design guidelines .... – Vineet Choudhary Aug 21 '15 at 12:10
  • Nothing wrong. I just need to know if this won't give me problems when submitting. – Tiago Veloso Aug 21 '15 at 12:30
  • It won't. But it's hacky, so would recommend subclassing scrollview and writing your own tracker indicatorView – Legolas Apr 13 '16 at 22:10
  • How do you guaranteer that scrollView.subviews.count-1 and scrollView.subviews.count-2 are correct indexes for those indicators? – Paweł Brewczynski Jan 09 '17 at 14:48
  • @PaulBrewczynski because scroll indicator added as a subview at run times, means when some scroll the scrollview then scrollview add these two scroll indicator as a subview... so scroll indicator always available at last and second last index's when user scrolling – Vineet Choudhary Jan 10 '17 at 03:00
  • The code looks like it's setting the vertical background to red and the horizontal to blue. However, the sample image shows vertical blue and horizontal red. – Gary Z May 10 '17 at 02:00
  • This is a terrible idea, as there's no guarantee that the subviews at those indices are the correct indices, or that they even exist. If they don't, you'll get an index out of bounds exception. – Christopher Pickslay Dec 22 '17 at 23:16
  • @PaulBrewczynski you don't, that's why this is a bad and hacky solution. – Jan Erik Schlorf May 24 '19 at 09:23
10

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

j_gonfer
  • 1,547
  • 21
  • 21
8

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)
            }
        }
    }
Berlin
  • 2,115
  • 2
  • 16
  • 28
3

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()
}
Alvin George
  • 14,148
  • 92
  • 64
2

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.

ρss
  • 5,115
  • 8
  • 43
  • 73
karthik
  • 71
  • 6
  • Please provide some explanation to your answer too. – ρss May 17 '14 at 11:14
  • 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. – karthik May 17 '14 at 11:22
2

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

akaDuality
  • 384
  • 5
  • 6
2

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()
   }
}
Alex
  • 2,100
  • 19
  • 26
  • not working. scrollview has empty subviews array :( – Serj Rubens Oct 22 '19 at 20:03
  • turn on Show Horizontal Indicator, or/and Show Vertical Indicator? @SerjRubens – Alex Oct 23 '19 at 14:30
  • it is turned on @Alex – Serj Rubens Oct 24 '19 at 16:30
  • and scrollview has empty subviews array even without this check ^^^. Just scrollView.subviews.count == 0? (Sure, before you add something on it) – Alex Oct 24 '19 at 17:54
  • in the end its not UIScrollView but UITableView. but UITableView is inherited from UIScrollView, so I expected it gonna work the same way. well and yes, I don't add anything on it, but cells could be already initialised and count as subviews. Didn't know that it's so important (-_-) @Alex – Serj Rubens Oct 25 '19 at 18:10
  • ok, maybe after init subviews count can be 0. But later, when you scroll table view - scroll (table) view should have. So, even after scrolling this method doesn't find scroll indicators? Have you tried just with scroll view, not table? – Alex Oct 28 '19 at 08:18
  • 1
    This is the only solution that (almost) worked fine for me, thanks @Alex! I would just add `.subviews[0]` to each indicator view in order to change the color of the first layer instead of the deeper layer. Will post a new answer based on yours – j_gonfer Apr 22 '20 at 15:05
1

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];
rkyr
  • 3,131
  • 2
  • 23
  • 38
1

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.

bpolat
  • 3,879
  • 20
  • 26
0

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/

Nikita Leonov
  • 5,684
  • 31
  • 37
0

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.

Stefan
  • 518
  • 2
  • 6
0

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
}
DevAndArtist
  • 4,971
  • 1
  • 23
  • 48
0

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;
       }
}
Sudheesh Singanamalla
  • 2,283
  • 3
  • 19
  • 36
  • 1
    Added to the code, it's always good to explain why this solves the problem that the question had posed. It'd be ideal to edit this post to include a detailed description. – Sudheesh Singanamalla Jan 10 '18 at 06:17
0

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];
}
}
Melany
  • 466
  • 7
  • 20
-1

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

Sumanth
  • 4,913
  • 1
  • 24
  • 39