I have a parent detail view controller that provides common custom functions to child detail views.
The parent includes two custom functions.
One function triggers a background save to two NSManagedObjectContexts that saves a main MOC immediately to free up UI, then saves a private MOC. Fairly standard setup pre iOS 8.
The following function presents a UIAlertView to confirm the save was successful. This includes code to automatically dismiss after a set amount of time (about half a second).
This all works fine running iOS 7, both on device and on simulator.
This causes a crash when running iOS 8, both on device and on simulator.
The problem exists for only one of the five child detail views. Following a detailed side by side comparison I confirm that these each have identical code blocks and methods.
I have break points inserted into the two custom functions. The save works fine, but the code crashes after trying to present the UIAlertView, as mentioned only when running iOS 8. The debugger steps into machine code that I do not understand. The attempted save does not persist.
If I comment out the alert view, the save persists, but obviously I no longer have the alert view for the user.
Any suggestions?
UPDATE
Think I may have found a solution... of sorts... not definite yet...
UIAlertController and this article by NSHipster
Augmenting the UIAlertView code in my parent detail view controller resolves the problem.
Within my message method I now check whether iOS responds to the UIAlertController class, and if it does instantiate a UIAlertController, otherwise instantiate a UIAlertView.
- (void)message {
...other code...
if ([UIAlertController class]) { //checking whether iOS responds to the UIAlertController class
UIAlertController *alert = [UIAlertController alertControllerWithTitle:titleComplete
message:messageComplete
preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:alert animated:YES completion: ^(void){
[self performSelector:(#selector(dismissAlertController:)) withObject:alert afterDelay:durationMessageCompleteSave];
}];
// [self dismissViewControllerAnimated:YES completion:nil];
// display time on screen too short using just the dismissViewController above,
// so add into completion handler in call to presentViewController...
} else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:titleComplete
message:messageComplete
delegate:self
cancelButtonTitle:nil//self.localisedAlertButtonRemain
otherButtonTitles:nil];//buttonOther, nil];
[alertView setTag:010];
[alertView show];
[self performSelector:(#selector(dismissAlertView:)) withObject:alertView afterDelay:durationMessageCompleteSave];
}
}
...
- (void)dismissAlertView:(UIAlertView *)alert {
[alert dismissWithClickedButtonIndex:0 animated:NO];
}
- (void)dismissAlertController:(UIAlertController *)alert {
[alert dismissViewControllerAnimated:YES completion:nil];
}
Related
I'm having a problem to find out if my keywindow.rootViewController is an UIAlertController object. This must be a very simple thing to do, but I don't know what is wrong with my code:
UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
if( [rootViewController isKindOfClass:[UIAlertController class]]){
// Do something
}
Why I never enter in that IF STATEMENT, even when I can see on the debug that the view of this controller is an _UIAlertControllerView*? There is any other way to check if my key window is an Alert?
Thanks.
I assume what you want to do is be able to check if an alert is showing from any view controller in your application, but you're not sure which controller is displaying the alert and don't have access to the alert itself.
I've had to deal with this a few times. In previous versions of iOS you were able to iterate through all the subviews of a window to check if an UIAlertView was on the top, but with the changes to alerts in iOS8 this not longer works because all the delegates are deprecated and apple now recommends you use UIAlertController instead of UIAlertView. In any case, all the techniques were dependent on using certain versions of iOS and I've found them to be extremely unreliable.
What I use now is a singleton that keeps track of how many alerts are displaying. The singleton has a method that returns the current number of alerts displaying, a method for adding one, and a method for subtracting one.
This is implemented by adding one to the singleton when you present the UIAlertController:
[self presentViewController:alert animated:YES completion:^(){
AlertSingleton *muhInstance = [AlertSingleton sharedInstance];
[muhInstance addOne];
//Anything else for completion
}];
And then subtracting one with every possible action choice you add to the alert like this:
UIAlertAction *myAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
AlertSingleton *muhInstance = [AlertSingleton sharedInstance];
[muhInstance removeOne];
//Any Other alert actions
}];
Now you can know anywhere in your application if an alert is showing by checking if the count in the singleton is greater than zero like this:
if ([[AlertSingleton sharedInstance] alertCount] > 0) {
//There is an alert showing
//Your code here
}
I've found this technique to be very reliable for tracking alerts.
I wanted to understand the following scenario
-(void) foo
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Could not save file"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
//This is called when ok is pressed
}
In the above code a UIAlertView strong pointer is created and a delegate to self is assigned. The reason I called it a strong reference pointer is because it is created in scope and will go out of scope when its reference count goes to 0. I believe the reference count goes to 0 when the method foo ends then why am I still getting a callback at clickedButtonAtIndex ? I was under the assumption that we would not get a callback because the destructor of the alertView instance would have been called as soon as the method foo ended.
I believe the reference count goes to 0
You believe wrong. When you say [alert show], you hand the alert object over to Cocoa, which retains it; otherwise there would be no alert view to appear on the screen! That alert view has a reference (which is actually weak) to you (self). And Cocoa thus is able to hand the very same alert view back to you in the delegate callback; the alert is still alive because Cocoa is still retaining it, and you are still alive because you are still alive, so the reference to self works as the target of the callback.
Also, I can't quite tell whether you grasp that as soon as you say [alert show], the code does not pause - it goes right on, immediately. Thus the first method is over before the alert actually appears on the screen. Again, this works because the alert has been handed over to Cocoa, which retains it and takes care of showing it on the next runloop. None of your code is running while the alert is present on the screen.
A completely parallel situation is
MyViewController* vc = [MyViewController new];
[self.presentViewController:vc animated:YES completion:nil];
The code ends, so why doesn't MyViewController vanish in a puff of smoke? Because presentViewController hands it over to Cocoa, which inserts it into the view controller hierarchy and retains it.
The object is deallocated in ARC mode and causes crash. My code below;
BlockAlertView* alert = [BlockAlertView alertWithTitle:title message:message];
[alert setCancelButtonWithTitle:NSLocalizedString(#"No", #"No Button") block:nil];
[alert addButtonWithTitle:NSLocalizedString(#"Yes", #"Yes Button") block:^{
//Do Something
}];
[alert show];
It appears the correct alert view(which is custom UIView) but when I click one of the button it crashes.
The crash log;
2015-04-07 22:28:17.065 Test[3213:781570] <BlockAlertView: 0x16bb4160>
2015-04-07 22:33:01.746 Test[3213:781570] *** -[BlockAlertView performSelector:withObject:withObject:]: message sent to deallocated instance 0x16bb4160
Here is the source code of BlockAlertView;
BlockAlertView on Github
For now I can't estimate any of the clues for this and makes me old.
Any input will be much appreciated!
Presumably, that code worked correctly before it was converted to ARC.
To fix it, you'll need to create a strong reference to self in the -show method, and release this reference in -dismissWithClickedButtonIndex:animated: (where you see [self autorelease] commented out).
You can do this with a simple instance variable:
id _selfReference;
Assign self to _selfReference in -show:
- (void)show
{
_selfReference = self;
...
and then set _selfReference to nil in -dismissWithClickedButtonIndex:animated: in the two places where you see [self autorelease] commented out.
Assign your alert object to live somewhere beyond your current function. One simple possibility is to just make it an instance variable. If that's not practical, create an instance NSMutableArray *retainedItems; which you allocate/init, and stuff this into.
Looks like a design flaw in that project. The class is poorly named BlockAlertView as it's not actually a subclass of UIView. If it were a view and it was added to the view hierarchy then the view hierarchy would ensure it stayed alive whilst being viewed. As it is the view is kept alive but the object that created the view BlockAlertView is not held onto by anything and by the time the actions are called BlockAlertView is long gone.
This will require you to keep a strong ivar around to reference this "controller" object and it would be sensible to nil out that ivar in the completion blocks.
BlockAlertView *alertController = [BlockAlertView alertWithTitle:title message:message]; {
[alertController setCancelButtonWithTitle:NSLocalizedString(#"No", #"No Button") block:nil];
__weak __typeof(self) weakSelf = self;
[alertController addButtonWithTitle:NSLocalizedString(#"Yes", #"Yes Button") block:^{
//Do Something
weakSelf.alertController = nil;
}];
[alertController show];
}
self.alertController = alertController;
My requirement is to make my app's third tab as the home screen,and also whenever the user moves the app in background and while again moving the app to foreground it should be the home screen as our third tab instead the app is in any of the places in the app.
This issue is handled(making third tab as home screen instead the app is in any of the places/scenarios in the app).,But i have an issue now which is generated by this resolution.
Problem :- If any alert is displaying in any view and we are making the app background-foreground ,the app comes to the third tab from that screen,and the alert is still there and if we click on the alert button ,then as per the rules of IOS the "self" object of the screen of alert is deallocated(because now we are in the home screen and the alert is here) and app CRASHES !
Tried some resolutions :-
1.In a screen ,I made an global object of UIAlertView and using below line of code in applicationDidBecomeActive method of the Screen...
[_alertToRemoveContact dismissWithClickedButtonIndex:1 animated:NO];
This is working code for this view,but my problem with resolution is that i need to create a global object of alert view in all places of the app which is a very much time consuming task because i am using around 250 alerts in the project.
2.I am killing the app whenever it moves to background ,In this resolution the problem is that my app will not work its downloading functionality in background cause the app is killed.
Need help for the resolution of this issue if any one need more explanations,please leave comments.
My Crash Log....
* -[ContactShowViewController respondsToSelector:]: message sent to deallocated instance 0x1138c4e0*
*Where ContactShowViewController will differ a/c to the screen
Thanks in advance !!!
Let's try singleton:
__strong static AlertUtil* _sharedInstance = nil;
#implementation AlertUtil
{
UIAlertView *alertView;
}
+ (AlertUtil *)sharedInstance
{
#synchronized(self)
{
if (nil == _sharedInstance)
{
_sharedInstance = [[AlertUtil alloc] init];
}
}
return _sharedInstance;
}
- (void)showConfirmAlertWithMessage:(NSString *)msg cancelBtnTitle:(NSString *)btn1 okbtnTitle:(NSString *)btn2 delegate:(id)delegate tag:(int)tag
{
alertView = [[UIAlertView alloc] initWithTitle:nil message:msg delegate:delegate cancelButtonTitle:btn1 otherButtonTitles:btn2, nil];
alertView.tag = tag;
[alertView show];
}
- (void)cancel
{
if (alertView){
[alertView dismissWithClickedButtonIndex:0 animated:NO];
}
}
I have resolved this issue with the help of below solution :-
1.I created an app delegate instance of UIAlertView.
2.I implemented an alert view delegate method "will present alert view...",this method gives me all the alert view objects as a parameter where i have assigned it to my app delegate object of alert view.
3.On application life cycle method "applicationDidEnterBackground" i am using below code ...,which resigns my alert dailog on coming from background to foreground...
if ([AppDelegate shared].alertObserver)
{
//Dismissing alert which was shown before moving to background
[[AppDelegate shared].alertObserver dismissWithClickedButtonIndex:0 animated:NO];
[AppDelegate shared].alertObserver=nil;
}
I have a simple application which was working fine until I added some code which launches a new thread at some point and then tries to show an alert from that thread. Now, the app crashes whenever the code for showing the alert is hit.
UIAlertView * addAlert = [[UIAlertView alloc] initWithTitle:#"New alert"
message:#"Example alert"
delegate:nil
cancelButtonTitle:#"Cancel", otherButtonTitles:#"OK", nil];
[addAlert show];
[addAlert release];
My question is: is it possible to display UI elements such as alerts from multiple threads on iOS?
You definitely don't want to be displaying an alert (or anything UI-related) from any thread other than the main thread. I'd suggest putting your alert code in a function and call one of the performSelectorOnMainThread calls.
- (void) showAlert
{
UIAlertView * addAlert = [[UIAlertView alloc] initWithTitle:#"New alert"
message:#"Example alert"
delegate:nil
cancelButtonTitle:#"Cancel", otherButtonTitles:#"OK", nil];
[addAlert show];
[addAlert release];
}
// ... somewhere in the worker thread ...
[self performSelectorOnMainThread:#selector(showAlert) withObject:nil waitUntilDone:NO];
I'm pretty sure that the main thread is the only thread that should be the one that handles UI recognition/drawing things to screen. What I would do if I were in your position would to be either use KVO notifications or implement a protocol that some class subscribes to. Going the protocol route, when you get to the alerting part of your code merely have that thread call its protocol method, the subscribing class will be alerted by having the delegate function triggered and you can easily present whatever you have to in that view via the main thread.
Hope that helps.
Better and simple and just one line approach is to call performSelectorOnMainThread method with alertView.
In your case try this line
[addAlert performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
instead of
[addAlert show];
It will call show method of Alertview on main thread. No need to write any extra method.