Dismissing a UIAlertView in ios 7 not working?

倾然丶 夕夏残阳落幕 提交于 2019-11-28 00:30:36

问题


I'm trying to dismiss a UIAlertView before showing another and I found the answer here: iOS dismiss UIAlertView beforing showing another

The problem is that this is not working on iOS7 but works on iOS6.

This is working in iOS6

-(void)closePreviousAlert{
for (UIWindow* w in [UIApplication sharedApplication].windows)
    for (NSObject* o in w.subviews)
        if ([o isKindOfClass:[UIAlertView class]])
            [(UIAlertView*)o dismissWithClickedButtonIndex:[(UIAlertView*)o cancelButtonIndex] animated:YES];
}

Is there another solution for this?


回答1:


Rather than using your O(n^2) approach to close the alert, it would probably be more lightweight (and iOS 7 valid) to create private properties for your alerts and reference and dismiss them via their synthesized getters. Also, I have from time to time set a tag on the alertview and referenced it via its tag as a quick and dirty solution.

If either of these solutions are too simple for the context of your application I might suggest rethinking your use of alertviews. Too many apps abuse alertviews and in my opinion they should be used very sparingly - just to add some unsolicited feedback :).

A different approach that could help you is to implement a block-based callback upon completion of the alertview's life. See Simplify UIAlertView with Blocks.




回答2:


Your code is not valid in iOS7 because [UIApplication sharedApplication].windows doesn't have reference to UIAlertView since the UIAlertView itself is never added to any window in iOS7.

You need to keep reference to your actionSheet, this is best thing you can do.

You can do this with a reference to https://stackoverflow.com/a/19275311/1262634:

Class UIAlertManager = NSClassFromString(@"_UIAlertManager");
UIAlertView *alertView = [UIAlertManager performSelector:@selector(topMostAlert)];

Edit: this is a private API.




回答3:


Another method to keep track of visible UIAlertView instances is possible by using method swizzling:

UIAlertView+Dismiss.h:

#import <UIKit/UIKit.h>

@interface UIAlertView (Dismiss)

+ (void)dismissAllVisibleAlertViews;

@end

UIAlertView+Dismiss.m:

#import "UIAlertView+Dismiss.h"
#import <objc/runtime.h>

// see http://nshipster.com/method-swizzling/
static inline void swizzle(Class class, SEL originalSelector, SEL swizzledSelector)
{
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

    BOOL didAddMethod =
    class_addMethod(class,
                    originalSelector,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));

    if (didAddMethod) {
        class_replaceMethod(class,
                            swizzledSelector,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

@implementation UIAlertView (Dismiss)

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        swizzle([self class], @selector(show), @selector(xxx_show));
        swizzle([self class], @selector(dismissWithClickedButtonIndex:animated:), @selector(xxx_dismissWithClickedButtonIndex:animated:));
    });
}

+ (void)dismissAllVisibleAlertViews
{
    for (NSValue *value in [self visibleAlertViews])
    {
        id val = value.nonretainedObjectValue;

        if ([val isKindOfClass: [UIAlertView class]])
        {
            [val dismissWithClickedButtonIndex: 0 animated: YES];
        }
    }
}

#pragma mark - Method Swizzling

- (void)xxx_show
{
    [self xxx_show];

    [[self.class visibleAlertViews] addObject: [NSValue valueWithNonretainedObject: self]];
}

- (void)xxx_dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated
{
    [self xxx_dismissWithClickedButtonIndex: buttonIndex animated: animated];

    [[self.class visibleAlertViews] removeObject: [NSValue valueWithNonretainedObject: self]];
}

#pragma mark - Cache

+ (NSMutableSet *)visibleAlertViews
{
    static NSMutableSet *views = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        views = [NSMutableSet new];
    });

    return views;
}

@end

This works since UIAlertViews are being shown by calling the show method. The swizzled method then tracks the instance until it gets dismissed via the dismissWithClickedButtonIndex:animated: method. You can then easily dismiss all alert views by calling [UIAlertView dismissAllVisibleAlertViews];.




回答4:


Xcode 6.4, for iOS8.4, ARC enabled

There are many posts on this topic. None seemed to be the clear solution to me, so I spent a few hours testing and finally have put together a solution that works to solve the OP's problem:

"I'm trying to dismiss a UIAlertView before showing another..."

As the OP stated the ".windows" method will no longer work. There are some other ways that I have read about that involve creating a category to UIAlertView and others using notifications; however, they were too complex for me.

Here is what to do...

1) Conform your class to UIAlertViewDelegate.

In your class' "*.h" file...

@interface YourViewController : UIViewController <UIAlertViewDelegate>

This will allow the UIAlertView object in your class to send messages to the following method:

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex

and for the UIAlertView object in your class to receive messages from the following method:

- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated:

A word to the wise, you do not have to, in some situations conform your class to UIAlertViewDelegate, but it is the safer choice. It all depends on how you will use your object in your class.

2) Declare the UIAlertView objet as a class variable or as a property.

Some advantages to creating a property is that you can have access to some getters and setters of the object.

As instance variable, in your class' "*.h" file...

@interface YourViewController : UIViewController <UIAlertViewDelegate>
{
   UIAlertView *yourAlertView;
{

//other properties

@end

As property (recommended) in your class's "*.h" file...

@interface YourViewController : UIViewController <UIAlertViewDelegate>
{
   //other instance variables
{

@property (strong, nonatomic) UIAlertView *yourAlertView;

@end

3) Avoid generating multiple references to your UIAlertView object.

For example, if you have a method that monitors a certain condition and shows the alert, then do not instanciate the UIAlertView object each time. Instead, instantiate it once in -(void)viewDidLoad and use it where you need it. Otherwise, this will prevent the

- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated:

method from sending the desired message to the correct UIAlertView object.

4) Assign the tag to the UIAlertView object and manipulate the properties to change the title, message, etc.

self.yourAlertView.title = @"some title string";
self.yourAlertView.message = @"some message string";

5) Show the UIAlertView object.

[self.yourAlertView show];

6) Dismiss before showing changed UIAlertView object.

self.yourAlertView.title = @"some other title string";
self.yourAlertView.message = @"some other message string";

[self.yourAlertView show];

7) UIAlertView is depreciated in iOS8.

https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIAlertView_Class/index.html#//apple_ref/doc/uid/TP40006802-CH3-SW8

Important: UIAlertView is deprecated in iOS 8. (Note that UIAlertViewDelegate is also deprecated.) To create and manage alerts in iOS 8 and later, instead use UIAlertController with a preferredStyle of UIAlertControllerStyleAlert.

In apps that run in versions of iOS prior to iOS 8, use the UIAlertView class to display an alert message to the user. An alert view functions similar to but differs in appearance from an action sheet (an instance of UIActionSheet).

Use the properties and methods defined in this class to set the title, message, and delegate of an alert view and configure the buttons. You must set a delegate if you add custom buttons. The delegate should conform to the UIAlertViewDelegate protocol. Use the show method to display an alert view once it is configured.




回答5:


I had the same problem and I didn't wanted to save all possible alert views as properties. I found a great alternative here:

UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Alert!" message:@"This alert will dismiss when application resigns active!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alert show];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification){
    [alert dismissWithClickedButtonIndex:0 animated:NO];
}];

In the same thread, as comment to one of the posts, there is an explanation why the old approach doesn't work in iOS 7:

In iOS7, windows does not contain the alert view windows. They are managed by another stack of windows which are not exposed.

Hope it helps someone else. :)




回答6:


Code to dismiss all alert view. This is PRIVATE API, so your app may be reject by Apple when upload to Appstore:

Class UIAlertManager = objc_getClass("_UIAlertManager");
    UIAlertView *topMostAlert = [UIAlertManager performSelector:@selector(topMostAlert)];
    while (topMostAlert) {
        [topMostAlert dismissWithClickedButtonIndex:0 animated:NO];
        topMostAlert = [UIAlertManager performSelector:@selector(topMostAlert)];
    }


来源:https://stackoverflow.com/questions/19530424/dismissing-a-uialertview-in-ios-7-not-working

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!