How do I properly observe the contentOffset property of my scrollView subclass?
Asked Answered
B

3

6

In my iOS app I am observing changes to the contentOffset property of my scrollView subclass. My observer handler looks like this:

- (void)observeContentOffsetHandler:(id)aContentOffset {

    NSLog(@"%@", aContentOffset);

}

I chose the parameter to the method arbitrarily as an id for simplicity.

My NSLog'ging looks like this:

-[MyScrollView observeContentOffsetHandler:] [Line 111] NSPoint: {296, 375}
-[MyScrollView observeContentOffsetHandler:] [Line 111] NSPoint: {296, 389}
-[MyScrollView observeContentOffsetHandler:] [Line 111] NSPoint: {295, 401}
-[MyScrollView observeContentOffsetHandler:] [Line 111] NSPoint: {291, 415}

I need to use the x and y values but I have no idea how to get at them. I've tried casting the id to a CGPoint, nope. I've tried changing the param to a CGPoint, nope.

UPDATE

It gets deeper. @mgold no joy. Here is how I set up observation:

self.contentOffsetObserver = [[[Observer alloc] initWithTarget:self action:@selector(observeContentOffsetHandler:)] autorelease];
[self.myScrollViewSubclass addObserver:self.contentOffsetObserver forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:NULL];

Observer is a handy class I use to make observation easy. Note the observer callback observeContentOffsetHandler:. When I change the signature of this method from its current:

- (void)observeContentOffsetHandler:(id)aContentOffset

to @mgold's suggestion of CGPoint:

- (void)observeContentOffsetHandler:(CGPoint)aContentOffset

It is incorrect as NSLog shows with all zeros for aContentOffset:

-[MyScrollController observeContentOffsetHandler:] [Line 74] aContentOffset 0 0
-[MyScrollController observeContentOffsetHandler:] [Line 74] aContentOffset 0 0
-[MyScrollController observeContentOffsetHandler:] [Line 74] aContentOffset 0 0
-[MyScrollController observeContentOffsetHandler:] [Line 74] aContentOffset 0 0

Not sure what my move here is.

Bartel answered 4/1, 2012 at 15:13 Comment(0)
B
6

Got it. The method correct signature is:

- (void)observeContentOffsetHandler:(NSValue *)aContentOffset

Retrieval of the CGPoint is then trivial:

CGPoint pt = [aContentOffset CGPointValue];

Cheers,
Doug

Bartel answered 5/1, 2012 at 12:52 Comment(0)
B
2

Since you have a UIScrollView subclass, you have access to layoutSubViews
It is called every time contentOffset changes.

That is the "proper way" to get the changes as they happen. Don't use KVO Yes contentOffset is a CGPoint....unless you were talking about NSScrollView.....but even then the basic idea stays same.

Override layoutSubviews....remember to call super

OR

register your ViewController as delegate of the scrollView and implement scrollView:didScroll

Bwana answered 4/1, 2012 at 19:52 Comment(4)
not sure what you mean by proper. One could argue that cluttering layoutSubviews with functionality that is unrelated to the underlying purpose of a UIView subclass is bad design. My observation will happen in the controller where from an MVC perspective it makes more senses. Just sayin'...Bartel
I thought you wanted to do something UI related inside the subclass, hence the layoutSubviews. Your viewController (the one that manages the scrollview) should be the scrollview's delegate and implement scrollView:didScroll this will be called every time contentOffset changes :) and it won't break MVC either.Bwana
@AnimishH, in the past I have indeed followed the popular approach of the scrollViews controller handling scrollViewDelegate duties. I now do that less often making the scrollView it's own delegate. The popular approaches tends to clutter the controller with scrollView management duties that on closer inspection scrollView is perfectly capable of handling and involve no domain logic at all. For example viewForZoomingInScrollView: has no business in the controller. That is view plumbing code not domain code.Bartel
layoutSubViews belongs to the UIScrollViewController not to UIScrollView. It is possible that someone wants to use only the scrollview without controller, i.e. because it is embedded into a simple UIViewController's view..Bibcock
A
0

contentoffset is indeed a CGPoint, which is a C struct with CGFloats x and y. So simply

aContentOffset.x
aContentOffset.y

Because you are subclassing UIScrollView, you also have the contentoffset property, just saying.

Almanac answered 4/1, 2012 at 15:19 Comment(2)
thanks. I could indeed use contentOffset directly, but the semantics around the observing I am doing are different. Cheers.Bartel
Won't work because CGPoint is a scalar not a class. Observation handlers take a class (id, NSValue*, etc.) as their param.Bartel

© 2022 - 2024 — McMap. All rights reserved.