Saturday, October 06, 2012

Caveats of calling subviews in a UIScrollView, and how to customize the scrollbars.

In a recent project I had a scroll view defined in a storyboard, together with 2 subviews. I was looking for a way to access them in the layoutSubviews method of the scroll view.

The first approach I took was to create 2 outlets in the scroll view, pointing to the to the said subviews. Even though I had it working fine, I had to twist Xcode's arm in doing that, since the normal drag-to-create-outlet maneouvre in the Assistant editor seems to work only when the target of the drag is the code pane of a view controller, not a view.

So, as an alternative, I figured, hey, we have this subviews property available, why not fast-iterate through it and call it a day. When testing that approach weird things started to happen. A little debugging showed this:

(lldb) po [self subviews]
(id) $1 = 0x06b805b0 <__NSArrayM 0x6b805b0>(
<SomePageView: 0x68968b0; frame = (0 0; 320 452.507); transform = [0.450704, 0, 0, 0.450704, 0, 0]; opaque = NO; autoresize = W+H; layer = <CALayer: 0x68965f0>>,
<AnotherPageView: 0x6896e40; frame = (0 0; 320 460); autoresize = W+H; layer = <CATiledLayer: 0x6896e90>>,
<UIImageView: 0x6897330; frame = (313 453; 7 7); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x68973a0>>,
<UIImageView: 0x6897290; frame = (313 453; 7 7); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x6897300>>

What ? I thought I had 2 subviews - this shows 4. Looking at the frames, autoresize mask and the fact that they ignore touches it's a strong indication that those 2 extra subviews may be the scrollbars - LOL.

Inspecting the UIScrollView confirms that. See the _horizontalScrollIndicator and _verticalScrollIndicator ivars:

This has one clear implication - if you're nuts enough you can customize the appearance of the scrollbars in a UIScrollView. They seem to be simple UIImageViews. However rummaging through UIKit internals like this is not condoned by Apple, since future updates may break your assumptions.

Incidentally, another approach I could have taken to access my own subviews would have been to tag them in the storyboard and use the viewWithTag method to get to them.

No comments: