I have an UIScrollview and which contains UIView as a subview like,
here i can able to drag the UIView inside the ScrollView, when the uiview goes to at END RIGHT or END LEFT the uiview disappears inside scrollview, after that i has to scroll the scrollview then i can able to see the uiview, here i need to SCROLL THE SCROLLVIEW when uiview comes at the rightside or left side, for that i used this code but not working.
[myscrollview scrollRectToVisible:myview.frame animated:YES];
Here I have used on UIButton (as it is already used in one of my application I dont need to write whole code again)
[cropRectangleButton addTarget:self action:#selector(imageMoved:withEvent:) forControlEvents:UIControlEventTouchDragInside];
The cropRectangleButton is one UIButton and the imageMoved:withEvent: method is as below
- (IBAction) imageMoved:(id)sender withEvent:(UIEvent *)event
{
CGPoint point = [[[event allTouches] anyObject] locationInView:self.view];
CGPoint prev = lastTouchDownPoint;
lastTouchDownPoint = point;
CGFloat diffX = point.x - prev.x;
CGFloat diffY = point.y - prev.y;
UIControl *button = sender;
CGRect newFrame = button.frame;
newFrame.origin.x += diffX;
newFrame.origin.y += diffY;
scrollView.contentSize = CGSizeMake(2*scrollView.frame.size.width, scrollView.frame.size.height);
[scrollView scrollRectToVisible:newFrame animated:YES];
button.frame = newFrame;
}
Here I change my contentSize of scrollView as it is very narrow to test that the scrollView is scrolling or not you don't need that line of code.
And when I drag the button left the scrollView is also automatically scrolled to the button frame to show the whole button try to implement this using UIView and if not possible just put one transparant UIButton on your view fully covered the view and you it to do the drag :)
Related
I am working on Scroll View with gestures. I added a UIView in Scroll View whose size is equal to the ScrollView content size. I want to apply the pinch gesture and rotate gesture on the View which is subview of ScrollView. I have done the work of the pinch gesture by using zoom property and delegate of the ScrollView which give me same effect which I want. But Rotation Gesture is creating Problem. When I add rotation gesture on the view then zooming of the scroll view also get disturb.
So how can i apply the pinch gesture and Rotate gesture on the Scroll View's subview whose size must be equal to the content size of the ScrollView initially.
Can anybody give me the way to do this!
This is the code of .m file, when we rotate the view it become invisible
#import "ViewController.h"
#interface ViewController ()
{
UIView *backgroundView;
UIScrollView *scrollView;
CGFloat lastRotation;
}
#end
#implementation ViewController
-(void)loadView
{
[super loadView];
//Scroll View
scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
scrollView.contentSize = self.view.frame.size;
scrollView.delegate = self;
scrollView.backgroundColor = [UIColor grayColor];
//Zooming factors of the Scroll View
scrollView.minimumZoomScale = 1.0;
scrollView.maximumZoomScale = 5.0f;
scrollView.zoomScale = 1.0;
[self.view addSubview:scrollView];
//Scroll View's subview
backgroundView = [[UIView alloc] initWithFrame:scrollView.frame];
[backgroundView setBackgroundColor:[UIColor orangeColor]];
[scrollView addSubview:backgroundView];
UIRotationGestureRecognizer *bgRotationGstr = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotateBackgroundView:)];
bgRotationGstr.delegate = self;
bgRotationGstr.cancelsTouchesInView = NO;
[backgroundView addGestureRecognizer:bgRotationGstr];
//Child of background view
UIView *childView = [[UIView alloc] initWithFrame:CGRectMake(20, 50, 100, 100)];
childView.backgroundColor = [UIColor grayColor];
[backgroundView addSubview:childView];
}
//Rotation of the background view
-(void)rotateBackgroundView:(UIRotationGestureRecognizer*)gesture
{
CGFloat rotation = 0.0 - (lastRotation - [(UIRotationGestureRecognizer*)gesture rotation]);
CGAffineTransform currentTransform = backgroundView.transform;
CGAffineTransform newTransform = CGAffineTransformRotate(currentTransform,rotation);
[backgroundView setTransform:newTransform];
lastRotation = [(UIRotationGestureRecognizer*)gesture rotation];
if (gesture.state == UIGestureRecognizerStateBegan || gesture.state == UIGestureRecognizerStateChanged)
{
scrollView.scrollEnabled = NO;
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
lastRotation = 0.0;
scrollView.scrollEnabled = YES;
return;
}
}
#pragma mark<UIScrollViewDelegate>
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return backgroundView;
}
#pragma mark<UIGetsureRecognizer>
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
return YES;
}
#end
After many days of struggling I found a definitive solution in order to use UIScrollView integrated zoom behavior together with UIRotationGestureRecognizer working like a charm. You have to add a container dummy view as subview of the scroll view, and put the UIImageView as subview of the container view. Afterthat, return the container view in the viewForZoomingInScrollView method and add the UIRotationGestureRecognizer to the scrollview, applying CGAffineTransformRotate to the UIImageView. Finally, return true in the shouldRecognizeSimultaneouslyWithGestureRecognizer method. In this way the scrollView will capture both the two fingers rotation gesture and the pinch to zoom gesture: the zoom will be applied to the dummy view and rotation to the uiimageview, without conflicts between transformations.
In code: let's think to have a UIViewController presenting a UIScrollView. We want to use scrollview's zoom behaviour out of the box together with UIImageView rotation.
1) The controller (or any other object) containing UIScrollView must conforms to UIGestureRecognizerDelegate protocol.
In myViewController.h
#interface myViewController : UIViewController < UIGestureRecognizerDelegate> {
}
2) Create a UIScrollView, add a dummy view as subview and finally add a UIImageView as subview of the dummy view.
In myViewController.m
//Scrollview
myScrollView=[[UIScrollView alloc] initWithFrame:CGRectMake(0,0, view.frame.size.width, view.frame.size.height)];
myScrollView.delegate=self;
[view addSubview:myScrollView];
//Dummy View
UIView *dummyView=[[UIView alloc] initWithFrame:CGRectMake(0, 0, myScrollView.frame.size.width, myScrollView.frame.size.height)];
[self addSubview:dummyView];
//ImageView
imageView=[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, dummyView.frame.size.width, dummyView.frame.size.height)];
imageView.contentMode=UIViewContentModeScaleAspectFit;
[dummyView addSubview:imageView];
//Add rotation gesture to the scrollView
rotationGestureRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(handleRotate:)];
[myScrollView addGestureRecognizer:_rotationGestureRecognizer];
//Set the controller as delegate of the recognizer
rotationGestureRecognizer.delegate=self;
[...]
#pragma UIScrollViewDelegate
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
//Set the dummy view (imageview's superview) as view for zooming
return imageView.superview;
}
[...]
#pragma Mark - UIGestureRecognizerDelegate
- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
//Make it possibile to recognize simultaneously pinch and rotation gestures
return TRUE;
}
[...]
- (IBAction) handleRotate:(UIRotationGestureRecognizer*)recognizer {
//Apply the rotation to imageView
imageView.transform = CGAffineTransformRotate(imageView.transform, recognizer.rotation);
recognizer.rotation = 0;
}
For simplyfing purposes, I wrote everything in the same controller. You are free to subclass the UIScrollView. Remember that the tricks are:
1) returning the container dummy view as viewForZoomingInScrollView so zoom will affect the container view and rotation will affect the uiimageview.
2) set the viewcontroller containing the scrollview as delegate of the rotation gesture recognizer and return TRUE for shouldRecognizeSimultaneouslyWithGestureRecognizer.
Add this to your .m
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
Also make sure you dont have exclusive touch enabled on any gesture recognizers.
Try to put you scalable/rotatable content in a subview of your content view. ( Maybe you must set the "clip content" property of your content view to true - not sure )
This way the scrollview is not concerned anymore by transformations, since its content view stays still.
If you have to display the clipped content ( if you rotate a square, for example, the corners go out the initial area), recompute your content view and update the scrollview.
Since you gave the code, I suggest to try:
//Scroll View's subview
backgroundViewHolder = [[UIView alloc] initWithFrame:scrollView.frame];
[backgroundViewHolder setBackgroundColor:[UIColor orangeColor]];
[scrollView backgroundViewHolder];
//Holder View's subview
backgroundView = [[UIView alloc] initWithFrame:backgroundViewHolder.bounds];
[backgroundViewHolder addSubview:backgroundView];
Everything else should remain the same. This is just an idea... Not sure it is the right answer.
Add this to your implement file, make it a UIGestureRecognizerDelegate.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
rotationGeustureRecognozier.delegate = self;// (the implement file)
I have a UIView C which is a subview of UIView B wich is a subview of UIView A. I add a UIPanGestureRecogizer to UIView C with a selector called "selectorX" and I want that when UIView C goes out of UIView A frame, then its superview changes to UIView A and I want also to change its UIPanGestureRecognizer with another selector "selectorY".
This is my code:
-(void)selectorX:(UIPanGestureRecognizer*)sender{
CGPoint translation = [sender translationInView:self.view];
sender.view.center = CGPointMake(sender.view.center.x, sender.view.center.y +translation.y);
[sender setTranslation:CGPointMake(0, 0) inView:sender.view];
UIView *uiViewC=(ImagenFichaView *)sender.view;
if (sender.view.center.y+translation.y<-50) {
[uiViewC removeFromSuperView]
CGPoint newCenter=[sender locationInView:uiViewA];
[uiViewA addSubview:uiViewC];
uiViewC.center=newCenter;
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(selectorY:)];
panRecognizer.delegate=self;
[uiViewC removeGestureRecognizer:sender];
[uiViewC addGestureRecognizer:panRecognizer];
}
}
Everything goes well but the transition between the 2 UIPanGestureRecognizers is not continuous. When uiViewC changes its superView, the drag action stops. I have to take off my finger from the screen and start the movement again. What can I do to make the movement continuos?
Thank you very much
To get a continuous movement I didn't remove uiViewC from uiViewB, I just added it to uiViewA and it solved the problem.
I can get the UIPinchGestureRecognizer handler to work with scaling an object but I don't want to scale I want to change the size. For example I have a UIView and I've attacked a UIPinchGestureRecognizer gesture to it and if the user pinches I want to change the width of the UIView to match the pinch. I don't want to scale it so the UIView is larger(zooming)
If you have the UIPinchGestureRecognizer call a method pinch, you can do:
- (void) pinch:(UIPinchGestureRecognizer *)pinch
{
CGRect frame = [self.view frame];
frame.size.width = frame.size.width * pinch.scale;
[self.view setFrame:frame];
}
I have the following code:
float yOffset = activeTextView.frame.origin.y - keyboardSize.height + 55;
CGPoint scrollPoint = CGPointMake(0.0, yOffset);
[scrollView setContentOffset:scrollPoint animated:YES];
This animates the scrollView in - (void)keyboardWasShown:(NSNotification *)notification
I am trying to return the scrollView to it's original location after the hiding the keyboard like this:
- (void) keyboardWillHide:(NSNotification *)notification {
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
But it doesn't work!
How can I return the UIScrollView and actually the whole screen to its original location so the user will see what he saw before animation of the scrollview?
In your keyboardWasShown: method, you're setting the contentOffset property ([scrollView setContentOffset:] is equivalent to scrollView.contentOffset). However, in keyboardWillHide:, you're setting contentInset, which is something completely different (essentially, it's the amount of internal padding of the scroll view's content). Try
scrollView.contentOffset = CGPointZero; // non-animated by default
or
[scrollView setContentOffset:CGPointZero animated:YES]; // animated
Also, as NSResponder mentioned, make sure your keyboardWillHide: method is being called.
In the app I am working on, my goal right now is to scroll content when the keyboard shows and to allow users to scroll while it is showing. I have tried a few different solutions and none have been able to achieve this yet.
I'm using storyboards in the app and here is the element hierarchy within the view controller:
View Controller
UIScrollView
UIView
Buttons/textfields/labels/UIPickerView
I first set the UIScrollView's content size to be the same size of the view that was inside of it holding all of the form elements. When that didn't work, I tried over-exagerating the height of the content manually setting the content size to be 320 x 2000. Again, that didn't work. I have user interaction enabled set to YES on the scroll view as well. This is the code I have in there at this point.
CGSize contentSize = CGSizeMake(320, 2000);
[self.scrollView setContentSize:contentSize];
Within the scroll view I had a button that sits behind the whole form that has an action to close the keyboard if a user touches outside of it. I disabled that to see if it may have been a conflict in events that would keep it from scrolling. Again, didn't work.
-(IBAction)closeKeyboard:(id)sender
{
if(![self isFirstResponder]){
[self.view endEditing:YES];
}
}
I even set up some observers to see if the keyboard is about to appear or disappear. The observers would adjust the height of the scroll view, not the content size, just the scroll view itself, based on where the keyboard was currently sitting. So at this point, the content in the scroll view would be much taller than the scroll view itself, but still no scrolling is happening.
Here is the code for my observers:
// adjust view based on keyboard
- (void)keyboardWillHide:(NSNotification *)n
{
NSDictionary* userInfo = [n userInfo];
// get the size of the keyboard
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
// resize the scrollview
CGRect viewFrame = self.view.frame;
// I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
//viewFrame.size.height += keyboardSize.height;
CGRect scrollRect = CGRectMake(viewFrame.origin.x, viewFrame.origin.y, viewFrame.size.width, viewFrame.size.height + keyboardSize.height + 100);
[UIScrollView beginAnimations:nil context:NULL];
[UIScrollView setAnimationBeginsFromCurrentState:YES];
[UIScrollView setAnimationDuration:0.3];
[self.scrollView setFrame:scrollRect];
self.scrollView.userInteractionEnabled = YES;
[UIScrollView commitAnimations];
keyboardShowing = false;
}
- (void)keyboardWillShow:(NSNotification *)n
{
// This is an ivar I'm using to ensure that we do not do the frame size adjustment on the UIScrollView if the keyboard is already shown. This can happen if the user, after fixing editing a UITextField, scrolls the resized UIScrollView to another UITextField and attempts to edit the next UITextField. If we were to resize the UIScrollView again, it would be disastrous. NOTE: The keyboard notification will fire even when the keyboard is already shown.
if (keyboardShowing) {
return;
}
NSDictionary* userInfo = [n userInfo];
// get the size of the keyboard
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
// resize the noteView
CGRect viewFrame = self.view.frame;
// I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
CGRect scrollRect = CGRectMake(viewFrame.origin.x, viewFrame.origin.y, viewFrame.size.width, viewFrame.size.height - keyboardSize.height - 100);
//scrollView.frame.size.height -= keyboardSize.height;
//viewFrame.size.height -= keyboardSize.height;
[UIScrollView beginAnimations:nil context:NULL];
[UIScrollView setAnimationBeginsFromCurrentState:YES];
[UIScrollView setAnimationDuration:0.3];
[self.scrollView setFrame:scrollRect];
self.scrollView.userInteractionEnabled = YES;
[UIScrollView commitAnimations];
keyboardShowing = YES;
}
I would not be surprised if this is one of those simple mistakes that keeps slipping my mind, but this sort of accessibility feature would be really nice to have in the app. Any help would be much appreciated, or even other possible solutions to the problem I am trying to solve would be great too.
It looks like you're using a gesture recognizer in IB to detect a tap outside event. Because this recognizer is in the highest view in the hierarchy, it overrides the scrollview's detectors. You might need to change it slightly depending on which areas you want to be able to tap on to close the keyboard.
This is the UIResponder class reference. It lists all of the UIResponder events that your view controller automatically inherits. What might fix your problem is subclassing your UIScrollView and adding the keyboard closing code to it. Also, make sure that you set it to the first responder.
Final code that worked:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
mouseSwiped = NO;
UITouch *touch = [touches anyObject];
if ([touch tapcount] == 1)
for(UIView *view in self.view.subviews){
if([view isKindOfClass:[UITextField class]]){
[view resignFirstResponder];
}
}
}
}