Manipulating UIImage with GestureRecognizers - NOT UIImageView - objective-c

Using Gesture Recognizers such as Pan, Pinch, and Rotate, is it at all possible to manipulate a UIImage within a UIImageView?
Whenever I try to define the gesture recognizer for the UIImage, I get an error stating
No visible UIImage supports 'addGestureRecognizer:'
With this code:
UIRotationGestureRecognizer *rotateGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotateImage:)];
[imageStrip addGestureRecognizer:rotateGesture];
Is there anyway to transform a UIImage inside of a UIImageView without transforming the UIImageView?

You could add gesture recognizers to any kind of UIView and in your event handling methods you could redraw UIImage to a freshly created CGContextRef. This context would have to be transformed with CGContextRotateCTM, CGContextScaleCTM , CGContextTranslateCTM that would be called with parameters matching the ones you receive from gesture recognizers. That's the hard way.
Alternatively you could create a scratch UIImageView with your image, transform it with gesture recognizers then render it to a CGContextRef and get the transformed image out of this context.
Certainly doable, but not trivial.

UIGestureRecognizer can only be attached to a UIView - UIImage is not a descendant of UIView.
This is because an UIView (or it's descendant) can receive touches - UIImage can not.
Please see Gestures Recognizers Are Attached to a View.
You could recalculate/rerender the UIImage based on parameters after user is done manipulating it. On how to do this: it really depends on what you want to achieve.

Related

CG drawing in UIView to use as UIImage?

I am attempting to create a custom back button for UINavigationbar.
I know that I can use backButtonBackgroundImageForState:barMetrics: to set an image.
My problem is that I don't what to have to use an image file. I would rather draw the back button in code.
My question is can I subclass a UIView, override the drawrec: to draw a back button, then use that UIView as an image in backButtonBackgroundImageForState:barMetrics:
I'm thinking that it would go something like:
UIImage * backButtonImage = // somehow get image from my subclassed UIView;
backButtonImage = [backButtonImage stretchableImageWithLeftCapWidth: 15.0 topCapHeight: 30.0];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage: backButtonImage forState: UIControlStateNormal barMetrics: UIBarMetricsDefault];
Is this even the best way to be going about this? Or should I just suck it up and create an image in photoshop?
You can create a bitmap image context using UIGraphicsBeginImageContextWithOptions, without making a subclass of UIView and overriding drawRect:.
UIGraphicsBeginImageContextWithOptions(CGSizeMake(80, 30), NO, 0);
// drawing code here
// Use UIKit functions and objects
// or use CoreGraphics functions on UIGraphicsGetCurrentContext()
UIImage *image = UIGraphicsGetImageFromCurrentContext();
UIGraphicsEndImageContext();

How to dynamically reposition UIButton as subview of UIImageView when rotating

I'm working on an iPad app that lets you control different things in a prototype of an intelligent house. For example it lets you turn lights on and off. For this, I have made a UIImageView that shows the floor plan of the house and added UIButtons as subviews for each lamp that can be toggled.
As you can see the buttons are placed perfectly on the floor plan using the setFrame method of each UIButton. However, when I rotate the iPad to portrait orientation, the following happens:
The buttons obviously still have the same origin, however it is not relative to the repositioning of the image.
The floor plan image has the following settings for struts and springs:
and has its content mode set to Aspect Fit.
My question is
how do I dynamically reposition each UIButton, such that it has the same relative position. I figure I have to handle this in the {did/should}AutorotateToInterfaceOrientation delegate method.
It should be noted that the UIImageView is zoomable and to handle this I have implemented the scrollViewDidZoom delegate method as follows:
for (UIView *button in _floorPlanImage.subviews) {
CGRect oldFrame = button.frame;
[button.layer setAnchorPoint:CGPointMake(0.5, 1)];
button.frame = oldFrame;
button.transform = CGAffineTransformMakeScale(1.0/scrollView.zoomScale, 1.0/scrollView.zoomScale);
}
Thank you in advance!
I find the best way to layout subviews is in the - (void) layoutSubviews method. You will have to subclass your UIImageView and override the method.
This method will automatically get called whenever your frame changes and also gets called the first time your view gets presented.
If you put all your layout code in this method, it prevents layout fragmentation and repetition, keeps your view code in your views, and most things just work by default.

drawRect cropping in UIScrollView

I am trying to draw an image into a UIView subclass overriding drawRect. This image drawing class needs moving and zooming capabilities, so I made it a subview of a scrollview.
My problem is that my image seems to get cropped, either by the screen or the scrollView bounds (the image is larger than the screen). I don't have a clue why, this seems to e pretty basic stuff.
The view hierarchy in my ViewController looks like this:
View Controller
View
ScrollView
MapView
I've created outlets for the scroll view and the map view (which is the view implementing drawRect).
My ViewController initial setup code for configuring the scroll view looks like this:
- (void)viewDidLoad
{
[super viewDidLoad];
scrollView.delegate = self;
scrollView.contentSize = CGSizeMake(450, 476);
scrollView.minimumZoomScale = 0.6;
scrollView.maximumZoomScale = 6.0;
}
My drawRect implementation:
- (void)drawRect:(CGRect)rect
{
UIImage *myImage = [UIImage imageNamed:#"map.jpg"];
CGRect rectangle = CGRectMake(0,0,450,476);
[myImage drawInRect:rectangle];
}
The image is draggable and zoomable within the scroll view, but it's cropped at what seems to be the screen bounds.
Any help would be appreciated.
drawRect can't draw outside the rect passed as a parameter to the drawRect method, which is determined by the view frame. Make your view 1200,1200 and make sure that clipToBounds is disabled on all the containing views.
Your problem is probably in drawInRect. From documentation of UIImage class:
drawInRect:
Draws the entire image in the specified rectangle, scaling it as needed to fit.
Make that rectangle bigger as you wish and center it in UIImageView. This question is answered here UIScrollView image/photo viewer with paging enabled and zooming

Why is this UIImageView autocentering itself?

I have a UIImageView in a UIScrollView in another UIScrollView (based on Apple's
PhotoScroller sample code). When the UIScrollView calls back to its controller to dismiss itself, it calls this method:
- (void)dismiss {
[scrollView removeFromSuperview];
ImageScrollView *isv = [self currentImageScrollView];
UIImage *image = isv.imageView;
image.frame = [self.view convertRect:image.frame fromView:isv];
[self.view insertSubview:image belowSubview:captionView];
[(NSObject *)delegate performSelector:#selector(scrollViewDidClose:)
withObject:self
afterDelay:2.0];
}
Now here's the weird part: the image view jumps to a different position right after this method executes, but before the scollViewDidClose method gets called on the delegate. If the image is larger than its new super view, it jumps so that its left edge is aligned with the left edge of its super view. If it's smaller than its new super view, it jumps to the very center of the view. There is no animation to this change.
So my question is, how do I prevent it from doing that? I've tweaked both the super view (self.view) class and the image view class to see what methods might be called. Neither the frame nor the center is set on the image view after this method is called, and while the layoutSubviews method is called on the super view, that is not what jumps the image to the center or left side of the superview. I've also tried turning off autoResizesSubviews in the super view, and setting the autoresizingMask of the image view to UIViewAutoresizingNone, with no change in behavior.
So where is this happening, and why? And more importantly, how do I make it stop?
I've been beating my head on this for days. Any pointers or guidance would be greatly appreciated.
ImageScrollView is the one centering your UIImageView. Set a breakpoint in ImageScrollView layoutSubviews and you'll see how your UIImageView is being centered.
You're taking ImageScrollView's internal imageView and placing it into another view. That's not going to work because ImageScrollView still retains ownership of that UIImageView instance and is still managing its layout.
You'll either need to copy the image into another UIImageView instance, or you'll need to change ImageScrollView to allow it to relinquish ownership of its imageView.
You're not setting up the frame of the 'image' view when you insert it as a subview. You probably want to do that explicitly if you want the view to appear at a particular position in the scroll view.

How do you position a larger NSImage inside of a smaller NSImageView programmatically?

Let's say I have an NSImage that's 100x100. I also have an NSImageView that's 50x50. Is there a way I can place the NSImage at coordinates inside the NSImageView, so I can control which part of it shows? It didn't seem like NSImage had an initWithFrame method...
I did this in my NSImageView subclass, as Andrew suggested.
- (void)drawRect:(NSRect)rect
{
[super drawRect:rect];
NSRect cropRect = NSMakeRect(x, y, w, h);
[image drawAtPoint:NSZeroPoint
fromRect:cropRect
operation:NSCompositeCopy
fraction:1];
}
I don't believe so, but it's trivial to roll your own NSImageView equivalent that supports center/stretch options by drawing the image yourself.
Make your imageview as big as the image, and put it inside a scrollview. Hide the scrollers if you want. No need for subclassing in this case.
NSImageView has a method -setImageAlignment: which lets you control how the image is aligned within the image view. Unfortunately, if you want to display part of the image that doesn't correspond to any of the NSImageAlignment values, you're going to have to draw the image programmatically.
Depends on what your eventual goal is but the easiest thing to me seems to put your NSImageView inside an NSView (or a subclass – doesn't have to be NSScrollView as "#NSResponder" user suggests but this should work well too), set its imageScaling to NSImageScaleProportionallyUpOrDown and its frameSize to image's size. Then you can move your NSImageView freely around the upper view using setFrame:myDesiredFrame. No subclassing, no manual redrawing, etc.