swapping images using pan gesture - objective-c

I have multiple UIImageView added into the main view, each UIImageView is displayed in its own parent UIView for cropping purpose, each UIImageView is also attached to a PanGestureRecognizer.
I am curious on how to implement both pan gesturerecgonizer and also allow image swap by dragging one image (A) to hover over the other image (B) and hold, then the image A and B will be swapped (so A is now displayed in old B's UIView).
This is something like the iPhoto app just implemented.
Any ideas?

Implement the pan gesture
In the method which handles the pan gesture check if the frames of
two imageview's intersect by using CGRectIntersectsRect.
if ([gestureRecognizer state] == UIGestureRecognizerStateEnded)
{
if(CGRectIntersectsRect(frame1,frame2))
// Swap both the imageview's
}

Related

Setting UIScrollView's origin for pinch gestures

I'm creating a scroll view for displaying a very large view, and I need both scroll and zoom functionality (just like an image viewer). Here are the steps that I've taken:
In interface builder, I've put a scroll viewer to the view controller.
I've added a pinch gesture recognizer to the scroll viewer.
I've connected the gesture recognizer's action to the code to handle the gesture events.
When the view controller is loaded, I change my view's origin to the center (viewer is my scroll viewer): self.viewer.contentOffset = CGPointMake(384, 512);
In my code for the handler, I handled the event as such:
(startScale is 1.0 in the beginning)
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)sender {
if(sender.state == UIGestureRecognizerStateEnded){
startScale *= sender.scale;
}else{
float result = sender.scale * startScale;
self.viewer.transform = CGAffineTransformMakeScale(result, result);
}
}
When I run the app, the gesture is recognized and scaling works correctly, however, the whole view scales with respect to the 0,0 point of the screen (top left). I want it to scale with respect to the middle point that I'm applying the gesture, just as a natural pinch gesture for zooming into a photo.
I've also tried setting self.viewer.frame's origin, but nothing changed. I've searched about the problem and found these:
How to set a UIView's origin reference? (already tried)
https://stackoverflow.com/questions/13163279/pinch-punch-gestures-center (about my problem, but unanswered)
UIPinchGestureRecognizer for zooming and panning an image in xcode (looks like an overkill, too complicated for me, and I'm not sure if this would really help my situation)
How can I achieve natural pinching with my scroll view, what am I doing wrong?
Thanks,
Can.
Well, the answer to the problem is very simple: Remove the pinch gesture altogether. The benefit of using a UIScrollView is that it handles the panning/zooming internally, and you have to do nothing
Edit: To make sure the content is scaled properly, you are going to need a UIView (called contentView or whatever you want) where you put all the content, and then on the delegate method of your UIScrollView do this:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return contentView;
}
This should solve your problem
Edit 2: Also remember to set the minimum / maximum zoom scales for your UIScrollView

Vertical UIToolBar and also its UIBarButtonItem

In my application i have one UIToolBar. When in portrait mode this toolbar is on top and is horizontal, but when the device is rotated this toolbar should be converted to vertical toolbar and should be place on left side. Also its subviews i.e 5 UIBarButtonItems should also be placed accordingly.
Does any one know the solution for this ?
I have read this for reference, But my tool bar should align itself according to the orientation. I am using iOS 6.
There is no built-in vertical menu feature (yet). There are 2 parts in your question:
Vertical UIToolbar:
You may restrict the device orientation and the toolbar will remain at the same location
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
(this will affect all views)
Or you can listen for orientation changes and rotate your toolbar accordingly using its transform property, e.g.
toolbar.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI/2);
Rotated UIBarButtonItem:
If you have rotated the toolbar (case 2 above), items will rotate too.
If not, you need to rotate your items. Several post shows how to deal with the fact that as UIBarButtonItem does not extend UIView, it has no transform property (see here). In the end you will have again to listen for orientation changes and rotate the subviews of your toolbar, e.g.
for (UIView *view in toolbar.subviews) {
view.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI/2);
}
Of course apply the rotation related to your orientation (M_PI/2 or -M_PI/2).
I ran into this same issue, so I subclassed UIToolbar and made it into exactly what I wanted it to be.
Here's the GitHub link: https://github.com/fennelouski/NKFToolbar

Touch on UISlider prevents scrolling of UIScrollView

I have a (vertical) UISlider inside a UIScrollview. I'd like to be able to change the value of the slider, and, without lifting my finger, scroll the scrollview left or right.
Desired behavior:
Touch down inside vertical UISlider, followed by a finger drag left or right causes the scrollview to scroll
Actual behavior:
Touch down inside vertical UISlider, followed by a finger drag left or right causes no movement in UIScrollview. A touch down outside the UISlider followed by a drag will scroll the scrollview as expected
UIView has a property called exclusiveTouch which seems as if it might be related to my problem. I tried setting it to NO, with no luck.
So, how can is set up my UISliders so that the scrollview beneath them will respond to touches which originate inside the UISliders?
Have you tried subclassing UIScrollView and implementing - (BOOL)touchesShouldCancelInContentView:(UIView *)view? According to the Apple documentation:
// called before scrolling begins if touches have already been delivered to a subview of the scroll view. if it returns NO the touches will continue to be delivered to the subview and scrolling will not occur
// not called if canCancelContentTouches is NO. default returns YES if view isn't a UIControl
If you simply return NO if the view is your UISlider, this may do what you want, assuming your UIScrollView only scrolls horizontally. If this doesn't work, you likely will have to do custom touch handling (ie. overriding touchesBegan:withEvent:, touchesChanged:withEvent:, etc.) for both your UIScrollView and your UISlider.
What you are seeing is the intended behavior.
Each touch event only gets handled by one control. What exclusiveTouch does is actually to prevent other touch events from being delivered to other views.
To do what are trying to do you would have to do some of the touch handling yourself. Passing the event to both your views. You could do either do it by implementing all the touchesBegan:, touchesMoved: etc. methods and pass the events to both views. You can read more about that approach in the UIResponder documentation. Another approach is to do the event handling in a UIGestureRecognizer on the scroll view that hit tests the slider and updates the value of the slider using the y-delta. You can read more about gesture recognizers and event handling in the section about Gesture Recognizers in the Event Handling Guide for iOS.
Side note:
Go to the Settings app and toggle a switch half way (for example the Airplane mode toggle) and then drag down. Nothing will happen. The rest of the OS behaves the same way. Are you sure that this is the interaction that you really want to do? Apps that behave differently often feel weird and unfamiliar.
Your question confused me a bit. You are saying a vertical slider - but dragging left and right?
If you wish to scroll the scrollview when dragging the UISlider, the proper way to do so is
[mySlider addTarget:self action:#selector(sliderMoved:) forControlEvents:UIControlEventValueChanged];
and
- (void) sliderMoved:(UISlider*) slider {
myScrollView.contentOffset.x = slider.value * (myScrollView.contentSize.width - myScrollView.bounds.size.width);
}
Hope this is what you want.
You need to set delaysContentTouches as NO and prevent for UISlider objects to scroll, Check below code.
mySlider.delaysContentTouches = NO;
- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view {
if ([view isKindOfClass:[UISlider class]])
{
UITouch *touchEvent = [[event allTouches] anyObject];
CGPoint locationEvent = [touchEvent locationInView:view];
CGRect thumbRect;
UISlider *mySlide = (UISlider*) view;
CGRect trackRect = [mySlide trackRectForBounds:mySlide.bounds];
thumbRect = [mySlide thumbRectForBounds:mySlide.bounds trackRect:trackRect value:mySlide.value];
if (CGRectContainsPoint(thumbRect, locationEvent))
return YES;
}
return NO;
}
I think you can get some reference from this example in this example it is shown that how to cancel any touch or any gesture recognizers and apply them to other views.
May this lead you to the solution of your problem and if it will just let me know about it
Happy Codding :)

Possible to manually rotate UIPopoverController's view?

The view system in my app is highly customized and uses a number of views that are manually rotated from portrait to landscape based on user interactions (the rotation is done by applying an affine transform to the view/layer).
I want to present a popover inside one of these rotated views, but the orientation of the popover always appears relative to the orientation of the device (i.e., not relative to the view). I'm guessing the answer is no, but just in case someone has a clever idea: is there any way to manually rotate the view that is presented by UIPopoverController?
Sean, I just tested it for kicks, yes it works.
It has to be done (in my case at least) in viewDidAppear (if done in viewWillAppear, it gets knocked back to the original setting.)
This worked just fine (just tested now) to have a popover at a 90 degree angle. i.e in my case my main view is in portrait mode and the popover is turned 90 deg.
self.navigationController.view.superview.superview.transform = CGAffineTransformMakeRotation (M_PI/2.0);
Are you trying to rotate the popover or just the content shown in the popover? You can control some of the former by setting which arrow orientations are possible. I'm interested in the latter, and it seems to work just by grabbing the content view controller. E.g.:
aPopoverController.contentViewController.view.transform = CGAffineTransformMakeRotation(M_PI);
DISCLAIMER: If you're at all interested in trying to get your app into the store, this code is almost certainly grounds for rejection. It dives into UIKit's private API's which is a big no-no as far as apple is concerned.
#RunningPink had the right idea. Depending on how the view hierarchy is set up, the popover may be back up farther than two superviews. The popover itself it an instance of the (private) class _UIPopover (at least in iOS 5). You can find this view by doing:
UIView *possiblePopover = popoverController.contentViewController.view;
while (possiblePopover != nil) {
// Climb up the view hierarchy
possiblePopover = possiblePopover.superview;
if ( [NSStringFromClass([possiblePopover class]) isEqualToString:#"_UIPopoverView"] ) {
// We found the popover, break out of the loop
break;
}
}
if (nil != possiblePopover) {
// Do whatever you want with the popover
}
In doing this, I found that transforming the view often ended up making the popover look blurry. I found the reason was that the popover's superview was an instance of another private class called UIDimmingView which is responsible for accepting touches outside of the popover and causing the popover to dismiss. Performing the rotation on the dimming view removed the blurriness I was seeing in the popover.
However, transforming the dimming can result in weirdness where certain parts of the window are not "covered" by the dimming view so the popover will not dismiss if these parts of the window are tapped. To get around this, I applied the rotation to the dimming view, reset the dimming view's frame to cover the screen, and then translated the popover view into place.
if (nil != possiblePopover) {
// Found the popover view
CGAffineTransform rotation = CGAffineTransformMakeRotation(-M_PI_2);
CGAffineTransform translation = // Whatever translation in necessary here
// Rotate the UIDimming View and reset its frame
[possiblePopover.superview setTransform:rotation];
[possiblePopover.superview setFrame:CGRectMake(0, 0, possiblePopover.superview.frame.size.height, possiblePopover.superview.frame.size.width)];
// Translate the popover view
[possiblePopover setTransform:translation];
}

Zoom multiple objects simultaneously in UIScrollView

I have a UIScrollview that contains multiple UIImageViews. I could zoom using the UIScrollView delegate but can only return one UIImageView at a time. I want to be able to zoom all objects inside the scrollview simultaneously when i try to zoom the uiscrollview. How can I achieve this?
This is what my delegate returns when zooming:
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)aScrollView {
return imageView;
}
Put your image views into one single UIView, which in turn is inside the UIScrollView. Then return your UIView as the view to zoom. Since the image views are inside it, they'll get zoomed as well.