How to animate change button title? - ios

I want to animation change button title. example, the title of button is "one", after I click that button, fade out "one" and then fade in "NewTitle".

Rather than messing around with duplicate views, simple fade transitions can be made with the CATransition class.
// CATransition defaults to fade
CATransition *fade = [CATransition animation];
// fade.duration = ...
[button.layer addAnimation:fade];
[button setTitle:#"New title" forControlState:UIControlStateNormal];
The button will fade to its new state. This works for labels, entire view hierarchies, whatever.

So creating two buttons was not a good Idea so I have created a simple project to test code for your question and here is what I come up with
viewcontroller.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController{
IBOutlet UIButton *myButton;
}
#property(nonatomic,strong) IBOutlet UIButton *myButton;
-(IBAction)animateFadeOutButtonTitle:(id)sender;
-(void)animateFadeInButtonTitle;
#end
viewcontroller.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize myButton=_myButton;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[_myButton setTitle:#"One" forState:UIControlStateNormal];
}
-(IBAction)animateFadeOutButtonTitle:(id)sender
{
[UIView animateWithDuration:0.25 animations:^{_myButton.titleLabel.alpha = 0.0;}];
[self performSelector:#selector(animateFadeInButtonTitle) withObject:self afterDelay:1.0];
}
-(void)animateFadeInButtonTitle;
{
[_myButton setTitle:#"New Title" forState:UIControlStateNormal];
[UIView animateWithDuration:2.0
delay:0.0
options: UIViewAnimationCurveEaseInOut
animations:^{_myButton.titleLabel.alpha = 1.0;}
completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end

Related

Implementing Firebase's GoogleSignIn results in 2 iterations of the same view controller popping up, one on top of the other

Essentially, whenever a user clicks on a button that transitions them to a screen where they can sign in using their google credentials, the end-result is that 2 of the same exact screen pop up. The second screen has a useable button, which functions perfectly, however when they use the button I've implemented, which allows them to transition to previous screens in the ViewController stack, the result is that the application crashes. Now, I have no idea why there are even 2 iterations of this view controller in the first place, is there anyone that can help diagnose my issue? I will attach the code for the view controller below, however I must mention that this same behaviour is observed with only the " [GIDSignIn sharedInstance].uiDelegate = self;" being preserved (I.E. If I delete all other google sign in related functions, it still displays this behaviour). The code:
#import "FifthViewController.h"
#import Firebase;
#import GoogleSignIn;
#interface FifthViewController ()
#property (weak, nonatomic) IBOutlet UIButton *googleLoginBackButton;
#property (weak, nonatomic) IBOutlet GIDSignInButton *googleLoginButton;
#end
#implementation FifthViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self setNeedsStatusBarAppearanceUpdate];
[GIDSignIn sharedInstance].uiDelegate = self;
[GIDSignIn sharedInstance].delegate = self;
_googleLoginBackButton.layer.cornerRadius = _googleLoginBackButton.frame.size.width / 2;
self.googleLoginBackButton.layer.borderWidth = 1.5f;
self.googleLoginBackButton.layer.borderColor = [UIColor whiteColor].CGColor;
[_googleLoginBackButton addTarget:self action:#selector(backButtonHighlightBorder) forControlEvents:UIControlEventTouchDown];
[_googleLoginBackButton addTarget:self action:#selector(backButtonUnhighlightBorder) forControlEvents:UIControlEventTouchUpInside];
[_googleLoginBackButton addTarget:self action:#selector(backButtonUnhighlightBorder) forControlEvents:UIControlEventTouchDragExit];
_googleLoginButton.style = kGIDSignInButtonStyleWide;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
}
- (void)backButtonHighlightBorder
{
_googleLoginBackButton.layer.borderColor = [UIColor colorWithRed:0.61 green:0.00 blue:0.02 alpha:1.0].CGColor;
}
- (void)backButtonUnhighlightBorder
{
_googleLoginBackButton.layer.borderColor = [UIColor whiteColor].CGColor;
}
- (IBAction)dismissViewControllerAnimated:(UIButton *)sender
{
[UIView animateWithDuration:0.2
animations:^
{
_googleLoginBackButton.frame = CGRectMake(168, 700, 40, 40);
}
completion:^(BOOL finished)
{
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
[UIView animateWithDuration:0.1
delay:0.45
options:0
animations:^
{
_googleLoginBackButton.frame = CGRectMake(168, 564, 40, 40);
}
completion:^(BOOL finished)
{
}
];
}
];
}
- (void)signIn:(GIDSignIn *)signIn didSignInForUser:(GIDGoogleUser *)user withError:(NSError *)error
{
if (!error)
{
[self performSegueWithIdentifier:#"toHome" sender:self];
}
}
#end
For any of you as neglectful as I am, double check the implementation of the controller that navigates to this one. I found that I had a manual segue in the actual GUI Storyboard area that navigated to a controller on the press of a button, however I also implemented a segue programatically. The simple solution is to convert the GUI Storyboard segue into one that links between controllers, not between actions and controllers.

how to present modal UIViewController dialog from bottom in IOS like this (image attached)

How would one present a UIViewController (from Storyboard say) that is modal, and slides up from the bottom of the presenting view controller. Requirements would be:
slides up from bottom, with widths aligning with the width of the presenting view controller
does NOT take up whole screen or whole parent presenting view controller (rather only is as high as required to show itself)
can be shown within the context of a view controller which doesn't take the whole screen
I do not use storyboards so I wrote it all out. You can copy paste this into a brand new project and run it to see it working.
Your PresentingController needs to conform to two things. The first protocol is: UIViewControllerTransitioningDelegate which allows the controller to provide a custom presenter (namely itself in our case below). Whatever you return here (be it self, or some other object) needs to conform to UIViewControllerAnimatedTransitioning and provide the custom animations. For this self-contained example, I chose the current viewController to be the presenter and animator.
Next, it needs to conform to protocol: UIViewControllerAnimatedTransitioning which provides the custom animation for any presenting or dismissing controllers.
In other words, when we present or dismiss a viewController, animateTransition from the UIViewControllerAnimatedTransitioning protocol will be called to determine how the child controller should animate into perspective or dismiss from the view-port.
Example (With Transition Animation):
//
// ViewController.m
// SO
//
// Created by Brandon T on 2017-01-23.
// Copyright © 2017 XIO. All rights reserved.
//
#import "ViewController.h"
//Some view controller that will be presented modally.
//I have coloured it red.
#interface ModalController : UIViewController
#end
#implementation ModalController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
}
#end
//The view controller that will present or dismiss some other view controller modally.
//I have coloured it white.
#interface ViewController () <UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning>
#property (nonatomic, assign) bool presentingModalController;
#property (nonnull, nonatomic, strong) ModalController *modalController;
#property (nonnull, nonatomic, strong) UIButton *button;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
//For this example, I add a button to present and dismiss the redViewController.
self.button = [[UIButton alloc] initWithFrame:CGRectMake(15, self.view.center.y - 100, self.view.frame.size.width - 30, 45)];
[self.button setTitle:#"Present" forState:UIControlStateNormal];
[self.button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[self.button setBackgroundColor:[UIColor lightGrayColor]];
[self.button.layer setCornerRadius:5.0];
[self.button addTarget:self action:#selector(onButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.button];
//Create the redViewController and set its transitioning delegate to self (this controller will be providing the animation and presenter).
//We also set the style to OverFullScreen because we don't want this controller to disappear.
//When a view controller is presented, the one that presented it usually disappears or gets removed from the hierarchy until the child is dismissed. In the case of alerts, or controllers that need to display OVER the current one, we need to set the modalPresentationStyle.
self.modalController = [[ModalController alloc] init];
self.modalController.transitioningDelegate = self;
self.modalController.modalPresentationStyle = UIModalPresentationOverFullScreen;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)onButtonClicked:(UIButton *)button {
if (self.modalController.view.window == nil) {
[self presentViewController:self.modalController animated:YES completion:nil];
[self.button setTitle:#"Dismiss" forState:UIControlStateNormal];
//not a good idea but meh.. I need to keep this example short.
[self.view.window addSubview:self.button];
}
else {
[self.modalController dismissViewControllerAnimated:YES completion:nil];
[self.button setTitle:#"Present" forState:UIControlStateNormal];
[self.view addSubview:self.button];
}
}
//Custom Animations and Presenters.
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
self.presentingModalController = true; //We are presenting the controller.
return self; //Who is animating it? We are.
}
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
self.presentingModalController = false; //We are dismissing the view controller.
return self; //Who animated it? We did.
}
//How fast should it present? I chose 0.5 seconds.
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
return 0.5;
}
//The actual animation code.
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
if (self.presentingModalController) {
//If we are presenting, we need to add the new controller's view as a sub-view.
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//We need a starting frame for the animation.
CGRect startingFrame = transitionContext.containerView.bounds;
startingFrame.origin.y = startingFrame.size.height; //Starts from the bottom of the parent.
startingFrame.size.height = 100; //Has a height of 100.
//We need an end frame for the animation.
CGRect finalFrame = transitionContext.containerView.bounds;
finalFrame.origin.y = finalFrame.size.height - 100; //100 from the bottom of the parent.
finalFrame.size.height = 100; //Present with a size of 100 height.
//Add the controller's view as a subview of the context.
[transitionContext.containerView addSubview:toViewController.view];
[toViewController.view setFrame:startingFrame];
//Start animating from "startFrame" --> "endFrame" with 0.5 seconds duration and no delay. I chose easeIn style.
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
[toViewController.view setFrame:finalFrame];
} completion:^(BOOL finished) {
//We are finished animating, complete the transition!
[transitionContext completeTransition:YES];
}];
}
else {
//If we are dismissing the view controller, we need to animate it down the screen and then remove its view from the context.
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
//We only need one frame. This is the first frame. We are animating from "endFrame" --> "startingFrame" (backwards/reverse animation).
CGRect startingFrame = transitionContext.containerView.bounds;
startingFrame.origin.y = startingFrame.size.height; //Starts from the bottom of the parent.
startingFrame.size.height = 100; //Has a height of 100.
//Start the animation with 0.5 seconds duration and I chose easeOut style.
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
[fromViewController.view setFrame:startingFrame];
} completion:^(BOOL finished) {
//Remove the view controller's view from the context and complete the transition!
[fromViewController.view removeFromSuperview];
[transitionContext completeTransition:YES];
}];
}
}
#end
Example (Without Transition Animation):
//
// ViewController.m
// SO2
//
// Created by Brandon Thomas on 2017-01-23.
// Copyright © 2017 XIO. All rights reserved.
//
#import "ViewController.h"
#interface ModalController : UIViewController
#end
#implementation ModalController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
}
#end
#interface ViewController ()
#property (nonatomic, assign) bool presentingModalController;
#property (nonnull, nonatomic, strong) ModalController *modalController;
#property (nonnull, nonatomic, strong) UIButton *button;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.button = [[UIButton alloc] initWithFrame:CGRectMake(15, self.view.center.y - 100, self.view.frame.size.width - 30, 45)];
[self.button setTitle:#"Present" forState:UIControlStateNormal];
[self.button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[self.button setBackgroundColor:[UIColor lightGrayColor]];
[self.button.layer setCornerRadius:5.0];
[self.button addTarget:self action:#selector(onButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.button];
self.modalController = [[ModalController alloc] init];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)onButtonClicked:(UIButton *)button {
if (self.modalController.view.window == nil) {
//Present
CGRect startingFrame = self.view.bounds;
startingFrame.origin.y = startingFrame.size.height; //Starts from the bottom of the parent.
startingFrame.size.height = 100; //Has a height of 100.
CGRect finalFrame = self.view.bounds;
finalFrame.origin.y = finalFrame.size.height - 100; //100 from the bottom of the parent.
finalFrame.size.height = 100; //Present with a size of 100 height.
[self.modalController.view setFrame:startingFrame];
[self.modalController willMoveToParentViewController:self];
[self addChildViewController:self.modalController];
[self.view addSubview:self.modalController.view];
[self.modalController didMoveToParentViewController:self];
[UIView animateWithDuration:0.5 animations:^{
[self.modalController.view setFrame:finalFrame];
} completion:^(BOOL finished) {
}];
}
else {
//Dismiss
CGRect startingFrame = self.view.bounds;
startingFrame.origin.y = startingFrame.size.height; //Starts from the bottom of the parent.
startingFrame.size.height = 100; //Has a height of 100.
[UIView animateWithDuration:0.5 animations:^{
[self.modalController.view setFrame:startingFrame];
} completion:^(BOOL finished) {
[self.modalController.view removeFromSuperview];
[self.modalController willMoveToParentViewController:nil];
[self.modalController removeFromParentViewController];
[self.modalController didMoveToParentViewController:nil];
}];
}
}
#end
Check out the Apple documentation for this:
Presenting a View Controller Using Custom Animations
To present a view controller using custom animations, do the following
in an action method of your existing view controllers:
Create the view controller that you want to present. Create your
custom transitioning delegate object and assign it to the view
controller’s transitioningDelegate property. The methods of your
transitioning delegate should create and return your custom animator
objects when asked. Call the
presentViewController:animated:completion: method to present the view
controller. When you call the
presentViewController:animated:completion: method, UIKit initiates the
presentation process. Presentations start during the next run loop
iteration and continue until your custom animator calls the
completeTransition: method. Interactive transitions allow you to
process touch events while the transition is ongoing, but
noninteractive transitions run for the duration specified by the
animator object.
EDIT:
Your alternative option is to create a container with your custom sizes and animate your UIViewController added as a child view of your UIViewController:
[self addChildViewController:content];
content.view.frame = [self frameForContentController];
[self.view addSubview:self.currentClientView];
[content didMoveToParentViewController:self];
Taken from this Thread

Setting a UILabel while in the middle or end of an animation from a UIButton action cancels the animation

As the title says, whenever I want to set the text of a UILabel after the animation of the movement of the label, it returns the UILabel back to the origin before the animation. What's interesting is that this only happens when the animation is triggered by a UIButton action.
I've replicated this in a new project. Copy the below code and see what happens.
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *hello;
- (IBAction)move:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.hello.text = #"Hello";
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)move:(id)sender {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
self.hello.text = #"Howdy";
});
[UIView animateWithDuration:1.0f animations:^{
self.hello.center = CGPointMake(self.hello.center.x + 50, self.hello.center.y + 50);
}];
}
#end
Where self.hello and move are linked to a UILabel and UIButton in the storyboard.
Playing around with this I can see that this error does not occur when instead of using the self.hello.center, you use self.hello.transform = CGAffineTransformMakeTranslation(50, 50). Why is this? Is there any way to continue using .center or do I have to change all of my animations to .transform?
Thanks for your help.
I've had to give into the ways of .transform
Please use constraints rather then modifying centre of label. Such issue happens when we mix auto layout and coordinate system handling. Which I feel you should do something similar to this :
[UIView animateWithDuration:1.0f animations:^{
self.xPos.constant = self.hello.center.x + 50;
self.yPos.constant = self.hello.center.y + 50;
[self.hello layoutIfNeeded];
} completion:^(BOOL finished) {
self.hello.text = #"Howdy";
}];
where xPos and yPos are x and y NSLayoutConstraint position of UILabel

Can someone please help me understand why my view does not shift up and how I can fix it?

I am a beginner to IOS programming (and programming in general) and I simply don't understand why my code does not shift everything on my screen when the keyboard appears on the screen, as I intend it to. Can someone please help me understand what I am missing here?
ViewController.h
#interface ViewController : UIViewController <UITextFieldDelegate> {
}
#property (strong, nonatomic) IBOutlet UITextField *firstRoommateTextField;
#property (strong, nonatomic) IBOutlet UITextField *secondRoommateTextField;
#property (strong, nonatomic) IBOutlet UILabel *calculatedValueLabel;
- (IBAction)calculateButton:(UIButton *)sender;
- (IBAction)textFieldDismiss2:(id)sender;
- (IBAction)textFieldDismiss1:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize firstRoommateTextField = _firstRoommateTextField;
#synthesize secondRoommateTextField = _secondRoommateTextField;
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
const int movementDistance = 210; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self animateTextField: textField up: YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextField: textField up: NO];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
I think you forgot to set the delegate for text field which you can set as
_firstRoommateTextField.delegate = self;
_secondRoommateTextField.delegate = self;
in viewDidLoad
or from storyBoard
ctrl+drag from textField to ViewController (right top just below View Controller scene ) and select delegate .
First check the textfield object is assigned to the delegate..i.e.
_firstRoommateTextField.delegate = self;
_secondRoommateTextField.delegate = self;
and then check your delegate methods are getting called or not.
Finally try this, instead of CGRectOffset, try CGRectMake.

How to make a button to behave like a table view cell

How do I customize a UIButton to behave like a table view cell?
I've tried make it in Interface Builder, but since the button is invisible and have no image, it's never gets highlighted when you press it. I want this grayed out behavior like on a table view cell.
Does anyone knows how to do it?
Simple way ever:
Make a 10x10px gray square png image, save it and name it like "greayButtBkg.png" and drag it into SupportingFiles in XCode.
Declare an Outlet of your button into your .h file and an IBAction method into your .m file.
yourFile.h:
#property (weak, nonatomic) IBOutlet UIButton *myButton;
yourFile.m:
- (IBAction)pressButton:(id)sender {
[_myButton setBackgroundImage:[UIImage imageNamed:#"greyButtBkg"] forState:UIControlStateHighlighted];
}
Your Button will get a grey background color when you'll tap it, and go back to its Default clearColor when you'll release your finger.
Hope this helps!
I have created a Custom Control that would behave like UITableViewCell. See code below
MyButton.h
#import <UIKit/UIKit.h>
#interface MyButton : UIView
- (id)initWithFrame:(CGRect)frame selector:(SEL)sel target:(id)target;
#end
MyButton.m
#import "MyButton.h"
#import <QuartzCore/QuartzCore.h>
#interface MyButton()
#property(assign)SEL selector;
#property(assign)id target;
#end
#implementation MyButton
- (id)initWithFrame:(CGRect)frame selector:(SEL)sel target:(id)target
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.selector=sel;
self.target=target;
self.backgroundColor=[UIColor darkGrayColor];
self.layer.cornerRadius=5.0;
UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapAction:)];
[self addGestureRecognizer:tapGesture];
}
return self;
}
-(void)tapAction:(id)sender{
[UIView animateWithDuration:.2 animations:^{
[self setBackgroundColor:[UIColor lightGrayColor]];
}];
[self performSelector:#selector(deSelect) withObject:nil afterDelay:.2];
}
-(void)deSelect{
[UIView animateWithDuration:.2 animations:^{
[self setBackgroundColor:[UIColor darkGrayColor]];
} completion:^(BOOL finished) {
[_target performSelector:_selector withObject:self];
}];
}
/*- (void)drawRect:(CGRect)rect
{
}*/
#end
Since, It uses UIView you can easily add, or draw anything on this control, and easily customize this.
Hope this helps.
Cheers.

Resources