Swapping images in IOS - iphone

I have to swap four images on a single screen . The images can be swapped only in left/right/upwards/downwards directions only and not diagonally . For example , the 1st image can be swapped with the images on its right and below it , the 2nd image can be swapped only with the one on the left and below it , and so on . Can anyone please help me about how to do it . Thanks

To add drag and drop functionality, assuming that is what you mean, you'll want to look into UIPanGestureRecognizer

Add a swipe gesture recognizer. When the user swipes determine which direction and handle the image swap.
[EDIT] - Imagine a square divided into four equal sections. The top left section has an index of zero, the top right an index of one, the bottom left an index of 2 and finally the bottom right an index of 3. The code below checks the current index and from that it determines if an image swap can be made, if not it does nothing.
This code is from the top of my head so there may be syntax errors but the logic is sound (I hope :D ).
- (void) viewDidLoad {
// turn on user interaction on the image view as its off by default.
[self.imageView setUserInteractionEnabled:TRUE];
UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
[recognizer setDirection:(UISwipeGestureRecognizerDirectionRight | UISwipeGestureRecognizerDirectionDown | UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionUp)];
[self.imageView addGestureRecognizer:recognizer];
self.currentImageIndex = 0;
self.images = [NSArray arrayWithObjects:[UIImage imageNamed:#"top-left"],[UIImage imageNamed:#"top-right"],[UIImage imageNamed:#"bottom-left"],[UIImage imageNamed:#"top-right"],nil];
}
-(void)handleSwipeFrom:(UISwipeGestureRecognizer *)recognizer {
if (recognizer.direction == UISwipeGestureRecognizerDirectionRight) {
if (self.currentImageIndex == 0 || self.currentImageIndex == 2) self.currentImageIndex++; // change to image to the right
else return; // do nothing
}
else if (recognizer.direction == UISwipeGestureRecognizerDirectionLeft) {
if (self.currentImageIndex == 1 || self.currentImageIndex == 3) self.currentImageIndex--; // change to the image to the left
else return; // do nothing
}
else if (recognizer.direction == UISwipeGestureRecognizerDirectionUp) {
if (self.currentImageIndex == 2 || self.currentImageIndex == 3) self.currentImageIndex -= 2; // change to the above image
else return; // do nothing
}
else if (recognizer.direction == UISwipeGestureRecognizerDirectionDown) {
if (self.currentImageIndex == 0 || self.currentImageIndex == 1) self.currentImageIndex += 2; // change to the above image
else return; // do nothing
}
[UIView animationWithDuration:0.5 animations:^{
[self.imageView setAlpha:0];
} completion^(BOOL finished){
if (finished) {
[UIView animationWithDuration:0.5 animations:^{
[self.imageView setImage[self.images objectAtIndex:self.currentImageIndex]];
[self.imageView setAlpha:1];
}];
}
}];
}

Related

UIPercentDrivenInteractiveTransition cancelTransition turns screen black

I'm implementing custom navigation transitions, and using UIPercentDrivenInteractiveTransition to do the trick.
I've got the noninteractive transition working fine, and I'm using an animation block to handle the transition animation, but once I added in interactive transitions, the screen now goes black if I cancel the transition.
Here's my code that tracks the gesture:
-(void)userDidPan:(UIScreenEdgePanGestureRecognizer *)recognizer {
CGPoint location = [recognizer locationInView:nil];
if (recognizer.state == UIGestureRecognizerStateBegan) {
// We're being invoked via a gesture recognizer – we are necessarily interactive
self.hasActiveInteraction = YES;
self.interactiveTransition = [BXTPercentDrivenInteractiveTransition new];
[[BXTNavigationController sharedNavigationController] popViewControllerAnimated:YES];
}
else if (recognizer.state == UIGestureRecognizerStateChanged) {
CGFloat ratio = location.x / CGRectGetWidth(recognizer.view.frame);
NSLog(#"Percentage complete: %0.2f",ratio*100);
[self.interactiveTransition updateInteractiveTransition:ratio];
}
else if (recognizer.state == UIGestureRecognizerStateEnded) {
// Depending on our state and the velocity, determine whether to cancel or complete the transition.
CGFloat ratio = location.x / CGRectGetWidth(recognizer.view.frame);
if (ratio > 0.50) {
NSLog(#"Completing");
[self.interactiveTransition finishInteractiveTransition];
}
else {
NSLog(#"Canceling");
[self.interactiveTransition cancelInteractiveTransition];
}
}
}
...and here's an (abbreviated) look at my animation block when popping off the view controller:
[UIView animateWithDuration:kBXTNavigationTimingDuration
delay:0
options:animationOption
animations:^{
toVC.view.frame = destinationFrameForPoppedView;
fromVC.view.frame = CGRectOffset(fromVC.view.frame, fromVC.view.frame.size.width, 0);
} completion:^(BOOL finished) {
[darkeningView removeFromSuperview];
[_transition.secondaryTransitionViews enumerateObjectsUsingBlock:^(BXTTransitionView *transitionView, NSUInteger idx, BOOL *stop) {
[transitionView.view removeFromSuperview];
}];
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
Any ideas why the screen goes black when cancelling a swipe-from-the-left pop animation with a custom navigation transition?
Solved! I deleted all the removeFromSuperview calls in the animation completion block and it solved it (I had the same problem like you with almost the same code, see my comment to your q'). Try removing these lines from your code:
[darkeningView removeFromSuperview];
[_transition.secondaryTransitionViews enumerateObjectsUsingBlock:^(BXTTransitionView *transitionView, NSUInteger idx, BOOL *stop) {
[transitionView.view removeFromSuperview];
}];

check if scrollview.contentset went over certain x value

I have a scrollview and above some image. When the scrollview scrollView.contentOffset.x is past a certain X my image above should animate.
I know how to animate. At the moment I'm doing this in the - (void)scrollViewDidScroll:(UIScrollView *)scrollView method.
if (scrollView.contentOffset.x == 160) {
//animate Image
}
but sometimes it gets the 160, but other times it passes over the 160. How can I solve this ?
Add an instance variable, set it to the offset that you've seen in the last invocation of scrollViewDidScroll:, and use it to decide if you would like to animate:
// Instance variable
CGPoint lastOffset;
...
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
...
if (lastOffset.x < 160 && scrollView.contentOffset.x >= 160) {
//animate Image
}
lastOffset = scrollView.contentOffset;
}
This would let you animate the image every time the scroll view crosses from below 160 to above 160.
Use >= 160 but also use a flag so you know if you have already done the animation:
if (scrollView.contentOffset.x == 160 && !self.animatedImage) {
self.animatedImage = YES;
...
}
I think you should add some flag to allow image animation,
and manage this flag during scrolling/after image animating
BOOL isCanAnimate_;
// some code here
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.x >= imageView.frame.size.width / 2 && isCanAnimate_)
{
isCanAnimate_ = FALSE;
[UIView animateWithDuration:2.0
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^
{
// Animation here
}
completion:^(BOOL finished)
{
isCanAnimate_ = TRUE;
}];
}
}

Scroll effect with swipe gesture in iOS

I have a view which has two labels. When I swipe left I fill next content to label text. Similarly swiping right loads previous content. I want to give an effect to labels like they are scrolling from left or right.
I used a scrollview before but it had a memory problem. So I'm using one view, and swipe gesture loads next or previous content. I want to add scrollview's sliding effect to labels. How can I do that?
I'm not quite sure precisely what effect you're looking for, but you could do something like this, which creates a new, temporary label, puts it off screen, animates the moving it over the label you have on screen, and then when done, resets the old one and deletes the temporary label. This is what a non-autolayout implementation might look like:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UISwipeGestureRecognizer *left = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(leftSwipe:)];
[left setDirection:UISwipeGestureRecognizerDirectionLeft];
[self.view addGestureRecognizer:left];
// if non-ARC, release it
// [release left];
self.label1.text = #"Mo";
}
- (void)leftSwipe:(UISwipeGestureRecognizer *)gesture
{
NSString *newText;
UILabel *existingLabel = self.label1;
// in my example, I'm just going to toggle the value between Mo and Curly
if ([existingLabel.text isEqualToString:#"Curly"])
newText = #"Mo";
else
newText = #"Curly";
// create new label
UILabel *tempLabel = [[UILabel alloc] initWithFrame:existingLabel.frame];
[existingLabel.superview addSubview:tempLabel];
tempLabel.text = newText;
// move the new label off-frame to the right
tempLabel.transform = CGAffineTransformMakeTranslation(tempLabel.superview.bounds.size.width, 0);
// animate the sliding of them into place
[UIView animateWithDuration:0.5
animations:^{
tempLabel.transform = CGAffineTransformIdentity;
existingLabel.transform = CGAffineTransformMakeTranslation(-existingLabel.superview.bounds.size.width, 0);
}
completion:^(BOOL finished) {
existingLabel.text = newText;
existingLabel.transform = CGAffineTransformIdentity;
[tempLabel removeFromSuperview];
}];
// if non-ARC, release it
// [release tempLabel];
}
This animation animates the label with respect to its superview. You may want to ensure that the superview is set to "clip subviews". This way, the animation will be constrained to the bounds of that superview, which yields a slightly more polished look.
Note, if using auto layout, the idea is the same (though the execution is more complicated). Basically configure your constraints so new view is off to the right, then, in animation block update/replace the constraints so the original label is off to the left and the new one is in the spot of the original label, and, finally, in the completion block reset the constraints of the original label and remove the temporary label.
By the way, this is all infinitely easier if you're comfortable with one of the built in transitions:
- (void)leftSwipe:(UISwipeGestureRecognizer *)gesture
{
NSString *newText;
UILabel *existingLabel = self.label1;
// in my example, I'm just going to toggle the value between Mo and Curly
if ([existingLabel.text isEqualToString:#"Curly"])
newText = #"Mo";
else
newText = #"Curly";
[UIView transitionWithView:existingLabel // or try `existingLabel.superview`
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
existingLabel.text = newText;
}
completion:nil];
}
If you prefer animation that behaves more like a scroll view (i.e., with continuous feedback on the gesture), it might look something like the following:
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panHandler:)];
[self.view addGestureRecognizer:pan];
self.label1.text = #"Mo";
}
- (void)panHandler:(UIPanGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateBegan)
{
_panLabel = [[UILabel alloc] init];
// in my example, I'm just going to toggle the value between Mo and Curly
// you'll presumably set the label contents based upon the direction of the
// pan (if positive, swiping to the right, grab the "previous" label, if negative
// pan, grab the "next" label)
if ([self.label1.text isEqualToString:#"Curly"])
_newText = #"Mo";
else
_newText = #"Curly";
// set the text
_panLabel.text = _newText;
// set the frame to just be off screen
_panLabel.frame = CGRectMake(self.label1.frame.origin.x + self.containerView.frame.size.width,
self.label1.frame.origin.y,
self.label1.frame.size.width,
self.label1.frame.size.height);
[self.containerView addSubview:_panLabel];
_originalCenter = self.label1.center; // save where the original label originally was
}
else if (sender.state == UIGestureRecognizerStateChanged)
{
CGPoint translate = [sender translationInView:self.containerView];
if (translate.x > 0)
{
_panLabel.center = CGPointMake(_originalCenter.x - self.containerView.frame.size.width + translate.x, _originalCenter.y);
self.label1.center = CGPointMake(_originalCenter.x + translate.x, _originalCenter.y);
}
else
{
_panLabel.center = CGPointMake(_originalCenter.x + self.containerView.frame.size.width + translate.x, _originalCenter.y);
self.label1.center = CGPointMake(_originalCenter.x + translate.x, _originalCenter.y);
}
}
else if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateFailed || sender.state == UIGestureRecognizerStateCancelled)
{
CGPoint translate = [sender translationInView:self.containerView];
CGPoint finalNewFieldLocation;
CGPoint finalOriginalFieldLocation;
BOOL panSucceeded;
if (sender.state == UIGestureRecognizerStateFailed ||
sender.state == UIGestureRecognizerStateCancelled)
{
panSucceeded = NO;
}
else
{
// by factoring in the velocity, we can capture a flick more accurately
//
// (by the way, I don't like iOS's velocity, because if you stop moving, it records the velocity
// prior to stopping the move rather than noting that you actually stopped, so I usually calculate my own,
// but I'll leave this as is for purposes of this example)
CGPoint velocity = [sender velocityInView:self.containerView];
if (translate.x < 0)
panSucceeded = ((translate.x + velocity.x * 0.5) < -(self.containerView.frame.size.width / 2));
else
panSucceeded = ((translate.x + velocity.x * 0.5) > (self.containerView.frame.size.width / 2));
}
if (panSucceeded)
{
// if we succeeded, finish moving the stuff
finalNewFieldLocation = _originalCenter;
if (translate.x < 0)
finalOriginalFieldLocation = CGPointMake(_originalCenter.x - self.containerView.frame.size.width, _originalCenter.y);
else
finalOriginalFieldLocation = CGPointMake(_originalCenter.x + self.containerView.frame.size.width, _originalCenter.y);
}
else
{
// if we didn't, then just return everything to where it was
finalOriginalFieldLocation = _originalCenter;
if (translate.x < 0)
finalNewFieldLocation = CGPointMake(_originalCenter.x + self.containerView.frame.size.width, _originalCenter.y);
else
finalNewFieldLocation = CGPointMake(_originalCenter.x - self.containerView.frame.size.width, _originalCenter.y);
}
// animate the moving of stuff to their final locations, and on completion, clean everything up
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
_panLabel.center = finalNewFieldLocation;
self.label1.center = finalOriginalFieldLocation;
}
completion:^(BOOL finished) {
if (panSucceeded)
self.label1.text = _newText;
self.label1.center = _originalCenter;
[_panLabel removeFromSuperview];
_panLabel = nil; // in non-ARC, release instead
}
];
}
}
Note, I have put both the original label, as well as the new label being panned on, in a container UIView (called containerView, surprisingly enough), so that I can clip the animation to that container.

How to Check an UIImageView Present in position of a view in iphone

I have a imageview in a specific position , i know the position but i want to check if there is an imageview in that position.
please help me out to solve
Here is a helper method to check if two rects overlap:
-(BOOL) rect:(CGRect)rect overlap:(CGRect)anotherRect
{
BOOL xOverlap = ((rect.origin.x >= anotherRect.origin.x) && (rect.origin.x <= anotherRect.origin.x + anotherRect.size.width)) ||
((anotherRect.origin.x >= rect.origin.x) && (anotherRect.origin.x <= rect.origin.x + rect.size.width));
BOOL yOverlap = ((rect.origin.y >= anotherRect.origin.y) && (rect.origin.y <= anotherRect.origin.y + anotherRect.size.height)) ||
((anotherRect.origin.y >= rect.origin.y) && (anotherRect.origin.y <= rect.origin.y + rect.size.height));
return xOverlap && yOverlap;
}
Usage:
You can iterate though all subviews of a view and check if the frame of your view overlaps another view's frame. Suppose you have a property myView then:
for(UIView *v in self.view.subviews)
{
if(v == self.myView) //skip the same view
continue;
if([self rect:self.myView.frame overlap:v.frame])
{
//Do something...
}
}
First you get the array of all your views inside your main view:
NSArray *subviews = [view subviews];
Then, for every view, you can check the frame of every view:
for (int i = 0 ; i < [subviews count]; i++){
CGRect frame = [subviews objectAtIndex:i].frame;
}
You can navigate through the subviews of the superview of that UIImageView in a for loop. And compare the position of each view to that specific UIImageView's position.
But this sounds bad to me. Maybe if you can tell more about the issue, we can help better.
You can use this approach as well:
Since you have said you have position of the ImageView then save this in some CGRect variable.Try this
UIImageView *yourImgView = (UIImageView *)[view subviews];
This will return your imageView . Then you can check its frame with earlier saved position or its visibility depending on your need.

Making use of velocityInView with UIPanGestureRecognizer

I have a custom slider-type object, that I wish to make more usable. Currently I use UIPanGestureRecognizer and translationInView to make it work. It works pretty well but I'd like some sort of velocity in there to make it feel a bit more useful. I've tried a few things but cant quite figure out how to properly implement velocity changedLevel equation.
- (void)panDetected:(UIPanGestureRecognizer *)gesture {
CGPoint swipeLocation = [gesture locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:swipeLocation];
LevelCounterTableCell *swipedCell = (LevelCounterTableCell *)[self.tableView cellForRowAtIndexPath:indexPath];
if([gesture state] == UIGestureRecognizerStateBegan) {
NSString *originalLevelString = swipedCell.levelNumber.text;
originalLevel = [originalLevelString intValue]; // int originalLevel
}
if ([gesture state] == UIGestureRecognizerStateChanged) {
CGFloat xTranslation = [gesture translationInView:[[gesture view] superview]].x;
CGFloat xVelocity = [gesture velocityInView: [[gesture view] superview]].x;
// Pan threshold is currently set to 8.0.
// 8.0 is a decent level for slow panning
// for fast panning 2.0 is more reasonable
changedLevel = ceilf((xTranslation / panThreshold) + originalLevel); // int changedLevel
// Raw velocity seems to go from around 3 (slow)
// to over 200 (fast)
NSLog(#"raw velocity = %f", xVelocity);
if (changedLevel >= 15 && changedLevel <= 100) {
swipedCell.levelNumber.text = [NSString stringWithFormat:#"%i", changedLevel];
swipedCell.meter.frame = [self updateMeter: changedLevel];
}
}
if ([gesture state] == UIGestureRecognizerStateEnded || [gesture state] == UIGestureRecognizerStateCancelled) {
if (changedLevel >= 15 && changedLevel <= 100) {
//... Save the values...
}
}
}
Any help would be greatly appreciated. Thank you.
In my experience, the velocityInView: of a pan gesture recognizer isn't important until the user lifts their finger(s) and the recognizer finishes. At that point, you can use the velocity to calculate an animation duration to move your views.
Just stick with translationInView: until the state is UIGestureRecognizerStateEnded and then use velocityInView: to animate any onscreen view changes.