layoutSubviews and animations - ios

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

Related

Is it a bug for pushViewController

I used the code below to present vViewController1
#property (retain,nonatomic) ViewController1 *vViewController1;
...
push vViewController1 from rootViewController
[self.navigationController pushViewController:vViewController1 animated:NO];
Viewcontroller1's viewWillAppear
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
CGRect frame = CGRectMake(0, 20, 320, 480);
self.view.frame = frame; ///aaa
NSLog(#"%f:%f:%f:%f",
frame.origin.x,frame.origin.y,frame.size.width,frame.size.height);
}
it outputs
0.000000:20.000000:320.000000:480.000000
but the view y position is always at
0
rather than
20
it looks like it is always
CGRectMake(0, 0, 320, 480);
and
self.view.frame = frame; ///aaa
can not relocate the position of the view.
I have try to set the position using code when call pushViewController,
but there is no way to make it work correctly.
Just wonder if it is a bug for pushViewController
Your comment welcome
I think you have autolayout turned on. So you need to override viewDidLayoutSubviews method like this:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.view.frame = CGRectMake(0, 0, 320, 480);
NSLog(#"%#", NSStringFromCGRect(self.view.frame));
}
And it's better to use NSStringFromCGRect for NSLog, rather creating 4 outputs doubles.

Scrolldown a TableView makes the UIImage background move up

Hi here is what I'm trying to do.
I've got a Table View with transparent cell's background.
cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"trans.png"]];
And I added to the ViewDidLoad a background image.
- (void)viewDidLoad {
bgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
bgView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"homelights.png"]];
[super viewDidLoad];
self.title = #"";
}
So this works fine, I've got a tableView with transparent cells and a background image. Now the plan is when I scroll the TableView down, the image should react and move up.
Any idea how to trigger the scrolldown? Or where to start?
I enabled scrollViewDidScroll;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat scrollOffset = scrollView.contentOffset.y;
Works like a charm, I can read the scroll position, but when I'm trying to animate UIView, by linking the scrollView.contentOffset.y to the UIView "y" position;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat scrollOffset = scrollView.contentOffset.y;
[UIView beginAnimations:#"UIViewAnimationCurveEaseInOut" context:nil];
bgView.center = CGPointMake(bgView.center.x, bgView.center.y+scrollOffset);
[UIView commitAnimations];
}
So I tried to make the UIView bigger to the original size of the image 640x832 to be able to navigate on it, but doesn't seems to work.
bgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 640, 832)];
bgView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"homelights.png"]];
Any idea why this is happening?
Your table view is a subclass of UIScrollView and your delegate for the table view is also (automatically) a delegate of the scroll view. Implement the scrollViewDidScroll: delegate method and use it to monitor how the table view has changed and move your background view.

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.

UIScrollView paging enabled with content inset is working strange

I created the UIScrollView with content insets.
scrollView.frame = CGRectMake(-80, 180, 480, 190)
self.scrollView.contentInset = UIEdgeInsetsMake(0, 160, 0, 160);
self.scrollView.pagingEnabled = YES;
[self.scrollView setContentSize:CGSizeMake(480, 190)];
// Add three Views
[self.scrollView addSubview:view1];
[self.scrollView addSubview:view2];
[self.scrollView addSubview:view3];
[view1 setFrame:CGRectMake(0, 0, 160, 190)];
[view2 setFrame:CGRectMake(160, 0, 160, 190)];
[view3 setFrame:CGRectMake(320, 0, 160, 190)];
At first time, scrollView.contentOffset.x was -160.0
But the weird problem is when I tap on scrollView(Yellow Area), content offset x value is resetting to 0 and shown like this.
I tried several times, but tapping on Scroll View resets the content offset to 0.
How can I prevent this?
UIScrollView paging works by scrolling pages with the same width of the scrollView (in your case pages of 480 width). This means that you have 1 single page (you'd still be able to scroll left and right due to the 160 content inset).
One way to make this work would be:
self.scrollView.frame = CGRectMake(80, 180, 160, 190);
self.scrollView.clipsToBounds = NO;
self.scrollView.contentInset = UIEdgeInsetsZero;
self.scrollView.pagingEnabled = YES;
[self.scrollView setContentSize:CGSizeMake(480, 190)];
This will draw and scroll correctly, however, the sides of the screen will not be interactive (80 pixels on each side, since the control starts at frame.origin.x=80 and ends at 80+160=240).
Second option would be to handle paging yourself, by using methods provided by UIScrollViewDelegate.
- (void)viewDidLoad
{
[super viewDidLoad];
// pageIndex must be declared as a class member - this is used to prevent skipping pages during scroll
pageIndex = 0;
self.scrollView.frame = CGRectMake(0, 180, 320, 190);
self.scrollView.contentInset = UIEdgeInsetsMake(0, 80, 0, 80);
self.scrollView.pagingEnabled = NO;
self.scrollView.clipsToBounds = YES;
[self.scrollView setContentSize:CGSizeMake(480, 190)];
self.scrollView.delegate = self;
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
int pageWidth = 160;
int pageX = pageIndex*pageWidth-scrollView.contentInset.left;
if (targetContentOffset->x<pageX) {
if (pageIndex>0) {
pageIndex--;
}
}
else if(targetContentOffset->x>pageX){
if (pageIndex<3) {
pageIndex++;
}
}
targetContentOffset->x = pageIndex*pageWidth-scrollView.contentInset.left;
NSLog(#"%d %d", pageIndex, (int)targetContentOffset->x);
}
In addition to alex's first approach with setting clipsToBounds to NO, I need to react to the scroll outside of the scroll view bounds. So I created ScrollForwarderView class which is subclass of UIView.
ScrollForwarderView.h
#import <UIKit/UIKit.h>
#interface ScrollForwarderView : UIView
#property (nonatomic, weak) UIScrollView *scrollView;
#end
ScrollForwarderView.m
...
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if ([self pointInside:point withEvent:event]) {
return _scrollView;
}
return nil;
}
Then place UIView with custom class ScrollForwarderView above the scroll view, link scrollView property to my scroll view and it nicely forwarded the user events to scrollview.

UIView animate alpha when view is offscreen doesn't work

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.

Resources