PDA

View Full Version : UIView Animation in Navigation App Help




sleaver
Jul 21, 2010, 03:32 AM
Hi

I know this is my first post so hopefully I'm not breaking any rules, but I am trying to write my first app and have run into a problem which hopefully the following will explain.

The app consists of a navigation bar and a tab bar controller and I push UIViewControllers on when a new page is selected. However with one view I have two views in the NIB/XIB that I wish to animate using a segmented control in the nav bar. So I push a new UIViewController with an empty NIB file and then try and load the UIView class in code and that is the bit I can't get to work.

If I'm right from what I've read, for the animation I need a struct of UIView but the nav aspect of the app expects a struct of UIViewController. So what can I do to load the UIView class into the UIViewController class? Is the problem that I'm instanciating the view controller with a blank NIB?

Your help would be appreciated but please bear in mind I'm new to this so accept I may not be doing it the correct way.



bweberapps
Jul 21, 2010, 09:33 AM
You could try initializing the UIViewContoller without specifying a nib file (just init and alloc) and edit your UIViewContoller class to load your UIView. All of your code to do the animations of your UIViews would be handled in the UIViewController that is pushed onto the navigation controller. If I'm not mistaken you also put the code for the segmented control buttons in this UIViewController class as well, and it will edit the Navigation Bar to add the buttons at the top. So a quick summary, Navigation Control loads UIViewController, which adds segmented control to the top Navigation Bar, and controls the animation of loading your two UIViews.

sleaver
Jul 21, 2010, 12:25 PM
You could try initializing the UIViewContoller without specifying a nib file (just init and alloc) and edit your UIViewContoller class to load your UIView. All of your code to do the animations of your UIViews would be handled in the UIViewController that is pushed onto the navigation controller. If I'm not mistaken you also put the code for the segmented control buttons in this UIViewController class as well, and it will edit the Navigation Bar to add the buttons at the top. So a quick summary, Navigation Control loads UIViewController, which adds segmented control to the top Navigation Bar, and controls the animation of loading your two UIViews.
Thanks for the reply.

I've got the new UIViewController being pushed via the following

StoryDetailViewController *storyController = [[StoryDetailViewController alloc] init];
[self.navigationController pushViewController:storyController animated:YES];
[storyController release];


I can then get the segmented control but it's loading the UIView sub class that I'm really having problems with. My viewDidLoad of StoryDetailViewController look like this:

- (void)viewDidLoad {

//UIView *mainView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
TestView *mainView = [[TestView alloc] initWithNibName:@"TestView" bundle:nil];

self.view = mainView;
//[self.view setBackgroundColor:[UIColor redColor]];

// "Segmented" control to the right
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:
[NSArray arrayWithObjects:
[UIImage imageNamed:@"up.png"],
[UIImage imageNamed:@"down.png"],
nil]];
[segmentedControl addTarget:self action:@selector(segmentAction:) forControlEvents:UIControlEventValueChanged];
segmentedControl.frame = CGRectMake(0, 0, 90, 30.0);
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
segmentedControl.momentary = YES;

UIBarButtonItem *segmentBarItem = [[UIBarButtonItem alloc] initWithCustomView:segmentedControl];
[segmentedControl release];

self.navigationItem.rightBarButtonItem = segmentBarItem;
[segmentBarItem release];

[super viewDidLoad];

}

However it crashes with:

2010-07-21 18:18:13.008 RSS Reader[2535:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[TestView initWithNibName:bundle:]: unrecognized selector sent to instance 0x4dbb970'

on the line TestView *mainView = [[TestView alloc] initWithNibName:@"TestView" bundle:nil];

Although if I comment that line out and uncomment the UIView and background colour line it works!

The TestView class is a blank class of type UIView with a black view NIB file all created from adding new files so does anyone know where I'm going wrong?

Plus once I get it working how do you reference controls in the TestView NIB from the UIViewController?

dejo
Jul 21, 2010, 01:05 PM
However it crashes ...

on the line TestView *mainView = [[TestView alloc] initWithNibName:@"TestView" bundle:nil];
initWithNibName:bundle: is a UIViewController instance method, not a UIView one.

Plus once I get it working how do you reference controls in the TestView NIB from the UIViewController?
IBOutlets.

sleaver
Jul 21, 2010, 01:45 PM
OK, so I've given up with NIB's and I'm creating the UIView progmatically in the TestView class I have

UIViewController (StoryViewOne is declared as a UIView in the .h file. The idea is to also have StoryViewTwo to animate them)
- (void)viewDidLoad {

// UIView* mainView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
TestView *mainView = [[TestView alloc] init];
storyViewOne = mainView;
self.view = storyViewOne;
[mainView release];
storyViewOne.titleLabel = @"Testing 123";

TestView
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
[self setBackgroundColor:[UIColor blackColor]];

UILabel *titleLabel = [ [UILabel alloc ] initWithFrame:CGRectMake(0.0, 20.0, 320.0, 41.0) ];
titleLabel.textAlignment = UITextAlignmentCenter;
titleLabel.textColor = [UIColor blackColor];
titleLabel.font = [UIFont fontWithName:@"Helvetica" size:(12.0)];
[self addSubview:titleLabel];
titleLabel.text = [NSString stringWithFormat: @"%d", 10];

UILabel *dateLabel = [ [UILabel alloc ] initWithFrame:CGRectMake(149, 69, 155.0, 21.0) ];
dateLabel.textAlignment = UITextAlignmentCenter;
dateLabel.textColor = [UIColor blackColor];
dateLabel.font = [UIFont fontWithName:@"Helvetica" size:(12.0)];
[self addSubview:dateLabel];
dateLabel.text = [NSString stringWithFormat: @"%d", 10];
}
return self;
}


So as you can see in the first bit I am trying to set the titleLabel label. So do I need IBOutlets in my TestView class or the main class? I'm really sorry for asking but as much help as possible will be appreciated.

bweberapps
Jul 21, 2010, 01:46 PM
Check out this (http://developer.apple.com/iphone/library/featuredarticles/ViewControllerPGforiPhoneOS/ViewControllerPGforiPhoneOS.pdf) document on View Controllers. Page 27 talks about detached nib files and how to attach the nib view to your programmatically created view controller. This document should have everything you need to get you going.

bweberapps
Jul 21, 2010, 02:13 PM
So as you can see in the first bit I am trying to set the titleLabel label. So do I need IBOutlets in my TestView class or the main class? I'm really sorry for asking but as much help as possible will be appreciated.

IBOutlets are only used in conjunction with NIB files. If you create then programmatically you have direct access to the objects themselves and don't need IBOutlets. Make your labels instance variables in the header and access them by doing something like this:

[[StoryViewOne titleLabel] setText:@"new string"]

Also you should use my above syntax, not the "dot" syntax.

sleaver
Jul 21, 2010, 02:35 PM
OK, I'm getting lost. Do I need to declare the variable in the class or subclass and could you please give an example?

I've got to try an put in an animated flip yet :confused:

EDIT - Fixed this one, just the animation :)

dejo
Jul 21, 2010, 02:35 PM
Also you should use my above syntax, not the "dot" syntax.
Why not?

sleaver
Jul 21, 2010, 02:49 PM
OK, so it's not fixed as I'm getting the following warning on setting the texts label

warning: 'UIView' may not respond to '-titleLabel'

bweberapps
Jul 21, 2010, 03:06 PM
OK, I'm getting lost. Do I need to declare the variable in the class or subclass and could you please give an example?

I've got to try an put in an animated flip yet :confused:

Ok here we go.

TestView.h
@interface TestView : UIView
{
UILabel *titleLabel;
}
@property (nonatomic, retain) UILabel *titleLabel;

@end

In TestView.m just inside of the implementation add the following line
@synthesize titleLabel;

Then this
-(id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
[self setBackgroundColor:[UIColor blackColor]];

titleLabel = [[UILabel alloc ] initWithFrame:CGRectMake(0.0, 20.0, 320.0, 41.0)];
titleLabel.textAlignment = UITextAlignmentCenter;
titleLabel.textColor = [UIColor blackColor];
titleLabel.font = [UIFont fontWithName:@"Helvetica" size:(12.0)];
[self addSubview:titleLabel];
titleLabel.text = [NSString stringWithFormat: @"%d", 10];
...

Then you should be able to use the line I stated up above to access the property of the label from your view controller. Do the same thing for your other label.

sleaver
Jul 21, 2010, 04:16 PM
OK, thanks to that I now don't have a warning! Even though you said IBOutlets aren't needed I still for some reason put them. But now the animation :rolleyes:

As you know my viewDidLoad in the UIViewController class as follows BUT I have now created two UIViews from my TestView class
- (void)viewDidLoad {

// UIView* mainView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
TestView *mainView = [[TestView alloc] init];
TestView *mainView2 = [[TestView alloc] init];
storyViewOne = mainView;
storyViewTwo = mainView2;
self.view = storyViewOne;

[[storyViewOne titleLabel] setText:@"new string"];
[[storyViewTwo titleLabel] setText:@"other view"];

// "Segmented" control to the right
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:
[NSArray arrayWithObjects:
[UIImage imageNamed:@"up.png"],
[UIImage imageNamed:@"down.png"],
nil]];
[segmentedControl addTarget:self action:@selector(segmentAction:) forControlEvents:UIControlEventValueChanged];
segmentedControl.frame = CGRectMake(0, 0, 90, 30.0);
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
segmentedControl.momentary = YES;

UIBarButtonItem *segmentBarItem = [[UIBarButtonItem alloc] initWithCustomView:segmentedControl];
[segmentedControl release];

self.navigationItem.rightBarButtonItem = segmentBarItem;
[segmentBarItem release];

[super viewDidLoad];

}


My segmentAction method which is in the same class as the above is
- (IBAction)segmentAction:(id)sender
{
// The segmented control was clicked, handle it here
UISegmentedControl *segmentedControl = (UISegmentedControl *)sender;

if(segmentedControl.selectedSegmentIndex == 0){
// TODO
}
if(segmentedControl.selectedSegmentIndex == 1){

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationCurveEaseIn forView:self cache:YES];
[storyViewOne removeFromSuperview];
self.view = storyViewTwo;
[UIView commitAnimations];
}
}


Firstly I get the following warning on the setAnimationTransition line:
Incompatible Objective-C types 'struct StoryDetailViewController *' expected 'struct UIView *' when passing argument 2 of 'setAnimationTransition:forView:cache:' from distinct Objective-C type

I'm guessing part of my problems is using the UIView word but from the code, if I'm along the right lines pleas help me join up the dot's or if not please point me in the right direction :)

bweberapps
Jul 21, 2010, 04:42 PM
Try putting the view you want to animate in the arguement forView: instead of self. So forView:storyViewOne

*edit*
or use self.view because self is UIViewController

sleaver
Jul 22, 2010, 04:44 AM
Try putting the view you want to animate in the arguement forView: instead of self. So forView:storyViewOne

Do you mean this (change in bold, well bold tags!)
// UIView* mainView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
TestView *mainView = [[TestView alloc] init];
TestView *mainView2 = [[TestView alloc] init];
storyViewOne = mainView;
storyViewTwo = mainView2;
forView:view = storyViewOne;


*edit*
or use self.view because self is UIViewController
Not sure what you mean by this as I'm already using self.view?

Do I need to put the views in a UIView first, maybe commenting out the top line in viewDidLoad and placing storyViewOne & storyViewTwo in that?

Any help would be greatfully appreciated as this is driving me mad!

bweberapps
Jul 22, 2010, 08:47 AM
Do you mean this (change in bold, well bold tags!)
// UIView* mainView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
TestView *mainView = [[TestView alloc] init];
TestView *mainView2 = [[TestView alloc] init];
storyViewOne = mainView;
storyViewTwo = mainView2;
forView:view = storyViewOne;



Not sure what you mean by this as I'm already using self.view?

Do I need to put the views in a UIView first, maybe commenting out the top line in viewDidLoad and placing storyViewOne & storyViewTwo in that?

Any help would be greatfully appreciated as this is driving me mad!

No I mean in this part (look for**self.view**, but don't include the *s in your code):
- (IBAction)segmentAction:(id)sender
{
// The segmented control was clicked, handle it here
UISegmentedControl *segmentedControl = (UISegmentedControl *)sender;

if(segmentedControl.selectedSegmentIndex == 0){
// TODO
}
if(segmentedControl.selectedSegmentIndex == 1){

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationCurveEaseIn forView: **self.view** cache:YES];
[storyViewOne removeFromSuperview];
self.view = storyViewTwo;
[UIView commitAnimations];
}
}


Since this code is in your TestView class, which is an UIViewController, self will return a view controller, but self.view will return the view of that view controller.

sleaver
Jul 22, 2010, 12:30 PM
Thanks bweberapps, that got it working, however there is a but!

If I first load storyViewOne I do not get the coloured background, its just white. If I click the down arrow the labels do change but the background is filled from top left to bottom right. If I click down again I do get the right transition. So whats going on?

The code is as above and the only change has been your last one and the transition changed to a curl. However I doubt it's the transition as the colour isn't right before it's got to that code.

bweberapps
Jul 22, 2010, 02:15 PM
Thanks bweberapps, that got it working, however there is a but!

If I first load storyViewOne I do not get the coloured background, its just white. If I click the down arrow the labels do change but the background is filled from top left to bottom right. If I click down again I do get the right transition. So whats going on?

The code is as above and the only change has been your last one and the transition changed to a curl. However I doubt it's the transition as the colour isn't right before it's got to that code.

I'm not going to debug all of your code for you, but I will just make a few comments and suggestions you should look into. First you should fill out the code for segmentedControl.selectedSegmentIndex == 0. Also, I'm not sure what your reasoning is for this, but make storyViewOne and storyViewTwo of type TestView instead of creating the mainView's and setting them equal (this will require importing TestView in your view controller header and replacing UIView *storyViewOne with TestView *storyViewOne and repeat for storyViewTwo). This should clean up the code a bit for you, and you can delete the lines in your view controller .m that declare the mainView's. Other than that, I have not actually implemented a segmented control with animation before so you will need to debug the rest on your own.

sleaver
Jul 22, 2010, 02:57 PM
I'm not going to debug all of your code for you, but I will just make a few comments and suggestions you should look into. First you should fill out the code for segmentedControl.selectedSegmentIndex == 0. Also, I'm not sure what your reasoning is for this, but make storyViewOne and storyViewTwo of type TestView instead of creating the mainView's and setting them equal (this will require importing TestView in your view controller header and replacing UIView *storyViewOne with TestView *storyViewOne and repeat for storyViewTwo). This should clean up the code a bit for you, and you can delete the lines in your view controller .m that declare the mainView's. Other than that, I have not actually implemented a segmented control with animation before so you will need to debug the rest on your own.
Thanks for the comments. I've got the animation working now and I always refactor afterwards so the code would have got sorted out.

The only problem I'm having now is that the background colour isn't getting set when it was yesterday. Do you know why that would suddenly stop?

bweberapps
Jul 22, 2010, 07:46 PM
Thanks for the comments. I've got the animation working now and I always refactor afterwards so the code would have got sorted out.

The only problem I'm having now is that the background colour isn't getting set when it was yesterday. Do you know why that would suddenly stop?

I'm not sure what all you have changed, but assuming your storyView's are setup as TestView's and declared in the header you should make your viewDidLoad: function in the ViewController contain the following:

storyViewOne = [[[TestView alloc] initWithFrame:CGRectZero] autorelease];

This is because in your TestView you change the background color in the initWithFrame function.

sleaver
Jul 23, 2010, 12:39 PM
Thanks for all your help. I got it working by doing this:

storyViewOne = [[[TestView alloc] initWithFrame:CGRectMake(0,0,480,320)] autorelease];

Now on to the next problem but hopefully I'll solve it, otherwise it will be another thread :)

bweberapps
Jul 23, 2010, 03:05 PM
Thanks for all your help. I got it working by doing this:

storyViewOne = [[[TestView alloc] initWithFrame:CGRectMake(0,0,480,320)] autorelease];

Now on to the next problem but hopefully I'll solve it, otherwise it will be another thread :)

You're welcome sleaver. Good luck on your app.