Detect a pan over multiple objects? - iphone

So here I have a loop that creates a bunch of different squares on the screen, using an array, and I've added a gesture recognizer to detect for a pan. The thing is, I want to be able to smoothly slide over one object to the next, without lifting my finger. But, with my current code I have to lift my finger and slide over each object individually. Does anyone know how to make it so that I can smoothly slide my finger over each object, and have them perform their functions when they've been slid over? Here's my code:
int numby = [squareLocations count];
for (int i = 0; i < numby; i++)
{
NSValue *pointLocation = [squareLocations objectAtIndex:i];
CGPoint tmpPoint = [pointLocation CGPointValue];
UIImage *theSquare = [UIImage imageNamed:#"square.png"];
UIButton *squareButton = [[UIButton alloc] init];
squareButton = [UIButton buttonWithType:UIButtonTypeCustom];
squareButton.frame = CGRectMake(tmpPoint.x, tmpPoint.y, theSquare.size.width, theSquare.size.height);
squareButton.tag = *(&i);
[squareButton setImage:theSquare forState:UIControlStateNormal];
UIPanGestureRecognizer *slide = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
[squareButton addGestureRecognizer:slide];
[whereStuffActuallyHappens addSubview:squareButton];
}
}
- (void) handlePan:(UIPanGestureRecognizer *)slide {
[slide setTranslation:CGPointZero inView:slide.view];
NSInteger tag = slide.view.tag;
NSLog(#"Tag is: %i", tag);
[slide.view removeFromSuperview];
}

A kinda hack-ish way to do this would be to add the pan gesture to the superview of your squares instead, and check which one has the location of the gesture colliding with its frame.

Related

Pinch to take photo full screen like Reeder apps

Trying to come up with a method for doing the exact same thing the reeder apps creator does in his iphone/ipad apps with pinch-to-expand photos to full screen.
I have a uiimageview in a table cell that I want to transition to a full screen view on pinch open, or maybe double tap. Would like to use a similar animation as well.
Any tips would be appreciated!
Ok I managed to put this together myself. Not really sure how to use a transition method, but I needed to duplicate the view in the same location and then blow it up.
http://screencast.com/t/MLTuGkIYh
So in my cell that contains the big image I hook up both the pinch and tap gesture recognizers.
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
self.imageView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
UIPinchGestureRecognizer *pinchGesture = [[[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handlePinchGesture:)] autorelease];
UITapGestureRecognizer *tapGesture = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)] autorelease];
tapGesture.numberOfTapsRequired = 2;
self.imageView.userInteractionEnabled = YES;
self.imageView.multipleTouchEnabled = YES;
[self.imageView addGestureRecognizer:pinchGesture];
[self.imageView addGestureRecognizer:tapGesture];
[cell.contentView addSubview:self.imageView];
and then here's the rest of the code. Basically when I recognized the gesture (and for pinching, make sure its finished) I place the duplicate view in the same exact location (gained via the convertRect stuff), and then animate its frame and background color. When returning from it, I do the inverse.
- (void)handlePinchGesture:(id)sender
{
if (((UIPinchGestureRecognizer *)sender).state == UIGestureRecognizerStateEnded) {
if(((UIPinchGestureRecognizer *)sender).view == self.imageView)
{
if (((UIPinchGestureRecognizer *)sender).scale > 1) {
[self showFloorPlanFullScreen];
}
} else {
if (((UIPinchGestureRecognizer *)sender).scale < 1) {
[self closeFloorPlanFullScreen];
}
}
}
}
- (void)handleTap:(id)sender
{
if (((UITapGestureRecognizer *)sender).view == self.imageView) {
[self showFloorPlanFullScreen];
} else {
[self closeFloorPlanFullScreen];
}
}
- (void)showFloorPlanFullScreen
{
CGRect newRect = [self.imageView convertRect:self.imageView.bounds toView:[self.splitViewController.view superview]];
UIImage *image = self.imageView.image;
self.fullScreenImageView = [[[UIImageView alloc] initWithImage:image] autorelease];
UIPinchGestureRecognizer *pinchGesture = [[[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handlePinchGesture:)] autorelease];
UITapGestureRecognizer *tapGesture = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)] autorelease];
tapGesture.numberOfTapsRequired = 2;
self.fullScreenImageView.userInteractionEnabled = YES;
self.fullScreenImageView.multipleTouchEnabled = YES;
[self.fullScreenImageView addGestureRecognizer:pinchGesture];
[self.fullScreenImageView addGestureRecognizer:tapGesture];
self.fullScreenImageView.contentMode = UIViewContentModeScaleAspectFit;
self.fullScreenImageView.frame = newRect;
self.fullScreenImageView.backgroundColor = [UIColor clearColor];
[[self.splitViewController.view superview] addSubview:self.fullScreenImageView];
CGRect splitViewRect = self.splitViewController.view.frame;
[UIView animateWithDuration:0.5 animations:^{
self.fullScreenImageView.backgroundColor = [UIColor blackColor];
self.fullScreenImageView.frame = splitViewRect;
}];
}
- (void)closeFloorPlanFullScreen
{
CGRect newRect = [self.imageView convertRect:self.imageView.bounds toView:[self.splitViewController.view superview]];
[UIView animateWithDuration:0.5
animations:^{
self.fullScreenImageView.backgroundColor = [UIColor clearColor];
self.fullScreenImageView.frame = newRect;
}
completion:^(BOOL finished) {
[self.fullScreenImageView removeFromSuperview];
self.fullScreenImageView = nil;
}];
}
If you want the picture to actual resize while zooming, I would recommend adding the duplicate view as soon as the pinching starts (and as long as its scaling > 1) and then apply the transformation:
CGAffineTransform myTransformation = CGAffineTransformMakeScale(((UIPinchGestureRecognizer *)sender).scale, ((UIPinchGestureRecognizer *)sender).scale);
self.fullScreenImageView.transform = myTransformation;
As soon as the pinching hits a end state, I would then fade in the black and adjust the frame. I decided not to go with this method as I think just recognizing the pinch out or double tap is good enough.
You have to embed your UIImageView in an UIControl and link an IBAction to UIControl events.
Use a UIPinchGestureRecognizer on the image view to recognize the pinch and the UIView transition methods to blow the image view up to full size.

Move left and right in UIScrollView

I have a UIScrollView which scrolls only horizontally. I have a situation where I have views filled by array in UIScrollView. There are three views which altogether make one single component in UIScrollView (there are many such components). Component contains :
UILabel
UIButton
UIView (for underlying text)
I have placed two arrow keys(left and right) at the ends of UIScrollView. On touching left arrow I am shifting one position left (with complete string to display) in UIScrollView and one position right (with complete string to display) in case of right arrow button.
Till now I am able to perform left arrow action. But I have no idea how to perform right arrow touch and how to display complete string to touching arrow keys if only part of that string is being displayed.
I know it is weird that I have a UIScrollView and still want to add arrow keys to move it. But can't help it. This is client's requirement. The following is how I implemented left arrow action:
-(IBAction) onClickLeftArrow
{
NSLog(#"BUTTON VALUE : %i",iButtonValue);
if(iButtonValue <= [m_BCListArray count]) {
NSArray *arr = [scrollDemo subviews];
for(int j = 0; j < [arr count]; j++) {
UIView *view1 = [arr objectAtIndex:j];
[view1 removeFromSuperview];
}
XX = 5.0;
int tagcount = [m_BCListArray count]-iButtonValue;
NSLog(#"%#",m_BCListArray);
for(int i = iButtonValue; i >= 1; i--)
{
UILabel *blabel = [[UILabel alloc] initWithFrame:CGRectMake(XX, 6, 120, 26)];
blabel.text = [m_BCListArray objectAtIndex:[m_BCListArray count]-i];
blabel.backgroundColor = [UIColor clearColor];
blabel.textColor = [UIColor whiteColor];
blabel.font = [UIFont systemFontOfSize:14];
[scrollDemo addSubview:blabel];
//underline code
CGSize expectedLabelSize = [[m_BCListArray objectAtIndex:[m_BCListArray count]-1] sizeWithFont:blabel.font constrainedToSize:blabel.frame.size lineBreakMode:UILineBreakModeWordWrap];
CGRect newFrame = blabel.frame;
newFrame.size.height = expectedLabelSize.height;
blabel.frame = CGRectMake(blabel.frame.origin.x, blabel.frame.origin.y, expectedLabelSize.width+10, expectedLabelSize.height);
blabel.numberOfLines = 1;
[blabel sizeToFit];
int width=blabel.bounds.size.width;
int height=blabel.bounds.size.height;
UIButton *btnContent = [UIButton buttonWithType:UIButtonTypeCustom];
[btnContent addTarget:self action:#selector(SelectButton:)forControlEvents:UIControlEventTouchDown];
btnContent.tag = tagcount+1;
btnContent.frame=CGRectMake(XX, 2.0, width+10, height);
[scrollDemo addSubview:btnContent];
scrollDemo.contentSize = CGSizeMake(XX+50, 32);
//scrollDemo.contentSize = CGSizeMake(expectedLabelSize.width,expectedLabelSize.height);
UIView *viewUnderline=[[UIView alloc] init];
viewUnderline.frame=CGRectMake(XX, 26, width, 1);
viewUnderline.backgroundColor=[UIColor whiteColor];
[scrollDemo addSubview:viewUnderline];
[viewUnderline release];
scrollDemo.contentSize = CGSizeMake(XX+width+10, 32);
XX = XX + width+10;
iRight = iRight + 1;
tagcount ++;
// iRight --;
}
}
iButtonValue = iButtonValue+1;
}
Set the contentOffset of UIScrollView on the Button click action and u'll get your desired effect.

Trying to reuse variable to avoid abandoned memory

I've got a TapDetectingImageView class that is pertaining in memory after usage because I do not reuse it in my code.
I change my code now to reuse it instead of re creating a new one each time.
The goal is to configure that TapDetectingImageView object and put it in a view. I'll do it 8 times but with the code below, only the last one is showing. I'm sure I'm missing something, but what? ;)
The view holding the imageViews is potatoeView. I'm adding new TapDetectingImageView through:
[potatoeView addSubview:imageView];
What's wrong in that code?
for (int i = 0; i <= numberOfPotatoes-1; i++)
{
NSLog(#"potatoesView Generation : %d",i);
NSArray *imgArray = [[NSArray alloc] initWithArray:[p objectAtIndex:i]];
UIImage *img1 = (UIImage *) [imgArray objectAtIndex:0];
UIImage *img2 = (UIImage *) [imgArray objectAtIndex:1];
NSString *imgName = (NSString *) [imgArray objectAtIndex:2];
if (imageView == nil) {
imageView = [[TapDetectingImageView alloc] initWithImage:img1 imageHighlighted:img2];
}else {
[imageView setImage:(UIImage *)img1];
[imageView setImageHighlighted:(UIImage *)img2];
}
[imageView setDelegate:self];
// setup each frame to a default height and width, it will be properly placed when we call "updateScrollList"
CGRect rect = imageView.frame;
rect.size.height = [potatoesSize floatValue] ;
rect.size.width = [potatoesSize floatValue] ;
imageView.frame = rect;
[imageView setName:(NSString *)imgName];
imageView.tag = i+1; // tag our images for later use when we place them in serial fashion
NSValue *val = [potatoesSizeAndPositions objectAtIndex:i];
CGPoint point = [val CGPointValue];
imageView.center = point;
[potatoeView addSubview:imageView];
[imgArray release];
}
You seem to be fundamentally misunderstanding how your views are drawn. When you set up the same view instance with eight different positions and images, it doesn't "stamp" them all out on some canvas as it goes. Its state won't be queried until the main run loop gets around to drawing your view, at which point it'll ask your view for its frame and contents and so on to draw it. If you want to draw eight different images with UIViews, you'll need eight different views.
You only ever create one view, set it as the subview and then change it n-1 times. Drop the if (imageView==nil) and create all 8 of them.

adding image to array at runtime and clear it on button click

wat i am trying to is, each time user touches screen, i am adding a small bulletr image on screen
UIImage *img = [UIImage imageNamed:#"shoot_a.png"]; //bullet shot image
im.image = img;
[self.view addSubview:im];
, if user touches 50 times, there will be 50 images on screen.
now i want, if user hit a button, all these bullet should be remove from the screen.
i hope i am clear about my question.
[im removefromsuperview] doesnt work for me.
if i want to add images to array at run time, how can i add and release?
or is there any better way to clear all images
regards.
this is my touch method, i m adding bullet images on screen frm heere,, but these images are not getting added in Array
// Touch method
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(restrictTap == YES){
NSUInteger numTaps = [[touches anyObject] tapCount];
NSString *numTapsMessage = [[NSString alloc] initWithFormat:#"%i", numTaps];
[numTapsMessage release];
// getting location
CGPoint touchLocation = [[touches anyObject] locationInView:self.view];
NSString *locationMessage = [[NSString alloc] initWithFormat:#"Location: X = %.0f Y = %.0f", touchLocation.x, touchLocation.y];
//bullet shot image
im = [[UIImageView alloc] initWithFrame:CGRectMake(self.location.x, self.location.y, 12.0, 12.0)];
float j =self.location.x;
float i =self.location.y;
img = [UIImage imageNamed:#"shoot_a.png"]; //bullet shot image
im.image = img;
//im.hidden = NO;
[self.view addSubview:im]; ///bullet shot image is over transparent view
[imageArray addObject:img];
}
}
You could keep track of them via a NSMutableArray property and use that to remove them:
// create e.g. in initializer:
imageArray = [[NSMutableArray alloc] init];
// clean-up, e.g. in dealloc:
self.imageArray = nil;
// add image:
[imageArray addObject:im];
// remove all:
for (UIView *img in imageArray) {
[img removeFromSuperview];
}
[imageArray removeAllObjects];
You can set a particular tag to all the bullet imageViews when you add it to your view
im.tag = 1000;
and then on button touch, you can iterate through subviews array of your view and remove all views with that tag. something like :
int count = [[self.view subviews] count];
for(int i = 0; i < count; i++)
{
if([(UIView*)[[self.view subviews] objectAtIndex:i] tag] == 1000)
{
[(UIView*)[[self.view subviews] objectAtIndex:i] removeFromSuperview]
}
}

Adding multiple UIButtons to an UIView

I've added some buttons to an UIView (via addSubview) programmatically. However, they appear as overlays (so that I always see the last button only). How do I add new buttons below existing buttons?
Regards
you can offset the button like this
int newX = previousButton.frame.origin.x + previousButton.frame.size.width ;
int newY = previousButton.frame.origin.y ;
and either set the frame for new button when you create it:
[[UIButton alloc] initWithFrame:CGRectMake(newX,newY,100,100)];
or set the frame later
newButton.frame = CGRectMake(newX,newY,100,100);
Set the UIView's frame origin to layout the UIButtons in the locations you wish:
CGRect buttonFrame = button.frame;
buttonFrame.origin = CGPointMake(100.0f, 100.0f);
button.frame = buttonFrame;
view.addSubview(button);
You can either use the insertSubview:atIndex method or insertSubview:belowSubview of your view.
UIButton *myButton = [[UIButton alloc] initWithFrame:CGRectMake(0,0,100,100)];
[myView insertSubview:myButton belowSubview:previousButton];
OR
[myView insertSubview:myButton atIndex:0];
Thanks for your answers guys.
I did the (horizontal) align with this code:
if([myContainer.subviews lastObject] == nil){
NSLog(#"NIL");
[myContainer insertSubview:roundedButton atIndex:0];
}else{
[myContainer insertSubview:roundedButton belowSubview:[tagsContainer.subviews lastObject]];
}
It works technically, but still overlays the buttons. I have to find a way, how to not overlay them...