是否可以为iOS创建自定义崩溃动画?

时间:2013-05-03 12:16:39

标签: ios objective-c crash

我想在应用关闭之前显示玻璃碎片动画。通过设置ExceptionHandler

,我设法在应用关闭之前捕获屏幕截图
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler); 
    return YES;
}

void uncaughtExceptionHandler(NSException *exception) {

    UIWindow *lastWindow = [[UIApplication sharedApplication].windows lastObject];
    UIGraphicsBeginImageContext(lastWindow.bounds.size);
    [lastWindow.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *pngImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    NSData * data = UIImagePNGRepresentation(pngImage);
}

但是我甚至无法在视图中添加一个简单的图像,应用程序在下一个绘图周期之前崩溃。在app关闭之前,是否可以在屏幕上显示任何内容?

2 个答案:

答案 0 :(得分:3)

一旦发出异常,应用程序的状态就不确定了;没有人知道你的应用程序在做什么,虽然有很多事情可能会出错(参见上面的链接),我将在这里关注的是数据损坏。

一些崩溃记者试图在程序终止后立即通过网络提交崩溃报告;在您的情况下,您正在尝试保持应用程序运行并显示消息,这具有相同的效果:保持应用程序运行意味着执行应用程序自己的代码,然后应用程序可以自由尝试编写可能已损坏的用户数据

考虑基于核心数据的应用程序,其中更新模型对象,然后保存:

person.name = name;
person.age = age; // an exception occurs here
person.birthday = birthday;
[context save: NULL];

在崩溃时,托管对象上下文包含部分更新的记录 - 当然不是您想要保存到数据库的内容。但是,如果未捕获的异常处理程序继续执行以保持应用程序正常运行,则还将运行应用程序中的任何网络连接,计时器或其他挂起的runloop调度。如果从runloop调度的任何应用程序代码包含对 - [NSManagedObjectContext save:]的调用,则会将部分更新的记录写入数据库,从而破坏用户的数据。

当您的应用程序处于未知/非确定状态时,最安全的做法就是退出。

答案 1 :(得分:1)

好吧,不知道如果这会对你有所帮助,但在我的应用程序中,我设法向用户显示UIAlertView有关崩溃,异常类型,描述和堆栈跟踪的解释(全部使用NSSetUncaughtExceptionHandler方法),如下所示:

Sample in spanish

然后我提供了杀死应用程序的建议选项或继续,尽管应用程序可能不稳定。在我的情况下,它部分影响了应用程序功能,因此在大多数情况下,用户可以保存其工作并安全地关闭应用程序。

如果你想我可以编辑答案并在这里发布代码(我将不得不搜索我的Xcode项目文件夹,这就是我没有发布它的原因。)

编辑:

在AppDelegate委托的方法willFinishLaunchingWithOptions上设置NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);

然后我按如下方式创建处理程序方法:

static void uncaughtExceptionHandler(NSException *exception)
{
    [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"kDisculpe", nil) message:[NSString stringWithFormat:@"%@ %@%@ %@%@ %@", NSLocalizedString(@"kErrorText", nil), [exception name], NSLocalizedString(@"kErrorDescripcion", nil), [exception reason], NSLocalizedString(@"kErrorTrazaPila", nil), [exception callStackReturnAddresses]] delegate:[[UIApplication sharedApplication] delegate] cancelButtonTitle:NSLocalizedString(@"kContinuar", nil) otherButtonTitles:NSLocalizedString(@"kSalir", nil), nil] show];
    [[NSRunLoop currentRunLoop] run];
}

然后在AlertView的委托方法clickedButtonAtIndex上设置:

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if ([[alertView title] isEqualToString:NSLocalizedString(@"kDisculpe", nil)]) {
        switch (buttonIndex) {
            case 0:
                if ([[alertView title] isEqualToString:NSLocalizedString(@"kDisculpe", nil)]) {
                    [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"kAdvertencia", nil) message:NSLocalizedString(@"kAppContinuaraInestable", nil) delegate:[[UIApplication sharedApplication] delegate] cancelButtonTitle:NSLocalizedString(@"kContinuar", nil) otherButtonTitles:nil] show];
                }
                break;
            case 1:
                exit(0);
                break;
        }
    }
}

请注意,我唯一重要的事情是[[NSRunLoop currentRunLoop] run];我希望这会对你有帮助。