Is it possible to animate the frame width of a UISearchBar? I find when I apply uiview animations to widen the bounds of a search bar it pops immediately to the final result as if the object internally is assuming control of how it animates and not allowing me to apply my own animations to it smoothly.
If I animate the position it moves smoothly, but I suspect the fact that the text input adjusts according to the presence of the cancel button might mean we don't have public access to animate the width through UIView animation. The sample snippet below slides the bar from x = 0 to 100 but pops the width to 600 pixels wide.
CGRect searchBarFrame = self.searchViewController.searchBar.frame;
searchBarFrame.origin.x = 100;
searchBarFrame.size.width = 600;
[UIView animateWithDuration:1.0
delay:0.0
options:0
animations:^{
self.searchViewController.searchBar.frame = searchBarFrame;
}
completion:^(BOOL completion){
}];
there is an "issue" with UISearchBar due to the inner views forcing the resize to ignore the animation. However, this can be overcome by the use of - layoutSubviews. I have included the expand and contract code in my project below
[UIView animateWithDuration:.3
animations:^ {
CGRect newBounds = locationSearch.frame;
newBounds.size.width += 215; //newBounds.size.width -= 215; to contract
locationSearch.frame = newBounds;
[locationSearch layoutSubviews];
}];
Hope this helps.
FYI, you can use UIViewAnimationOption instead of calling layoutsubviews explicitly,
So the code would look something like this..
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionLayoutSubviews
animations:^{
//Set the frame you want to the search bar
}
completion:^(BOOL finished) {
}];
This is how to overcome enlargement just to the Left side in swift
(This code will enlarge/shrinks the searchBar 93 pixels over the left side when user start/end editing)
SharedNavigationbBar is the UIView that implements the UISearchBarDelegate
searchBarWidth is an outlet to a constrain holding the width of the UISearchBar
An autolayout constrain must exists on your storyboard or nib file to allow resizing into the left side.
In this case, the neighbord left component is an UIButton.
Add the following code as an extension or inside your class to perform the animated resizing.
extension SharedNavigationBar: UISearchBarDelegate
{
//amount of pixels to enlarge to the left
private var offsetSearchBarLeft:CGFloat
{
get {
return 93
}
}
///Enlarges search bar
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
self.animateSearchBar(self.searchBar, enlarge: true)
}
///Shrinks search bar
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
self.animateSearchBar(self.searchBar, enlarge: false)
}
//shrinks or enlarge the searchbar (this will be the function to call inside the animation)
private func animateSearchBar(searchBar:UISearchBar, enlarge:Bool)
{
///Important here, for this to work, the option and the searchbar size must be handled this way
UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.LayoutSubviews, animations: { [weak self] () -> Void in
let multiplier: CGFloat = enlarge ? 1 : -1
let origin = searchBar.frame.origin.x + self!.offsetSearchBarLeft * multiplier
let width = searchBar.frame.width + self!.offsetSearchBarLeft * multiplier
//This Block of code, setting the new frame, needs to be inside the animation in order to work
var newBounds:CGRect = searchBar.frame;
newBounds.origin.x = origin
newBounds.size.width = width
//Sets the new frame
self?.searchBarWidth.constant = width
searchBar.frame = newBounds
}, completion: nil)
}
}
Related
I have created two UIViews Programmatically and set its frame in ViewWillLayoutSubview function, Now I want to create a swapping animation so that each view swap each others position with animation on a click. For getting this animation I have to interchange its frame inside an animation but at the same time ViewWillLayoutSubview get called which sets frame to initial position.
How can I get my UIViews to be swapped with animation using ViewWillLayoutSubview?
I am not using any type of constraint or xib or storyboard. Whole screen is designed programatically.
Thanks in advance.
-(void)viewWillLayoutSubviews
{
if(Once)
{
// create 2 views here
once = NO;
}
}
put this code in button click
CGRect view2Frame = self.view2.frame;
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
//code with animation
self.view2.frame = self.view1.frame;
self.view1.frame = view2Frame;
} completion:^(BOOL finished) {
//code for completion
}];
I have a small button in the top left of the screen and it is set in place with constraints. When this button is tapped I want a UIlabel to expand out of it to the right edge of the screen. The UIlabel will take up almost the width of the screen and be about 8Points in height. It contains a line of text. It will display for 2 seconds before reversing the animation so that the UILabel shrinks back into the small button. I am new to ios animation and am confused as there are many types! I need to know:
1) What type of animation technique could I use to create this effect? Code would be nice but I'd like to be pointed in the right direction of study.
2) I've read that UILabel can respond inadequately to animations. Is it problematic to animate them in the way I described?
3) Its imperative that the text within the UILabel(and the label itself!) adapts to various screen sizes via some kind of constraint assignment that works hand in hand with the animations. The size/position of the UILabel must be set via autolayout programmatically.
Thanks
UPDATE:
I added this line before the animationWithDuration method(note: (10,5, ....) is where the small button is):
_questionDisplay = [[UILabel alloc]initWithFrame:CGRectMake(10, 5, 0, 30)];
..this line within the first animation block:
_questionDisplay.frame = CGRectMake(10, 5, 600, 30);
..and this line after in the following animationWithDuration method which gets called 3.5 seconds after the first via an NSTimer:
_questionDisplay.frame = CGRectMake(10, 5, 0, 30);
Once the label shrinks it is removed from the view.
This looks good on iPhone6+ but looks wrong in 4s because the label is not being sized with constraints which is what I need.
objective-c
[UILabel animateWithDuration:1
animations:^{
yourView.transform = CGAffineTransformMakeScale(1.5, 1.5);
}
completion:^(BOOL finished) {
[UILabel animateWithDuration:1
animations:^{
yourView.transform = CGAffineTransformIdentity;
}];
}];
Swift
UILabel.animateWithDuration(1, animations: { () -> Void in
yourView.transform = CGAffineTransformMakeScale(1.5, 1.5)
}) { (finished: Bool) -> Void in
UILabel.animateWithDuration(1, animations: { () -> Void in
yourView.transform = CGAffineTransformIdentity
})}
It disappears because you are giving a size explicitly. (If you want multiple screens to size it correctly use equal width(proportional) to superview constraint instead of fixed width. After this, use the method mentioned. When your label will appear, it'll have the correct width due to proportional width constraint and the animation will make it grow and shrink back to its original size. Leave the animation blocks empty (no explicit frame changing!)
Just change the scaling factor(currently 1.2) until it suits you!
1) 2) and 3) are all handled here! The label font itself adjusts so you just need to add the animation
[UIView animateWithDuration:0.5 animations:^{
// grow the label up to 130%, using a animation of 1/2s
yourLabel.transform = CGAffineTransformMakeScale(1.2,1.2); //
} completion:^(BOOL finished) {
// When the "grow" animation is completed, go back to size 100% using another animation of 1/2s
[UIView animateWithDuration:3.5 animations:^{
yourLabel.transform = CGAffineTransformIdentity;
}];
}];
Beginner iOS Developer here.
What I have is a UITableVIewController that has 1 section with 3 static grouped cells. Under that I have a UIView that has some buttons and text fields, when pressing one of the buttons, the UIView height increases. My problem is I cant scroll down to see the content that is at the bottom of the UIView
Screenshots:
When the green plus button is pressed, these elements are moved down making room for some new elements to be inserted (which I haven't implemented yet as I am stuck on this issue)
EDIT
Here is an example project: https://www.dropbox.com/s/vamxr12n6rrsam7/resizeSample.zip
You problem is that in your method to change the invoice. You only change the constraint of the invoiceBottomView, but you are not changing the size of invoiceBottomView's superview, that is the invoicePickerViewsContainer. So your tableView won't know you want to use more space of the tableFooterView (your invoicePickerViewsContainer).
You can change your code for this:
- (IBAction)invoiceLinesAddLine:(id)sender {
[UIView animateWithDuration:.25 animations:^{
CGRect invoiceBottomViewFrame = self.invoiceBottomView.frame;
NSInteger invoiceBottomViewFrameHeight = invoiceBottomViewFrame.size.height;
NSInteger invoiceLinesTopConstraintValue = invoiceLinesControlsViewTopConstraint.constant;
NSInteger invoiceLinesBottomConstraintValue = invoiceLinesControlsViewBottomConstraint.constant;
invoiceBottomViewFrameHeight = invoiceBottomViewFrameHeight + 40;
invoiceLinesTopConstraintValue = invoiceLinesTopConstraintValue + 40;
//invoiceLinesBottomConstraintValue = invoiceLinesBottomConstraintValue - 40;
invoiceBottomViewFrame.size.height = invoiceBottomViewFrameHeight;
invoiceLinesControlsViewTopConstraint.constant = invoiceLinesTopConstraintValue;
invoiceLinesControlsViewBottomConstraint.constant = invoiceLinesBottomConstraintValue;
CGRect invoicePickerViewsContainerFrame = self.invoicePickerViewsContainer.frame;
invoicePickerViewsContainerFrame.size.height += 40;
self.invoicePickerViewsContainer.frame = invoicePickerViewsContainerFrame;
self.tableView.tableFooterView = self.invoicePickerViewsContainer;
[self.view layoutIfNeeded];
} completion:^ (BOOL finished){
if (finished) {
//actions when animation is finished
}
}];
}
You don't need to change the invoiceLinesBottomConstraintValue, because you will increment the invoicePickerViewsContainer's height.
Then you need to add again the tableFooterView, so the table view will know that the size change, and the table will change the contentSize
Now, you have problems with the pickers... But I don't understand why you have the pickers in this view. If you want to preserve those pickers inside that view, you will need to change the constraint of those or its frame.
The way you setup the - (IBAction)invoiceLinesAddLine:(id)sender {} and the view within the tableview does not update the self.tableView.contentsize as you add more items. so add these lines to the completion function of the animation function within invoiceLinesAddLines and then twice the incremental height addition (i picked 80 arbitrarily)
completion:^ (BOOL finished){
if (finished) {
//actions when animation is finished
CGSize size=self.tableView.contentSize;
size.height+=80;
self.tableView.contentSize=size;
}
}];
I have a UITextView, and I am trying to animate a change of frame when the user taps on a button. Basically the text view grows larger to fit the screen so it can display more text, and then when the user taps the button again it shrinks to its original frame.
I perform the animation using blocks, like this:
if(!isDisplayingDetailView)
{
//Expand view
[UIView animateWithDuration:0.5
animations:^{
self.detailView.frame = self.view.frame;
}
completion:^(BOOL finished){
isDisplayingDetailView = YES;
}];
}
else{
//Shrink view.
[UIView animateWithDuration:0.5
animations:^{
self.detailView.frame = self.detailFrame;
}
completion:^(BOOL finished){
isDisplayingDetailView = NO;
}];
}
Where self.detailView is the UITextView, and self.detailFrame is just a CGRect holding the original frame. isDisplayingDetailView is just a BOOL to check if the view is expanded or not.
My problem is that the text resize is not animating. I made some screenshots from a test app to illustrate my problem:
App default view:
The expanded view:
The textview just a moment after tapping the button:
As you can see the text automatically shrinks to the final frame size, without any kind of animation, while the bounds still are animating. I guess that's the right behavior, but I'd like to know if it's possible to animate the text along with its view.
The text in textviews doesn't always act as expected. For this you'll have to set up a NSTimer and set the frame size on every tick.
Do something like:
textview.frame = CGRectMake (textview.frame.origin.x, textview.frame.origin.y, textview.frame.size.width-1, textview.frame.size.height-1);
Then when it's done I would completely remove the textview from superview and set it to nil, and then init a new one. The reason for this is that when changes the frames of textviews for some reason padding is added around the text's box. You won't notice it unless you change the frame a probably at least 100 times.
[textview removeFromSuperview];
textview = nil;
textview = [[UITextView alloc] initWithFrame:CGRectMake(x, y, width, height)];
textview.text = yourTextString;
//You will also have to set whatever non default properties you want, such as text or background color
[view addSubview:textview];
Another solution is to animate the bounds change.
There are a few ways, but here's a simple subclass of UITextView which does exactly this.
import Foundation
import UIKit
import QuartzCore
class SmoothTextView : UITextView {
override func actionForLayer(layer: CALayer!, forKey key: String!) -> CAAction! {
if key == "bounds" {
let x = super.actionForLayer(layer, forKey: "backgroundColor")
if let action:CAAnimation = x as? CAAnimation {
let transition = CATransition()
transition.type = kCATransitionFade
transition.beginTime = action.beginTime
transition.duration = action.duration
transition.speed = action.speed
transition.timeOffset = action.timeOffset
transition.repeatCount = action.repeatCount
transition.repeatDuration = action.repeatDuration
transition.autoreverses = action.autoreverses
transition.fillMode = action.fillMode
transition.timingFunction = action.timingFunction
transition.delegate = action.delegate
return transition
}
}
return super.actionForLayer(layer, forKey: key)
}
}
This as of iOS 8.
As an extra tweak, you might want to configure the text of your text view instance by adjusting the text container by zeroing all padding or insets:
textView.textContainer.lineFragmentPadding = 0.0
textView.textContainerInset = UIEdgeInsetsZero
I am trying to simulate the keyboard appear animation, only using a custom subview that will show the user three buttons. Is there any way I can accomplish this with storyboard (i.e. without having to programmatically create a subview)?
Quick Answer
Yes, although you will programmatically have to set some of the subviews properties. What you want to do is have your UIViewController call:
[UIView animateWithDuration:animations:completion:]
Detailed Example
in side of whatever method should bring up the keyboard try the following:
CGFloat windowWidth = self.mainView.frame.size.width;
CGFloat windowHeight = self.mainView.frame.size.height;
// center myCustomSubview along the x direction, and put myCustomSubview just below the screen when UIViewController initially gets onto the screen
CGPoint offScreenBelow = CGPointMake(windowWidth/2, windowHeight + (myCustomView.frame.size.y/2));
CGPoint onScreen = CGPointMake(windowWidth/2,windowHeight/2);
// change the second argument of the CGPointMake function to alter the final height of myCustomSubview
// start myCustomSubview offscreen
myCustomSubview.center = offScreenBelow;
// make sure to add myCustomSubview to the UIViewController's view's subviews
[self.view addSubview:myCustomSubview];
float duration = 1.0; // change this value to make your animation slower or faster. (units in seconds)
// animate myCustomSubview onto the screen
[UIView animateWithDuration:duration
animations:^{
myCustomSubview.center = onScreen;
}
completion:^(BOOL finished){
// add anything you want to be done as soon as the animation is finished here
}];
make sure you method is being called after 'viewDidAppear:' or inside of it.
when you want to get animate myCustomSubview back down off screen make sure to do the following in your UIViewController:
// set offscreen position same way as above
CGFloat windowWidth = self.mainView.frame.size.width;
CGFloat windowHeight = self.mainView.frame.size.height;
CGPoint offScreenBelow = CGPointMake(windowWidth/2, windowHeight + (myCustomView.frame.size.y/2));
// myCustomSubview is on screen already. time to animate it off screen
[UIView animateWithDuration:duration // remember you can change this for animation speed
animations:^{
myCustomSubview.center = offScreenBelow;
}
completion:^(BOOL finished){
[myCustomSubview removeFromSuperView];
}];
If Your Subview Is Not Showing Up
As always when dealing with subviews, make sure the frame is properly set, the subview has been added to a superview with addSubview:, the subview is not nil (and that it is properly initialized), and that neither the alpha nor opacity properties of the subview are set to 0.