Stop drawing of CATiledLayer - objective-c

Is is possible to stop CATiledLayer to draw (drawLayer:inContext)?
It draws asynchronously and when i try to release CGPDFDocumentRef, which is used by CATiledLayer, the app crashes (EXC_BAD_ACCESS).
That's my view:
#implementation TiledPDFView
- (id)initWithFrame:(CGRect)frame andScale:(CGFloat)scale{
if ((self = [super initWithFrame:frame])) {
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
tiledLayer.levelsOfDetail = 4;
tiledLayer.levelsOfDetailBias = 4;
tiledLayer.tileSize = CGSizeMake(512.0, 512.0);
myScale = scale;
}
return self;
}
// Set the layer's class to be CATiledLayer.
+ (Class)layerClass {
return [CATiledLayer class];
}
- (void)stopDrawing{
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
[tiledLayer removeFromSuperlayer];
tiledLayer.delegate = nil;
}
// Set the CGPDFPageRef for the view.
- (void)setPage:(CGPDFPageRef)newPage
{
CGPDFPageRelease(self->pdfPage);
self->pdfPage = CGPDFPageRetain(newPage);
//self->pdfPage = newPage;
}
-(void)drawRect:(CGRect)r
{
}
// Draw the CGPDFPageRef into the layer at the correct scale.
-(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context
{
// First fill the background with white.
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context,self.bounds);
CGContextSaveGState(context);
// Flip the context so that the PDF page is rendered
// right side up.
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// Scale the context so that the PDF page is rendered
// at the correct size for the zoom level.
CGContextScaleCTM(context, myScale,myScale);
CGContextDrawPDFPage(context, pdfPage);
CGContextRestoreGState(context);
}
// Clean up.
- (void)dealloc {
CGPDFPageRelease(pdfPage);
[super dealloc];
}
And this is where i try to stop and release PDF in view controller:
v is instance of TiledPDFView
-(void) stopDwaring {
[v stopDrawing];
[v removeFromSuperview];
[v release];
[self.view removeFromSuperview];
self.view = nil;
CGPDFDocumentRelease(pdf);
}

this post helped me solving my own trouble with CATiledLayer. I used TiledPDFview.m from Apple's documentation as example.
Since I need to redraw the entire view and all tiles at some point, I use a CATiledLayer as property.
When exiting and deallocating the viewcontroller, it crashed with [CATiledLayer retain]: Message sent to deallocated instance.
Here is my dealloc method of the view controller:
- (void)dealloc {
self.tiledLayer.contents=nil;
self.tiledLayer.delegate=nil;
[self.tiledLayer removeFromSuperlayer];
// note: releasing the layer still crashes-
// I guess removeFromSuperlayer releases it already,
// but couldn't find documentation so far.
// So that's why it's commented out:
// [self.tiledLayer release], self.tiledLayer=nil;
//release the other viewcontroller stuff...
[super dealloc];
}
That works for me. Hope it helps someone.

Remove the CATiledLayer from its superlayer before releasing the CGPDFDocumentRef.
[yourTiledLayer removeFromSuperlayer];
Dont forget to set it's delegate to nil too.
yourTiledLayer.delegate = nil;
After that, you can safely release your CGPDFDocumentRef.
Edit after OP adds code:
Did you get pdfPage using CGPDFDocumentGetPage()? If so, you shouldn't release it, it is an autoreleased object.
Regarding how to add it as sublayer:
You don't actually need TiledPDFView. In your view controller, you can simply do this:
CATiledLayer *tiledLayer = [CATiledLayer layer];
tiledLayer.delegate = self; //sets where tiledLayer will look for drawLayer:inContext:
tiledLayer.tileSize = CGSizeMake(512.0f, 512.0f);
tiledLayer.levelsOfDetail = 4;
tiledLayer.levelsOfDetailBias = 4;
tiledLayer.frame = CGRectIntegral(CGRectMake(0.0f, 0.0f, 512.0f, 512.0f));
[self.view.layer addSublayer:tiledLayer];
Then move your drawLayer:inContext: implementation to your view controller.
Then in your view controller's dealloc, release it as:
[tiledLayer removeFromSuperlayer];
tiledLayer.delegate = nil;
CGPDFDocumentRelease(pdf);
Note that you can't do this on a UIView subclass, as the drawLayer:inContext: will conflict with the UIView's main layer.

object.layer.contents = Nil
This should wait for the thread to finish. It helped in my case.

TiledPDFView *pdfView;
In dealloc of pdfView's superview class, write below line of codes.
- (void)dealloc {
if (nil != self.pdfView.superview) {
self.pdfView.layer.delegate = nil;
[self.pdfView removeFromSuperview];
}
}
This works for me. Hope it will help.

I've had a similar problem.
I ended up setting a float variable "zoom" in my TiledPDFView which I set as the zoomScale of PDFScrollView in the UIScrollview Delegate method: scrollViewDidZoom
Then in my drawLayer method inside TiledPDFView I only called the contents of that method if the float variable "zoom" was above 2.
This fixes any issues of someone leaving the view without zooming. It may not be ideal for your case as this error still occurs if someone zooms above 2 then releases the viewcontroller quickly, but you might be able to find a similar technique to cover all bases.

It looks like you're doing the same thing I am, which is borrowing the ZoomingPDFView code and integrating it into your project. If your UIScrollView delegate methods in PDFScrollView are unchanged, you can solve your problem by just commenting both lines of your dealloc method (in TiledPDFView). That stuff should only be happening when you kill the parent view, anyway.

Related

EXC_BAD_ACCESS while addingAnnotations in MKMapView

I am calling a web service that returns an array of annotations in a delegate method that I am adding to my map with the addAnnotations method for a MKMapView. Everything goes swimmingly until the delegate method send two arrays in quick succession (usually about 150ms - 500ms) and then I get a EXC_BAD_ACCESS (code=1 address 0x20) on this line [kMap addAnnotations:tileArray]; - This appears to be a memory issue but i am not really sure what to do how it or how to change my code to address it.
Here is the delegate method
-(void)rebelBaseManager:(RebelBaseManager *)manager didUpdateTileHour:(NSArray *)tileArray boundaryBreak:(NSString *)breakType atTileLevel:(int)callTileLevel {
if (timerStarted == NO) {
[self startTimer];
}
//Check for tileLevel in case multiple calls were made at different tile levels
if (tileLevel == callTileLevel) {
[kMap addAnnotations:tileArray];
[HourInMap addObjectsFromArray:tileArray];
}
}
I also added a method to allow me to animate the removal of annotations which is below in case it makes a difference:
- (void)removeAnnotationsWithFade:(NSArray *)annotations animated:(BOOL)shouldAnimate {
if (!shouldAnimate)
[self removeAnnotations:annotations];
else {
for (HourAnnotation *annotation in annotations) {
MKAnnotationView *annotationView = [self viewForAnnotation:annotation];
[UIView animateWithDuration:2
animations:^{
annotationView.alpha =0;
}completion:^(BOOL finished) {
[self removeAnnotation:annotation];
}];
}
}
--------- ADDITION ---------
Adding in my code from a custom annotation in response to #3 in Rob's answer below.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (id)initWithHourAnnotation:(HourAnnotation *)hourAnnotation reuseIdentifier:(NSString *)reuseIdentifier {
CGRect myFrame = self.frame;
myFrame.size.width = hourAnnotation.frameSize;
myFrame.size.height = hourAnnotation.frameSize;
self = [super initWithFrame:myFrame];
//When I use this here I seem to get the frame and color of the old annotation displayed
//self = [super initWithAnnotation:velocityAnnotation reuseIdentifier:reuseIdentifier];
if (self)
{
self.layer.cornerRadius = self.frame.size.width / 2;
self.clipsToBounds = YES;
[self setBackgroundColor:[UIColor clearColor]];
NSArray *alphaValue = [[NSArray alloc]initWithArray:[self alphaForTileLevel]];
self.fillColor = [UIColor colorWithHue:hourAnnotation.color saturation:.06 brightness:.23 alpha:[[alphaValue objectAtIndex:hourAnnotation.tileLevel-1]doubleValue]];
self.strokeColor = [UIColor colorWithHue:hourAnnotation.color saturation:.06 brightness:.23 alpha:.35];
self.enabled = NO;
self.canShowCallout = NO;
self.userInteractionEnabled = NO;
}
return self;
}
- (void)drawRect:(CGRect)rect
{
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
[self.fillColor set];
[path fill];
[self.strokeColor set];
[path setLineWidth:2.0f];
[path stroke];
}
A couple of thoughts.
You're not showing where you are calling this removeAnnotationsWithFade. We have to assume that you're removing the appropriate model objects. use instruments to confirm that you're not leaking anywhere.
If the app is crashing when removing with fade, but not crashing without fade, then you might consider refactoring this code. Specifically, your animation code isn't actually removing the old animations until the completion block (i.e. two seconds later). Thus you are holding on to old annotations longer than needed. And if the annotations are coming in fast and furious, you could run into memory problems.
If you, for example, used transitionWithView:mapView instead, just removing the annotations directly, you might be freeing memory associated with the old annotations more quickly. It's a less elegant animation, but might be "good enough" and minimize your peak memory usage:
// this will immediately remove the annotations, but animate the fading of the transition
[UIView transitionWithView:self.mapView duration:2.0 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
[self.mapView removeAnnotations:annotations];
} completion:nil];
// presumably remove the annotations from your array, too
Is your viewForAnnotation dequeuing old annotation views and only instantiating new ones if it couldn't dequeue an old one, or is it always instantiating new ones? Stuff like that might help control peak memory usage, too.

CALayer doesn't display

(I already read this page, but it didn't help me CALayer not displaying)
I have a class called Image that has this field data:
uint8_t *data;
I already use this data to display this Image on a CALayer that I got gathering code from the internet.
I saw how to create another windows in my application, and I put a NSView inside it to display an Image using the method, I intend to display the histogram latter, but now I'm just trying to display again the same image:
-(void)wrapImageToCALayer: (CALayer*) layer{
if(!layer) return;
CGColorSpaceRef grayColorSpace = CGColorSpaceCreateDeviceGray();
CGContextRef context = CGBitmapContextCreate(data, width, height, 8, step, grayColorSpace, kCGImageAlphaNone);
CGImageRef dstImage = CGBitmapContextCreateImage(context);
dispatch_sync(dispatch_get_main_queue(), ^{
layer.contents = (__bridge id)dstImage;
});
CGImageRelease(dstImage);
CGContextRelease(context);
CGColorSpaceRelease(grayColorSpace);
}
And this is my Window Controler:
#implementation HistogramControllerWindowController
#synthesize display;
- (id)initWithWindow:(NSWindow *)window{
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
return self;
}
- (void)windowDidLoad
{
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
[super windowDidLoad];
histogramDisplayLayer = [CALayer layer];
[histogramDisplayLayer setContentsGravity:AVLayerVideoGravityResizeAspectFill];
histogramDisplayLayer.position = CGPointMake(display.frame.size.width/2., display.frame.size.height/2.);
[histogramDisplayLayer setNeedsDisplay];
[display.layer addSublayer: histogramDisplayLayer];
}
#end
And I'm calling this way:
[frame wrapImageToCALayer:histogramDisplayLayer];
Note that histogramDisplayLayer is an external (CALayer *)
Your histogramDisplayLayer have no size defined, you just set a position but not its size. So init its frame, and this should fix your problem.
Just insert:
[display setWantsLayer:YES]; // view's backing store is using a Core Animation Layer
Before:
[display.layer addSublayer: histogramDisplayLayer];
[ Best way to change the background color for an NSView ]

Disabling NSView fade animation for NSView `setHidden:`

I am working on a project that has the concept of draggable controls, everything is working fine except that NSView seems to employ a fade in/out animation when calling setHidden:.
I have been able to work around the problem by changing the line session.animatesToStartingPositionsOnCancelOrFail = YES; to NO and implementing the image snapback myself with a custom animated NSWindow subclass. it looks great, but I know there must be an easier way.
I have tried:
using NSAnimationContext grouping with duration of 0 around the setHidden: calls
setting the view animations dictionary using various keys (alpha, hidden, isHidden) on the control and superview
overriding animationForKey: for both the control and its superview
I am not using CALayers and have even tried explicitly setting wantsLayer: to NO.
Does anybody know how to either disable this animation, or have a simpler solution then my animated NSWindow?
here is my stripped down altered code with the bare minimum to see what I'm talking about.
#implementation NSControl (DragControl)
- (NSDraggingSession*)beginDraggingSessionWithDraggingCell:(NSActionCell <NSDraggingSource> *)cell event:(NSEvent*) theEvent
{
NSImage* image = [self imageForCell:cell];
NSDraggingItem* di = [[NSDraggingItem alloc] initWithPasteboardWriter:image];
NSRect dragFrame = [self frameForCell:cell];
dragFrame.size = image.size;
[di setDraggingFrame:dragFrame contents:image];
NSArray* items = [NSArray arrayWithObject:di];
[self setHidden:YES];
return [self beginDraggingSessionWithItems:items event:theEvent source:cell];
}
- (NSRect)frameForCell:(NSCell*)cell
{
// override in multi-cell cubclasses!
return self.bounds;
}
- (NSImage*)imageForCell:(NSCell*)cell
{
return [self imageForCell:cell highlighted:[cell isHighlighted]];
}
- (NSImage*)imageForCell:(NSCell*)cell highlighted:(BOOL) highlight
{
// override in multicell cubclasses to just get an image of the dragged cell.
// for any single cell control we can just make sure that cell is the controls cell
if (cell == self.cell || cell == nil) { // nil signifies entire control
// basically a bitmap of the control
// NOTE: the cell is irrelevant when dealing with a single cell control
BOOL isHighlighted = [cell isHighlighted];
[cell setHighlighted:highlight];
NSRect cellFrame = [self frameForCell:cell];
// We COULD just draw the cell, to an NSImage, but button cells draw their content
// in a special way that would complicate that implementation (ex text alignment).
// subclasses that have multiple cells may wish to override this to only draw the cell
NSBitmapImageRep* rep = [self bitmapImageRepForCachingDisplayInRect:cellFrame];
NSImage* image = [[NSImage alloc] initWithSize:rep.size];
[self cacheDisplayInRect:cellFrame toBitmapImageRep:rep];
[image addRepresentation:rep];
// reset the original cell state
[cell setHighlighted:isHighlighted];
return image;
}
// cell doesnt belong to this control!
return nil;
}
#pragma mark NSDraggingDestination
- (void)draggingEnded:(id < NSDraggingInfo >)sender
{
[self setHidden:NO];
}
#end
#implementation NSActionCell (DragCell)
- (void)setControlView:(NSView *)view
{
// this is a bit of a hack, but the easiest way to make the control dragging work.
// force the control to accept image drags.
// the control will forward us the drag destination events via our DragControl category
[view registerForDraggedTypes:[NSImage imagePasteboardTypes]];
[super setControlView:view];
}
- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp
{
BOOL result = NO;
NSPoint currentPoint = theEvent.locationInWindow;
BOOL done = NO;
BOOL trackContinously = [self startTrackingAt:currentPoint inView:controlView];
BOOL mouseIsUp = NO;
NSEvent *event = nil;
while (!done)
{
NSPoint lastPoint = currentPoint;
event = [NSApp nextEventMatchingMask:(NSLeftMouseUpMask|NSLeftMouseDraggedMask)
untilDate:[NSDate distantFuture]
inMode:NSEventTrackingRunLoopMode
dequeue:YES];
if (event)
{
currentPoint = event.locationInWindow;
// Send continueTracking.../stopTracking...
if (trackContinously)
{
if (![self continueTracking:lastPoint
at:currentPoint
inView:controlView])
{
done = YES;
[self stopTracking:lastPoint
at:currentPoint
inView:controlView
mouseIsUp:mouseIsUp];
}
if (self.isContinuous)
{
[NSApp sendAction:self.action
to:self.target
from:controlView];
}
}
mouseIsUp = (event.type == NSLeftMouseUp);
done = done || mouseIsUp;
if (untilMouseUp)
{
result = mouseIsUp;
} else {
// Check if the mouse left our cell rect
result = NSPointInRect([controlView
convertPoint:currentPoint
fromView:nil], cellFrame);
if (!result)
done = YES;
}
if (done && result && ![self isContinuous])
[NSApp sendAction:self.action
to:self.target
from:controlView];
else {
done = YES;
result = YES;
// this bit-o-magic executes on either a drag event or immidiately following timer expiration
// this initiates the control drag event using NSDragging protocols
NSControl* cv = (NSControl*)self.controlView;
NSDraggingSession* session = [cv beginDraggingSessionWithDraggingCell:self
event:theEvent];
// Note that you will get an ugly flash effect when the image returns if this is set to yes
// you can work around it by setting NO and faking the release by animating an NSWindowSubclass with the image as the content
// create the window in the drag ended method for NSDragOperationNone
// there is [probably a better and easier way around this behavior by playing with view animation properties.
session.animatesToStartingPositionsOnCancelOrFail = YES;
}
}
}
return result;
}
#pragma mark - NSDraggingSource Methods
- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
{
switch(context) {
case NSDraggingContextOutsideApplication:
return NSDragOperationNone;
break;
case NSDraggingContextWithinApplication:
default:
return NSDragOperationPrivate;
break;
}
}
- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation
{
// now tell the control view the drag ended so it can do any cleanup it needs
// this is somewhat hackish
[self.controlView draggingEnded:nil];
}
#end
There must be a layer enabled somewhere in your view hierarchy, otherwise there wouldn't be a fade animation. Here is my way of disabling such animations:
#interface NoAnimationImageView : NSImageView
#end
#implementation NoAnimationImageView
+ (id)defaultAnimationForKey: (NSString *)key
{
return nil;
}
#end
The solution you already tried by setting the view animations dictionary should work. But not for the keys you mention but for the following. Use it somewhere before the animation is triggered the first time. If you have to do it on the window or view or both, I don't know.
NSMutableDictionary *animations = [NSMutableDictionary dictionaryWithDictionary:[[theViewOrTheWindow animator] animations];
[animations setObject:[NSNull null] forKey: NSAnimationTriggerOrderIn];
[animations setObject:[NSNull null] forKey: NSAnimationTriggerOrderOut];
[[theViewOrTheWindow animator] setAnimations:animations];
Or also just remove the keys if they are there (might not be the case as they are implicit / default):
NSMutableDictionary *animations = [NSMutableDictionary dictionaryWithDictionary:[[theViewOrTheWindow animator] animations];
[animations removeObjectForKey:NSAnimationTriggerOrderIn];
[animations removeObjectForKey:NSAnimationTriggerOrderOut];
[[theViewOrTheWindow animator] setAnimations:animations];
Ok. I figured out that the animation I'm seeing is not the control, the superview, nor the control's window. It appears that animatesToStartingPositionsOnCancelOrFail causes NSDraggingSession to create a window (observed with QuartzDebug) and put the drag image in it and it is this window that animates back to the origin and fades out before the setHidden: call is executed (i.e. before the drag operation is concluded).
Unfortunately, the window that it creates is not an NSWindow so creating a category on NSWindow doesn't disable the fade animation.
Secondly, there is no public way that I know of to get a handle on the window, so I can't attempt directly manipulating the window instance.
It looks like maybe my workaround is the best way to do this, after all its not far from what AppKit does for you anyway.
If anybody knows how to get a handle on this window, or what class it is I would be interested to know.

Making fading effect with NSView

I have a subview which I want to show up to make a dissolve fade effect. For that purpose, I'm trying to:
Show it
Fade it
For that purpose, I set a "showFade" method on my superview (Fader is the subview defined previously. someImage is already defined).
- (void)showFade {
[Fader setHidden:NO];
[Fader setImage: someImage];
[[Fader animator] setHidden:YES];
}
The problem is: it works for the first time, and the subview fades, but it never shows up again. What am I doing wrong?
EDIT: As requested, here is a more complete sample of the code
- (void)awakeFromNib {
Fader = [[NSImageView alloc] initWithFrame: [self bounds]];
Images[0] = #"Tower";
Images[1] = #"Desert";
Images[2] = #"Fall";
image = 0;
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(showFader:) userInfo:nil repeats:YES];
[self addSubview: Fader];
[self nextImage];
[super awakeFromNib];
}
- (void)showFader: (NSTimer*)timer {
[Fader setImage: [self image]];
[Fader setHidden:NO];
[[Fader animator] setHidden:YES];
[self nextImage];
}
- (void)nextImage {
if (image == 2) {
image = 0;
}
else {
image++;
}
[self setImage: [NSImage imageNamed: Images[image]]];
}
So, basically, I have a repeating timer making the parent NSImageView to loop between an array of images and making the "Fader" to show up with the previous image and fade out. The problem is that it only shows up once.
I had the same problem a long time ago (yet I never solved it), but I think this could have something to do with the fact that you ask your view to be showed and dissolved in the same event loop.
I guess you should let the event loop make an iteration (so that your view is actually displayed) before asking to dissolve the view.
I guess you could do it by setting a (short) timer that would fire a couple of millisecs after the view is displayed (by setHidden:NO).
I also guess there are easier ways to solve the problem (and I would be glad to hear them).
What is more, you could also try animator setAlphaValue:0.0 to have a smooth transition.
I am not definitely not sure I have a correct answer, that's only a suggestion...
hidden is a BOOL property, you can't animate BOOLs cause there are only two discrete values: NO and YES. Use the opacity instead, you can animate opacity from 1.0 (fully visible) to 0.0 (invisible) to fade out. And hidden has to be set to NO for this to work, otherwise it will be hidden all the time.
This method works perfectly for me to make an image start out blurry and smoothly come into focus.
let parentView = UIView()
let imageView = UIImageView(image: UIImage(named: "ImageToFocus"))
parentView.addSubview(imageView)
let blurEffect = UIBlurEffect(style: .Light)
let blurView = UIVisualEffectView(effect: blurEffect)
blurView.frame = imageView.frame
blurView.alpha = 1.0
self.blurTimer = NSTimer.scheduledTimerWithTimeInterval(0.05, target: self, selector: Selector("animateBlur:"), userInfo: blurView, repeats: true)
func animateBlur(timer:NSTimer)
{
let blurView = timer.userInfo as UIVisualEffectView
if blurView.alpha > 0.0 {
blurView.alpha -= 0.05
} else {
blurTimer = nil
}
}

CGAffineTransformMakeScale Makes UIView Jump to Original Size before scale

I have a UIView that I set up to respond to pinch gestures and change its size, except, once you enlarge it and then try and pinch it again, it jumps to its original size (which just so happens to be 100x200). Here is the code:
#implementation ChartAxisView
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// do gesture recognizers
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(onPinch:)];
[self addGestureRecognizer:pinch];
[pinch release];
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorRef redColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0].CGColor;
CGContextSetFillColorWithColor(context, redColor);
CGContextFillRect(context, self.bounds);
}
- (void)onPinch: (UIPinchGestureRecognizer*) gesture {
self.transform = CGAffineTransformMakeScale(gesture.scale, gesture.scale);
}
- (void)dealloc {
[super dealloc];
}
#end
Any thoughts?
So there are two types of Scale (or, transform in general) functions: CGAffineTransformMakeScale and CGAffineTransformScale
The first one, CGAffineTransformMakeScale which you are using, always transforms with respect to the image's original size. And that is why you see the jump to its original size before the scaling happens.
The second one, CGAffineTransformScale, transforms from the image's current position. This is what you need. For this, it requires an additional 'transform' arg. The 'transform' arg in your case represents the enlarged image.
Read this very informative blog post about transformations.
- (void)onPinch: (UIPinchGestureRecognizer*) gesture {
if ([gesture state] == UIGestureRecognizerStateBegan)
{
curTransform = self.transform;
}
self.transform = CGAffineTransformScale(curTransform,gesture.scale, gesture.scale);
}
use CGAffineTransformScale instead of CGAffineTransformMakeScale
you will need a member -> CGAffineTransform curTransform;
;)
you can set transform with following code:
ivClone setTransform:CGAffineTransformMakeScale(scale, scale)];
and you can get current transform with following coee:
newV.transform.a //x scale
newV.transform.d // y scale