Reputation: 7789
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.'
Upvotes: 0
Views: 2081
Reputation: 50089
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"];
}
Upvotes: 1
Reputation: 46578
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
Upvotes: 1