Change Frame of UIScrollView Dynamically? - iphone

I have a custom UIScrollView in my app. When the user touches the scroll view I want it to enlarge to fill the entire screen.
I have tried using this code, however nothing happens :
-(void) touchesBegan: (NSSet *) touches withEvent:(UIEvent *) event {
NSLog(#"Touch Detected");
self.frame = CGRectMake(0, 0, 768, 1024);
}
Is there a way to dynamically change the size of the view ? Also is it possible to somehow animate the change in size ?
Thank you in advance.

Your code looks correct. Perhaps your view is clipped within a parent view making it seem as though it didn't resize, in which case you should be mindful of the clipsToBounds property of the parent view. Some other considerations (without knowing what makes you think its not working) is the contentMode property of your UIScrollView. Depending on that value, your UIScrollView may not be redrawing itself. See UIView for details (read frame property)
Animate example:
[UIView animateWithDuration:<some float value in seconds> animations:^{
self.frame = CGRectMake(0, 0, 768, 1024);
}];
Details on animate: UIView (read animateWithDuration:animations: class method).

Related

UIView not detecting touch when scaled

I have a view and its subview is UIScrollView and having an UIImageView which is a subview of UIScrollView. When I try to scale UIView,UIScrollView and UIImageView is also getting scaled. But the problem is I'm getting touches on half portion of view.
Here is my code
[UIView animateWithDuration:0.5 delay:0.0 options:0 animations:^{
CGFloat DesiredWidth = 250.0;
CGFloat DesiredHeight = 300.0;
CGAffineTransform scalingTransform1;
scalingTransform1 = CGAffineTransformMakeScale(DesiredWidth/self.view1.bounds.size.width,
DesiredHeight/self.view1.bounds.size.height);
self.view1.transform = scalingTransform1;
self.view1.center=CGPointMake(268, 250); }completion:^(BOOL finished)
{ }];
Please anyone can tell me how can I get touch.I searched a lot but could not find
Thanks
I think your problem is the size of the very root UIView(i.e. the superview of the UIView you talk about) of your views. When a certain subview is larger than its superview it will not receive touch events in this portions of it's frame that go outside of the superview frame.
If you have read about the responder chain in iOS you should know that UIView calls -[hitTest: withEvent:] on it's subviews in the order they have been laid out. This method checks if a certain touch points inside the UIView frame then starts to check if a certain subview can handle the touch event in the order they are positioned in the responder chain(the view hierarchy in this case).
So when the touch event is first received a certain UIView first hit tests itself and when the location of the touch is outside it's frame it does not check it's subviews. So check very carefully all your views' frames and if some root view (it could be the view property of the UIViewController you use) is actually smaller than the subview you touch.
Also mind the transforms. In the documentation is stated that when some transforms are applied the frames of the views become irrelevant.
To summarize my reply let's have this situation:
some_superview.frame = CGRectMake(0, 0, 200, 200);
some_subview_of_the_superview.frame = CGRectMake(0, 0, 200, 300);
in the region of the subview that is actually (0, 200, 200, 100) which is the bottom 100 points the touches will be ignored by the superview because they are outside it's frame
Make sure.. your subview's frame is within its superview. If it went wrong way.. it will not detect any touch.

Resize UICollectionView while scrolling

My collection view begins life short, at the bottom of it's view controller's view. When the user scrolls up a little bit, I'd like the collection to fill the view. I'd like it to shrink again when the user pulls down into the bounce area.
Here's the code that I'd like to make work (and I think should work):
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.collectionView) {
CGFloat offsetY = self.collectionView.contentOffset.y;
if (offsetY > 20.0) {
[self setCollectionViewExpanded:YES];
} else if (offsetY < -10.0) {
[self setCollectionViewExpanded:NO];
}
}
}
- (void)setCollectionViewExpanded:(BOOL)expanded {
// avoid starting a do-nothing animation
BOOL currentlyExpanded = self.collectionView.frame.origin.y == 0.0;
if (expanded == currentlyExpanded) return;
// note: the collection view is the uppermost subview of the vc's view
// expanding to view bounds makes it cover every other view
CGRect newFrame = (expanded)? self.view.bounds : CGRectMake(0.0, 240.0, 320.0, self.view.bounds.size.height-240.0);
[UIView animateWithDuration:0.3 animations:^{
self.collectionView.frame = newFrame;
}];
}
But after the expand happens, the cells remain in the same position relative to the parent view (still near the bottom) and the collection view stops responding to panning touches and sends no further didScroll notification.
I can get the content repositioned properly by doing a reloadData in an animation completion block, but the scrolling touches stop working either way.
A while back, I tried something like this with a table view (or another kind of scroll view) and ran into a similar hangup. Much obliged if anyone has this solved...
Can you describe your view hierarchy a bit better? Is the UICollectionView a subview of the UIScrollView?
Is self.view a UIScrollView?
In that case, the bounds origin is the scrollview's contentOffset (not 0,0). That could give your UICollectionView an unexpected frame.
If the UICollectionView is a subview of the scrollview, the frame origin is the origin of the view in the scrollview-space. So don't change the frame's origin (just its size) and set the contentOffset of the scrollView to make it visible.

User interaction outside the UIView bounds

What i'm trying to do is "navigating" into a bigger UIView with buttons and controls positioned in different places. I've made the size of the main UIView twice bigger than the usual. It is 640x480 instead of 320x480.
After clicking a button in the first part of the screen i've made a moving translation of -320px in the x-direction to show the second "hidden" part of the screen where other functions will reveals to the user. Everything works perfect apart the fact i can't get the UIView back to the original position. It seems that the button i use to get back to the original position and that is "outside the bounds" of the 320px, doesn't work.
My UIView is referenced as "introScreen", and the following is the function i call to translate the screen through the x direction:
- (void)secondPartOfTheScreen {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.15];
introScreen.transform = CGAffineTransformMakeTranslation(-320, 0);
[UIView commitAnimations];
}
Calling this function my UIView moves correctly, but when my button appear in this part of the screen, it doesn't get any user interaction. If i move that button in the first part of the screen, it works correctly. It have something to do with the screen bounds? It is possibile to solve it?
Thanks in advance!
You should try to subclass your UIView and override its pointInside:withEvent: method so that also a point outside its bounds is recognized as belonging to the view. Otherwise, there is no chance that user interaction outside of the view bounds are handled as you would like to.
This is what the Event Handling Guide say:
In hit-testing, a window calls hitTest:withEvent: on the top-most view of the view hierarchy; this method proceeds by recursively calling pointInside:withEvent: on each view in the view hierarchy that returns YES, proceeding down the hierarchy until it finds the subview within whose bounds the touch took place. That view becomes the hit-test view.
You could use something like this in your custom UIView:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
for (UIView* view in self.subviews) {
if (view.userInteractionEnabled && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
return YES;
}
}
return NO;
}
You could do with this:
#interface MyView: UIView
#end
#implementation MyView
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
for (UIView* view in self.subviews) {
if (view.userInteractionEnabled && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
return YES;
}
}
return NO;
}
#end
add this class definition to the beginning of the .m file where you are using the view. (You could also use separate .h/.m files, but for the sake of testing if everything works, it is enough). Then, replace UIView with MyView in the instantiation of your current UIView, and it should work.
You're not using the transform property properly, you should take into account the previous value of the property like this:
introScreen.transform = CGAffineTransformConcat(introScreen.transform, CGAffineTransformMakeTranslation(-320, 0));
Hope this helps, and check Apple's UIView documentation and CGAffineTransform documentation for more details.
2 things.
First why would you use transform when you are merely moving the object. You can set the frame just as easily.
CGRect frame = introScreen.frame;
frame.origin.x -= 320;
introScreen.frame = frame;
(Yes this will work inside an animation)
second, if you translate a view its contents should be within its bounds regardless of the size you want displayed in the first place.
therefore your container view (the one with the buttons) should be a size so that all contained pieces are within its bounds. Anything outside its bounds will not function. Having the main view twice its size is not as useful as having the contained view at twice its size.
Think of it like this.
Your table should be able to hold whatever your looking at. If you have a map you want to display you would slide it around on the table. the table does not need to be as big as the map but the map needs to be big enough to present you with all of its contents. if the map was say too short to display the poles and you would be without the information. No matter how much you transformed the map you would not be able to use the north or south poles..
To ensure the app reflects this you can turn on clip subviews on all the views in question, then if its outside the bounds it will not be visible.

UIScrollView content offset blocks user interaction

I'm making an app what contains a scroll view with some subviews what are built up on UIGestures...
The idea is to be able to use it in both orientations, in portrait.. it would be full screen but in landscape the user would have to scroll to access all the content however I have had some issues trying to get it to work correctly.
Here is how I have my view laid out:
UIView -> UIScrollView -> UIView -> UIImageView -> Subviews with Gesture recognisers.
the first UIView is just the UIViewControllers View, the UIScrollView is what will/should handle the difference in orientation, the second UIView is just a container to stop positioning screwing up and the UIImageView holds my image what is a diagram where I have placed a series of custom checkboxes and labels into/over the UIImageView to display content and receive and handle interaction...
In Portrait, this will work flawlessly however when I rotate to landscape, the content inside the UIScrollView is shifted up by 128pixels so the top is inaccessible... this also makes the bottom 128pixels too big so there is a transparent gap..
I can fix this by running this code on the didRotateFromInterfaceOrientation: function:
int offset = 0;
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]))
offset = 128;
else
offset = 0;
[scrollView setContentInset:UIEdgeInsetsMake(offset, 0, -offset, 0)];
The code above just alters the ContentInset's top and bottom property to adjust the postion by 128 pixels each way... the scroll view will then display the content fine...
However, when I attempt to touch any of the gestures within 128pixels from the bottom, they will not work!
I've tried so much to get it working but also if i don't alter the contentInset they will still not work (within 128 from the bottom)... It's really confusing me as I have userInteractionEnabled set to YES on all of my views but it still wont work, anything else will and content is displayed fine within this 128pixels area...
Does anybody know what is wrong with this or have anything I can try as I feel that I have tried everything possible.
Thanks for reading,
Liam
I figured it out in the end... It was an issue with the frame size changing... all I had to do was run this code on the didRotate function:
int offset = 0;
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
[scrollView setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
offset = 128;
} else {
[scrollView setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
offset = 0;
}
[scrollView setContentInset:UIEdgeInsetsMake(offset, 0, -offset, 0)];
It just makes sure that the frame is reset back to the size of the view once rotated... I figure it was an issue with autoresize masks but I didn't have the time to play around with them and this works just as well.

How to know the present visible view's tag in iphone

i have an scroll view and i am adding few views to that
when the user scrolls how to get the current visible view's tag.
then i can add some thing to that view...
its just like getting the indexpathrow in table view..
how to do that..?
Thanks
You basically want to check if the frame of the subviews inside the UIScrollView intersect the scrollview's frame (if you only want to determine partial visibility), or if the frame is contained in the other frame (if you want to determine full visibility).
However, in order to check if the subview's frame intersects and/or is contained in the scrollview's frame you need to translate it from the local coords inside the scrollview to the global coordinates outside the scrollview.
That is probably pretty confusing, so here is some code. This will loop through all the subviews of a scrollview and print out whether it is fully visibile or partially visible:
for (UIView *subview in scrollView)
{
CGRect globalRect = CGRectOffset(subview.frame, -scrollView.contentOffset.x, -scrollView.contentOffset.y);
CGRect scrollViewBounds = CGRectMake(0.0f, 0.0f, scrollView.bounds.size.width, scrollView.bounds.size.height);
if (CGRectContainsRect(scrollViewBounds, globalRect)) {
NSLog(#"FULLY VISIBLE");
} else if (CGRectIntersectsRect(scrollViewBounds, globalRect)) {
NSLog(#"PARTIALLY VISIBLE");
}
}
You could put this in a UIScrollViewDelegate method to do these checks while the user is scrolling the content around.