Reputation: 828
I have a UIView in my class (besides the original view) made in interface builder.
@interface TimeLineGrid : UIViewController {
UIView *toggleView;
}
@property (nonatomic, retain) IBOutlet UIView *toggleView;
I have synthesized it as well. I have implemented a swipe gesture so that when swiped up, the toggle view is added and when swiped down the toggle view is removed.
-(void)swipedUp {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:2.0f];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.view cache:NO];
[self.view addSubview:self.toggleView];
[UIView commitAnimations];
}
-(void)swipedDown {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:2.0f];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:NO];
[self.toggleView removeFromSuperview];
[UIView commitAnimations];
}
It works fine when I swipe up once and when I swipe down after that. But when i swipe up once again, it crashes with EXC_BAD_ACCESS error. I know this has something to do with the retain count increasing when I addSubview and reducing when I removeSubview. Can someone shed more light on this? How do I achieve this toggle?
EDIT: My view hierarchy is as follows:
->UIView (toggleView)
->UIView (mainView to which toggleView is being added)
-->UIToolBar
Upvotes: 2
Views: 1426
Reputation: 10011
I have made an application containing UITabbar and I got the same behavior but with UIButton.
That happens when you dont release the object on which you call removeFromSuperview
, I have the same effect when I was removing a UIButton from my viewControllers view.
In my case I could still see the button and if I click on it, I get an exception like unrecognized selector send to object of type NSCFString.
But if I change the tab then return to my view controller where the button was showing previously after removing it from my view, then it was gone.
So my conclusion is that if you dont release the object on which your are calling removeFromSuperview
then the app will show some unexpected behavior if you have the object as an instance variable of your class and added it to the view programmatically by addSubView
method.
What are your options:-
You can call removeFromSuperview
on a uiview or its subclass objects and call release on it or make it nil.
You can hide or unhide those objects using hidden
property instead of removing them from superview.
(I haven't tried it exactly)You can create a UIView
object in your class implementation and then add it as a subview
and then assign it to your instance variable your class using dot operator and then release the previously created UIView
object.
I dont know how my answer might help you but I think its good to share your experience with others.
Upvotes: 1
Reputation: 48398
Most likely, the view is being released when you call [self.toggleView removeFromSuperview];
. I recommend that you restructure your code a little.
Since you are creating the view in IB, declare it in the header file as
IBOutlet UIView *toggleView;
Do not include the property call or the synthesize. Since you are not setting or getting the toggleView, just refer to it by the pointer.
In the implementation, set the pointer to nil
in viewDidUnload
and release it in dealloc
. Then, adjust your toggle methods as follows:
-(void)swipedUp {
if (![toggleView superview]) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:2.0f];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.view cache:NO];
[self.view addSubview:toggleView];
[UIView commitAnimations];
}
}
-(void)swipedDown {
if ([toggleView superview]) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:2.0f];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:NO];
[toggleView removeFromSuperview];
[UIView commitAnimations];
}
}
Here, the if
clause will catch the event that the user swipes up twice in a row and not respond to the second swipe. Notice also that this uses toggleView
in place of self.toggleView
.
This should resolve the problem.
Upvotes: 3
Reputation: 6545
I suggest making the views you want to show/hide, be both subviews of a container view. Then, in your animation block you first remove a view that dissapears and add the view that you want to show. For example:
@interface TimeLineGrid : UIViewController {
// declare the views you want to flip between
UIView *mainView;
UIView *toggleView;
}
@property (nonatomic, retain) IBOutlet UIView *mainView;
@property (nonatomic, retain) IBOutlet UIView *toggleView;
In your implementation:
// self.view is the container view (an instance of UIView).
// Initially, self.mainView is a subview of self.view
-(void)transitionFromView:(UIView *)fromView toView:(UIView *)toView withAnimation:(UIViewAnimationTransition)animation {
if ([toView superview]) {
// toView already shown
return;
}
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:2.0f];
[UIView setAnimationTransition:animation forView:self.view cache:NO];
[self.view removeFromSuperview:fromView];
[self.view addSubview:toView];
[UIView commitAnimations];
}
-(void)swipedUp {
[self transitionFromView:self.mainView
toView:self.toggleView
withAnimation:UIViewAnimationTransitionFlipFromLeft];
}
-(void)swipedDown {
[self transitionFromView:self.toggleView
toView:self.mainView
withAnimation:UIViewAnimationTransitionFlipFromRight];
}
Note however, that use of the method setAnimationTransition:forView:cache:
is now discouraged. Apple recommends using the UIView transitionWithView:duration:options:animations:completion:
method (see the UIView reference).
I hope this helps!
Upvotes: 1
Reputation: 950
Could you please apply such changes and tell which UIView is exactly has proble.
After starting the application, you will see, which exactly object was disposed, when you call it.
Upvotes: 3