Scale a UIView with animation but without Transform - ios

I try to scale a UIView with a circle shape to make it double in size when I click on it.
my final target is to reproduce the apple music behavior:
-When you click on a circle, it grows and pushes other circles around.
I m trying to achieve this with UIKit dynamics but the first problem is that animating a scale without CATransform (it doesn't work with collisions) fails.
at the end of the animation, even if width and height are 100,
the final shape is a rectangle...
here is my code
func ballTapped(sender:UITapGestureRecognizer) {
if sender.state == .Ended {
collision.removeItem(sender.view!)
let item = sender.view!
var theFrame = item.frame
theFrame.size.height = 100
theFrame.size.width = 100
UIView.animateWithDuration(0.5, animations: { () -> Void in
item.frame = theFrame
}, completion: { (Bool) -> Void in
self.collision.addItem(sender.view!)
}
)
}
}

[UIView animateWithDuration:0.5
animations:^{
[UIView animateWithDuration: 0.5 delay: 0.0 options:UIViewAnimationOptionAllowUserInteraction |UIViewAnimationOptionCurveEaseInOut animations:^{
_iCarouselCarVW.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.001, 0.001);
_iCarBackView.hidden = YES;
NSLog(#"%f",self.view.frame.size.width);
_iCarouselCenterConstant.constant = self.view.frame.size.width/2 + 50;
[self.iCarouselCarVW setCurrentItemIndex:0];
} completion:^(BOOL finished)
{
}];
[self.view layoutIfNeeded]; // Called on parent view
}];

Related

UIView appereance from bottom to top and vice versa(Core Animation)

My goal is to understand and implement feature via Core Animation.
I think it's not so hard,but unfortunately i don't know swift/Obj C and it's hard to understand native examples.
Visual implementation
So what exactly i want to do(few steps as shown on images):
1.
2.
3.
4.
And the same steps to hide view(vice versa,from top to bottom) until this :
Also,i want to make this UIView more generic,i mean to put this UIView on my StoryBoard and put so constraints on AutoLayout(to support different device screens).
Any ideas? Thanks!
You can use like this Extension
extension UIView{
func animShow(){
UIView.animate(withDuration: 2, delay: 0, options: [.curveEaseIn],
animations: {
self.center.y -= self.bounds.height
self.layoutIfNeeded()
}, completion: nil)
self.isHidden = false
}
func animHide(){
UIView.animate(withDuration: 2, delay: 0, options: [.curveLinear],
animations: {
self.center.y += self.bounds.height
self.layoutIfNeeded()
}, completion: {(_ completed: Bool) -> Void in
self.isHidden = true
})
}
}
Assuming the original view is something like:
var view = new UIView(new CGRect(View.Frame.Left, View.Frame.Height - 200, View.Frame.Right, 0));
view.BackgroundColor = UIColor.Clear;
Show:
UIView.Animate(2.0, 0.0,
UIViewAnimationOptions.CurveLinear,
() =>
{
view.BackgroundColor = UIColor.Blue;
var height = 100;
view.Frame = new CGRect(View.Frame.Left, view.Frame.Y - height , view.Superview.Frame.Right, height);
},
() =>
{
// anim done
}
);
Hide:
UIView.Animate(2.0, 0.0,
UIViewAnimationOptions.CurveLinear,
() =>
{
view.BackgroundColor = UIColor.Clear;
var height = 100;
view.Frame = new CGRect(View.Frame.Left, view.Frame.Y + height, view.Superview.Frame.Right, 0);
},
() =>
{
view.Hidden = true;
}
);
See my view case was opposite i am directly doing changes in that , test if it is working for you,
Show Logic
//Add your view on storyBoard / programmatically bellow tab bar
[self.view bringSubviewToFront:self.miniMenuView];
CGRect rectformedicationTableViewcell;// = CGRectZero;
rectformedicationTableViewcell = CGRectMake(0.0f, self.view.frame.size.hight, self.view.frame.size.width, 150.0f);
self.miniMenuView.frame = rectformedicationTableViewcell;
if([self.miniMenuView superview]) {
self.miniMenuView.hidden = YES;
}
self.miniMenuView.hidden = NO;
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
[self.miniMenuView setFrame:CGRectMake(0.0f, self.view.frame.size.hight - 150.0f, self.view.frame.size.width, 150.0f)];
}
completion:nil];
Hide Logic
[self.view sendSubviewToBack:self.miniMenuView];
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
[self.miniMenuView setFrame:CGRectMake(0.0f, self.view.frame.size.hight, self.view.frame.size.width, 150.0f)];
}
completion:^(BOOL completed){
if([self.miniMenuView superview]) {
self.miniMenuView.hidden = YES;
}
}];
Consider this as basic idea do changes as per your requirements Best luck.
Update answer from #SushiHangover in swift 4.x
Assuming the original view is something like:
yourView.frame = CGRect(x: 0, y: 900, width: yourView.frame.width, height: yourView.frame.height)
Show:
UIView.animate(withDuration: 0.5, delay: 0.2, options: .curveLinear, animations: {
let height:CGFloat = 280;
self.yourView.frame = CGRect(x: 0, y: self.yourView.frame.minY - height, width: self.yourView.frame.width, height: self.vBaseView.frame.height)
}) { finished in
// animation done
}
Hide
Just add the height
self.yourView.frame.minY + height
I have also faced the issue like https://i.stack.imgur.com/fuVhy.gif commented https://stackoverflow.com/users/4793465/xtl for the above solution.
Am using the view at the bottom of web-view to show and hide like safari mobile browser.
attached the sample code below
UIView *viewV;
UILabel *label;
and viewdidload
-(void)viewDidLoad {
[super viewDidLoad];
WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:theConfiguration];
webView.navigationDelegate = self;
webView.UIDelegate = self;
webView.scrollView.delegate = self;
NSURL *nsurl=[NSURL URLWithString:#"https://www.google.com/"];
NSURLRequest *nsrequest=[NSURLRequest requestWithURL:nsurl];
[webView loadRequest:nsrequest];
[self.view addSubview:webView];
viewV = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 50, self.view.frame.size.width, 50)];
viewV.backgroundColor = [UIColor blueColor];
[webView addSubview:viewV];}
and scroll view delegate
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint velocity = [[scrollView panGestureRecognizer] velocityInView:scrollView.superview];
if (velocity.y == 0) {
return;
}
if (velocity.y < -1) {
// Scrolling left
NSLog(#"Top");
if (viewV.frame.origin.y != self.view.frame.size.height - 50) {// if already hidden, don't need to hide again
return;
}
[UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
viewV.backgroundColor = [UIColor clearColor];
viewV.frame = CGRectMake(0, viewV.frame.origin.y + 50, self.view.frame.size.width, 0);
} completion:^(BOOL finished) {
}];
} else if (velocity.y > 1) {
// Scrolling Right
NSLog(#"Bottom");
if (viewV.frame.origin.y != self.view.frame.size.height) { // if already shown, no need to do show again
return;
}
[UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
viewV.backgroundColor = [UIColor blueColor];
viewV.frame = CGRectMake(0, viewV.frame.origin.y - 50, self.view.frame.size.width, 50);
} completion:^(BOOL finished) {
}];
}}
This worked for me.

Animate rectangle by change width and height

I have ImageView in my UIViewController:
And i want to animate this Image, periodically change width and height. The same animation you can see at Apple Wallet app on Iphone, if you click "Scan code". I tried a lot, but my code working incorrect or app begin lagging after 3-5 min...
This is my last code:
class ViewController: UIViewController {
#IBOutlet weak var focusImage: UIImageView!
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.resizeFocusImageUp()
}
func resizeFocusImageUp() {
UIView.animateWithDuration(2.0, delay: 0.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
self.focusImage.frame = CGRectMake(
self.focusImage.frame.origin.x+50,
self.focusImage.frame.origin.y-50,
self.focusImage.frame.size.width-50,
self.focusImage.frame.size.height+50
)
self.view.layoutIfNeeded()
self.view.bringSubviewToFront(self.focusImage)
}, completion: {(Bool) in
self.resizeFocusImageDown()
})
}
func resizeFocusImageDown() {
UIView.animateWithDuration(2.0, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self.focusImage.frame = CGRectMake(
self.focusImage.frame.origin.x-50,
self.focusImage.frame.origin.y+50,
self.focusImage.frame.size.width+50,
self.focusImage.frame.size.height-50
)
self.view.layoutIfNeeded()
self.view.bringSubviewToFront(self.focusImage)
}, completion: {(Bool) in
self.resizeFocusImageUp()
})
}
}
Also, for my ImageView i use constraints:
As the other poster said, if you're using AutoLayout then you need to animate the constraints, not the frame.
Create constraints for the height & width of your view, and position if you need to move it in your animation. Then control-drag from your CONSTRAINTS into the header of your view controller to create outlets.
Then in your animation block change the constant value of each constraint, then call layoutIfNeeded to trigger the animation. (The critical part is that the call to layoutIfNeeded needs to be inside the animation block. It seems that what actually causes the constraint to change the size/position of the view(s).)
All that being said, for a line drawing like you show in your post, you'd be much better off using a CAShapeLayer and animating the path of the layer. You'd get much crisper-looking shapes, and the animations are very smooth.
//Below Objective-C code working correctly in my app. Do your stuff as per your requirements.
Note: If you are using autoalyout for that object(Image view) then add below two line code in view did load method.
imageView.translatesAutoresizingMaskIntoConstraints = YES;
imageView.frame = CGRectMake(x, y, width, height);
//Method For Zoom IN Animations Of View
-(void)ZoomInAnimationOfView{
imageView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.01, 0.01);
[UIView animateWithDuration:1.0 animations:^{
imageView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.0, 1.0);
} completion:^(BOOL finished) {
imageView.transform = CGAffineTransformIdentity;
}];
}
//Method For Zoom Out Animations
-(void)ZoomOutAnimationOfView{
imageView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.0, 1.0);
[UIView animateWithDuration:1.0 animations:^{
imageView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.001, 0.001);
} completion:^(BOOL finished) {
imageView.transform = CGAffineTransformIdentity;
}];
}

Flip image to reveal back iOS

Basic newbie question.
I am looking to place several images into a collection view. How can I have the image flip over to reveal the back of the image in Xcode using swift? Thank you all for your help.
I'm not sure about what exactly are you trying to achieve, but I'm using this function to rotate image horizontally and to change the image during the transition. Hope it helps.
private func flipImageView(imageView: UIImageView, toImage: UIImage, duration: NSTimeInterval, delay: NSTimeInterval = 0)
{
let t = duration / 2
UIView.animateWithDuration(t, delay: delay, options: .CurveEaseIn, animations: { () -> Void in
// Rotate view by 90 degrees
let p = CATransform3DMakeRotation(CGFloat(GLKMathDegreesToRadians(90)), 0.0, 1.0, 0.0)
imageView.layer.transform = p
}, completion: { (Bool) -> Void in
// New image
imageView.image = toImage
// Rotate view to initial position
// We have to start from 270 degrees otherwise the image will be flipped (mirrored) around Y axis
let p = CATransform3DMakeRotation(CGFloat(GLKMathDegreesToRadians(270)), 0.0, 1.0, 0.0)
imageView.layer.transform = p
UIView.animateWithDuration(t, delay: 0, options: .CurveEaseOut, animations: { () -> Void in
// Back to initial position
let p = CATransform3DMakeRotation(CGFloat(GLKMathDegreesToRadians(0)), 0.0, 1.0, 0.0)
imageView.layer.transform = p
}, completion: { (Bool) -> Void in
})
})
}
Don't forget to import GLKit.
-(void)showButtton
{
self.userInteractionEnabled = false;
[UIView transitionWithView:self duration:0.3 options:UIViewAnimationOptionTransitionFlipFromRight animations:^{
//code to change the image of UIButton
} completion:^(BOOL finished) {
self.userInteractionEnabled = true;
}];
}
Here is a code to do so.

How to animate (fade) UILabel textColor in Swift?

UIView.animateWithDuration(self.fadeTime, delay: 0,
options: UIViewAnimationOptions.AllowUserInteraction,
animations: { [weak self] () -> Void in
var randomIndex = Int(arc4random_uniform(UInt32(CONSTANTS.MainColorScheme.count)))
self?.startButton.titleLabel!.textColor = CONSTANTS.MainColorScheme[randomIndex]
}) { (stuff Bool) -> Void in
}
This doesn't seem to work...it just "jumps" to the next color. It doesn't fade.
However, if I apply the same approach to self.view.backgroundColor, my code works.
Is there an alternative?
UILabel.textColor does not support animation. But you can animate CATextLayer:
Swift:
let textLayer = CATextLayer()
textLayer.string = "Your text"
textLayer.foregroundColor = yourFirstColor
textLayer.frame = yourButton.bounds
yourButton.layer.addSublayer(textLayer)
UIView.animateWithDuration(1) {
textLayer.foregroundColor = yourSecondColor
}
Objective C:
CATextLayer *textLayer = [CATextLayer layer];
[textLayer setString:#"Your text"];
[textLayer setForegroundColor:yourFirstColor];
[textLayer setFrame:yourButton.bounds];
[[yourButton layer] addSublayer:textLayer];
[UIView animateWithDuration:1 animations:^{
textLayer.foregroundColor = yourSecondColor;
}];
Indeed, the UIView.animateWithDuration will work with UILabel backgroundColor, while for the textColor it won't because color doesn't animate with UIView animations. Then, how to achieve?
Solution 1:
You can go for CATextLayer instead of a UILabel and then animate the color. For objective-C you can refer to this - iPad: Animate UILabels color changing
Solution 2:
You can use NSTimer and decrease the UILabel alpha as the time passes
Solution 3:
Or simply you can go for "FadeIn - FadeOut" animation with UIView.transitionWithView
Try this..
UIView.transitionWithView(YOURLABEL, duration: 0.325, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
// animation...
}, completion: { (fininshed: Bool) -> () in
// completion...
})
Also check for other syntax here
Cheers! :)

Resize UITableView height, and keep scrolled at bottom

Im trying to resize the tableview's height with a animation, it works fine by animating the tableview's frame.size.height.
The problem is, i have a tableview that is 200px height and scrolled to the bottom, i want to animate this to 100px, i run a simple
[UIView animateWithDuration:0.245f animations:^{
CGRect frame = tableview.frame;
frame.size.height = 100.f;
tableview.frame = frame;
}];
this works fine, but after i resized it it is no longer scrolled to the bottom of the tableview. i want the tableview to always be scrolled at the bottom while animating. i tried alot of differnet things like calling
[tablview scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
right before/after i start the resize animation, but i have not managed to get them to sync 100%. Is there a way to resize the tableview while the bottom of the tableview displays the last element of the scrollview.
I found a solution for my problem, there might very well be a better solution but this actually works quite well :)
Before i start animating i see if the contentSize.height is larger than the target height, if so i do following:
if (mMessages.contentSize.height > 150.f)
{
CGFloat expandedOffsetY = 52.f;
CGFloat collapsedBottomOffset = (150.f + 18.f);
CGFloat expandedBottomOffset = (MIN(320.f, mMessages.contentSize.height) + expandedOffsetY);
tableFrame.origin.y = (collapsedBottomOffset - expandedBottomOffset) + expandedOffsetY;
}
else
{
tableFrame.origin.y = 18.f;
tableFrame.size.height = 150.f;
}
this will put the tableview in a minus origin.y, then i have wrapped the tableview inside a "parent" uiview with clip sub views = YES.
Then i have a "completed" animation block that "resets" to the target values.
CGRect tableFrame = mMessages.frame;
tableFrame.origin.y = 18.f;
tableFrame.size.height = 150.f;
mMessages.frame = tableFrame;
if (mMessages.contentSize.height > tableFrame.size.height)
{
float contentOffsetY = mMessages.contentSize.height - tableFrame.size.height;
mMessages.contentOffset = CGPointMake(0.0f, contentOffsetY);
}
Try updating the contentOffset of the table view as you animate the frame.
[UIView animateWithDuration:0.245f animations:^{
CGRect frame = tableview.frame;
frame.size.height = 100.f;
tableview.frame = frame;
float contentOffsetY = tableview.contentSize - tableview.frame.size.height;
tableview.contentOffset = CGPointMake(0, contentOffsetY);
}];
[UIView animateWithDuration:0.245f animations:^{
CGRect frame = tableview.frame;
frame.size.height = 100.f;
tableview.frame = frame;
} completion:^(BOOL finished) {
// Find your last indexpath
[tablview scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
} ];
Scrolling the tableview to the bottom in the animation block was working for me, with one little twist: when I reduced the hight of the tableView I called scrollToIndexPath without animating, and when I expand the tableView I called the scrollToIndexPath with animation.
UIView.animate(withDuration: 0.25) { [weak self] in
guard let strongSelf = self
else { return }
// ...
// updateConstraints
// ...
strongSelf.view.layoutIfNeeded()
if reduceSize {
tableView.scrollToLastRow(animated: false)
} else {
tableView.scrollToLastRow(animated: true)
}
}
Extension for UITableView
extension UITableView {
func scrollToLastRow(animated: Bool) {
let lastSection = numberOfSections-1
let lastRow = numberOfRows(inSection: lastSection)-1
let lastIndexPath = IndexPath(row: lastRow, section: lastSection)
scrollToRow(at: lastIndexPath, at: .bottom, animated: animated)
}
}

Resources