UIView animate alpha when view is offscreen doesn't work - ios

I have a UIView which is located offscreen and I'm animating the frame so that the view slides in offscreen from the bottom and is visible. I'd like to simultaneously animate the alpha property of a UILabel on the view as well so it fades in. Unfortunately it appears I can't do the alpha animation because the view is offscreen and doesn't appear to take hold. It looks something like this:
nextCell.titleLabel.alpha = 0;
[UIView animateWithDuration:collapsedAnimationDuration animations:^{
CGRect newFrame = lastCell.frame;
newFrame.origin = CGPointMake(lastCell.frame.origin.x , lastCell.frame.origin.y + THREAD_CELL_HEIGHT);
nextCell.frame = newFrame;
nextCell.titleLabel.alpha = 1;
}];
Is it not possible to start animating the alpha of the subview because it's offscreen? If I position the view on screen and then try the animation it looks great but that's not the effect I'm going for. Thanks for your help.

Is this code executed in cellForRowAtIndexPath? If so, try moving it to tableView:willDisplayCell:forRowAtIndexPath:. The table view resets various properties of the cell before displaying it.

From the AppDelegate didFinishLaunching method:
self.myView = [[MyView alloc] initWithFrame:CGRectMake(320, 480, 400, 400)];
self.myView.titleLabel.text = #"test text";
self.myView.titleLabel.alpha = 0;
[UIView animateWithDuration:10.0 animations:^{
CGRect newFrame = self.myView.frame;
newFrame.origin = CGPointMake(0 , 0);
self.myView.frame = newFrame;
self.myView.titleLabel.alpha = 1;
}];
[self.viewController.view addSubview:self.myView];
MyView is just this:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self addSubview:self.titleLabel];
}
return self;
}
- (UILabel *)titleLabel
{
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
}
return _titleLabel;
}
I did no important changes to the code you presented and it worked fine. So assuming you're not doing what Tim mentioned (it won't work if you're doing it), we need more details to help you out.

Related

Getting black screen when app returning from background

I'm dealing with a tricky bug on my app. I have a chat view controller where I deal with keyboard showing and hiding. But the weird part is when putting the app on background.
This is the view controller before going to background (tapping the home button)
And this is the app when returning from background
If I put the app on background using the home button, the black screen only appears for a second, but if I block the phone and then unlock, the black screen keeps there.
This is how I deal with the keyboard
- (void)keyboardWillShowWithRect:(CGRect)rect
{
//We adjust inverted insets because tableview will be inverted
CGFloat displacementDistance = rect.size.height - self.composeBarView.bounds.size.height;
self.tableView.contentInset = UIEdgeInsetsMake(self.composeBarView.bounds.size.height, 0, displacementDistance, 0);
[self.view setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y - displacementDistance, self.view.frame.size.width, self.view.frame.size.height)];
}
- (void)keyboardWillDismissWithRect:(CGRect)rect
{
//We adjust inverted insets because tableview will be inverted
CGFloat displacementDistance = rect.size.height - self.composeBarView.bounds.size.height;
self.tableView.contentInset = UIEdgeInsetsMake(self.composeBarView.bounds.size.height, 0, 0, 0);
[self.view setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y + displacementDistance, self.view.frame.size.width, self.view.frame.size.height)];
}
The tableView used to display the messages is inverted in order to display the first message at the bottom. Here's how I do it:
- (void)setupTableView
{
self.tableView.delegate = self;
self.tableView.dataSource = self;
//We adjust inverted insets because tableview will be inverted
self.tableView.contentInset = UIEdgeInsetsMake(self.composeBarView.bounds.size.height, 0, 0, 0);
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 500.0;
self.tableView.sectionFooterHeight = 30.0;
self.tableView.showsVerticalScrollIndicator = NO;
[self.tableView registerNib:[UINib nibWithNibName:#"MessagesDateHeader" bundle:nil] forHeaderFooterViewReuseIdentifier:#"MessagesDateHeader"];
// Table View is inverted so we display last messages at the bottom instead of top
self.tableView.transform = CGAffineTransformMakeRotation(-M_PI);
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTapOnTableView)];
tap.numberOfTapsRequired = 1;
[self.tableView addGestureRecognizer:tap];
}
Also, I have self.definesPresentationContext = YES; set on viewDidLoad
Anyone can give me a hint of what's happening?
Thanks
I used to have a similar problem. Issue was the resizing of the tableView when keyboard appears/disappears based on its frame.
What I did was set a constraint between the bottom of the tableView and the superView and then change its constant with the appearance/disappearance of the keyboard.
Instead of relying on keyboardWillShowWithRect: and keyboardWillDismissWithRect: you can use listen to UIKeyboardWillChangeFrameNotification and handle it like follows
- (void) keyboardWillChangeFrame:(NSNotification *)notification {
NSDictionary *userInfo = [notification userInfo];
CGRect keyboardFrameEnd = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardFrameEnd = [self.view convertRect:keyboardFrameEnd toView:nil];
[UIView animateWithDuration:[userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]
delay:0.f
options:[userInfo[UIKeyboardAnimationCurveUserInfoKey] intValue] << 16
animations:^{
self.bottomConstraint.constant = CGRectGetHeight(self.view.frame) - keyboardFrameEnd.origin.y;
[self.view layoutIfNeeded];
} completion:nil];
}
[self.view layoutIfNeeded] is used so that view shows the whole animation

Making a list of UIViews that slide up and down when touched

I'm trying to figure out an approach to build something like the image below, which is a list of items that when a section is clicked slides out content. It's a really common UX on most websites and what not. My idea is to have each gray box (button) slide out a UIView containing some other items. I'm still new to iOS development but I'm struggling to find how you can animate a UIView to slide down and push the content below it down as well. Hoping some one can give me a good starting point or point to some info outside the realm of the apple docs.
Thanks!
So if you just have a few views, I would not recommend the UITableView approach, since it is not so easy to customize with animations and table views usually want to fill the whole screen with cells. Instead write a expandable UIView subclass that has the desired two states. Add a method to switch between extended and collapsed state. On expanding/collapsing adjust their positions so that they always have enough space.
I provide you an example of views adjusting their frames. I guess it should be easy to do the same with auto layout constraints: give the views a fixed height constraint and change this on collapsing/expanding. The same way set the constraints between the views to be 0 so that they are stacked on top of each other.
Expandable View:
#interface ExpandingView(){
UIView *_expandedView;
UIView *_seperatorView;
BOOL _expanded;
}
#end
#implementation ExpandingView
- (id)init
{
self = [super initWithFrame:CGRectMake(15, 0, 290, 50)];
if (self) {
_expanded = NO;
self.clipsToBounds = YES;
_headerView = [[UIView alloc] initWithFrame:self.bounds];
_headerView.backgroundColor = [UIColor colorWithWhite:0.8 alpha:1];
[self addSubview:_headerView];
_seperatorView = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height-1, self.bounds.size.width, 1)];
_seperatorView.backgroundColor = [UIColor lightGrayColor];
[self addSubview:_seperatorView];
_expandedView = [[UIView alloc] initWithFrame:CGRectOffset(self.bounds, 0, self.bounds.size.height)];
_expandedView.backgroundColor = [UIColor blueColor];
[self addSubview:_expandedView];
}
return self;
}
- (void)layoutSubviews{
[self adjustLayout];
}
- (void)adjustLayout{
_headerView.frame = CGRectMake(0, 0, self.bounds.size.width, 50);
_seperatorView.frame = CGRectMake(0, 49, self.bounds.size.width, 1);
_expandedView.frame = CGRectMake(0, 50, self.bounds.size.width, self.bounds.size.height-50);
}
- (void)toggleExpandedState{
_expanded = !_expanded;
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, _expanded?200:50);
[self adjustLayout];
}
#end
ViewController:
#interface ExpandingViewController (){
NSArray *_expandingViews;
}
#end
#implementation ExpandingViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_expandingViews = #[
[[ExpandingView alloc] init],
[[ExpandingView alloc] init],
[[ExpandingView alloc] init],
];
for(ExpandingView *view in _expandingViews){
[view.headerView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(expandingViewTapped:)]];
[self.view addSubview:view];
}
}
- (void)viewWillLayoutSubviews{
int y = 100;
for(ExpandingView *view in _expandingViews){
view.frame = CGRectOffset(view.bounds, (CGRectGetWidth(self.view.bounds)-CGRectGetWidth(view.bounds))/2, y);
y+=view.frame.size.height;
}
}
- (void)expandingViewTapped:(UITapGestureRecognizer*)tapper{
ExpandingView *view = (ExpandingView*)tapper.view.superview;
[UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:0 options:0 animations:^{
[view toggleExpandedState];
[self.view layoutIfNeeded];
} completion:nil];
}

layoutSubviews and animations

I am experiencing animation problems.
This is my current setup so far
I have initialized my objects (UIViews) in the
- (id)initWithFrame:(CGRect)frame method
I adjust and layer the frames of views as well as added subviews in the - (void)layoutSubviews method.
When I try to animate the views/subviews, nothing happens. Everything worked well until I moved the layering to the "initWithFrame" method, it works good.
Could somebody explain what the the cause could be and why the layered views in layoutSubviews can't be animated well?
I believe that it might be due to the hierarchy of calling methods, am I right in thinking that?
Code (subclass of UIView):
- (id)initWithFrame:(CGRect)frame
{
(...)
backgroundView = [[UIView alloc] initWithFrame:CGRectZero];
backgroundView.backgroundColor = [UIColor whiteColor];
(...)
}
- (void)layoutSubviews
{
CGRect frame = CGRectZero;
(...)
frame = (CGRect){ 100, 100, 200, 200 };
[backgroundView setFrame:frame];
(...)
[self addSubview:backgroundView];
}
- (void)animate
{
[UIView animateWithDuration:1.0f animations:^{
backgroundView.frame = CGRectMake(100, 100, 300, 300);
} completion:^{BOOL f){
NSLog(#"w: %f h: %f", backgroundView.frame.size.width, backgroundView.frame.size.height);
}];
}
Tried to call "animate" from subview (by using [(MyClass *)self.superview animate]) as well as send notification.
Always log was
w: 200 h: 200

UIView animation completes immediately

I've been struggling with this for a long and I just can't find out what's wrong here. I actually do have an idea - I think that somehow the view I am adding and trying to animate is not yet included to the view hierarchy and that's why core animation isn't wasting cycles to animate. I did some other animations in the project successfully but I am running them in the ViewController's viewDidLoad method. Here is my code:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.opaque = NO;
self.backgroundColor = [UIColor colorWithWhite: 1.0 alpha: 0.7];
[self addTrack];
}
return self;
}
- (void)addTrack
{
//init the track here
UIView *track = [[UIView alloc] initWithFrame: CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
track.backgroundColor = [UIColor whiteColor];
[self addSubview: track];
//transform the track for its initial scale
float scaleFactorX = 0.01;
track.layer.anchorPoint = CGPointMake(0, 0);
track.layer.position = CGPointMake(0, 0);
track.transform = CGAffineTransformMakeScale(scaleFactorX, 1.0);
//animate the track here
[UIView animateWithDuration:8 delay:0.3 options:UIViewAnimationOptionCurveEaseOut
animations:^{
NSLog(#"animation is running.");
track.transform = CGAffineTransformIdentity;
}
completion:^(BOOL finished) {
NSLog(#"animation finished.");
}];
}
So the result is that the "track" is scaling to its original size immediately. I also tried to call addTrack inside the didMoveToSuperview method so I can make sure that the containing view is added to the view hierarchy but with the same result. I tried other animations inside that class like adding alpha animation to the containing view but with no success again ..
Try calling your animations after loading the view so that you can see it.
-(void)viewDidAppear:(BOOL)animated
{
[self addTrack];
}
You shouldn't have put animations in the init method to start with. Try calling them immediately after your view was actually displayed (added on a superview) and see what happens then.

How to properly implement multiple subview operations in IPAD

I am creating an iPAD app. I have got a MAIN VIEW controller which covers the whole screen. And I have another class DROP-DOWN Class which i will be using to mimic the functionality of a drop-down as in Windows OS components. For that I am using a simple button, a scroll view and a few labels. I instantiate an object of that class and use it in my MAIN VIEW controller. To create the effect of a drop down, I alter the instance's views frame on the touch up of the button in toggle mode.(Click once, frame increased, click again frame decreased).
The creation part was fine, and the function to toggle to worked fine when implemented on a single instance of the DROP DOWN object.
But the problem comes when I instantiate these objects thru a for loop. The views seem to just vanish on button click.
Here is the code within the dropdowns.
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
UIScrollView *scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(10,54,100,50)];
scrollView.contentSize = CGSizeMake(50, 100);
for (int i = 0; i< 10; i++)
{
UILabel *newLable = [[UILabel alloc]initWithFrame:CGRectMake(0, 10*i, 100, 10)];
[newLable setFont:[UIFont boldSystemFontOfSize:10]];
newLable.text = [NSString stringWithFormat:#"Testing %i",i];
[scrollView addSubview:newLable];
}
scrollView.backgroundColor = [UIColor greenColor];
[self.view addSubview:scrollView];
}
-(IBAction)btnClicked:(id)sender
{
if (!isOpen) {
oldRect = self.view.frame;
CGRect newFrame = self.view.frame;
newFrame.size = CGSizeMake(190, 200);
NSLog(#"New Frame - %f,%f,%f,%f",self.view.frame.origin.x,self.view.frame.origin.y,self.view.frame.size.height,self.view.frame.size.width);
self.view.frame = newFrame;
}
else {
self.view.frame = oldRect;
NSLog(#"Old Frame - %f,%f,%f,%f",self.view.frame.origin.x,self.view.frame.origin.y,self.view.frame.size.height,self.view.frame.size.width);
}
isOpen = !isOpen;
}
And here is how this is called from the main view.
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:#selector(drawTheFilters)];
}
-(void)drawTheFilters
{
for (int i = 0; i<5 ; i++) {
DropDownView *dropView = [[DropDownView alloc]initWithNibName:#"DropDownView" bundle:[NSBundle mainBundle]];
CGRect newFrame = dropView.view.frame;
newFrame.origin = CGPointMake(200*i, 200);
dropView.view.frame = newFrame;
[self.view addSubview:dropView.view];
NSLog(#"Frame %i, %f,%f,%f,%f",i,dropView.view.frame.origin.x,dropView.view.frame.origin.y,dropView.view.frame.size.height,dropView.view.frame.size.width);
}
}

Resources