Braintree Drop In UI view is not in the window hierarchy - ios

Hi I am attempting to add in Braintrees drop In UI. I am following their guide on their website and have successfully added their cocoa pods to my project. I am now attempting to test out their Drop in UI. However when I attempt to execute the example code I get the following error.
Warning: Attempt to present <UINavigationController: 0x7dc09a00>
on <CheckOutViewController: 0x7c26ca20>
whose view is not in the window hierarchy!
Here is my source code I'm using
#import "CheckOutViewController.h"
#import "BraintreeCore.h"
#import "BraintreeUI.h"
#interface CheckOutViewController ()<BTDropInViewControllerDelegate>
#property (nonatomic, strong) BTAPIClient *braintreeClient;
#end
#implementation CheckOutViewController
- (void)viewDidLoad {
[super viewDidLoad];
// TODO: Switch this URL to your own authenticated API
/* NSURL *clientTokenURL = [NSURL URLWithString:#"https://braintree-sample-merchant.herokuapp.com/client_token"];
NSMutableURLRequest *clientTokenRequest = [NSMutableURLRequest requestWithURL:clientTokenURL];
[clientTokenRequest setValue:#"text/plain" forHTTPHeaderField:#"Accept"];
[[[NSURLSession sharedSession] dataTaskWithRequest:clientTokenRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// TODO: Handle errors
*/
NSString *clientToken = [[NSString alloc] init];
clientToken = #"eyJ2ZXJzaW9uIjoyLCJhdXRob3JpemF0aW9uRmluZ2VycHJpbnQiOiJiMzJlMDJmNmJkNjNkM2M5MzY2ZDg0YzEyNmI3ZDIyNmE0YTJjZDliYTQ1ZWNlYjk5ZGE5ZTY3NTlkOTAzYTgyfGNyZWF0ZWRfYXQ9MjAxNi0wMi0xOFQwNToxNDowOS44NzM0MDUzNTUrMDAwMFx1MDAyNm1lcmNoYW50X2lkPTM0OHBrOWNnZjNiZ3l3MmJcdTAwMjZwdWJsaWNfa2V5PTJuMjQ3ZHY4OWJxOXZtcHIiLCJjb25maWdVcmwiOiJodHRwczovL2FwaS5zYW5kYm94LmJyYWludHJlZWdhdGV3YXkuY29tOjQ0My9tZXJjaGFudHMvMzQ4cGs5Y2dmM2JneXcyYi9jbGllbnRfYXBpL3YxL2NvbmZpZ3VyYXRpb24iLCJjaGFsbGVuZ2VzIjpbXSwiZW52aXJvbm1lbnQiOiJzYW5kYm94IiwiY2xpZW50QXBpVXJsIjoiaHR0cHM6Ly9hcGkuc2FuZGJveC5icmFpbnRyZWVnYXRld2F5LmNvbTo0NDMvbWVyY2hhbnRzLzM0OHBrOWNnZjNiZ3l3MmIvY2xpZW50X2FwaSIsImFzc2V0c1VybCI6Imh0dHBzOi8vYXNzZXRzLmJyYWludHJlZWdhdGV3YXkuY29tIiwiYXV0aFVybCI6Imh0dHBzOi8vYXV0aC52ZW5tby5zYW5kYm94LmJyYWludHJlZWdhdGV3YXkuY29tIiwiYW5hbHl0aWNzIjp7InVybCI6Imh0dHBzOi8vY2xpZW50LWFuYWx5dGljcy5zYW5kYm94LmJyYWludHJlZWdhdGV3YXkuY29tIn0sInRocmVlRFNlY3VyZUVuYWJsZWQiOnRydWUsInRocmVlRFNlY3VyZSI6eyJsb29rdXBVcmwiOiJodHRwczovL2FwaS5zYW5kYm94LmJyYWludHJlZWdhdGV3YXkuY29tOjQ0My9tZXJjaGFudHMvMzQ4cGs5Y2dmM2JneXcyYi90aHJlZV9kX3NlY3VyZS9sb29rdXAifSwicGF5cGFsRW5hYmxlZCI6dHJ1ZSwicGF5cGFsIjp7ImRpc3BsYXlOYW1lIjoiQWNtZSBXaWRnZXRzLCBMdGQuIChTYW5kYm94KSIsImNsaWVudElkIjpudWxsLCJwcml2YWN5VXJsIjoiaHR0cDovL2V4YW1wbGUuY29tL3BwIiwidXNlckFncmVlbWVudFVybCI6Imh0dHA6Ly9leGFtcGxlLmNvbS90b3MiLCJiYXNlVXJsIjoiaHR0cHM6Ly9hc3NldHMuYnJhaW50cmVlZ2F0ZXdheS5jb20iLCJhc3NldHNVcmwiOiJodHRwczovL2NoZWNrb3V0LnBheXBhbC5jb20iLCJkaXJlY3RCYXNlVXJsIjpudWxsLCJhbGxvd0h0dHAiOnRydWUsImVudmlyb25tZW50Tm9OZXR3b3JrIjp0cnVlLCJlbnZpcm9ubWVudCI6Im9mZmxpbmUiLCJ1bnZldHRlZE1lcmNoYW50IjpmYWxzZSwiYnJhaW50cmVlQ2xpZW50SWQiOiJtYXN0ZXJjbGllbnQzIiwiYmlsbGluZ0FncmVlbWVudHNFbmFibGVkIjp0cnVlLCJtZXJjaGFudEFjY291bnRJZCI6ImFjbWV3aWRnZXRzbHRkc2FuZGJveCIsImN1cnJlbmN5SXNvQ29kZSI6IlVTRCJ9LCJjb2luYmFzZUVuYWJsZWQiOmZhbHNlLCJtZXJjaGFudElkIjoiMzQ4cGs5Y2dmM2JneXcyYiIsInZlbm1vIjoib2ZmIn0=";
NSLog(#"here we go again");
self.braintreeClient = [[BTAPIClient alloc] initWithAuthorization:clientToken];
// As an example, you may wish to present our Drop-in UI at this point.
// Continue to the next section to learn more...
// }] resume];
NSLog(#"HMM");
[self here];
}
-(void)here{
BTDropInViewController *dropInViewController = [[BTDropInViewController alloc]
initWithAPIClient:self.braintreeClient];
dropInViewController.delegate = self;
// This is where you might want to customize your view controller (see below)
// The way you present your BTDropInViewController instance is up to you.
// In this example, we wrap it in a new, modally-presented navigation controller:
UIBarButtonItem *item = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self
action:#selector(userDidCancelPayment)];
dropInViewController.navigationItem.leftBarButtonItem = item;
UINavigationController *navigationController = [[UINavigationController alloc]
initWithRootViewController:dropInViewController];
[self presentViewController:navigationController animated:YES completion:nil];
}
- (void)userDidCancelPayment {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)dropInViewController:(BTDropInViewController *)viewController
didSucceedWithTokenization:(BTPaymentMethodNonce *)paymentMethodNonce {
// Send payment method nonce to your server for processing
[self postNonceToServer:paymentMethodNonce.nonce];
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)postNonceToServer:(NSString *)paymentMethodNonce {
// Update URL with your server
NSURL *paymentURL = [NSURL URLWithString:#"https://your-server.example.com/checkout"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:paymentURL];
request.HTTPBody = [[NSString stringWithFormat:#"payment_method_nonce=%#", paymentMethodNonce] dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPMethod = #"POST";
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// TODO: Handle success and failure
}] resume];
}
#end
Can anyone help explain how I fix this problem?
Here is Braintrees documentation I am following
https://developers.braintreepayments.com/start/hello-client/ios/v4#present-drop-in-ui

The problem can be rectified by presenting the view controller outside the super viewDidLoad method
Specifically viewDidAppear:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
showDropIn(clientTokenOrTokenizationKey: clientToken)
}

Related

iOS: authentication and token expiration using NSURLSession

I have an application with fairly simple authorization/authentication and need a bit of help on how to handle the authorization life cycle. Here's the jist of it:
The user enters their credentials to log in and the server responds with a token and stores in the keychain
The token is used in the "Authorization" header for every subsequent URL request (REST calls)
The token eventually expires and the server will respond with 401 status code
At this point the user needs to re-authenticate via the login screen so we can get a new token and retry the request
Here's some code that represents the best I could come up with right now.
static NSString * _authorizationToken;
#implementation MyRestfulModel
+ (id) sharedRestfulModel
{
// singleton model shared across view controllers
static MyRestfulModel * _sharedModel = nil;
#synchronized(self) {
if (_sharedModel == nil)
_sharedModel = [[self init] alloc];
}
return _sharedModel;
}
+ (NSString *) authorizationToken
{
if (!_authorizationToken)
_authorizationToken = #"";
return _authorizationToken;
}
+ (void) setAuthorizationToken: (NSString *token)
{
_authorizationToken = token;
}
-(void) doSomeRestfulCall: (NSString *) restURL
completionHandler: (void (^)(NSData * data)) callback
{
// construct url path and request
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:restURL];
[request setValue:MyRestfulModel.authorizationToken forHTTPHeaderField:#"Authorization"];
[[[NSURLSession sharedSession] dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse * response, NSError * error) {
NSHTTPResponse * httpResponse = (NSHTTPResponse *) response;
if([httpResponse statusCode] == 401) { // WHAT TO DO HERE ?
dispatch_sync(dispatch_get_main_queue(), ^{
MyAppDelegate * delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate showLoginViewController callback:^(NSString * username, NSString * newToken) {
// recreate the NSURLSession and NSURLConfiguration with the new token and retry
MyRestfulModel.authenticationToken = token;
[self doSomeRestfulCall:callback];
}];
}
return;
} else {
callback(data);
}
}] resume];
}
I hope to do it like this so that the view controllers never need to worry about retrying a call due to a token expiration, all of the scary session handling and authentication can be done by one object.
I was considering trying to use NSURLSessionDelegate but I couldn't figure out the didReceiveChallenge part w.r.t. popping up the login view controller. The other option I was considering is adding a "retryHandler" similar to the completion handler which would be called after the user re-authenticates.
I think I found a nice solution. Take the "doSomeRestfulCall" method for example.
-(void) doSomeRestfullCall: (NSString *) restURL
completionHandler: (void (^)(NSData * data)) callback
{
// construct url path and request
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:restURL];
[request setValue:MyRestfulModel.authorizationToken forHTTPHeaderField:#"Authorization"];
[[[NSURLSession sharedSession] dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse * response, NSError * error) {
NSHTTPResponse * httpResponse = (NSHTTPResponse *) response;
if([httpResponse statusCode] == 401) {
[LoginViewController showWithCompletion:^(NSString *username, NSString *token) {
NSLog(#"Retrying request after user reauthenticated");
MyRestfulModel.authorizationToken = token;
[self doSomeRestfulCall:restURL completionHandler:callback];
}];
return;
} else {
callback(data);
}
}] resume];
}
Then the login view controller is where a lot of the magic happens. (You can also use an alert view if you don't want a custom login controller, check this post out: http://nscookbook.com/2013/04/ios-programming-recipe-22-simplify-uialertview-with-blocks/)
#interface LoginViewController
#property (copy) void(^completionBlock)(NSString * username, NSString * tokenCredentials);
#end
#implementation LoginViewController
+ (void) showWithCompletion: (void (^)(NSString * username, NSString * tokenCredentials))completion
{
AppDelegate * appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
UIViewController * rootController = appDelegate.window.rootViewController;
UIStoryboard * storyboard = rootController.storyboard;
LoginViewController * controller = [storyboard instantiateViewControllerWithIdentifier:#"Login"];
controller.completionBlock = completion;
controller.delegate = wrapper;
controller.modalPresentationStyle = UIModalPresentationFormSheet;
[rootController presentViewController:controller animated:YES completion:nil];
}
//
// <code for contacting your authentication service>
//
-(void) loginSuccessful: (NSString *) username withTokenCredentials:(NSString *)token
{
// <code to store username and token in the keychain>
if (self.completionBlock)
self.completionBlock(username, token);
}

Show next ViewController in block

I have next simple code, for downloading info from service.
UIViewController *nextController;
[request authWithBlock:^(NSDictionary *result, NSError *error)
{
// Result of auth request
if (!nextController) nextController = [UIViewController alloc] init];
[navigationController pushViewController: nextController];
}];
And I need show next view when request return success.
I want to know - this code are correct, or I must create and show viewcontroller in another place, not in block?
Thanks
Your code format is correct. But you can improve your code by below
__weak typeof(self) weakSelf = self;
[request authWithBlock:^(NSDictionary *result, NSError *error)
{
// Result of auth request
if (!error)
{
UIViewController *nextController = [UIViewController alloc] init];
// pass result to nexview controller if you want..
[weakSelf.navigationController pushViewController: nextController];
}
}

Adding a modal loading view to Master-Detail application (in applicationDidBecomeActive method)

In Xcode 5.0.2 I have created a blank Master Detail application for iPhone and it works okay in the simulator:
When the iPhone app is being started or awaken from a background, I would like to display a modal view with a Label "Loading..." in its middle, fetch a web page (in this test case; in the real app this will be game updates and player scores) and then dismiss the modal view on the web page fetch completion or error or timeouts.
So I have created 2 new files, LoadingViewController.h and LoadingViewController.m (with no custom code by me for now).
And since it is Xcode version 5, there are no xib files, but a Main.storyboard - so I have dragged a View Controller from the Object Library onto the storyboard. Then on the right side I have selected the LoadingViewController class as the Custom Class in the Identity Inspector:
And finally I have added 3 methods to the AppDelegate.m:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[self showLoadingView];
}
- (void)applicationWillResignActive:(UIApplication *)application
{
[self dismissLoadingView];
}
- (void)showLoadingView
{
NSLog(#"%s", __PRETTY_FUNCTION__);
[self fetchHttp];
LoadingViewController *other = [[LoadingViewController alloc] init];
[self.window.rootViewController presentViewController:other animated:YES completion:nil];
}
- (void)dismissLoadingView
{
NSLog(#"%s", __PRETTY_FUNCTION__);
[self.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
}
- (void)fetchHttp
{
NSLog(#"%s", __PRETTY_FUNCTION__);
NSString *urlAsString = #"http://stackoverflow.com";
NSURL *url = [NSURL URLWithString:urlAsString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection
sendAsynchronousRequest:urlRequest
queue:queue
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *error) {
if ([data length] > 0 &&
error == nil) {
NSString *html = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSLog(#"HTML = %u", [html length]);
}
else if ([data length] == 0 &&
error == nil) {
NSLog(#"Nothing was downloaded.");
}
else if (error != nil) {
NSLog(#"Error happened = %#", error);
}
// XXX how to dismiss the modal view here, it's a different thread?
}];
}
Unfortunately now I get a black screen in the simulator and the following output:
2013-12-01 22:37:01.332 LoadingTest[3840:a0b] -[AppDelegate showLoadingView]
2013-12-01 22:37:01.334 LoadingTest[3840:a0b] -[AppDelegate fetchHttp]
2013-12-01 22:37:01.857 LoadingTest[3840:a0b] Unbalanced calls to begin/end appearance transitions for <UINavigationController: 0x8c74f10>.
2013-12-01 22:37:01.870 LoadingTest[3840:4607] HTML = 196885
It is difficult for me to understand, how to use storyboard here (and I'd like to use it, if possible) - because the books I'm reading (in O'Reilly Safari) all talk about xib files (probably for the older Xcode versions?).
And also I don't understand how to dismiss the modal view from my completionHandler since it is in a different thread and I probably shouldn't call dismissViewControllerAnimated from there?
UPDATE:
I've added a "Storyboard ID" to my View: loadingView and the following code to AppDelegate.m:
- (void)showLoadingView
{
NSLog(#"%s", __PRETTY_FUNCTION__);
[self fetchHttp];
UIStoryboard *board = [self.window.rootViewController storyboard]; //[UIStoryboard storyboardWithName:#"Main.storyboard" bundle:nil];
LoadingViewController *other = [board instantiateViewControllerWithIdentifier:#"loadingView"];
[self.window.rootViewController presentViewController:other animated:YES completion:nil];
}
- (void)fetchHttp
{
NSLog(#"%s", __PRETTY_FUNCTION__);
NSString *urlAsString = #"http://stackoverflow.com";
NSURL *url = [NSURL URLWithString:urlAsString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[NSURLConnection
sendAsynchronousRequest:urlRequest
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *error) {
if ([data length] > 0 &&
error == nil) {
NSString *html = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSLog(#"HTML = %u", [html length]);
}
else if ([data length] == 0 &&
error == nil) {
NSLog(#"Nothing was downloaded.");
}
else if (error != nil) {
NSLog(#"Error happened = %#", error);
}
[self dismissLoadingView];
}];
}
but now I get the warning below and the loadingView isn't dismissed (probably because the web page loads faster than the modal view is shown?):
2013-12-03 01:49:12.208 LoadingTest[631:70b] -[AppDelegate showLoadingView]
2013-12-03 01:49:12.210 LoadingTest[631:70b] -[AppDelegate fetchHttp]
2013-12-03 01:49:12.756 LoadingTest[631:70b] HTML = 200949
2013-12-03 01:49:12.757 LoadingTest[631:70b] -[AppDelegate dismissLoadingView]
2013-12-03 01:49:12.757 LoadingTest[631:70b] Warning: Attempt to dismiss from view controller <UINavigationController: 0x8a70ce0> while a presentation or dismiss is in progress!
2013-12-03 01:49:12.844 LoadingTest[631:70b] Unbalanced calls to begin/end appearance transitions for <UINavigationController: 0x8a70ce0>.
First thing, when you instantiate a controller in a storyboard, you don't use alloc init, you use the UIStoryboard method instantiateViewControllerWithIdentifier:. You need to give your controller a "Storyboard ID", which I can see from your image that you haven't done yet (and if you don't understand storyboards, read Apple's documents about it).
You can dismiss your modal view from the completion handler -- the handler is the code that's called after the async operation finishes, so you you should use [NSOperationQueue mainQueue] as the queue argument.

SplitViewController reference logic

I read allot about the SplitViewControllers but i am walking in circles because i dont understand something.
You have a masterviewcontroller and a popoverview as a bar button item (filter)
lets say masterviewcontroller is a tableview and in the popoverview is a uiview controller
On the iphone i always alloced the masterviewcontroller and update the reference after some modifications, when you hit the button "search", it pushed a new controller with new data (come to think of it,maybe this wasnt the best idea) now that logic doesnt work anymore.
I have read you have to reference the controllers to each other, so i did it like this.
in the filtercontroller (this is the popoverview)
.h
#property (strong, nonatomic) MasterViewController *masterviewController;
#property (weak, nonatomic) IBOutlet UISlider *filterPrice;
- (IBAction)filterSearch:(id)sender;
.m
- (IBAction)filterSearch:(id)sender {
self.masterviewController.filterSearchPrice = [NSNumber numberWithInt:self.filterPrice.value];
[self.masterviewController performFilterSearch];
}
the performFilterSearch checks the fields, makes a call to an url with the filternames and json objects come back,parse and reload data happens..
Now i expect the masterviewcontroller to show new data but that doesnt happen, in fact nothing happens...
Update this is FilterSearch:
-(void)performFilterSearch
{
[queue cancelAllOperations];
[[AFImageCache sharedImageCache] removeAllObjects];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
isLoading =YES;
[self.tableView reloadData];
searchResults = [NSMutableArray arrayWithCapacity:10];
NSURL *url = [self urlFilterWithSearchPrice:filterSearchPrice];
NSLog(#"%#",url);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
[self parseDictionary:JSON];
isLoading = NO;
[self.tableView reloadData];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
[self showNetworkError];
isLoading = NO;
[self.tableView reloadData];
}];
operation.acceptableContentTypes = [NSSet setWithObjects:#"application/json", #"text/json", #"text/javascript",#"text/html", nil];
[queue addOperation:operation];
}
btw when i Nslog in filterSearch to check if its updated:
NSLog(#"%d",self.masterviewController.filterSearchPrice);
NSLog(#"%d",[self.filterTypeSegmentedControl selectedSegmentIndex]);
the first one never gets updated the second one gets updated off course
Update 2: (how do i launch the popview):
I added a bar button item on the masterviewcontrollers navigation that has an action.
I added a popover segue from the masterviewcontroller -> filtercontroller
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
filterPopOver = [(UIStoryboardPopoverSegue *)segue popoverController];
}
- (IBAction)filterPopButton:(id)sender {
if (filterPopOver){
[filterPopOver dismissPopoverAnimated:YES];
}
else{
[self performSegueWithIdentifier:#"showFilterPopover" sender:sender];
}
}
When you launch your filterController, you need to pass in a reference to the MasterViewController. You have a property for it in the filter controller, but you never assign a value to that property.
After Edit:
Your prepareForSegue method should look like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
FilterController *fc = (FilterController *)segue.destinationViewController;
fc.masterViewController = self;
}
Make sure that you've imported MasterViewController.h into you FilterController.m

DismissModalView Not Working

I've been pulling my hair out a bit over this. I'm creating a very simple app, it simply downloads an rss feed and displays it in a UITableview, which is inside a UINavigationController. Whilst it's downloading the feed I'm presenting a Modal View.
In my modal view I'm displaying a UIImageView and a UIActivityIndicatorView that is set to spin. I'm using ASIHTTRequest to asynchronously grab the feed and then using the either the completion block to get the response string and stop the spinner or the failure block to get the NSError and display a alert View. This all works perfectly.
I've then created a protocol to dismiss the modal view from the tableview which is called inside the completion block. But the modal view is never dismissed! I've tried pushing it into the navigation controller but exactly the same problem occurs. I even have tried setting the modal view delegate to nil but still no luck.
I've checked it without blocks using the ASIHTTPRequest delegate methods and it's the same, and if I don't present the modal view the table view is displayed normally.
Any Ideas? I've skipped out all the tableview delegate and datasource methods as well as the dealloc and any unused functions.
#interface MainTableViewController ()
-(void)loadModalView;
#end
#implementation MainTableViewController
#synthesize tableView;
#synthesize modalView;
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
[super loadView];
tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height) style:UITableViewStylePlain];
tableView.delegate = self;
tableView.dataSource = self;
[self.view addSubview:tableView];
[self loadModalView];
}
-(void)loadModalView
{
modalView = [[ModalViewController alloc]init];
modalView.delegate = self;
[self presentModalViewController:modalView animated:NO];
}
//Modal View Delegate
-(void)downloadComplete
{
modalView.delegate = nil;
[self dismissModalViewControllerAnimated:NO];
}
#end
#interface ModalViewController ()
- (void)loadView
{
[super loadView];
backgroundImage = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 320, 460)];
[self.view addSubview:backgroundImage];
spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spinner.frame = CGRectMake(160, 240, spinner.bounds.size.width, spinner.bounds.size.height);
spinner.hidesWhenStopped = YES;
[self.view addSubview:spinner];
[spinner startAnimating];
NSString* urlString = FEED_URL;
NSURL* url = [NSURL URLWithString:urlString];
ASIHTTPRequest* request = [ASIHTTPRequest requestWithURL:url];
[request setCompletionBlock:^{
// Use when fetching text data
NSString *responseString = [request responseString];
[spinner stopAnimating];
[delegate downloadComplete];
// Use when fetching binary data
}];
[request setFailedBlock:^{
NSError *error = [request error];
UIAlertView* alert = [[UIAlertView alloc]initWithTitle:#"Error" message:error.description delegate:self cancelButtonTitle:#"Continute" otherButtonTitles: nil];
[alert show];
[alert release];
}];
[request startAsynchronous];
}
Matt
In my understanding.. you solution is quite complicated..
wouldn't it be better if the class MainTableViewController is the
one who downloads the Feeds.. for the ModalView it will just act as an ActivityIndicator and dismiss after downloading..
so inside your MainTableViewController loadview:
- (void)loadView
{
NSString* urlString = FEED_URL;
NSURL* url = [NSURL URLWithString:urlString];
ASIHTTPRequest* request = [ASIHTTPRequest requestWithURL:url];
[request startAsynchronous];
//after starting the request show immediately the modalview
modalView = [[ModalViewController alloc]init];
[self presentModalViewController:modalView animated:NO];
[request setCompletionBlock:^{
// Use when fetching text data
NSString *responseString = [request responseString];
//then when it is complete dissmiss the modal
[modalView dismissModalViewControllerAnimated:NO];
// Use when fetching binary data
}];
[request setFailedBlock:^{
NSError *error = [request error];
UIAlertView* alert = [[UIAlertView alloc]initWithTitle:#"Error" message:error.description delegate:self cancelButtonTitle:#"Continute" otherButtonTitles: nil];
[alert show];
[alert release];
}];
}
i didnt use blocks in my projects, but i think it will work the same..
also I use a plain UIActivityIndicatorView (large) as subviews not modalViews.. sadly i cant test the code here now.. but i can check it later though
The only way I solved this error was to synchronously download the data and push and pop the download view onto the navigation stack. Not ideal but it works.

Resources