I have a MFMessageComposeViewController in my app, and sometimes the recipient count can get up to 200 or so. When testing on a 4S, it takes up to 10 seconds (!!) to load the sms view after I click my button. I imagine it could be even longer with older devices.
This is obviously too long for a blocking call without a loading view, but I can't put a loading view up if I have no callback when the view actually loads! There's only 1 callback in the MFMessageComposeViewControllerDelegate protocol, but that only calls back after the sms view is dismissed, not when it loads. Any ideas?
I'm thinking maybe grabbing callbacks from somewhere else in the modal view loading logic, or last resort I could just put a 5-10 second loading screen and hope it's up by then.
MFMessageComposeViewController is a subclass of UINavigationController. Have you tried implementing any of UINavigationControllerDelegate's methods. Specifically:
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
After some more digging, I found out that I was using an older version of UIViewController's presentViewController:animated:completion: so I didn't have the "completion" part implemented. This is definitely the best option for my purposes to just dismiss my loading view in the completion block, but it does only work on iOS 5.0 and up. If you have to support lower than 5.0 maybe Edwin's answer would help.
UPDATE: Using my above method actually didn't work in practice. If I threw up a progress view and then called presentViewController:animated:completion: on the MFMessageComposeViewController, it would still make the blocking call before the progress view even showed up. I'm assuming it was something to do with the run loop not completing before the blocking call started so no UI changes would come up.
Instead I just had my progress view make a callback when it actually showed up, and then in the callback I would start loading up the MFMessageComposeViewController. Then, when the MFMessageComposeViewController callback was fired I would dismiss my progress view as well as the MFMessageComposeViewController. Success!
Related
I have seen many similar questions on SO and as far as I can see all of them suggest that we should not push/pop any views while either the current view 'viewWillAppear' has not being called or the 'isViewLoaded' call returns false.
In my instance, I try to push a new view in the current view 'viewWillAppear' method and before I push it I also call [self isViewLoaded] to see if the current view is loaded (which is) yet I still keep getting the above error.
The only way I was able to solve this (which is a hack) is to sleep for few seconds before I call the push on a different non UI thread.
Since sleeping for few seconds before the call actually makes the pushing works, I assume that although both 'viewWillAppear' is already called and [self isViewLoaded] is true, it is still be sometimes too early to push/pop another view (maybe do to animation still not complete?).
I'm I missing something? Is there any other method other than 'viewWillAppear' or [self isViewLoaded] that will indicate %100 that the view has completely loaded and all animations are complete so I can safely push/pop other views?
you could try it in viewDidAppear.
however, what scenario/design you had in mind when implementing this?
IMO, it is a bad design to immediately push a new view controller from the view that isn't loaded yet.
I wonder if dealloc is always called when you close a ViewController in Objective-C for iOS. I've done some tests and it seems like it. Except I don't get any logs when I swipe close the application. I figured that maybe XCode doesn't log things if you fully close the app like that.
The reason I wonder this is because I'm sending analytic data when one of the ViewControllers closes and I kinda need to know if dealloc is always called or if there's any better way doing this.
If you want to get notified when app is killed via Swiping , you can use applicationWillTerminate method and in this method , you can check for current view controller in navigation stack and send analytical data for that View controller .
It's not defined when dealloc will be called for a view controller (or pretty much any class), therefore don't use it for sending analytics.
Use viewDidDisappear instead, which you can rely on to be called when the view controller has been removed.
I try to explain my problem. In appdelegate I have to select a rootViewController depending on the result of an asynchronous request (I'm using AFNetworking framework). In fact, I need to know if my user is profiled or not: if he is profiled I can show him app's Home, if he is not I have to show him a profilation view.
In storyboard I set the Home view as the designated entry point, but in this way this view is always shown until the asynchronous request is completed. Is there a way to make appdelegate wait for the response?
I think there is't good solution to let app delegate wait for the response because if the network connection will be poor the app loading time will be very long and OS could kill your app or user can turn it off.
You can add some loading view controller (with animation so user will know that the app is doing something) instead of home one and when you receive the response present appropriate view to the user (modal segue could do the job).
Hope this help
A better solution is to use splash screens. That is when your app gets loaded in AppDelegate, create and push a splash view controller. Which would just contain a single UIImageView covering whole screen showing your application splash image. Upon asynchronous call completion, pop that splash view controller and push your required view Controller.
Many apps use this way to download necessary asynchronous data before showing the app. So that user don't see empty screens or garbage data.
If something gets failed like internet connectivity failure or server response error, etc., Show error to user and perform error handling according to your app logic.
You can programatically navigate to the root view controller as
[self.navigationController popToRootViewControllerAnimated:YES];
This code can be put in the condition of result.
Or in your way, I think you are created a segue for navigation to the rootViewController. You can programatically perform a segue using
- (void)performSegueWithIdentifier:(NSString *)identifier sender:(id)sender
If you are using the AFNetworking, just add a method in the success block and pass the response to that method in a parameter of dictionary. Check your response in the method and choose the controller which you want to make make the root view controller from that method.
I'm currently working on an app with a viewcontroller that has a number of subviews. Some of these subviews make requests (as in urlRequests) for data to display within the subview.
Now if someone navigates away from the main viewController I want to cancel any unfinished requests in the subviews. Is it enough to cancel these requests in the "dealloc" method of a subview as obviously they don't have a viewwilldisappear method. I'm using ARC and iOS 6 for what it's worth. Otherwise should I create my own cancel method in the subviews, and then loop through them in the viewWillDisappear of the main viewcontroller to call this method?
Or is the approach of the subviews making the request wrong to begin with?
The closest thing I could find to my question was view will disappear is not firing which seems to suggest dealloc should work.
Thanks
From my point of view, your views (the subviews) should only take care of displaying a content.
The requests should be done by a dataManager singleton for example or from the view controller (You could use an NSOperationQueue).
I would then create a cancel method that stops the current request and remove the next ones from the queue.
Putting the cancel code in the dealloc or the viewDidDisappear is up to you. If you think you're view will only disappear for a short amount of time then put it in the dealloc.
You could also listen to the UIApplicationDidEnterBackgroundNotification notification to cancel the requests too.
I'm using storyboard to develop my app, and the initial view controller is a Tab Bar Controller. At the AppDelegate I did some thing work to get the jsonData from my website. It will take 10-20 seconds . You know, I want to do something to notice user is loading, not just showing a default.png picture. How can I do it?
Well, you can have your rootViewController have a loading state (maybe an UIActivityIndicatorView on top of the other views and some public methods like showLoading and hideLoading.
Steps :
In the AppDelegate application:didFinishLaunchingWithOptions: method, build your rootView controller as usual and call your method that loads the JSON files.
Call the showLoading method when you're starting the request
Call the hideLoading method when you receive the response.
Also, remember to use asynchronous request or make it in a different thread to prevent blocking the main thread.