How to push next view with TTNavigator? - objective-c

I'm using a modified View-Based Application, where I have starting UIViewController showing a input control & a TTThumbsViewController showing the results. I'm passing them in the AppDelegate using TTNavigator initWithName: for the TTThumbsViewController.
I did this by myself reading from the documentation:
The ViewDidLoad method inside my TTThumbsViewController:
- (void)viewDidLoad {
self.photoSource = [[PhotoSource alloc]
initWithType:PhotoSourceNormal
title:myTitle
photos:myImages
photos2:nil
];
}
The implementation of my AppDelegate:
#implementation geoFlickrAppDelegate
#synthesize window=_window;
#synthesize viewController=_viewController;
#synthesize navigator;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
navigator =[TTNavigator navigator];
navigator.window = self.window;
TTURLMap *map = navigator.URLMap;
[map from:#"app://home/" toViewController:[geoFlickrViewController class]];
[map from:#"app://album/(initWithName:)" toViewController:[AlbumController class]];
[navigator openURLAction:[TTURLAction actionWithURLPath:#"app://home/"]];
// Override point for customization after application launch.
[self.window makeKeyAndVisible];
return YES;
}
-(void)toGallery:(NSString*)txt
{
[navigator openURLAction:[TTURLAction actionWithURLPath:txt]];
}
The event inside my UIViewController for pushing the next view:
-(IBAction)search:(id)sender
{
NSString *str = [[NSString alloc] initWithFormat:#"app://album/%#",txtSearch.text];
geoFlickrAppDelegate *appDelegate = (geoFlickrAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate toGallery:str];
}
The result from this code is that the input is passed through my AppDelegate in the TTThumbsViewController using initWithName: but the never gets pushed in & my ViewDidLoad method never gets called. Any idea why is that so?

Whenever I had an error like this, it was the initializer (in your case initWithName:) returning nil. This is the first thing I would check. If that doesn't solve try setting a breakpoint in [TTBaseNavigator presentController:parentURLPath:withPattern:action:].
If that method is not reached something is wrong with your URL-Map. You may for Example need to urlencode your users input. URL-based Navigation works with URLs. Strings that can not be used in URLs can not be passed unencoded.
If that method is reached, you may use the debugger and step through the code from there to find out whats wrong.
Another thing I would like to mention, is that you really do not need to reference your appDelegate with [[UIApplication sharedApplication] delegate] when you like to use the navigator. This is one of the reasons why the navigator is so useful. Try to change the call to:
-(IBAction)search:(id)sender
{
NSString *str = [[NSString alloc] initWithFormat:#"app://album/%#",txtSearch.text];
TTOpenURLFromView(str, self.view);
TTOpenURL(str); // Three20 < 1.0.3
}
You can then get rid of the toGallery: method.

In my code I have no:
navigator.window = self.window;
and instantiate the window calling:
navigator =[TTNavigator navigator];
navigator.window;
and also change [self.window makeKeyAndVisible] into
[navigator.window makeKeyAndVisible]

Related

How do UIAlertView or UIActionSheet handle retaining/releasing themselves under ARC?

I want to create a similar class to UIAlertView which doesn't require a strong ivar.
For example, with UIAlertView, I can do the following in one of my UIViewController's methods:
UIAlertView *alertView = [[UIActionSheet alloc] initWithTitle:nil
message:#"Foo"
delegate:nil
cancelButtonTitle:#"Cancel"
otherButtonTitles:nil];
[alertView show];
... and the actionSheet will not be dealloced until it is no longer visible.
If I were to try to do the same thing:
MYAlertView *myAlertView = [[MYAlertView alloc] initWithMessage:#"Foo"];
[myAlertView show];
... the myAlertView instance will automatically be dealloced at the end of the current method I am in (e.g. right after the [myAlertView show] line).
What is the proper way to prevent this from happening without having to declare myView as a strong property on my UIViewController? (I.e. I want myView to be a local variable, not an instance variable, and I would like the MYAlertView instance to be in charge of its own lifecycle rather than my UIViewController controlling its lifecycle.)
Update: MYAlertView inherits from NSObject, so it cannot be added to the Views hierarchy.
UIAlertView creates a UIWindow, which it retains. The alert view then adds itself as a subview of the window, so the window retains the alert view. Thus it creates a retain cycle which keeps both it and its window alive. UIActionSheet works the same way.
If you need your object to stay around, and nothing else will retain it, it's fine for it to retain itself. You need to make sure you have a well-defined way to make it release itself when it's no longer needed. For example, if it's managing a window, then it should release itself when it takes the window off the screen.
If you add it as a subview of another view it will be retained. When the user selects and action or dismisses it, then it should call self removeFromSuperview as it's last act.
I've done my own AlertView with a little trick.
Just retain the object himself and release it on action. With this, you can call your custom alert vies as native one.
#import "BubbleAlertView.h"
#import <QuartzCore/QuartzCore.h>
#interface BubbleAlertView ()
...
#property (nonatomic, strong) BubbleAlertView *alertView;
...
#end
#implementation BubbleAlertView
...
- (id)initWithTitle:(NSString*)title message:(NSString*)message delegate:(id)delegate cancelButtonTitle:(NSString*)cancelButtonTitle okButtonTitle:(NSString*) okButtonTitle
{
self = [super init];
if (self)
{
// Custom initialization
self.alertView = self; // retain myself
//More init stuff
}
return self;
}
...
//SHOW METHOD
- (void)show
{
// We need to add it to the window, which we can get from the delegate
id appDelegate = [[UIApplication sharedApplication] delegate];
UIWindow *window = [appDelegate window];
[window addSubview:self.view];
// Make sure the alert covers the whole window
self.view.frame = window.frame;
self.view.center = window.center;
}
- (IBAction)btPressed:(id)sender
{
//Actions done
[UIView animateWithDuration:0.4f animations:^{
self.vContent.alpha = 0.f;
} completion:^(BOOL finished) {
[self.view removeFromSuperview];
self.alertView = nil; // deallocate myself
}];
}
You need to retain it somehow until it is released.
I do not really understand why you cannot implement it as subclass of UIView. Then you could use the view hierarchy as the keeper of a strong reference (retain +1). But you will have good reasons for not doing so.
If you don't have such a thing then I would use an NSMutableArray as class varialbe (meaning statc). Just declare it in the #interface block and initialize it with nil:
#interface
static NSMutableArray _allMyAlerts = nil;
provide an accessor.
-(NSMutableArray *) allMyAlerts {
if (_allMyAlerts == nil) {
_allMyAlerts = [[NSMutableArray alloc] init];
}
return _allMyAlerts
}
Within the init method do the following:
- (id) init {
self = [super init];
if (self) {
[[self allMyAlerts] addObject:self];
}
}
You will invode some method when the alert is dismissed.
- (void) dismissAlert {
// Do your stuff here an then remove it from the array.
[[self allMyAlerts] removeObject:self];
}
You may want to add some stuff to make it mutli threading save, which it is not. I just want to give an example that explains the concept.
allMyAlert could be an NSMutableSet as well. No need for an array as far as I can see. Adding the object to an array or set will add 1 to the retain count and removing it will reduce it by 1.

Xcode - I can't run the whole methods from AppDelegate

I'm using custom URL schemes to open my app, then get the link and run in a method. But I can't really run the method.
For example, I can't load web views or change labels or text fields. So how do I load web views and change labels?
AppDelegate.m:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
if (!url) { return NO; }
NSString *URLopen= [[url host] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
ViewController *vc = [[ViewController alloc]init];
vc.URLschemeLink = URLopen;
[vc URLscheme];
return YES;
}
ViewController.h:
#interface ViewController : UIViewController<MFMailComposeViewControllerDelegate> {
NSString *URLschemeLink;
}
-(void)URLscheme;
#end
ViewController.m:
#implementation ViewController
#synthesize URLschemeLink;
-(void)URLscheme {
//for example:
label.text = #"Hello"; //nothing will happen
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]]]; //Nothing will happen
NSLog ("Method Works Perfect"); //will happen
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Title:"
message:[NSString stringWithFormat:#"%#", URLSchemeLink]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
//UIAlertView will work perfectly and show the URLschemeLink from AppDelegate.
}
ok How to load the label/webview anyway?
i tested to pass a bool named runURLscheme (=true) from app delegate.
then i wrote in ViewDidLoad:
if(runURLscheme==true) {
[self URLScheme];
}
but this will not work, it will not run the URLscheme method.
How can i load the labels/webviews anyway?
The main views associated with view controllers are generally lazy loaded. Just because the view controller is initialised, it doesn't mean it's loaded its main view yet. Your views are only safe to access once viewDidLoad has been called.
Generally speaking, this pattern works well:
Store your data independently of any views that display it.
When your custom URL scheme runs, update this data.
Have a method on your view controller that updates its views from this data.
Call this method from viewDidLoad.
If there's a chance this data will be updated after your views have already been loaded (e.g. if you are receiving data from the network), then use notifications or KVO to call your view updating method again.

How to get the user's choice properly when the choice is too complex to use UIAlertView

I have been struggling with this problem for a while now, so any help would be greatly appreciated.
Here is the situation: My application has a UIViewController subclass called InitialViewController. This view controller has a UIButton, and when that button is pressed it creates a NSObject subclass called MyEngine. Something like this:
#interface InitialViewController : UIViewController <MyEngineDelegate>
...
#end
#implementation InitialViewController
...
-(IBAction)pressedButton:(id)sender {
MyEngine *engine = [[MyEngine alloc] init];
[engine start];
}
Inside start, I present a ViewController (ConflictViewController) modally to get the user's choice:
#interface MyEngine : NSObject <ConflictViewControllerDelegate>
...
-(void) start;
#end
#implementation MyEngine
...
-(void) start {
ConflictViewcontroller *cvc = [[ConflictViewController alloc] initWithNibName:#"ConflictViewController" bundle:nil];
cvc.modalPresentationStyle = UIModalPresentationFormSheet;
cvc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
cvc.delegate = self;
UIWindow *window = [(MyAppDelegate *) [[UIApplication sharedApplication] delegate] window];
[[window rootViewController] presentModalViewController:cvc animated:YES];
}
#end
ConflictViewController is really simple. It just waits for the user to decide, and when the user press the button, it send the message to the delegate, and dismiss itself.
-(IBAction)didSelectConflict:(id)sender {
UISegmentedControl *seg = (UISegmentedControl*) sender;
[self.delegate didResolveConflictChoice:seg.selectedSegmentIndex];
[self dismissModalViewControllerAnimated:YES];
}
I've checked every connection, all the delegates are working properly.
What is going wrong is:
When MyEngine receives the user's choice in it's implementation of didSelectConflict: it cannot continue properly because all of it's properties have gone null.
When the MyEngine presents the ConflictViewController, the program continues the execution and when start finishes, it goes back to pressedButton: and when this method is closed, the MyEngine object gets released.
What i want to know is if there is way around this ? Has anyone done something like this in another way ?
The question here is: How to get the user's choice properly when the choice is too complex to use UIAlertView.
Sorry for the long question, I simplified it as much as I could. Thanks for your time, any links, comments, or any kind of help is greatly appreciated
Why are you initializing MyEngine *engine in the IBAction, if you wish to use a MyEngine object why don't you make a global declaration in your InitialViewController and just call [engine start] in the IBaction. Then when the delegate method returns the selected index you can apply that to a global int in your initial view controller and continue on your way. Hope that makes sense
Make your method start as
-(void) startWithDelegate:(id)del {
ConflictViewcontroller *cvc = [[ConflictViewController alloc] initWithNibName:#"ConflictViewController" bundle:nil];
cvc.modalPresentationStyle = UIModalPresentationFormSheet;
cvc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
cvc.delegate = del;
UIWindow *window = [(MyAppDelegate *) [[UIApplication sharedApplication] delegate] window];
[[window rootViewController] presentModalViewController:cvc animated:YES];
}
and
-(IBAction)pressedButton:(id)sender {
MyEngine *engine = [[MyEngine alloc] init];
[engine startWithDelegate:self];
}
implement didResolveConflictChoice: in InitialViewController and get the delegate call there.
OR you can use UIActionSheet if suitable.

Proper view controller not displayed when tapping push notification

I have implemented push notifications and am attempting to take the user to another view controller (opposed to just opening the app) when they tap the notification. I was going off of the apple website example but have not had any luck yet.
It seems that it never even enters my code as it should. I tried putting an NSLog to prove that and I was right.
Does anyone know why? The view controller I want implemented is called statsViewController.
Thanks
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSDictionary *remoteNotif = [launchOptions objectForKey: UIApplicationLaunchOptionsRemoteNotificationKey];
if (remoteNotif) {
NSLog (#"We are here now");
[self handleRemoteNotification:application userInfo:remoteNotif]; //custom method where View controller will be implemented
return YES;
}
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
return YES;
}
-(void) handleRemoteNotification:(UIApplication *)application userInfo:(NSDictionary *)userInfo {
application.applicationIconBadgeNumber = 0;
statsViewController* viewController = [[statsViewController alloc] init];
[navController pushViewController:viewController animated:YES]; //Don't think this is right since I never use it in didFinishLaunching... I simply just declared it delegate.h
[viewController release];
}

UIApplication sharedApplication Help?

I'm trying to access a variable that creates a navigation controller that I defined in my App Delegate in a view controller (I'm building a tab bar application with a .plist for each tab, like a drill-down app). I'm trying to get rid of the error that says "request for member "indNavControl" in something not a structure or union. Could somebody help me out?
Code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
A37dgAppDelegate *AppDelegate = (A37dgAppDelegate *)[[UIApplication sharedApplication] delegate];
self.indNavControl = [AppDelegate.indNavControl];
//Get the dictionary of the selected data source.
NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row];
//Get the children of the present item.
NSArray *Children = [dictionary objectForKey:#"Children"];
if([Children count] == 0) {
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:#"DetailView" bundle:[NSBundle mainBundle]];
A37dgAppDelegate *AppDelegate = (A37dgAppDelegate *)[[UIApplication sharedApplication] delegate];
[AppDelegate.indNavControll push indNavControl];
[self.indNavControl pushViewController:dvController animated:YES];
[dvController release];
}
else {
//Prepare to tableview.
IndustriesViewController *indViewControl = [[IndustriesViewController alloc] initWithNibName:#"IndustryView" bundle:[NSBundle mainBundle]];
//Increment the Current View
indViewControl.CurrentLevel += 1;
//Set the title;
indViewControl.CurrentTitle = [dictionary objectForKey:#"Title"];
//Push the new table view on the stack
A37dgAppDelegate *AppDelegate = (A37dgAppDelegate *)[[UIApplication sharedApplication] delegate];
[self.indNavControl pushViewController:indViewControl animated:YES];
indViewControl.tableDataSource = Children;
[indViewControl release];
}
}
Really, all I want to do is reference my view controller using the UIApplication sharedApplication method, but I can't figure out how.
Did you import "A37dgAppDelegate.h" and "IndustriesViewController.h" here?
Did you declare 'indNavControl' in your AppDelegate as property? There should be
#property (nonatomic, retain) NSObject *indNavController;
or something like that in your AppDelegate.h file. Check if you have it. If not, add it.
EDIT: Also, since there is no ObjC method in
[AppDelegate.indNavControl];
you should get rid of [ and ] in this line.
EDIT2: You have multiple instances of your app delegate: at the beginning and inside 'if' statement. And their names are the same. You should delete
A37dgAppDelegate *AppDelegate = ...
inside "if" statement, because variable named "AppDelegate" already exists.
Which line? YOu have a typo (AppDelegate.indNavControll).
And it looks like you're using the horrible practice (yeah, I know Apple does it) of naming instance variables and properties the same, which makes things harder to read out of context. Is indNavControl a property of another class too? And an instance variable of that class too?