UINavigationController weird issue - iphone

In my app I have NavigationController as root, I have a navigation button as well.
When I'm pressed the button it flips to another controller.
the destination view code:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *filePath = [[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"]stringByAppendingPathComponent:#"favorites.plist"];
NSData *data = [[NSData alloc]initWithContentsOfFile:filePath];
NSKeyedUnarchiver *unArchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData:data];
self.title = [unArchiver decodeObjectForKey:#"title"];
self.pubDate = [unArchiver decodeObjectForKey:#"pubdate"];
self.description = [ unArchiver decodeObjectForKey:#"description"];
}
Button code 1:
-(void)navFavoritesButton{
[UIView beginAnimations:#"flipview" context:nil];
[UIView setAnimationDuration:2];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft
forView:self.view cache:YES];
FavoritesView *fav = [[FavoritesView alloc]initWithStyle:UITableViewStylePlain];
UINavigationController *favNav = [[UINavigationController alloc]initWithRootViewController:fav];
favNav.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:favNav animated:YES];
}
button code 2:
-(void)navFavoritesButton{
[UIView beginAnimations:#"flipview" context:nil];
[UIView setAnimationDuration:2];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft
forView:self.view cache:YES];
FavoritesView *fav = [[FavoritesView alloc]initWithStyle:UITableViewStylePlain];
fav.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:fav animated:YES];
}
I'm having a weird problem;
If I'm using button 2 code all works good, the view is loaded, unArchived the data and display it on a tableView.
But if I'm using the button 1 code the app crash with the following log:
2013-04-21 16:51:56.714 YnetXML[21898:f803] -[__NSArrayM isEqualToString:]: unrecognized selector sent to instance 0x6b4fd30
2013-04-21 16:51:56.715 YnetXML[21898:f803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayM isEqualToString:]: unrecognized selector sent to instance 0x6b4fd30'
*** First throw call stack:
(0x268c052 0x1a61d0a 0x268dced 0x25f2f00 0x25f2ce2 0xc312d1 0xc9830e 0x82123 0xc9764e 0xc96c1c 0xcbd56d 0xca7d47 0xcbe441 0xcbe4f9 0xeb5c68 0xc754a1 0xc7612b 0xeb54c7 0xc9e427 0xc9e58c 0x348c4 0xc9e5cc 0x34568 0x387d 0x268dec9 0xbd45c2 0xe0fd54 0x268dec9 0xbd45c2 0xbd455a 0xc79b76 0xc7a03f 0xc792fe 0xbf9a30 0xbf9c56 0xbe0384 0xbd3aa9 0x35a9fa9 0x26601c5 0x25c5022 0x25c390a 0x25c2db4 0x25c2ccb 0x35a8879 0x35a893e 0xbd1a9b 0x22ed 0x2215)
terminate called throwing an exception(lldb)
EDIT:
I deleted the line below and it works for both button 1 and 2 code.
What should cause the problem?
self.title = [unArchiver decodeObjectForKey:#"title"];
what could be the problem? Why it works well without the navigationController and crash with?
I'm trying to solve the problem for several hours, I tried all sorts of ways, I read online but nothing works. I just can not figure out what causes it to crash, help please!

I guess the problem lies with these three statements
self.title = [unArchiver decodeObjectForKey:#"title"];
self.pubDate = [unArchiver decodeObjectForKey:#"pubdate"];
self.description = [ unArchiver decodeObjectForKey:#"description"];
Try this:
//I guess problem with this, because only when you bring in a navigation controller , your app crashes so only when navigation controller comes into play the navigation bar appears and self.title is the text you see in the center of the navigation bar - so rename self.title to self.titlesArray or something
if([[unArchiver decodeObjectForKey:#"title"] isKindOfClass:[NSString class]])
{
//do not use self.title for other purposes, it is to set title of navigation bar.
self.title = [unArchiver decodeObjectForKey:#"title"];
}
self.pubDate = [unArchiver decodeObjectForKey:#"pubDate"];
self.description= [unArchiver decodeObjectForKey:#"description"];
Note: This is not the way to implement but this is just a way to find out whether these statements are the cause for your crash. Try this and tell me, we l figure out another way.

Related

ECSlidingViewController Zoom Animation

I'm trying to set my sliding transition with Zoom Animation in ECSLidingViewController2.
Like in the TransitionFun example (https://github.com/ECSlidingViewController/ECSlidingViewController/blob/master/Examples/TransitionFun/TransitionFun/METransitionsViewController.m), I'm doing this in viewDidLoad of my topView NavigationController:
id<ECSlidingViewControllerDelegate> transition = [[MEZoomAnimationController alloc] init];
self.slidingViewController.delegate = transition;
self.slidingViewController.topViewAnchoredGesture = ECSlidingViewControllerAnchoredGestureTapping | ECSlidingViewControllerAnchoredGesturePanning;
self.slidingViewController.customAnchoredGestures = #[];
[self.navigationController.view removeGestureRecognizer:self.dynamicTransitionPanGesture];
[self.navigationController.view addGestureRecognizer:self.slidingViewController.panGesture];
I'm adopting the ECSlidingViewControllerDelegate protocol in the NavigationController header file.
But it crashes in the ECSlidingViewController.m, at the first if condition of this method I get an EXC_BAD_ACCESS(1) error:
- (CGRect)frameFromDelegateForViewController:(UIViewController *)viewController
topViewPosition:(ECSlidingViewControllerTopViewPosition)topViewPosition {
CGRect frame = CGRectInfinite;
if ([(NSObject *)self.delegate respondsToSelector:#selector(slidingViewController:layoutControllerForTopViewPosition:)]) {
id<ECSlidingViewControllerLayout> layoutController = [self.delegate slidingViewController:self
layoutControllerForTopViewPosition:topViewPosition];
if (layoutController) {
frame = [layoutController slidingViewController:self
frameForViewController:viewController
topViewPosition:topViewPosition];
}
}
return frame;
}
Anyone having the same issue?
thx
This is what i do in the viewDidLoad: method of my view controller and it works great:
id<ECSlidingViewControllerDelegate> transition = self.zoomAnimationController;
self.slidingViewController.delegate = transition;
self.slidingViewController.topViewAnchoredGesture = ECSlidingViewControllerAnchoredGestureTapping | ECSlidingViewControllerAnchoredGesturePanning;
self.slidingViewController.customAnchoredGestures = #[];
[self.navigationController.view addGestureRecognizer:self.slidingViewController.panGesture];
If, like me, you aren't trying to implement the dynamic transition, you should get rid of the line you have that says:
[self.navigationController.viewremoveGestureRecognizer:self.dynamicTransitionPanGesture];
IMHO its what could be causing your EXC_BAD_ACCESS(1) error since you haven't actually set the dynamicTransitionPanGesture gesture recognizer.

Strange memory leak in Window:addSubView

first of all sorry for my English :-) not so good.
I have a strange memory leak with the following code (code after the explanation).
I have a class, FLWaitingView. It is a simple view with a waiting indicator (plus a view with background), used to say to the user "wait for the data to be loaded".
It has two simple methods: show and dismiss.
In the show method, I find the main Application Window and add the subviews (the waiting view and a background view, with different animations). In the dismiss method, I remove it from superview.
In every show, I verify that the view isn't already visible using a static bool var (is_visible).
The strange thing is this: In the dismiss method, I use:
[self.view removeFromSuperview];
[self.waitingView removeFromSuperview];
to remove the two views from the Window, to avoid them to be retained. They are correctly removed, I can verify this with NSLog (for cicle on each window subview). But, in INSTRUMENTS, using the "mark heap" function, I see that in every single reload (new instance of FLWaitingView, then show, then dismiss) the old instance remains in memory and continues to increase memory usage. Obviously is not a problem of the calling code, because I correctly release the object:
//CALLING CODE
//customWaitingView is a property retained
self.customWaitingView = [[[FLWaitingView alloc]init]autorelease];
[self.customWaitingView show];
Moreover, and I think that this is the most important information, if I move the view dismission in another method, called by a selector, the leak disappear!!!
Now I show the "wrong" code and, after, the "correction". I would like to understand why it happens.
- (void)show
{
if (!is_visible){
id appDelegate = [[UIApplication sharedApplication] delegate];
UIWindow *window = [appDelegate window];
self.waitingLabel.text = #"Attendere";
self.view.alpha = 1.0;
self.waitingView.alpha = 1.0;
[window addSubview:self.view];
[window addSubview:self.waitingView];
[self.waitingIndicator startAnimating];
self.view.frame = window.frame;
self.waitingView.center = window.center;
// "Pop in" animation for alert
[self doPopInAnimationWithDelegate:self];
// "Fade in" animation for background
[self doFadeInAnimation];
is_visible = YES;
} else {
NSLog(#"FLWaitingView %# already visible, do nothing", self);
}
}
- (void)dismiss
{
[UIView beginAnimations:nil context:nil];
self.view.alpha = 0.0;
self.waitingView.alpha = 0.0;
[UIView commitAnimations];
[self.waitingIndicator stopAnimating];
//here is the problem
[self.view removeFromSuperview];
[self.waitingView removeFromSuperview];
is_visible = NO;
}
the code above is the "wrong" one, but if I add
[self performSelector:#selector(alertDidFadeOut) withObject:nil afterDelay:0.5];
in the dismiss method and a new method (obviously removing the redundant code from dismiss method):
- (void)alertDidFadeOut
{
//here the memory is correctly released
[self.view removeFromSuperview];
[self.waitingView removeFromSuperview];
is_visible = NO;
}
the memory is correctly released.
Why??????
Thank you in advance
Fabio
Your view isn't getting released as you would be expecting because at the moment you're releasing it there are still animations linked to it. You can only properly release it after the animations are finished.
Your second method works because the animation lasts less than 0.5 seconds - the releasing code is called after view is freed of all the animations.
Proper way to animate the view would be to either create an animation and assign its delegate or maybe a bit more elegant soulution is to use block-based animation like this:
- (void)dismiss
{
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[UIView animateWithDuration: 0.15
animations: ^{
self.view.alpha = 0.0;
self.waitingView.alpha = 0.0;
}
completion: ^(BOOL finished){
[self.waitingIndicator stopAnimating];
    [self.view removeFromSuperview];
    [self.waitingView removeFromSuperview];
    is_visible = NO;
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
}];
}

Problem with removing a subview and then adding it again

I have a problem with removing a subview, or more precisely with checking if it is still there after having deleted it.
My app first adds a subview to self.view.
[self.view addSubview:tabsClippedView];
Then it adds another subview to this subview (to which it adds several buttons as subviews, but I guess this is unimportant in this context):
[tabsClippedView addSubview:tabsView];
Finally, I have a method which allows the tabsView to be deleted and then created again. I need to do this so as to update the number of buttons in that tabsView (as the user can delete buttons). The method looks basically like this:
[self.tabsView removeFromSuperview];
After that I call a method called showTabs (which I already called in the very beginning of the app in order to add the subViews). This is where it all becomes problematic and where my app crashes (I get no error in the debug console, so I don't really know what the issue is...):
if ([tabsClippedView isDescendantOfView:self.view]) {
NSLog(#"There is already a tabsClippedView.");
} else {
NSLog(#"There is no tabsClippedView. I'll add one...");
[self initTabsClippedView];
}
This is where the app crashes: when trying to assess if tabsView isDescendantOfView (I don't get any of the following logs):
if ([tabsView isDescendantOfView:tabsClippedView]) {
NSLog(#"There is already a tabsView");
} else {
NSLog(#"There is no tabsView for the buttons. I'll add one including buttons.");
[self initTabs];
}
I'd be grateful for any suggestions where the problem could be.
EDIT:
These are the methods to set up my views:
-(void) initTabsClippedView { // sets up tabsClippedView
NSLog(#"initTabsClippedView method started...");
CGRect tabsClippedFrame = CGRectMake(258,30,70,81*6);
tabsClippedView = [[[UIView alloc] initWithFrame:tabsClippedFrame] autorelease];
tabsClippedView.clipsToBounds = true;
[self.view addSubview:tabsClippedView];
NSLog(#"initTabsClippedView method ended.");
}
-(void) initTabs {
NSLog(#"initTabs started. Adding buttons to tabsClippedView...");
CGRect tabsFrame = CGRectMake(-30,0,50,480);
tabsView = [[[UIView alloc] initWithFrame:tabsFrame] autorelease];
[tabsClippedView addSubview:tabsView];
// sets up buttons in tabsClippedView
And this is where I delete the tabsClippedView (triggered by a button found in tabsClippedView):
-(void)tabDelete:(id)sender
{
UIButton *button = (UIButton *)sender;
[UIView animateWithDuration:0.75
delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
button.transform = CGAffineTransformMakeTranslation(-30, 0);
}
completion:^(BOOL finished){
[UIView animateWithDuration:0
delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
[self.tabsView removeFromSuperview];
//...
}
completion:^(BOOL finished){
NSLog(#"tabsView removed from Superview. Objects Deleted.");
[self showTabs];
NSLog(#"TabDelete finished. Button removed and tabsView updated accordingly.");
}
];
}];
And this is the showTabs method which was already called when I started the app:
-(void)showTabs {
NSLog(#"showTabs started...");
currentView = #"Tabs";
if ([tabsClippedView isDescendantOfView:self.view]) {
NSLog(#"There is already a tabsClippedView.");
} else {
NSLog(#"There is no tabsClippedView. I'll add one...");
[self initTabsClippedView];
}
if ([tabsView isDescendantOfView:tabsClippedView]) {
NSLog(#"There is already a tabsView");
} else {
NSLog(#"There is no tabsView for the buttons. I'll add one including buttons.");
[self initTabs];
}
Is it possible that you are getting EXC_BAD_ACCESS? Is it possible that the app is crashing because tabsView is deallocated when you send isDescendantOfView: to it. If you run with breakpoints enabled it should tell you the reason for the crash. If it is an EXC_BAD_ACCESS problem you should try NSZombie.
To activate NSZombie do the following:
Get info of the executable.
Go to the arguments tab.
In the "Variables to be set in the environment:" section add:
Name: NSZombieEnabled
Value: YES
Then run your app as usual and when it crashes it should tell you which deallocated object received what message.
EDIT: Just saw your edit. I think I nailed it. You're autoreleasing the views when you create them, so when they are removed from their superviews they are no longer retained and thus deallocated. You're app crashes because you're trying to run methods on deallocated views.
EDIT 2: Thought I should tell you that there is a better solution than the one posted by Praveen S.
Change your code as follows:
[tabsClippedView release];
tabsClippedView = [[UIView alloc] initWithFrame:tabsClippedFrame];
and
[tabsView release];
tabsView = [[UIView alloc] initWithFrame:tabsFrame];
The above code does the same thing as the code posted by Praveen S, but without the autorelease. An autorelease is more expensive than a regular release and should only be used when needed and in this case it isn't.
Rather than releasing before you allocate a new view you probably want to release the view when you're done with it:
[tabsView removeFromSuperview];
[tabsView release];
tabsView = nil;
or simply
[tabsView removeFromSuperview];
self.tabsView = nil;
and then instead of:
if ([tabsView isDescendantOfView:tabsClippedView]) ...
you can use:
if (tabsView) ...
As you might have noticed, there really is no need for you to retain the view. You could just as well do the following:
tabsView = [[UIView alloc] initWithFrame:tabsFrame];
[tabsClippedView addSubview:tabsView]; // This retains tabsView
[tabsView release];
and then to remove the view you would use:
[tabsView removeFromSuperview]; // This will release the tabsView
tabsView = nil;
Also remember to set the views to nil in viewDidUnload.
EDIT 3: Why self made such a difference:
What you need to understand is how properties and reference counting works. There are books and such you could read about it. I'm sure Google can provide you with some good references as well.
The difference between
self.tabsView = [[UIView alloc] initWithFrame:frame];
and
tabsView = [[UIView alloc] initWithFrame:frame];
is that self.tabsView is accessing the properties setter, while tabsView is accessing the instance variable directly.
A nonatomic, retain property's implementation looks something like the following:
- (void)setTabsView:(UIView *)view
{
if (view != tabsView) {
[tabsView release];
tabsView = [view retain];
}
}
So the property is taking care of the memory management for you. In my solution I take care of the memory management myself and thus I don't need the property to do it for me, so I don't use it.
I hope this explains why self made such a difference.
Change your code as follows:
self.tabsClippedView = [[[UIView alloc] initWithFrame:tabsClippedFrame] autorelease];
and
self.tabsView = [[[UIView alloc] initWithFrame:tabsFrame] autorelease];

using a viewController to open another instance of the same viewController

I have a MasterViewController.h.m.xib (UIViewController) that is opening a TestDummy.h.m.xib (UIViewController) in the following way:
TestDummy *controller = [[TestDummy alloc] initWithNibName:#"TestDummy" bundle:nil];
[scrollView addSubview:controller.view];
I have two buttons in TestDummy: (Open), (Close) and one label: (windowDepth).
I'm trying to create a second instance TestDummy that is open by the first TestDummy. Then allow multiple TestDummy (UIViewController) to open to N depth and allow the close button to take them back to zero depth. Here's what i have for my Open button.
-(IBAction) btnOpen_Clicked{
TestDummy *newController = [[TestDummy alloc] initWithNibName:#"TestDummy" bundle:nil];
newController.isNotRoot = YES;
newController.windowDepth = self.windowDepth + 1;
//do stuff...
childDummy = newController;
// start the animated transition
[UIView beginAnimations:#"page transition" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:YES];
//insert your new subview
[self.view addSubview:newController.view];
// commit the transition animation
[UIView commitAnimations];
[newController release];
}
When i do this i get an error in the debug console.
2010-10-07 00:59:12.549 OrionClient[5821:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType btnOpen_Clicked]: unrecognized selector sent to instance 0x6a339a0'
Must be a memory management issue but i can't figure it out.
Thanks in advance.
'NSInvalidArgumentException', reason:
'-[__NSCFType btnOpen_Clicked]:
unrecognized selector sent to instance
0x6a339a0'
It means you are trying to call a non existing method on that instance. How have you defined the btnOpen_Clicked selector? I'm guessing it should look more like, but really need to see how you defined the selector.
-(IBAction) btnOpen_Clicked:(id)sender
It means that the application can't find your method btnOpen_Clicked.
First rename your method with :
-(IBAction) btnOpen_Clicked:(id)sender
Then make sure this method specification is in the .h file
And in InterfaceBuilder with your TestDummy.xib, make also sure that the link between the button and this method is correctly done with for example TouchUpInside event.
solved it removed the last line [newController release]; need to figure out where to actually call this correctly though.

What is wrong with my code? I'm using MBProgressHUD

I'm using MBProgress HUD and I don't know what is the problem.
I have a UIButton that shows the HUD.
This is my code:
- (void)showHUD:(id)sender {
HUD = [[MBProgressHUD alloc] initWithView:self.view];
HUD.mode = MBProgressHUDModeCustomView;
[self.view addSubview:HUD];
HUD.delegate = self;
HUD.labelText = #"No Internet Connection...";
HUD.opacity = 0.7;
HUD.customView =
[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image.png"]];
[HUD showWhileExecuting:#selector(hudWasHidden)
onTarget:self
withObject:nil
animated:YES];
}
- (void)hudWasHidden {
float progress = 0.0f;
while (progress < 1.0f) {
progress += 0.01f;
HUD.progress = progress;
usleep(50000);
}
}
Here is the Console log:
2010-06-11 17:55:26.255 Dual Search[14166:207] * -[MBProgressHUD setCustomView:]:
unrecognized selector sent to instance 0x6321220 2010-06-11 17:55:26.256 Dual
Search[14166:207] Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '** -[MBProgressHUD setCustomView:]:
unrecognized selector sent to instance 0x6321220' 2010-06-11 17:55:26.256 Dual
Search[14166:207] Stack: ( 41853515, 2505499913, 42125115, 41587990, 41584658,
13036, 2853830, 3324117, 3332879, 3328066, 2977128, 2871789, 2903111, 49860988,
41394236, 41390152, 49854621, 49854818, 2895329, 10508, 10362 )
My app always crashes when clicking the UIButton.
Thanks
This is a common crash in Cocoa code: "unrecognized selector" is very explicit in this case. MBProgressHUD doesn't have a customView property, and attempting to set it is causing the crash. The setCustomView is the implicit selector (method) being called here, and Objective-C will crash when a called method isn't there.
Not sure what to tell you about how to accomplish what you're trying to do.