Changing text, the layout changes - ios

If I change the text of a label, its container changes position. (Auto layout)
My simple code:
BOOL nextCard = [self loadCardInfo:card];
if (nextCard == TRUE) {
[UIView animateWithDuration:0.3 animations:^{
[card setFrame:CGRectMake(hideCardPosition.x, hideCardPosition.y, card.frame.size.width, card.frame.size.height)];
}completion:^(BOOL complete){
}];
}
the problem is at last line:
-(BOOL)loadCardInfo:(CardView*)card {
if (dataArray.count > 0) {
card.string = [dataArray objectAtIndex:0];
[dataArray removeObjectAtIndex:0];
card.label.text = card.string; //THIS LINE
return YES;
} else
return NO;
}
Thanks

I'm not sure what you are trying to achieve in the +UIView animateWithDuration:animations: call but you probably don't want to be mixing Auto Layout with manually setting the frame like you are in the animations block.
Best practice is (usually) to stick with one or the other.
Also, perhaps you want some sort of vertical Auto Layout constraints (pinning to superview or setting a height?)

Related

Animating a UIButton to fullscreen

so I have a very simple button that when clicked goes to fullscreen and when clicked again goes back to the same position it was initially in. For some reason it works perfectly without the animation. When I uncomment the animation part when I initially click the button it does nothing, the second time I click it slightly enlarges. The third time I click it animates slowly but back to it's smaller original size... Why is it animating the opposite way?
- (IBAction)viewImage1:(id)sender {
UIButton *btn = (UIButton*) sender;
if (btn.tag == 0)
{
CGRect r = [[UIScreen mainScreen] bounds];
/*[UIView animateWithDuration:0.5f delay:0.0f options:0 animations:^{*/
[sender setFrame: r];
/*}completion:nil];*/
btn.tag = 1;
}
else
{
btn.tag = 0;
[sender setFrame:CGRectMake(0,0,370,200)];
}
}
There are two solutions to your problem either of which will work:
Disable Autolayout. (discouraged)
You can do that in Interface Builder by opening the File Inspector
in the right pane and unchecking the respective check box.
However, if you want to use Autolayout for constraining other UI elements in your view (which is quite a good idea in most cases) this approach won't work which is why I would recommend the second solution:
Keep Autolayout enabled, create an outlet for your button in your view controller and set
self.myButton.translatesAutoresizingMaskIntoConstraints = YES;
in your view controller's viewDidLoad method.
You could also add layout constraints to your button and animate those. (This excellent Stackoverflow post explains how it's done.)
The reason for this tricky behavior is that once you enable Autolayout a view's frame is no longer relevant to the actual layout that appears on screen, only the view's layout constraints matter. Setting its translatesAutoresizingMaskIntoConstraints property to YES causes the system to automatically create layout constraints for your view that will "emulate" the frame you set, in a manner of speaking.
It is easier to do this with auto layout and constraints. Create an IBOutlet for the height constraint of your button call it something like btnHeight. Do the same for the width constraint call it something like btnWidth. Then create an IBAction like so:
- (IBAction)buttonPress:(UIButton *)sender {
UIButton *btn = (UIButton *) sender;
CGRect r = [[UIScreen mainScreen] bounds];
if (CGRectEqualToRect(btn.frame, CGRectMake(0.0, 0.0, 370, 200))) {
[UIView animateWithDuration:0.5 delay:0.0 options:0 animations:^{
self.btnHeight.constant = r.size.height;
self.btnWidth.constant = r.size.width;
[self.view layoutIfNeeded];
} completion:^(BOOL finished){
}];
}else{
[UIView animateWithDuration:0.5 delay:0.0 options:0 animations:^{
self.btnHeight.constant = 200.0;
self.btnWidth.constant = 370.0;
[self.view layoutIfNeeded];
} completion:^(BOOL finished){
}];
}
}
In my experience animating the frame of a UIButton does not work well, a, the only method, I'm aware of, is to use CGAffineTransformScale which will rasterize the title of the button and scale it as well.

How can I animate the height constraint of a UIPickerView?

I've learned that the way to animate constraints in Cocoa Touch is to just set them and then put [self.view layoutIfNeeded] in an animation block, like so:
self.someViewsHeightConstraint = 25.0;
[UIView animateWithDuration:0.5 animations:^{
[self.view layoutIfNeeded];
}];
This is working fine, for example with a simple UIView. However, it does not work with a UIPickerView. It just snaps into the new position without animating.
Any ideas why this might be the case? What ways are there to work around this?
The effect I'm going for is that the Picker View should shrink to just show the chosen item, as the user goes on to input other things. One idea I had is to make a snapshotted view and animate that instead, but I couldn't quite get that working either.
I found trying to animate the height or the placement constraint of a UIPickerView to be problematic. However, doing transforms seems to work well -- even if you have Auto Layout Constraints everywhere, including in the view to be transformed.
Here's an example of what works for me. In this case, I've placed the picker view inside a blurring effects view -- but you don't even need to put your picker view inside another view to animate it.
In the code below, when I call show, it animates up vertically. When I call the hide method, it animates downwards.
- (void)showPickerViewAnimated:(BOOL)animated;
{
__weak MyViewController *weakSelf = self;
[UIView animateWithDuration:(animated ? kPickerView_AppearanceAnimationDuration : 0.0)
delay:(animated ? kPickerView_AppearanceAnimationDelay : 0.0)
options:(UIViewAnimationOptionCurveEaseInOut)
animations:^{
weakSelf.pickerViewContainerView.transform = CGAffineTransformMakeTranslation(0,0);
}
completion:^(BOOL finished) {
[weakSelf.view layoutIfNeeded];
}];
}
- (void)hidePickerViewAnimated:(BOOL)animated;
{
__weak MyViewController *weakSelf = self;
[UIView animateWithDuration:(animated ? kPickerView_DisappearanceAnimationDuration : 0.0)
delay:(animated ? kPickerView_DisappearanceAnimationDelay : 0.0)
options:(UIViewAnimationOptionCurveEaseInOut)
animations:^{
weakSelf.pickerViewContainerView.transform = CGAffineTransformMakeTranslation(0, kPickerView_Height);
}
completion:^(BOOL finished) {
[weakSelf.view layoutIfNeeded];
}];
}
picker view, If you have added constraint To TopLayout for yPosition remove it and add constraint to bottom layout instead.
That will solve the problem. here is my code and its working:
self.timePickerHeightConstraint.constant = pickerIsClosed ? 216 :
0;
[UIView animateWithDuration:0.5 animations:^{
[self.view layoutSubviews];
} completion:^(BOOL finished){
}];

Moving control with contraint rather than setting frame

I've built an app where a UISegmentedControl needs to be moved corresponding to its selected index. I'm using this code to achieve so:
- (IBAction)segmentControlAction:(id)sender {
// Change which container will be visible
int selectedIndex = self.overviewSegmentControl.selectedSegmentIndex;
if (selectedIndex == 0) {
// Show details and hide reviews & related
// SHOW THE DETAILS
[self showDetails];
} else if (selectedIndex == 1) {
// Show Reviews and hide details & related
// SHOW THE REVIEWS
[self showOther];
} else if (selectedIndex == 2) {
// Show related and hide details & reviews
}
}
-(void)showOther {
// Animate the reviews
[UIView animateWithDuration:.5 delay:0 usingSpringWithDamping:.9 initialSpringVelocity:1 options:UIViewAnimationOptionTransitionNone animations:^{
// Hide details controls
self.profileImageView.alpha = 0;
self.seperatorImageView.alpha = 0;
self.byLabel.alpha = 0;
self.authorLabel.alpha = 0;
// Move segmentControl
[self.overviewSegmentControl setFrame:CGRectMake(self.overviewSegmentControl.frame.origin.x, self.previewImageView.frame.size.height + 8, self.overviewSegmentControl.frame.size.width, self.overviewSegmentControl.frame.size.height)];
}completion:^(BOOL finished) {
// Completed
}];
}
-(void)showDetails{
// Animate the details
[UIView animateWithDuration:.5 delay:0 usingSpringWithDamping:.9 initialSpringVelocity:1 options:UIViewAnimationOptionTransitionNone animations:^{
// Move segmentControl
[self.overviewSegmentControl setFrame:CGRectMake(self.overviewSegmentControl.frame.origin.x, self.previewImageView.frame.size.height + 85, self.overviewSegmentControl.frame.size.width, self.overviewSegmentControl.frame.size.height)];
// Hide details controls
self.profileImageView.alpha = 1;
self.seperatorImageView.alpha = 1;
self.byLabel.alpha = 1;
self.authorLabel.alpha = 1;
}completion:^(BOOL finished) {
// Completed
}];
}
This code moves the UISegmentedControl, but it reverts it to it's original position before performing the actual move. This results in a weird jumping of the control.
My UIViewController:
Here you can see my constraints:
Can someone please explain me how this works and how to move controls with constraints rather than setting frames?
Thanks!
Erik
You can programmaticly modify the ".constant" properties of constraint objects at runtime to set positioning and/or size values. You do this in the updateConstraints method of your view controller.
You can bind the constraints in your storyboard to properties in your code the same way you bind other objects: control-click-drag from the object into your source code window. Then, you can access them at runtime.
Also, make sure you set translatesAutoresizingMaskIntoConstraints to FALSE or you might get conflict constraints.
For example you can "set" a constraint to top from your segment controller: "Top space" and modify this constraint accordingly.
Here you have an example testMovingControll

UILabel set text is drawn over older one during Animation

I'm having trouble understanding what is going wrong if you can help me it would be great!
I have a UIlabel being animated by UIView AnimationWithDuration like a marquee... (I need to make this code work, I can't fork something elsewhere please don't provide marquee reps.)
During this animation I set a new text, unfortunately the former one is not replace but drawn over. I end up with two text drawn over each other. And if I try to set the text again it's possible that it get cleaned or it's possible that it get a third line drawn over.
I've tried the following code thinking it's probably a thread issue but it doesn't do anything
#synchronized (self.songLabel.text) {
self.songLabel.text = nil;
self.songLabel.text = [trackItems objectForKey:#"TrackName"];
}
I've set my songLabel getter and setters as atomic, it does not help either.
I don't know what to try... thank you for your help!
EDIT: Here is the code that take care of the animation
- (void)animateSongLabel
{
if (!self.player.rate) {
[UIView animateWithDuration:.5 animations:^{
self.songLabel.left = 25.f;
}];
return;
}
if (!self.canAnimateLabel) {
return;
}
self.animateLabel = NO;
[UIView animateWithDuration:.25 delay:0. options:UIViewAnimationOptionCurveLinear animations:^{
self.songLabel.left -= 15.f;
} completion:^(BOOL finished) {
self.animateLabel = YES;
if (self.songLabel.right < -10.f) {
self.songLabel.left = 320.f;
}
[self animateSongLabel];
}];
}

dynamically change images in an animation IOS

I have a requirement to animate images . I have a large number of images and this needs to be played as an video. In between playing sometimes i need to change some images as they will be updated at server. so playing should automatically update this new images .
I have tried using UIImageView. There we cannot control the animation.
I then tried CAKeyframeAnimation supplying image array to values property. I could play and pause the animation. But here also i cannot dynamically change the image while playing.
Can anyone help me in solving this problem.
Thanks
mia
The animation system on UIImageView is very limited. I would suggest that you make your own implementation with say 2 image view.
You would than change the image in one imageview and the fade in and out using UIView animateWithDuration
I have written a method for you. Please note: I have not tested it.
It assumes you have your frames in a array called 'frames' and have two UIIMageView placed on top of each other called 'imgv1' and 'imgv2'
-(void)performAnimationOfFrameAtIndex:(NSNumber*)indexNum
{
int index = [indexNum intValue];
if(index >= frames.count)
{
return;
}
UIImage *img = [frames objectAtIndex:index];
UIImageView *imgIn;
UIImageView *imgOut;
if(index % 2 == 0)
{
imgIn = imgv1;
imgOut = imgv2;
}
else {
imgIn = imgv2;
imgOut = imgv1;
}
imgIn.image = img;
[self.view sendSubviewToBack:imgIn];
[UIView animateWithDuration:0.1 animations:^{
imgIn.alpha = 1;
imgOut.alpha = 0;
} completion:^(BOOL finished) {
[self performSelectorOnMainThread:#selector(performAnimationOfFrameAtIndex:) withObject:[NSNumber numberWithInt:index+1] waitUntilDone:NO];
}];
}
Use two UIImageViews and swap them.
If you have a weak reference on the "animation" UIView, be sure to check if animation has finished in the completion block. Otherwise you may experience performance issues when you navigate to another view, recursive calls to animation method will continue!
- (void)animateImagesAtIndex:(NSNumber *)imageIdx {
// Do something here
[UIView animateWithDuration:1 animations:^{
// Do swap
}
completion:^(BOOL finished) {
if (finished) {
[self animateImagesAtIndex:imageIdx];
}
}];
}
UIImageView supports animations for a series of images. You only have to set the properties animationImages with an array of the images, and call the methods startAnimating and stopAnimating.

Resources