jhabbott
jhabbott

Reputation: 19281

How to exclude scroll indicators while enumerating subviews of a UIScrollView?

I'm trying to adjust my subviews in a UIScrollView subclass, but I don't want to disturb the scroll indicators. There doesn't seem to be any public interface to access these and I want to check if a view is one of the scroll indicators or not (so that I can ignore it).

UIScrollView.h declares these two iVars:

UIImageView* _verticalScrollIndicator;
UIImageView* _horizontalScrollIndicator;

...but I tried the following and got a linker error:

for(UIView* v in self.subviews)
{
    // Ignore the scroll-indicator views
    if( (v == _horizontalScrollIndicator) ||
        (v == _verticalScrollIndicator))
    {
        continue;
    }
    // View is one of mine - do stuff to it...
}

Apple obviously don't want you messing with these, in which case they should do something clever so that the array returned from subviews doesn't include them (come on Apple, it's not that hard!), but until then how can I ignore them?

Upvotes: 11

Views: 1222

Answers (6)

hfossli
hfossli

Reputation: 22962

Based on MrTristan's method. I think he mistakenly multiplied with screen scale.

+ (NSArray <UIView *> *)reservedSubviewsInUIScrollView:(UIScrollView *)view
{
    BOOL (^isProbablyAScrollBar)(UIView *s) = ^BOOL(UIView *s) {
        return [s isKindOfClass:[UIImageView class]] && (s.frame.size.width <= 3.5 || s.frame.size.height <= 3.5);
    };

    NSMutableArray <UIView *> *reserved = [NSMutableArray new];
    for(UIView *s in view.subviews)
    {
        if(isProbablyAScrollBar(s))
        {
            [reserved addObject:s];
        }
    }
    return reserved;
}

Upvotes: 0

user3694446
user3694446

Reputation: 111

My solution was:

UIView* MYContentView=[[UIView alloc] initWithRect: CGrectMake:(0,0,Scrollview.frame.size.width, scrollview.frame.size.height)];

[ScrollView addsubview: MYContentView];

Then i added all the images to the MYContentView and later Rescaled the MYContentView and

  [ScrollView setContentSize:Cgsize];

Now you can Enumerate all Images in the MYContentView without worrying about the scrollbars.

Doing:

while ([[MYContentView subviews] count]>0) {
    [[[MYContentView subviews]lastObject] removeFromSuperview];
}

Removed only the images. Hope this helps

Upvotes: 0

clayzar
clayzar

Reputation: 71

This ended up being my work-around

BOOL showsVerticalScrollIndicator = self.showsVerticalScrollIndicator;
BOOL showsHorizontalScrollIndicator = self.showsHorizontalScrollIndicator;

self.showsVerticalScrollIndicator = NO;
self.showsHorizontalScrollIndicator = NO;

// loop through self.subviews here

self.showsVerticalScrollIndicator = showsVerticalScrollIndicator;
self.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator;

Upvotes: 7

MrTristan
MrTristan

Reputation: 740

i'm up against the same issue and i'm starting to think about looking at the values of those two UIImageViews to figure out if its them or not. they do have a pretty unique signature:

<UIImageView: 0x8ab3e00; frame = (313 812.5; 7 3.5); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x8ab9740>>
<UIImageView: 0x8ab3e90; frame = (314.5 522; 3.5 341); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x8ab3f20>>

amongst other hooks that you could listen for. not ideal but not much in the way of options at this point...

heres an example. in this case, i was looping through the scrollview's subviews to figure out the optimal contentSize height for the scrollview based on the subviews when it started coming out with some funky values which lead me to think that it was the scrollbars that were screwing with me...

CGRect contentRect = CGRectZero;
for (UIView *view in self.myScrollView.subviews) {
    //exclude scroll bars, the width/height is 3.5 for non-retina or 7 
    //for retina. you can put some logic in here if you like to check
    //the appropriate 3.5/7.0 values for the respective screen densities
    if(view.frame.size.width > 7.0f && view.frame.size.height > 7.0f) {
        contentRect = CGRectUnion(contentRect, view.frame);
    }
}
container.contentSize = CGSizeMake(container.contentSize.width, contentRect.size.height);

if checking the frame of each UIView isn't specific enough for you, you could also ensure that it is a UIImageView.

i'm thinking this is the only "true" solution at this point. i'd personally rather not have to maintain another array of views.

EDIT:

here's my consolidated method that i use:

-(bool)isProbablyAScrollBar:(UIView*)view {
    CGFloat screenScale = [[UIScreen mainScreen] scale];
    return (([view isKindOfClass:[UIImageView class]]) && (view.frame.size.width <= (screenScale*3.5f) || view.frame.size.height <= (screenScale*3.5f)));
}

Upvotes: 1

jamesmoschou
jamesmoschou

Reputation: 1173

Are the scroll indicator views added and removed throughout the lifetime of the scroll view, or are they just added once and hidden and shown as necessary? If it's the latter, you could store your own references to them in init... instead of using the private ivars and proceed as you have tried to already.

Upvotes: 0

Kurt Revis
Kurt Revis

Reputation: 27984

Presumably you're in control of the views that you put in the UIScrollView. Why not maintain your own array of just those views? That way, you are safe against any future changes in the implementation of UIScrollView.

Upvotes: 2

Related Questions