MKMapView panning causes parent UIScrollView to scroll - objective-c

I have a MKMapView added to a UIScrollView. I thought that the expected functionality would be that panning the map would cause the map to move, but it's parent UIScrollView would not scroll. i.e. if you were panning the map, the scrollview would stay in it's current position.
This is my code:
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.width, self.height)];
[scrollView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
[self.view addSubview:scrollView];
double mapHeight = 200;
if([AppDelegate isRunningOniPad])
mapHeight = 400;
map = [[MKMapView alloc] initWithFrame:CGRectMake(0, 200, self.width, mapHeight)];
[map setExclusiveTouch:NO];
[scrollView map];
I've tried setting exclusive touch to YES and to NO, but it doesn't seem to have any effect.
Is there a way to get the behaviour working so that panning the map will stop the UIScrollView from moving? I've tried changing map to a UIScrollView instead of an MKMapView too, but that had the same behaviour - so I doubt that this is MKMapView specific.

The expected outcome is correct, the scroll view should remain stationary when the Map View is touched, as this should receive the touch events before they are passed up the responder chain to the scroll view.
I have quickly tested the above code in an empty project, and this is working as expected with a couple of alterations to the creating of frame & sizes, iPad size detection, and adding the map as a sub-view of the scroll view.
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
[scrollView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
// Make the content size large to allow scroll view to move
[scrollView setContentSize:CGSizeMake(1000, 2000)];
[self.view addSubview:scrollView];
double mapHeight = 200;
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
mapHeight = 400;
}
MKMapView *map = [[MKMapView alloc] initWithFrame:CGRectMake(0, 200, self.view.bounds.size.width, mapHeight)];
[map setExclusiveTouch:NO];
[scrollView addSubview:map];

In case it helps anyone,
Adding a MKMapView to a UIScrollView should work as expected. In my situation, it wasn't working because my view hierarchy was setup incorrectly.
If you're having problems look at how you are adding sub views (ie [self.view addSubview:mapView]. Make sure the MKMapView is properly in the UIScrollView's hierarchy.
Also, I really like revealapp.com to inspecting the view hierarchy (and no I'm not associated with them ;)

Related

UIScrollView doesn't scroll offset content

I have an UIScrollView with this parameters:
[scrollView setPagingEnabled:YES];
[scrollView setContentSize:CGSizeMake(1024 * 2, 768)];
[scrollView setShowsHorizontalScrollIndicator:NO];
[scrollView setShowsVerticalScrollIndicator:NO];
[scrollView setScrollEnabled:NO];
[scrollView setDelegate:self];
For changing page:
CGRect frame = scrollViewPages.frame;
frame.origin.x = 1024;
[scrollViewPages scrollRectToVisible:frame animated:YES];
So this scrollview has a double area for display content. In the first rectangle the touch works, but when I change to the other segment, which has others components with different sizes, touch events don't work. I've read that UIScrollView doesn't apply touch events to hidden sections. I would like to know how update the view for getting touch interactions.
thanks
UPDATE: I was adding the UIViews into another UIVIew inside UIScrollView. Now it works

How to move scrollView down 44x to make room for NavBar

I am using the following code to create my scrollview. I would like to move the scrollView down 44px to make room for my nav bar.
scroll = [[UIScrollView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
scroll.backgroundColor = [UIColor clearColor];
scroll.delegate = self;
image = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Menu.png"]];
scroll.contentSize = image.frame.size;
[scroll addSubview:image];
scroll.minimumZoomScale = scroll.frame.size.width / image.frame.size.width;
scroll.maximumZoomScale = 2.0;
[scroll setZoomScale:scroll.minimumZoomScale];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
[scroll addGestureRecognizer:doubleTap];
self.view = scroll;
any help is appreciated.
Your code is correct, to manually create a frame do:
scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0,44,320,480)];
:)
This is for iPod and iphone View but this will let the scroll start at 44px
Cant you just add a scroll view in in interface builder as an outlet, place it underneath your nav bar.
Declare scroll view in .h
in .m under ViewDidLoad
[scroll setScrollEnabled:YES];
[scroll setContentSize: CGSizeMake(320, 1000)]; ////or whatever size you want here
You probably want to init it with a different frame... but it depends on where/how this nav bar is being created. If you have a view controller, and these are all subviews of the view controller's view, then you should be creating the objects in viewDidLoad, then just create the nav bar first, using self.view.bounds to obtain the initialization width. I assume you'll want to put UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin as the autoResizingMask here. Then if the scrollview is the rest of the view below the nav bar, you can create the frame for it using
CGRect scrollFrame = CGRectInset(self.view.bounds, 0, navbar.bounds.size.height)
Put an autoresizingMask of UIViewAutoresizingFlexibleWidth | UIViewAutoResizingFlexibleHeight on the scrollview.
If you are creating the views in a different place/way, then some of that might need modification. I was assuming your view is a nav bar at the top, x pixels tall (44 in this case but it doesn't and shouldn't matter in the context of setting the scrollview frame). and then a scrollview that fills the rest of the view.

UIScrollView metro theme

I am attempting to create a "metro" styled UIScrollView. It is similar to how iTunes app handles panels in the new ios version which wont be named.
I can't figure out how to have my views layout/scroll so that the next view in the sequence shows up. I've tried all sorts of things like keeping the contentSize the screen width but moving each view over -10ish so it will show up like above. I've tried making scrollView whose bounds were smaller than the screen so it would show the part of the next view. Nothing works.
Here is diagram of what I'm trying to do:
It seems extremely trivial on paper but I can't seem to get it work.
I'm not sure if I'm misinterpreting your requirements - but this might be a starting point to see how you could set it up:
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect viewBounds = self.view.bounds;
CGRect scrollViewFrame = CGRectMake(0, 0, floorf(CGRectGetWidth(viewBounds) / 2.2), CGRectGetHeight(viewBounds));
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:scrollViewFrame];
scrollView.center = self.view.center;
scrollView.contentSize = CGSizeMake(CGRectGetWidth(viewBounds) * 3, CGRectGetHeight(viewBounds) * 3);
scrollView.pagingEnabled = YES;
scrollView.clipsToBounds = NO;
UIPanGestureRecognizer *gestureRecognizer = scrollView.panGestureRecognizer;
[self.view addGestureRecognizer:gestureRecognizer];
for (int i = 0; i < 3; i++) {
CGRect frame = CGRectMake(10.f + (i * CGRectGetWidth(scrollView.bounds)), 10.f, CGRectGetWidth(scrollView.bounds) - 20.f, (CGRectGetHeight(scrollViewFrame) * 3) - 20.f);
UIView *view = [[UIView alloc] initWithFrame:frame];
view.backgroundColor = [UIColor redColor];
[scrollView addSubview:view];
}
[self.view addSubview:scrollView];
}
Literally just put this in an empty viewController's viewDidLoad:
The key things to note are
contentSize needs to be wide enough for all the panels
clipsToBounds should be NO so you can see the additional views
The bounds of the scrollview is essentially the main view port
pagingEnabled should be set
I've grabbed the panGestureRecognizer from the scrollview and attached it to the containing view instead so that panning is detected in the bounds of the containing view (which is larger) otherwise you are restricted to only detecting scrolls within the scrollviews bounds

NSScrollView doesn't scroll

I'm porting an iPhone app that uses this tutorial into a Mac app. I've been able to get text to display in NSScrollView, but I can't get the text to scroll and I hope someone knows what I am missing.
In the iPhone app, CTView is subclassed from UIScrollView
CTView.h:
#interface CTView : UIScrollView <UIScrollViewDelegate> {
1) But there is no NSScrollViewDelegate in AppKit. Is the missing delegate interface to NSScrollView the problem?
So for the Mac app, CTView.h looks like the following:
#interface CTView : NSScrollView
In CTView.m I have
[self setHasHorizontalScroller:YES];
[self setAcceptsTouchEvents:YES];
Also inside CTView.m I create a NSMutableArray of frames as
self.frames = [NSMutableArray array];
and fill the array up with frames. I also set the frame for the document view for the complete larger image that I want to be able to scroll through as:
[[self contentView] setFrame:[self bounds]];
[self.documentView setFrame: NSMakeRect(0, 0, totalPages * self.bounds.size.width, textFrame.size.height)];
The first frame is visible in the CTView, but when I move the horizontal scroll bar, the second frame doesn't show up in CTView.
I'm using Xcode 4.3.3 and Mac OS X 10.7.4.
Does anyone know what I need to do differently from UIScrollView for NSScrollView to be able to scroll through the frames?
Finally figured it out. Whew!
1) NSScrollView needs a view for its documentView, where the documentView is what is scrolled over. So in the nib I created a NSScrollView that contained a view for the documentView and set the documentView to that view as shown by the code in CTView.m
NSArray *viewArray = [self subviews];
NSArray *docViewArray = [[viewArray objectAtIndex:0] subviews];
NSView *docView = [docViewArray objectAtIndex:0];
[self setDocumentView:docView];
2) Then I added the array of frames, that would normally be added to UIScrollView, to the documentView of NSScrollView. The array of frames consists of multiple elements of content.
while (textPos < [attString length]) {
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(textPos, 0), innerPath, NULL);
CFRange frameRange = CTFrameGetVisibleStringRange(frame);
CTColumnView* content = [[[CTColumnView alloc] initWithFrame: NSMakeRect(0, 0, self.contentSize.width, self.contentSize.height)] autorelease];
[docView addSubview: content]; // <<-- Here is where the frames are added to the documentView
textPos += frameRange.length;
}
And that did the trick.

UIScrollView, Pull down effect similar to CalcBot

I'm trying to make a UI similar to CalcBot where you can pull down the view controller with your finger and it reveals a hidden background view.
I think this is done with the UIScrollView but cannot work out the exact method to have for example 2x Horizontal views with one of them having a vertical.
I do have the horizontal scroll working fine, i'm just stuck with how I can make screen 1 pull down with a vertical scroller.
Any tips or pointers would be appreciated.
Thanks Aaron
You can have nested scrollviews to get scrolling working in both directions. Let's have a vertical scroller inside a horizontal scroller (though you can very well do it the other way round too), so views 3 and 1 get into a vertical scrollview, which in turn gets inside a horizontal scroller along with view 2.
You could create such a view hierarchy in code (probably in the main viewcontroller's viewDidLoad). The following snippet assumes view1, view2 and view3 as webviews.
CGRect bounds = self.view.bounds;
// main guy is a horizontal scroller
UIScrollView *hScrollView = [[UIScrollView alloc] initWithFrame:bounds];
hScrollView.contentSize = CGSizeMake(bounds.size.width * 2, bounds.size.height);
hScrollView.delegate = self;
[self.view addSubview:hScrollView];
// the horizontal scroller contains a vertical scroller
UIScrollView *vScrollView = [[UIScrollView alloc] initWithFrame:bounds];
vScrollView.contentSize = CGSizeMake(bounds.size.width, bounds.size.height * 2);
vScrollView.delegate = self;
[hScrollView addSubview:vScrollView];
// add view3 and view1 to the vertical scroller
UIWebView *view3 = [[UIWebView alloc] initWithFrame:bounds];
[view3 loadHTMLString:#"<h1>3</h1>" baseURL:nil];
[vScrollView addSubview:view3];
UIWebView *view1 = [[UIWebView alloc] initWithFrame:CGRectOffset(bounds, 0, bounds.size.height)];
[view1 loadHTMLString:#"<h1>1</h1>" baseURL:nil];
[vScrollView addSubview:view1];
vScrollView.contentOffset = CGPointMake(0, bounds.size.height);
// add view2 to the horizontal scroller
UIWebView *view2 = [[UIWebView alloc] initWithFrame:CGRectOffset(bounds, bounds.size.width, 0)];
[view2 loadHTMLString:#"<h1>2</h1>" baseURL:nil];
[hScrollView addSubview:view2];
// enable paging in both directions
hScrollView.pagingEnabled = TRUE;
vScrollView.pagingEnabled = TRUE;
That would have you scrolling in both directions but that would also let you scroll-right from view3 to view2. If you want to prevent that, you should disable scrolling on hScrollView whenever the vScollView is showing view3. You can do that in scrollViewDidEndDecelerating: in the delegate of vScrollView. Something like:
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView == self.verticalScrollView) {
self.horizontalScrollView.scrollEnabled = (self.verticalScrollView.contentOffset.y > self.view.bounds.origin.y);
}
}