I have 10 UIImageViews in the same ViewController, and each one of these images need to be controlled with a Gesture Recognizer; this is my simple code:
- (void)viewDidLoad {
UIImageView *image1 = // image init
UIImageView *image2 = // image init
...
UIRotationGestureRecognizer *rotationGesture1 = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotatePiece:)];
UIRotationGestureRecognizer *rotationGesture2 = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotatePiece:)];
...
...
UIRotationGestureRecognizer *rotationGesture10 = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotatePiece:)];
[image1 addGestureRecognizer:rotationGesture1];
[image2 addGestureRecognizer:rotationGesture2];
...
...
[image10 addGestureRecognizer:rotationGesture10];
}
- (void)rotatePiece:(UIRotationGestureRecognizer *)gestureRecognizer {
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
[gestureRecognizer view].transform = CGAffineTransformRotate([[gestureRecognizer view] transform], [gestureRecognizer rotation]);
[gestureRecognizer setRotation:0];
}
}
Ok, all right, each image rotates, but I need to write similar code also for UIPanGestureRecognizer and UIPinchGestureRecognizer, obv for each UIImageView: is this the correct way, or there is a simpler method to avoid "redundant" code like this? Thanks!
Here's a possible solution. Make a method like so:
- (void)addRotationGestureForImage:(UIImageView *)image
{
UIRotationGestureRecognizer *gesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotatePiece:)];
gesture.delegate = self;
[image addGestureRecognizer:gesture];
}
Then in your viewDidLoad method create an array of image views and loop through them calling this method like so:
NSArray *imageViewArray = [NSArray arrayWithObjects:image1,image2,image3,nil];
for(UIImageView *img in imageViewArray) {
[self addRotationGestureForImage:img];
}
Related
I am using the zbarsdk to read the barcodes . It is working fine , but my problem is I have added the overlay view to the ZBarReaderViewController (reader in my code ). So now I tried to add the tap to focus functionality . But it is crashing . Below is my code . Thanks in advance . Any ideas would be grateful .
-(IBAction)scanBarCode:(id)sender
{
barcodeClicked = 1;
NSLog(#"TBD: scan barcode here...");
// ADD: present a barcode reader that scans from the camera feed
reader = [ZBarReaderViewController new];
reader.readerDelegate = self;
reader.supportedOrientationsMask = ZBarOrientationMaskAll;
ZBarImageScanner *scanner = reader.scanner;
// TODO: (optional) additional reader configuration here
// EXAMPLE: disable rarely used I2/5 to improve performance
[scanner setSymbology: ZBAR_I25
config: ZBAR_CFG_ENABLE
to: 0];
reader.showsZBarControls = NO;
UIView *ovlView = [[UIView alloc] init];
[ovlView setFrame:CGRectMake(0, 0, 320, 480)];
[ovlView setBackgroundColor:[UIColor clearColor]];
UIImageView *leftBracket = [[UIImageView alloc] init];
[leftBracket setFrame:CGRectMake(21, 100, 278, 15)];
[leftBracket setImage:[UIImage imageNamed:#"TopBracket.png"]];
UIImageView *rightBracket = [[UIImageView alloc] init];
[rightBracket setFrame:CGRectMake(21, 240, 278, 15)];
[rightBracket setImage:[UIImage imageNamed:#"BottomBracket.png"]];
UIToolbar *bottomBar = [[UIToolbar alloc] init];
[bottomBar setBarStyle:UIBarStyleBlackOpaque];
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && [UIScreen mainScreen].bounds.size.height * [UIScreen mainScreen].scale >= 1136)
{
[bottomBar setFrame:CGRectMake(0, 524, 320, 44)];
}
else
[bottomBar setFrame:CGRectMake(0, 436, 320, 44)];
UIBarButtonItem *cancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:#selector(cancelCamera)];
/*UIBarButtonItem *flexItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
target:nil
action:nil];*/
//UIBarButtonItem *infoButton = [[UIBarButtonItem alloc] initWithTitle:#" Info " style:UIBarButtonItemStyleBordered target:self action:#selector(infoButton)];
/*UIButton *info = [UIButton buttonWithType:UIButtonTypeInfoLight];
[info addTarget:self action:#selector(infoButton) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *infoButton = [[UIBarButtonItem alloc] initWithCustomView:info];
[bottomBar setItems:[NSArray arrayWithObjects:cancel,flexItem,infoButton, nil]];*/
[bottomBar setItems:[NSArray arrayWithObjects:cancel, nil]];
[ovlView addSubview:leftBracket];
[ovlView addSubview:rightBracket];
[ovlView addSubview:bottomBar];
reader.cameraOverlayView = ovlView;
// present and release the controller
[self presentModalViewController:reader
animated: YES];
[reader release];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UIView * previewView = [[[[[[[[[[
reader.view // UILayoutContainerView
subviews] objectAtIndex:0] // UINavigationTransitionView
subviews] objectAtIndex:0] // UIViewControllerWrapperView
subviews] objectAtIndex:0] // UIView
subviews] objectAtIndex:0] // PLCameraView
subviews] objectAtIndex:0]; // PLPreviewView
[previewView touchesBegan:touches withEvent:event];
}
Thanks for MacN00b's answer! That points out the right direction to go for me. I have implemented tap-focus for zbarViewController. Here is the idea:
You can add a custom view to zbarViewController by assigning a custom view to its cameraOverlayView. Then add a TapGestureRecagonizer to the custom view to catch the tap. Then, get the touch point and make the camera focus to the touch point. You would like possibly add a little rectangle around the touch point(that is what I did).
Here goes the code(assigning the custom view to cameraOverlayView:
UIView *view = [[UIView alloc] init];
UITapGestureRecognizer* tapScanner = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(focusAtPoint:)];
[view addGestureRecognizer:tapScanner];
reader.cameraOverlayView = view;
Then in selector focusAtPoint:
- (void)focusAtPoint:(id) sender{
CGPoint touchPoint = [(UITapGestureRecognizer*)sender locationInView:_reader.cameraOverlayView];
double focus_x = touchPoint.x/_reader.cameraOverlayView.frame.size.width;
double focus_y = (touchPoint.y+66)/_reader.cameraOverlayView.frame.size.height;
NSError *error;
NSArray *devices = [AVCaptureDevice devices];
for (AVCaptureDevice *device in devices){
NSLog(#"Device name: %#", [device localizedName]);
if ([device hasMediaType:AVMediaTypeVideo]) {
if ([device position] == AVCaptureDevicePositionBack) {
NSLog(#"Device position : back");
CGPoint point = CGPointMake(focus_y, 1-focus_x);
if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus] && [device lockForConfiguration:&error]){
[device setFocusPointOfInterest:point];
CGRect rect = CGRectMake(touchPoint.x-30, touchPoint.y-30, 60, 60);
UIView *focusRect = [[UIView alloc] initWithFrame:rect];
focusRect.layer.borderColor = [UIColor whiteColor].CGColor;
focusRect.layer.borderWidth = 2;
focusRect.tag = 99;
[_reader.cameraOverlayView addSubview:focusRect];
[NSTimer scheduledTimerWithTimeInterval: 1
target: self
selector: #selector(dismissFocusRect)
userInfo: nil
repeats: NO];
[device setFocusMode:AVCaptureFocusModeAutoFocus];
[device unlockForConfiguration];
}
}
}
}
}
I have added a white rectangle around the touch point, and then use selector dismissFocusRect to dismiss this rectangle. Here is the code:
- (void) dismissFocusRect{
for (UIView *subView in _reader.cameraOverlayView.subviews)
{
if (subView.tag == 99)
{
[subView removeFromSuperview];
}
}
}
I hope this could help!
Look at this documentation by apple in the "Focus Modes" section: https://developer.apple.com/library/ios/#documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/04_MediaCapture.html It talks all about how to implement tap to focus properly. I would try to implement this by
CGRect screenRect = [[UIScreen mainScreen] bounds];
screenWidth = screenRect.size.width;
screenHeight = screenRect.size.height;
double focus_x = thisFocusPoint.center.x/screenWidth;
double focus_y = thisFocusPoint.center.y/screenHeight;
[[self captureManager].videoDevice lockForConfiguration:&error];
[[self captureManager].videoDevice setFocusPointOfInterest:CGPointMake(focus_x,focus_y)];
Well if you are using that view controller, how about adding a (void) that should be ok to implement in the barcodeviewcontroller.
- (void) focusAtPoint:(CGPoint)point
{
AVCaptureDevice *device = [[self videoInput] device];
if ([device isFocusPointOfInterestSupported] && [device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
NSError *error;
if ([device lockForConfiguration:&error]) {
[device setFocusPointOfInterest:point];
[device setFocusMode:AVCaptureFocusModeAutoFocus];
[device unlockForConfiguration];
} else {
id delegate = [self delegate];
if ([delegate respondsToSelector:#selector(acquiringDeviceLockFailedWithError:)]) {
[delegate acquiringDeviceLockFailedWithError:error];
}
}
}
}
I'm developing a iOs 6 app for iPad. I've developed a UIImageView subclass adding gesture recognizing (zoom, pinch and pan), but I've a problem: it doesn't admit simultaneously gestures. I first code it as a normal UIImageView, and it worked great, but now with the subclass it doesn't. My code:
image.m (UIImageView subclass)
- (id)initWithImage:(UIImage *)image {
self = [super initWithImage:image];
if (self) {
[self setMultipleTouchEnabled:YES];
[self setUserInteractionEnabled:YES];
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panDetected:)];
[super addGestureRecognizer:panRecognizer];
UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(pinchDetected:)];
[super addGestureRecognizer:pinchRecognizer];
UIRotationGestureRecognizer *rotationRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotationDetected:)];
[super addGestureRecognizer:rotationRecognizer];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapDetected:)];
tapRecognizer.numberOfTapsRequired = 2;
[super addGestureRecognizer:tapRecognizer];
NSLog(#"lkjlkj");
}
return self;}
- (void)panDetected:(UIPanGestureRecognizer *)panRecognizer {
CGPoint translation = [panRecognizer translationInView:self.superview];
CGPoint imageViewPosition = self.center;
imageViewPosition.x += translation.x;
imageViewPosition.y += translation.y;
self.center = imageViewPosition;
[panRecognizer setTranslation:CGPointZero inView: self.superview];}
- (void)pinchDetected:(UIPinchGestureRecognizer *)pinchRecognizer{
CGFloat scale = pinchRecognizer.scale;
self.transform = CGAffineTransformScale(self.transform, scale, scale);
pinchRecognizer.scale = 1.0;}
- (void)rotationDetected:(UIRotationGestureRecognizer *)rotationRecognizer{
CGFloat angle = rotationRecognizer.rotation;
self.transform = CGAffineTransformRotate(self.transform, angle);
rotationRecognizer.rotation = 0.0;}
- (void)tapDetected:(UITapGestureRecognizer *)tapRecognizer{
[UIView animateWithDuration:0.25 animations:^{
self.center = CGPointMake(CGRectGetMidX(self.superview.bounds), CGRectGetMidY(self.superview.bounds));
self.transform = CGAffineTransformIdentity;
}];}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;}
viewcontroller.m (where I create a imageview from image which doesn't respond to multiple gestures simultaneously)
image *img = [[image alloc] initWithImage:imatgetemporal];
img.center = CGPointMake(525/2, 651/2);
[[self.view viewWithTag:2] addSubview:img];
Thanks for all!!
I guess you can solve it by adding the same delegate to the UIPanGestureRecognizer, UIPinchGestureRecognizer, UIRotationGestureRecognizer and to the UITapGestureRecognizer just after declaring them. The code would be like this:
tapRecognizer.delegate = self;
pinchRecognizer.delegate = self;
rotationRecognizer.delegate = self;
tapRecognizer.delegate = self;
Hope it helps.
I had added a UILongPressGestureRecognizer to a tableview in my ViewDidLoad method. I added this to detect long press on table view in my code. But it never works. In ViewDidLoad I added this code :
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
lpgr.minimumPressDuration = 2.0; //seconds
lpgr.delegate = self;
[self.resultTableView addGestureRecognizer:lpgr];
[lpgr release];
I also added this method :
-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
CGPoint p = [gestureRecognizer locationInView:self.resultTableView];
NSIndexPath *indexPath = [self.resultTableView indexPathForRowAtPoint:p];
if (indexPath == nil) {
NSLog(#"long press on table view but not on a row");
}
else {
NSLog(#"long press on table view at row %d", indexPath.row);
}
}
Please help me to resolve this?
Your code is working. I think you have to add UIGestureRecognizerDelegate delegate in .h file or how to declare resultTableView i mean you define programmatically or using .xib file.Check it once.
I have tried like this.
resultTableView = [[UITableView alloc] init];
resultTableView =[[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 420) style:UITableViewStylePlain];
resultTableView.rowHeight = 100.0;
resultTableView.delegate=self;
resultTableView.dataSource=self;
[self.view addSubview:resultTableView];
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
lpgr.minimumPressDuration = 2.0; //seconds
lpgr.delegate = self;
[resultTableView addGestureRecognizer:lpgr];
[lpgr release];
It looks like you want to add the gesture to the individual cells, but you are adding the gesture to the table. Try adding the gesture to your UITableViewCell instead.
If the gesture recognizer is being blocked by the UITableView panGestureRecognizer, implement the delegate to ensure both can work
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
I have a class called PhotoBoothController that I use to manipulate an image using gestures. It works fine when I run it on iPhone. However when I run it on iPad, I display the PhotoBoothController inside a UIPopoverController. The image appears fine, however I can't manipulate it as my gestures don't appear to be recognized. I'm not sure how to make the UIPopoverController take control of the gestures.
I present the popovercontroller and configure the view thus:
- (void)presentPhotoBoothForPhoto:(UIImage *)photo button:(UIButton *)button {
//Create a photoBooth and set its contents
PhotoBoothController *photoBoothController = [[PhotoBoothController alloc] init];
photoBoothController.photoImage.image = [button backgroundImageForState:UIControlStateNormal];
//set up all the elements programmatically.
photoBoothController.view.backgroundColor = [UIColor whiteColor];
//Add frame (static)
UIImage *frame = [[UIImage imageNamed:#"HumptyLine1Frame.png"] adjustForResolution];
UIImageView *frameView = [[UIImageView alloc] initWithImage:frame];
frameView.frame = CGRectMake(50, 50, frame.size.width, frame.size.height);
[photoBoothController.view addSubview:frameView];
//Configure image
UIImageView *photoView = [[UIImageView alloc] initWithImage:photo];
photoView.frame = CGRectMake(50, 50, photo.size.width, photo.size.height);
photoBoothController.photoImage = photoView;
//Add canvas
UIView *canvas = [[UIView alloc] initWithFrame:frameView.frame];
photoBoothController.canvas = canvas;
[canvas addSubview:photoView];
[canvas becomeFirstResponder];
[photoBoothController.view addSubview:canvas];
[photoBoothController.view bringSubviewToFront:frameView];
//resize the popover view shown in the current view to the view's size
photoBoothController.contentSizeForViewInPopover = CGSizeMake(frameView.frame.size.width+100, frameView.frame.size.height+400);
self.photoBooth = [[UIPopoverController alloc] initWithContentViewController:photoBoothController];
[self.photoBooth presentPopoverFromRect:button.frame
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
I thought [canvas becomeFirstResponder] might do it but it doesn't appear to make a difference.
Any advice much appreciate, thank you.
UPDATE: adding code as per comment
- (void)viewDidLoad {
[super viewDidLoad];
if (!_marque) {
_marque = [CAShapeLayer layer];
_marque.fillColor = [[UIColor clearColor] CGColor];
_marque.strokeColor = [[UIColor grayColor] CGColor];
_marque.lineWidth = 1.0f;
_marque.lineJoin = kCALineJoinRound;
_marque.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:10],[NSNumber numberWithInt:5], nil];
_marque.bounds = CGRectMake(photoImage.frame.origin.x, photoImage.frame.origin.y, 0, 0);
_marque.position = CGPointMake(photoImage.frame.origin.x + canvas.frame.origin.x, photoImage.frame.origin.y + canvas.frame.origin.y);
}
[[self.view layer] addSublayer:_marque];
UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(scale:)];
[pinchRecognizer setDelegate:self];
[self.view addGestureRecognizer:pinchRecognizer];
UIRotationGestureRecognizer *rotationRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotate:)];
[rotationRecognizer setDelegate:self];
[self.view addGestureRecognizer:rotationRecognizer];
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(move:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
[panRecognizer setDelegate:self];
[canvas addGestureRecognizer:panRecognizer];
UITapGestureRecognizer *tapProfileImageRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
[tapProfileImageRecognizer setNumberOfTapsRequired:1];
[tapProfileImageRecognizer setDelegate:self];
[canvas addGestureRecognizer:tapProfileImageRecognizer];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return ![gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && ![gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]];
}
I am working on a similar application, and i can easily manipulate images on the canvas.
1) Check if the userInteractionEnabled property of the canvas is set to YES.
2) You can try adding the gestures to the ImageView's rather than the canvas. (I am guessing u want to zoom , rotate n swipe the images).
some guesses:
is this because viewDidLoad method have been called before cavas property been set? In this case, you could try to add recognizers after present popover in another method such as viewDidAppear.
I also agree that the key may be that UIPopoverController allows interaction to other views outside of the popover. So i suggest to move [canvas becomeFirstResponder]; to the end of the viewDidLoad method in PhotoBoothController and try self.view becomeFirstResponder here because you actually add recognizers to self.view.
I'm trying to create a photo based app and I want to be able to crop, rotate and move the image thats been taken. I've done those things already but the UIImage created moves around the whole UIViewController and I only want it to be moved around within its UIImageView. Could anyone suggest how I might go about trying this? I've tried a few way already but they don't seemed to have worked. Any suggestions would be really helpful. Thanks!
Update
I'm adding in my code to help better explain what I'm trying to do . I'm sorry for not doing this before.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
//the 'image' below is the UIImage, it has already been declared before hand
image = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
//layoutOne is my UIView which holds my UIImageView and UIImage. all of those are declared in my .h file.
layoutOne = [[UIView alloc] initWithFrame:CGRectMake(95.0, 107.0, 578, 682)];
theImageView = [[UIImageView alloc] initWithFrame:[layoutOne frame]];
[theImageView setImage:image];
[layoutOne addSubview:theImageView];
UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(scale:)];
[pinchRecognizer setDelegate:self];
[layoutOne addGestureRecognizer:pinchRecognizer];
UIRotationGestureRecognizer *rotationRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotate:)];
[rotationRecognizer setDelegate:self];
[layoutOne addGestureRecognizer:rotationRecognizer];
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(move:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
[panRecognizer setDelegate:self];
[layoutOne addGestureRecognizer:panRecognizer];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
[tapRecognizer setNumberOfTapsRequired:1];
[tapRecognizer setDelegate:self];
[layoutOne addGestureRecognizer:tapRecognizer];
[self.view addSubview:layoutOne];
}
I'm using Interface Builder to create my app and I'm also using the newest version of Xcode. Thought I'd also add this incase it helped. Thanks
I don't think you can move the image within the image view. What you can to is to create a UIView as the frame and move the image within the bounds of the frame. Below is the code snippet (iOS 5 with ARC) that I hope it helps.
#implementation ImageFrame
{
UIImageView *imageView;
CGPoint previousPoint;
}
- (id)initWithFrame:(CGRect)frame andImage:(UIImage *)image
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor greenColor];
self.clipsToBounds = YES;
imageView = [[UIImageView alloc] initWithImage:image];
}
return self;
}
- (void)layoutSubviews
{
[self addSubview:imageView];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
previousPoint = [[touches anyObject] locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
CGPoint movePoint = CGPointMake(location.x - previousPoint.x, location.y - previousPoint.y);
CGPoint newCenter = CGPointMake(imageView.center.x + movePoint.x, imageView.center.y + movePoint.y);
imageView.center = newCenter;
previousPoint = location;
}
#end
Not sure what your current setup is, but make sure your UIImageView clipsToBounds property is set to YES.
If this doesn't answer your question, try checking the answers to Cropping an UIImage, which sounds like it may be describing a similar problem.