ScrollView outside visible View - iphone

I try to create something similar to the task-menu in iOS4:
(Sorry cant post images not enough Rep)
I have normal ViewController on thats view i addSubview of my menuView. I set menuViews Y to -95 so i can just move the viewControllers view 95 Down to Display the menu. But the scrollView in my menuView doesn't work if the menuViews Y is Negative.

Your scrollview doesn't receive the touch input because it is outside the bounds of the its superview (the view of the main ViewController you mentioned). The way the touch interaction system works is the AppKit framework goes down the view hierarchy testing each view and subview with the - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event function. If a view returns YES to this function, it will start the process all over again with all of its subviews until it finds the lowest subview that still returns YES. The problem with your setup is that the superview of Scrollview is going to return NO because the touch is above it and your menuView never even gets tested. So you have two possible solutions:
Implement your ViewControllers view as a subclass of UIView (if it is not already) and overload the pointInside:withEvent: function to return YES if the touch is within it's frame OR if it is within the bounds of menuView (ViewControllers frame minus the frame of menuView).
The second solution would be to simply put menuView at the origin of its superview (0,0) and leave the ViewControllers view at (0,0) as well.
I suggest the second as it is less convoluted and more to the point.

Related

How to get a UIView under a UIScrollView to detect touches?

I have a UIScrollView ontop of my UIViewController recreating an effect like in the Gowalla iPhone app when you're on a spot's page. Under my scroll view I have a button that I want to be able to perform it's action even when the scroll view's frame covers it up (where it's ontop of the button, the scroll view's clear). How would I do something like this? Is it possible? (it has to be, Gowalla [somehow] did it)
As for me, I will transfer touch event to another view by override following methods.
– touchesBegan:withEvent:
– touchesMoved:withEvent:
– touchesEnded:withEvent:
– touchesCancelled:withEvent:
Like this way,
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// pass touch event by default implement.
[super touchesBegan:touches withEvent:event];
// redirect the touch events.
[anotherView touchesBegain:touches withEvent:event];
}
hitTest:withEvent: is used to decide which view should response touch event on the view hierarchy tree. If the view doesn't want to response touch event, it will return nil in hitTest. As result the above touch event methods won't be called.
I have no idea what this "Gowalla" app might do; hopefully, your description of "a button behind the scroll view, that you can touch 'through' the scroll view" is accurate.
If your button behind the scroll view can be sized to fill the entire contentSize area of the scroll view without screwing up your interface, the easiest solution would be to just put it inside the scroll view (under all the other views) and do just that.
Otherwise, your best bet is probably to create a custom view with a clear background to be placed in the scroll view as above. The easy solution is to have the custom view (probably a UIControl) just do whatever touching the button does. If that's not possible for some reason, your best option would be to override hitTest:withEvent: on the custom view to return the underlying button. I'd be wary of overriding hitTest:withEvent: on the scroll view itself, as that might interfere with scrolling.
Try adding the button on top of the scroll view. The only problem with that is if you hit the button, you will not be able to interact with the scrollView, but it will still be visible.

Grab view from touch event

i have a quick question,
I have a view with multiple subviews.
In my view i have touches began etc..
is there a way to retrieve a subview from the touches began without having to detect the location from the touch and then have a big if else to c which subview is in that specific area.
Thanks.
My solution is instead of calculating the touch position from the super view. Sub class the child view and get the touch position form there only and convert the point with respect to the super view.
The method you want is the UIView instance method -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event. That will return you the lowest-level subview containing point.
So you get the point of your touch, pass it to that method of your biggest container UIView, and get back a handle to the frontmost subview that got touched. Easy peasy.

How to achieve smooth animation when using single-step rotation / How do I get the new frame size at the start of rotation?

I'm trying to switch from two-stage rotation to one-stage rotation (to avoid the console warning, and because Apple recommend doing so because one-stage is faster).
However I can't figure out how to get the new size of my view (taking into account the navigation bar, status bar, etc) early enough to perform the update of my UI during the animation (rather than simply snapping the items to their new positions at the end as many applications seem to do, which results in a big "jerk" right at the end of the animation).
The sizes I get in the willAnimateRotationToInterfaceOrientation:duration: method are (perhaps obviously) the old sizes.
I can see I should be able calculate it by hand, working out the current bar heights, then inferring the new view frame size by deducted those from the rotated screen dimensions? (which isn't that difficult to do, though is perhaps fragile as it assumes the navigation bar, status bar, etc will be the same height in both orientations, and you'd have to manually take account of the toolbar being different heights in portrait vs landscape - I just want to make sure I've not missed a more straightforward or common way.)
Any feedback on approaches other people have taken would be great!
Thanks
Joseph
I've had the most success using my view's layoutSubviews method for autorotations. When layoutSubviews gets called during an orientation change, the view's bounds are already set to what they will be at the conclusion of the rotation. You can also at this time query the status bar, navigation bar, and toolbar for their sizes and get the correct post-rotation values (although the status bar width and height may be swapped--I just take the smaller value as the height, works great). Using that information you can lay out subviews and they will then be animated as part of the rotation.
It can be annoying to create a UIView subclass for every situation where you want to do this, so I created a special subclass of UIView called DelegatingView, which looks like this:
#protocol DelegatingViewDelegate
- (void)layoutSubviewsForView:(UIView*)view;
#end
#interface DelegatingView : UIView {
id<DelegatingViewDelegate> _delegate;
}
#property (nonatomic,assign) id<DelegatingViewDelegate> delegate;
#end
#implementation DelegatingView
#synthesize delegate = _delegate;
- (void)layoutSubviews {
[super layoutSubviews];
[_delegate layoutSubviewsForView:self];
}
#end
I use this as my base view, add subviews to it, and set my UIViewController as the delegate. Then I can layout subviews from my view controller, having access to the current orientation and so on.
Which OS version are you targeting? Under OS 4.0 (which is what I did a quick test in), [view bounds] within willAnimateRotationToInterfaceOrientation:duration: returns the new, post-rotation bounds.
If you are not seeing that, I’d suggest double-checking that your view has the appropriate auto resize mask set.
If you have complex layout requirements in the view of your view controller, it is worth it to create a subclass of UIView and perform your layout code in -layoutSubviews (the correct place to do view layout). As Hilton Campbell pointed out, if you use this method for layout, you can check frame sizes and subview relationships and set their new positions appropriately.
If your view controller's view has a simple set of subviews, then you should probably just set their autoresizingMask properties appropriately, and let them animate themselves automagically.
I tried two methods to adjust view's size.
1.) notify child views to adjust their size after -(void)viewWillAppear;
The drawback is that it will be executed each time when viewWillAppear.
ex:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self notifyChildViewWillAppear:animated];
}
at child view
- (void)notifyChildViewWillAppear:(BOOL)animated
{
// at this time, the size of superview's bounds
//equals the content view's size
// (no navigationBar's height and toolbar's height.
self.frame = [self superview].bounds;
}
2.) Calculate the necessary size by hand.
I calculate as viewDidLoad.
The sizes of navigationBar and toolBar can be derived by following code.
self.navagationController.navigationBar.view.frame.size.height;
self.tabBarController.tabBar.frame.size.height;

subview outside parentview frame events

I'm adding programmatically an UIView B to another UIView A.
The A frame is {{0, 372}, {320, 44}}.
B UIView is add at {0, -74} and is {320, 74} wide.
My problem is that B touch events are not handled.
More precisely touch events are handled to a sibling UITableView of UIView A wich ends at {0, 372} even if B UIview is displayed over UITableView.
Any solution please ?
To determine the view in which a touch event falls, Cocoa traverses the view hierarchy (and sends each subview a hitTest:withEvent: message. In turn, this message calls pointInside:withEvent: on itself, which returns a boolean value that indicates whether the view contains the specified point.
Presumably, you could subclass your UIView A and override its pointInside:withEvent: method to return YES also for points that are outside A's bounds but inside B's.
Okay, your B-UIView is completely outside and above the bounds of A-UIView. If you set up two generic views with the same frames like this in Interface builder and set the backgrounds to different colors, you will see absolutely nothing of B-UIView.
alt text http://img256.imageshack.us/img256/6789/screenshot20091030at350.png
It's exactly like defining a view whose frame is outside the bounds of the screen. If no part of it is visible it will not trap touches because it's off in undefined space having tea with the square root of negative one.
The UITableView is handling the hits because the B UIView is not in the responder chain at all.
You need to make B-UIView a sibling of A-View instead of an invisible and inactive subview. Then overlay that on the bottom of the UITableView.
You cannot do this in such way. You should add both your A and B views to another view C. And it will work.

Anchor a UIView

I have a UITableViewController inside of a UINavigationController.
I want to have a UIView appear over the top of the table view but not susceptible to the scrolling of the table view.
I.e. if the table view is scrolled, the UIView should remain in the same position relative to the screen, rather than relative to the table view. It should appear anchored in a certain position.
What is the best way to achieve this?
EDIT: To clarify, the view should float transparently over the top of the table view.
Many thanks!
I also wanted to have a floating UIView over my tableView.
So, within my RootViewController (which is a UITableViewController), this worked for me
- (void)viewDidLoad {
/* mylabel is a UILabel set in this class */
[self.mylabel setUserInteractionEnabled:NO];
/* navigationController comes from higher up in the navigation chain */
[self.navigationController.view addSubview:self.mylabel];
}
Similar to what Peter said, create a UIView that will contain both the TableView and the subclassed UIView. Such as:
UIView *view = [[UIView alloc] initWithFrame:frame]; // Define frame as you like
[view addSubview:myTableView]; // This is the reference to your tableView
[view addSubview:myAnchoredView]; // This is the reference to your UIView "floating" subclass
You will also need to turn off user interaction for your floating view. I don't know if this will specifically pass the touches to the underlying UIView's or not though:
[myAnchoredView setUserInteractionEnabled:NO];
If this is blocking touches to your tableView, you may need to pass the reference to your tableView to the anchored view at initialization, then pass the touch events along. You can do this by overriding the touch response methods in UIResponder. (If there is a better way, someone please speak up.)
Do you mean the anchored view should appear transparent over the UITableView, or just above, i.e. anchored view uses top 20% of the available space, table view uses the rest?
In any case, create a UIView containing the anchored view and the table view. If you want the anchored view transparent over the table view, it's a bit tricky, because to scroll the table view, touches have to pass through the anchored view.
Add the surrounding view's view controller to the navigation controller instead of just the tableview.
I investigated how UIScrollView keeps its scrollIndicator above the UIScrollView's content and yet unmoving by examining a UIScrollView in the debugger.
The scrollIndicators are UIImageViews. And you can see they are direct descendants of the UIScrollView itself. You can also see that any scrolled content is also a direct descendent. So how is it that the scroll indicators don't move?
I tried updating the position of my static content constantly in - (void)scrollViewDidScroll:(UIScrollView *)scrollView this, surprisingly, works. I'm not sure if it is how UIScrollView itself does it, but without some private magic, it must be something like this.