How to animate a UIImageview to display fullscreen by tapping on it? - objective-c

I have an UIImageView in a UITableviewCell. When it is tapped, the UIImageView should animated to be displayed fullscreen. When the image is tapped when it is fullscreen it should shrink back to the original position.
How can this be achieved?

Add a gesture recognizer to the view controller.
Add the gesture Recognizer to your header file
#interface viewController : UIViewController <UIGestureRecognizerDelegate>{
UITapGestureRecognizer *tap;
BOOL isFullScreen;
CGRect prevFrame;
}
In your viewDidLoad add this:
isFullScreen = false;
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imgToFullScreen)];
tap.delegate = self;
Add the following delegatemethod:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
{
BOOL shouldReceiveTouch = YES;
if (gestureRecognizer == tap) {
shouldReceiveTouch = (touch.view == yourImageView);
}
return shouldReceiveTouch;
}
Now you just need to implement your imgToFullScreen method.
Make sure you work with the isFullScreen Bool (fullscreen if it is false and back to old size if it's true)
The imgToFullScreen method depends on how you want to make the image become fullscreen.
One way would be:
(this is untested but should work)
-(void)imgToFullScreen{
if (!isFullScreen) {
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
//save previous frame
prevFrame = yourImageView.frame;
[yourImageView setFrame:[[UIScreen mainScreen] bounds]];
}completion:^(BOOL finished){
isFullScreen = true;
}];
return;
} else {
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
[yourImageView setFrame:prevFrame];
}completion:^(BOOL finished){
isFullScreen = false;
}];
return;
}
}

The code from #AzzUrr1, small error corrections (brackets) and tapper implemented slightly different.
Worked for me. Now it would be great to have this implemented with a scrollView, that the user can zoom in/out if the picture is bigger.. Any suggestion?
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UIGestureRecognizerDelegate>{
UITapGestureRecognizer *tap;
BOOL isFullScreen;
CGRect prevFrame;
}
#property (nonatomic, strong) UIImageView *imageView;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
isFullScreen = FALSE;
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imgToFullScreen)];
tap.delegate = self;
self.view.backgroundColor = [UIColor purpleColor];
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 300, 200)];
_imageView.contentMode = UIViewContentModeScaleAspectFill;
[_imageView setClipsToBounds:YES];
_imageView.userInteractionEnabled = YES;
_imageView.image = [UIImage imageNamed:#"Muppetshow-2.png"];
UITapGestureRecognizer *tapper = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imgToFullScreen:)];
tapper.numberOfTapsRequired = 1;
[_imageView addGestureRecognizer:tapper];
[self.view addSubview:_imageView];
}
-(void)imgToFullScreen:(UITapGestureRecognizer*)sender {
if (!isFullScreen) {
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
//save previous frame
prevFrame = _imageView.frame;
[_imageView setFrame:[[UIScreen mainScreen] bounds]];
}completion:^(BOOL finished){
isFullScreen = TRUE;
}];
return;
}
else{
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
[_imageView setFrame:prevFrame];
}completion:^(BOOL finished){
isFullScreen = FALSE;
}];
return;
}
}

I ended up using MHFacebookImageViewer. Integration is easy, no subclassing UIImageView, and it also has image zooming and flick dismiss.
Although it requires AFNetworking (for loading larger image from URL), you can comment out some code (about 10 lines) to remove this dependency. I can post my AFNetworking-free version if someone needs it. Let me know :)

One possible implementation would be to use a modal view controller with UIModalPresentationFullScreen presentation style.

Just finished a version in swift, just download and add into your project:
GSSimpleImageView.swift
And usage:
let imageView = GSSimpleImageView(frame: CGRectMake(20, 100, 200, 200))
imageView.image = UIImage(named: "test2.png")
self.view.addSubview(imageView)

Related

Objective-C: Obscuring Data when in Background not working

I've been trying to obscure data when the application enters the background state by hiding it in the task switcher.
here is my AppDelegate.h file
#interface AppDelegate : UIResponder
<
UIApplicationDelegate,
MemberServicesManagerDelegate
>
{
UIWindow* window;
Reachability* reachbility;
}
#property(nonatomic, strong) UIViewController *mainViewController;
#end
This is my AppDelegate.m file
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{ window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
window.rootViewController = StartupController.instance.startingViewController;
if(DEVICE == IPAD){
self.mainViewController = window.rootViewController.childViewControllers[0];
}
[window makeKeyAndVisible];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
//1
//self.window.hidden = YES;
//2
//UIViewController *image = [UIViewController new];
//image.view.backgroundColor = [UIColor blackColor];
//[window makeKeyAndVisible];
//[window.rootViewController presentViewController:image animated:NO completion:NULL];
//3
UIView *colourView = [[UIView alloc]initWithFrame:window.frame];
colourView.backgroundColor = [UIColor blackColor];
colourView.tag = 1234;
colourView.alpha = 0;
[window addSubview:colourView];
[window bringSubviewToFront:colourView];
[UIView animateWithDuration:0.5 animations:^{
colourView.alpha = 1;
}];
NSLog(#"BACK");
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
//1
//self.window.hidden = NO;
//2
//[window.rootViewController dismissViewControllerAnimated:NO completion:NO];
//3
UIView *colourView = [window viewWithTag:1234];
[UIView animateWithDuration:0.5 animations:^{
colourView.alpha = 0;
} completion:^(BOOL finished) {
[colourView removeFromSuperview];
}];
NSLog(#"ForeGround");
}
I tried these 3 implementations but It won't hide the view when I enter the multitask switcher.
I tried this with both simulator and real device but it still won't work. Can you help me with this?
Try doing it in applicationWillResignActive
applicationDidEnterBackground is apparently too late and the system has already made a screenshot for the task switcher.
More about application lifecycle

Why doesn't my animation loop?

I need to perform a blinking effect on a image once the user click on a particular button. But my code doesn't work:
- (void)next
{
[UIView animateWithDuration:2.0f delay:0 options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat animations:^{
NSLog(#"Done"); // <- USED AS COUNTER
[image setAlpha:0];
[image setAlpha:0.5];
} completion:nil];
}
What happens is my image changing its alpha to 0.5 and stops. Furthermore my console shows DONE just one time. What did I wrong?
I just created a test project and this blinked just fine...
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic, strong)UIView *blinker;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.blinker = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
self.blinker.backgroundColor = [UIColor greenColor];
[self.view addSubview:self.blinker];
[self next];
}
- (void)next
{
[UIView animateWithDuration:2.0f delay:0 options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat animations:^{
NSLog(#"Done"); // <- USED AS COUNTER
[self.blinker setAlpha:0];
[self.blinker setAlpha:0.5];
} completion:nil];
}
With the exception that you expect the log to continue to print out in the console. So I suspect you are doing something to your image. You aren't calling self. so I am guessing it is an instance variable (ivar) and you are changing what it points to somewhere else in the code.
If you need a counter to go off every blink you may want to consider something like this...
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic, strong)UIView *blinker;
#property (nonatomic,)NSInteger counter;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.blinker = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
self.blinker.backgroundColor = [UIColor greenColor];
[self.view addSubview:self.blinker];
[self next];
}
- (void)next
{
[UIView animateWithDuration:1.0 animations:^{
self.counter++;
NSLog(#"Counter: %#", #(self.counter));
self.blinker.alpha = 0.0;
} completion:^(BOOL finished) {
[UIView animateWithDuration:1.0 animations:^{
self.blinker.alpha = 1.0;
} completion:^(BOOL finished) {
//check to see if you want to continue blinking if so call next again
[self next];
}];
}];
}
I don't have any context as to why you want to count, but hopefully that helps.

Modal custom segues in iOS 7.1 give warnings when being dismissed

Background
I've made a custom segue to allow for "overlay cards" to slide down from the top, and then fly away upon dismissal. When writing the code, I realized that all examples used a UIViewControllerTransitioningDelegate to capture when the segue was animate/dismissed. So far so good, but all examples that I found let the presentingController act as the delegate, which to me sounds strange since I need to "distribute" the logics of how my "overlay card" looks like, in two places; the custom segue, and the presenting controller.
Hence, I figured that I could combine the two into one; the custom segue, which would make my storyboard so much neater, since all view-controllers that I want to show as "overlay cards", simply are assigned the "CardSegue" that I've created, instead of having each and every presenting controller implement UIViewControllerTransitioningDelegate.
Problem
From one of the presenting controllers, I get a warning when dismissing the presented "Overlay card";
UIModalPresentationCustom presentation style can only be used with an animator or with unanimated dismissals.
I looked around, and all I could find was another post that seemed promising;
iOS 7.1 UIModalPresentationCustom warning message
But, the problem is that since my custom segue IS the transitionDelegate, trying to keep it as one logical unit, I can't really apply the reasoning of retaining the delegate...? I mean, the segue is running -- hence, the delegate is alive and well. I'm missing something, or there's an obvious reason that I cannot see right now for why the transitionDelegate MUST be separated from the custom segue?
My code
#define POPUP_MARGIN 25
#interface CardSegue () <UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning>
#property (nonatomic, assign, getter = isPresenting) BOOL presenting;
#end
#implementation CardSegue
- (void)perform
{
//Make sure that we are the delegate...
[self.destinationViewController setTransitioningDelegate:self];
[self.destinationViewController setModalPresentationStyle:UIModalPresentationCustom];
//Brute-force show the end-result!
[[self sourceViewController] presentViewController:[self destinationViewController] animated:YES completion:nil];
}
#pragma mark - UIViewControllerTransitioningDelegate Methods
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
presentingController:(UIViewController *)presenting
sourceController:(UIViewController *)source
{
//Configure the animator
self.presenting = YES;
return self;
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
self.presenting = NO;
return self;
}
#pragma mark - UIViewControllerContextTransitioning Methods
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
return 0.5f;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
// Grab the from and to view controllers from the context
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
if (self.presenting)
{
// Set our ending frame. We'll modify this later if we have to
CGRect frame = fromViewController.view.frame;
CGRect endFrame = CGRectMake(POPUP_MARGIN, POPUP_MARGIN, frame.size.width - 2 * POPUP_MARGIN, frame.size.height - 2 * POPUP_MARGIN);
fromViewController.view.userInteractionEnabled = NO;
toViewController.view.alpha = 0.0f;
toViewController.view.layer.cornerRadius = 15;
//toViewController.view.layer.borderWidth = 1.5f;
toViewController.view.layer.masksToBounds = YES;
UIView *mainView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
mainView.backgroundColor = [[UIColor alloc] initWithRed:0.0 green:0.0 blue:0.0 alpha:0.0];
[transitionContext.containerView addSubview:fromViewController.view];
[transitionContext.containerView addSubview:mainView];
[transitionContext.containerView addSubview:toViewController.view];
CGRect startFrame = endFrame;
startFrame.origin.y -= frame.size.height;
toViewController.view.frame = startFrame;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
mainView.backgroundColor = [[UIColor alloc] initWithRed:0.0 green:0.0 blue:0.0 alpha:0.5];
fromViewController.view.tintAdjustmentMode = UIViewTintAdjustmentModeDimmed;
toViewController.view.frame = endFrame;
toViewController.view.alpha = 1.0f;
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
else {
// Set our ending frame. We'll modify this later if we have to
CGRect frame = toViewController.view.frame;
CGRect endFrame = CGRectMake(POPUP_MARGIN, POPUP_MARGIN, frame.size.width - 2 * POPUP_MARGIN, frame.size.height - 2 * POPUP_MARGIN);
toViewController.view.userInteractionEnabled = YES;
UIView *mainView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
mainView.backgroundColor = [[UIColor alloc] initWithRed:0.0 green:0.0 blue:0.0 alpha:0.5];
[transitionContext.containerView addSubview:toViewController.view];
[transitionContext.containerView addSubview:mainView];
[transitionContext.containerView addSubview:fromViewController.view];
endFrame.origin.y += frame.size.height;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
toViewController.view.tintAdjustmentMode = UIViewTintAdjustmentModeAutomatic;
mainView.backgroundColor = [[UIColor alloc] initWithRed:0.0 green:0.0 blue:0.0 alpha:0.0];
fromViewController.view.frame = endFrame;
fromViewController.view.alpha = 0.0f;
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
}
#end

How to flip an individual UIView (without flipping the parent view)

This is an iPad project where I have a UIView with several subViews, and I am trying to animate one of this UIViews using [UIView transitionFromView:toView:duration:options:completion], but when I run this method the whole parent view gets flipped!
This is the code I'm using:
UIView *secondView = [[UIView alloc] initWithFrame:CGRectMake(200, 200, 300, 300)];
[newView setBackgroundColor:[UIColor redColor]];
[UIView transitionFromView:self.firstView.view toView:secondView duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft completion:nil];
Any idea on how can I flip between this views without animating the whole parent view?
Thanks!
this code may helps you:
put the two views you want to flip inside an unnamed view with the same size
and link the IBOutlet UIView *newView,*oldView; to the views and put the new view on top
bool a = NO;
#implementation ViewController
- (IBAction)flip:(id)sender
{
if (a == NO) {
[UIView transitionFromView:oldView toView:newView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromLeft
completion:NULL];
a = YES; // a = !a;
}
else {
[UIView transitionFromView:newView toView:oldView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromLeft
completion:NULL];
a = NO; // a = !a;
}
}
I had problems getting this to work also. I used the code written by Floris, but although it worked for the first "flip" the next flip started to go weird where the buttons and labels on the frontside UI View lost there positions.
I put the code below into place and it is working fine.
Couple of things to note:
panelView is a UIView control on the ViewController that has two other UIViews nested inside it (subviews). The first one is called frontView, second one is called backView. (see picture below)
I have a bool property called displayingFront on the class
(in my .h)
#property BOOL displayingFront;
in my .m
#synthesize displayingFront;
in my viewDidLoad method
self.displayingFront = YES;
This is the code in the .m that i have rigged up to the two buttons if front and back UIViews...
- (IBAction)flip:(id)sender
{
[UIView transitionWithView:self.panelView
duration:1.0
options:(displayingFront ? UIViewAnimationOptionTransitionFlipFromRight :
UIViewAnimationOptionTransitionFlipFromLeft)
animations: ^{
if(displayingFront)
{
self.frontView.hidden = true;
self.backView.hidden = false;
}
else
{
self.frontView.hidden = false;
self.backView.hidden = true;
}
}
completion:^(BOOL finished) {
if (finished) {
displayingFront = !displayingFront;
}
}];
}
Use a container view to hold your subviews, and make the container view the same size as your subview that you're trying to animate.
So, you can setup your views like this.
self.view-> "a container view" -> subviews
**//flipping view continuously**
bool a = NO;
-(void)viewDidLoad
{
// THIS is the canvass holding the two views this view is flipping
UIView *v=[[UIView alloc]initWithFrame:CGRectMake(40, 40, 150, 150)];
v.backgroundColor=[UIColor clearColor];
[self.view addSubview:v];
[v addSubview:yourfirstview];
[v addSubview:yoursecondview];
// calling the method
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(flip)
userInfo:nil
repeats:YES];
}
//flipping view randomly
-(void)flip
{
if (a == NO)
{
[UIView transitionFromView:yourfirstview toView:yoursecondview duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft completion:NULL];
a = YES;
}
else if(a==YES)
{
[UIView transitionFromView:yoursecondview toView:yourfirstview duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft completion:NULL];
a = NO;
}
}
I fixed this problem by using old-school animations:
[UIView beginAnimations:#"Flip" context:nil];
[UIView setAnimationDuration:1];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:firstView cache:YES];
[self addSubview:secondView];
[UIView commitAnimations];
Additionally you can remove the firstView:
[firstView removeFromSuperview];
I'd like to make an update of Steve Parker's answer in SWIFT 4:-
var displayBlankView:Bool = true
UIView.transition(with:flipView, duration:1.0, options:displayBlankView ? .transitionFlipFromLeft:.transitionFlipFromRight, animations:{
if(self.displayBlankView){
self.view1.isHidden=true
self.view2.isHidden=false
}else{
self.view1.isHidden=false
self.view2.isHidden=true
}
}, completion:{
(finished: Bool) in
self.displayBlankView = !self.displayBlankView;
})

Adding a TabBarController as the Subview of a View

I am loading a splash screen when my app starts. Then I want to load a TabBarController and it's ViewControllers. However, my TabBarController window does not scale to the screen size.
Probably 3/4 of the TabBar at the bottom is getting cut off and There is a slim aprox 20 pixel gap at the top of the screen below the status bar. How do I resize the TabBarController properly?
Here is the code in my SplashViewController loading the splash view, and the TabBarController:
-(void)loadView{
// Init the view
CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
UIView *view = [[UIView alloc] initWithFrame:appFrame];
view.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
self.view = view;
[view release];
splashImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Splash.png"]];
splashImageView.frame = CGRectMake(0,0,320,458);
[self.view addSubview:splashImageView];
viewController = [[FlashCardViewController alloc] initWithNibName:#"FlashCardViewController" bundle:[NSBundle mainBundle]];
//viewController.view.bounds = [[UIScreen mainScreen]bounds];
viewController.title = #"Quiz";
viewController.tabBarItem.image = [UIImage imageNamed:#"puzzle.png"];
UIViewController *viewController2 = [[UIViewController alloc] initWithNibName:nil bundle:nil];
viewController2.title = #"Nada";
viewController2.tabBarItem.image = [UIImage imageNamed:#"magnifying-glass.png"];
//viewController.view.alpha = 0.0;
//[self.view addSubview:viewController.view];
tabBarController = [[UITabBarController alloc] init];
tabBarController.viewControllers = [NSArray arrayWithObjects:viewController, viewController2, nil];
[viewController2 release];
tabBarController.view.alpha = 0.0;
//tabBarController.tabBarItem.image = [UIImage imageNamed:#"State_California.png"];
//tabBarController.tabBarItem.title = #"State_California.png";
tabBarController.view.bounds = [[self view] bounds];
//tabBarController.view.frame = [[UIScreen mainScreen] applicationFrame];
[self.view addSubview:tabBarController.view];
timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(fadeScreen) userInfo:nil repeats:NO];
}
-(void) fadeScreen
{
[UIView beginAnimations:nil context:nil]; // begin animation block
[UIView setAnimationDuration:0.75]; // sets animation duration
[UIView setAnimationDelegate:self]; // sets the delegate for this block
[UIView setAnimationDidStopSelector:#selector(finishedFading)]; // Calls finishFading
self.view.alpha = 0.0; // // Fades the alpha to 0 over animation
[UIView commitAnimations]; // Commits this block, done
}
-(void) finishedFading
{
[UIView beginAnimations:nil context:nil]; // Begin animation block
[UIView setAnimationDuration:0.75]; // set duration
self.view.alpha = 1.0; // fades the view to 1.0 alpha over .75 seconds
//viewController.view.alpha = 1.0;
tabBarController.view.alpha = 1.0;
[UIView commitAnimations];
[splashImageView removeFromSuperview];
}
I've just completed pretty much the same and ran into the same problems but eventually I got it working.
Create a View Controller class in Xcode called Test1ViewController and add the following:
#interface Test1ViewController : UIViewController {
IBOutlet UITabBarController *tbc;
}
#property (nonatomic,retain) IBOutlet UITabBarController *tbc;
#end
Create a View XIB called Test1View
Add a TabBarViewController to the XIB
Set the File's Owner in the XIB to be the Test1ViewController.
Connect the tbc IBOutlet in the File's Owner to the Tab Bar Controller in the XIB.
Connect the view IBOutlet in the File's Owner to the View in the XIB.
In your SplashViewController.h add the property
Test1ViewController *tabBarViewController;
Synthesize the tabBarViewController in your SplashViewController.m.
Replace your TabBarController creation code in your loadView method in SplashViewController with the following:
tabBarViewController = [[Test1ViewController alloc] initWithNibName:
#"Test1View" bundle:[NSBundle mainBundle]];
tabBarViewController.view.alpha = 0.0;
[self.view addSubview:[tabBarViewController view]];
Here's the bit that was missing for me. In Test1ViewController.m, you need to add the following line to the viewDidLoad method:
self.view = tbc.view;
Finally, I also had to change the finishedFading method in SplashViewController.m to set the alpha to 1.0 on the tabBarViewController view.
-(void) finishedFading
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.75];
self.view.alpha = 1.0;
tabBarViewController.view.alpha = 1.0;
[UIView commitAnimations];
[splashImageView removeFromSuperview];
}
I hope this helps.
I finally found someting that works. Instead of:
tabBarController.view.frame = [[UIScreen mainScreen] applicationFrame];
or
tabBarController.view.bounds = [[self view] bounds];
Because I couldn't find and automatic or named settings for this size, I had to create my own rectangle that is the size of the screen minus the statusBar.
tabBarController.view.frame = CGRectMake(0,0,320,460);