0

I m implementing one photo edit app in which i have one camera captured image and one is output image which is i shown beside of captured image. Whenever i change my captured image scaling or rotation then i want notify this changes to outputImage. For this senario i written following code,

-(void)imageCapturedByCameraAPI:(UIImage *)theCapturedImage{
[[arOverlayVC view] removeFromSuperview];
capturedImageView.image = theCapturedImage;
[self.view addSubview:imageEditorView];

rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotatePiece:)];
[capturedImageView addGestureRecognizer:rotationGesture];
**//KVO added for rotation**
[capturedImageView addObserver:self forKeyPath:@"transform.rotate" options:NSKeyValueObservingOptionNew context:NULL];

pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(scalePiece:)];
[pinchGesture setDelegate:self];
[capturedImageView addGestureRecognizer:pinchGesture];
**//KVO added for scaling**
[capturedImageView addObserver:self forKeyPath:@"transform.scale" options:NSKeyValueObservingOptionNew context:NULL];

}

   - (void)rotatePiece:(UIRotationGestureRecognizer *)gestureRecognizer {
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];

if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
    [gestureRecognizer view].transform = CGAffineTransformRotate([[gestureRecognizer view] transform], [gestureRecognizer rotation]);
    [self editedImage];
    [gestureRecognizer setRotation:0];
}
 }

- (void)scalePiece:(UIPinchGestureRecognizer *)gestureRecognizer {
if([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
    lastScale = [gestureRecognizer scale];
}

if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ||
    [gestureRecognizer state] == UIGestureRecognizerStateChanged) {

    CGFloat currentScale = [[[gestureRecognizer view].layer valueForKeyPath:@"transform.scale"] floatValue];

    const CGFloat kMaxScale = 2.0;
    const CGFloat kMinScale = 1.0;

    CGFloat newScale = 1 -  (lastScale - [gestureRecognizer scale]);
    newScale = MIN(newScale, kMaxScale / currentScale);
    newScale = MAX(newScale, kMinScale / currentScale);
    CGAffineTransform transform = CGAffineTransformScale([[gestureRecognizer view] transform], newScale, newScale);
    [gestureRecognizer view].transform = transform;
    lastScale = [gestureRecognizer scale];  
}
 }

  - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
// if the gesture recognizers are on different views, don't allow simultaneous recognition

if (gestureRecognizer.view != otherGestureRecognizer.view)
    return NO;

// if either of the gesture recognizers is the long press, don't allow simultaneous recognition
if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] || [otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]])
    return NO;

return YES;
   }

 - (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer    {
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
    UIView *piece = gestureRecognizer.view;
    CGPoint locationInView = [gestureRecognizer locationInView:piece];
    CGPoint locationInSuperview = [gestureRecognizer locationInView:piece.superview];

    piece.layer.anchorPoint = CGPointMake(locationInView.x / piece.bounds.size.width, locationInView.y / piece.bounds.size.height);
    piece.center = locationInSuperview;
}
  }

Now my observation method is,

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
   {
if(object == capturedImageView && ([keyPath isEqualToString:@"transform.rotate"] || [keyPath isEqualToString:@"transform.scale"])) {
    NSLog(@"KVO detected");
     }
  }

I found following error,

An instance 0x189f40 of class NSConcreteValue was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info: ( Context: 0x0, Property: 0x189ff0>

An instance 0x188cb0 of class NSConcreteValue was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info: ( Context: 0x0, Property: 0x188d80>

* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Cannot update for observer for the key path "transform.scale" from , most likely because the value for the key "transform" has changed without an appropriate KVO notification being sent. Check the KVO-compliance of the UIImageView class.'

Bryan Chen
  • 45,816
  • 18
  • 112
  • 143
Tirth
  • 7,801
  • 9
  • 55
  • 88
  • There are more issues with your code, for example not checking the context when observing changes. In this answer I've compiled some rules of thumb of how to get KVO right: http://stackoverflow.com/questions/9231896/kvo-how-to-check-if-an-object-is-an-observer – Nikolai Ruhe Feb 26 '13 at 09:30

2 Answers2

1

as the error message said

An instance of class was deallocated while key value observers were still registered with it

you need to remove the observation before capturedImageView is deallocated using removeObserver:forKeyPath:context:

It is very important to make sure you have balance the count of register/deregister in order to not break KVO.

I you want to know more about KVO, this link is helpful: http://www.mikeash.com/pyblog/key-value-observing-done-right.html

and part2: http://www.mikeash.com/pyblog/friday-qa-2012-03-02-key-value-observing-done-right-take-2.html

Bryan Chen
  • 45,816
  • 18
  • 112
  • 143
  • you mean i want to create capturedImageView subclass of UIImageview and there i want to implement delloc method and remove my observe key in it. Bu i m using ARC so delloc method working in subclass of UIImageview in ARC? – Tirth Feb 26 '13 at 09:51
1

arc only handles memory for NSObject&Co, for everything else (e.g. KVO or network connections or file writes) you need to manually clean up

overwrite -(void)dealloc for that

- (void)dealloc {
    [capturedImageView removeObserver:self forKeyPath:@"transform.scale"];   
} 
Daij-Djan
  • 49,552
  • 17
  • 113
  • 135
  • you mean i want to create capturedImageView subclass of UIImageview and there i want to implement delloc method and remove my observe key in it. Bu i m using ARC so delloc method working in subclass of UIImageview in ARC? – Tirth Feb 26 '13 at 09:52
  • no the class that ADDS the observer, removes it. so in your VC's dealloc method where you are 'done' with the capturedImageView, you remove the observer – Daij-Djan Feb 26 '13 at 10:18