【问题标题】:How to Properly Present Data from Core Spotlight and NSUserActivity如何正确呈现来自 Core Spotlight 和 NSUserActivity 的数据
【发布时间】:2016-10-14 10:17:11
【问题描述】:

我正在尝试使用 Spotlight 结果将用户引导至我的应用中的相应部分。

这是我的结构,希望它能很好地说明它。

Navigation Controller
|
|-- Main View
    |-- About View
    |
    |-- SplitView Controller (Presented Modally;StoryboardID: PhoneDirectory)
        |-- Master View Controller
        |-- Detail View Controller
    |--Another View

当搜索结果第一次将用户带到我的应用时,一切正常。我可以展示SplitViewController,然后将用户发送到相应的DetailView

但是,如果用户在当前状态下离开应用并返回搜索并点击另一个结果,则用户将返回应用并加载上一个视图。

我现在不知道该怎么做。

  • 我是否pop 上一个视图?如果有,怎么做?
  • 是否在详细视图中重新加载数据?如果有,怎么做?
  • 我如何确定我已经呈现了一个需要用不同数据呈现的视图?

使用下面的代码,我在第二次搜索结果选择中收到以下警告:

Warning: Attempt to present <PhoneDirectorySplitViewController: 0x7fdf398ab600> on <UINavigationController: 0x7fdf3c831600> whose view is not in the window hierarchy!

这是我到目前为止的代码(精简)

AppDelegate.m

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
    NSString *uid = nil;
    if ([[userActivity activityType] isEqualToString:CSSearchableItemActionType] || [[userActivity activityType] isEqualToString:@"com.myapp.activity"]) {
        uid = [userActivity userInfo][CSSearchableItemActivityIdentifier];
    }

    if (uid != nil) {
        NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
        f.numberStyle = NSNumberFormatterDecimalStyle;
        NSNumber *idNum = [f numberFromString:uid];

        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        UINavigationController *navController = nil;
        navController = [storyboard instantiateViewControllerWithIdentifier:@"PhoneDirectory"];

        if (navController != nil) {
            [[[self window] rootViewController] presentViewController:navController animated:YES completion:nil];
            PhoneDirectoryMasterViewController *master = [[[[navController viewControllers] firstObject] childViewControllers] firstObject];
            [master selectRowWithId:idNum];

            return YES;
        }
    }

    return NO;
}

MasterView.h

- (void)selectRowWithId:(NSNumber *)uid {
    [[self displayedPersons] enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(id == %@)", uid];
        NSArray *filteredPersonsArray = [obj filteredArrayUsingPredicate:predicate];
        if ([filteredPersonsArray count] > 0) {
            NSIndexPath *targetIndexPath = [NSIndexPath indexPathForRow:[[[self displayedPersons] objectForKey:key] indexOfObject:filteredPersonsArray[0]] inSection:[[self personIndexTitles] indexOfObject:key]];

            [[self tableView] selectRowAtIndexPath:targetIndexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
            [self performSegueWithIdentifier:@"showPhoneDirectoryDetail" sender:self];
        }
    }];
}

如果您需要更多信息,请告诉我。

【问题讨论】:

    标签: ios objective-c uinavigationcontroller uisplitviewcontroller


    【解决方案1】:

    您需要在适当的地方进行一些验证检查。将此声明添加到您的 AppDelegate.m

    UISplitViewController *splitViewController;
    

    您可能需要保留对splitViewController 的引用,因为您需要第二次访问它,并且您不能在每次需要时都创建新的控制器实例。目前,每次从外部返回应用程序时,您都会创建一个新的 splitViewController 实例。

    检查 #1

    if(splitViewController == nil)
    {
        // Only create a new splitViewController if it's not have been created already
        splitViewController = [storyboard instantiateViewControllerWithIdentifier:@"PhoneDirectory"];
    }
    

    检查 #2

    // presentingViewController points to the base viewController that presented it
        if(splitViewController.presentingViewController == nil)
        {
            // Only present splitViewController is it's not been presented already
            [[[self window] rootViewController] presentViewController:splitViewController animated:YES completion:nil];
        }
    

    检查 #3(在您的 MasterViewController.m 'selectRowWithId:' 内)

    // Finally before making the push with new instance, just pop the old one out of the navigationStack
        [self.navigationController popViewControllerAnimated:YES];
    
        [self performSegueWithIdentifier:@"showPhoneDirectoryDetail" sender:self];
    

    这就是你所需要的。希望这会有所帮助。

    【讨论】:

    • 谢谢。我会尝试一下。我确实尝试添加一些代码来删除presentedViewControllerpopViewController,但我认为你是对的,我需要首先引用UISplitViewController,因为我总是会得到nil。另外,我有打开UISplitViewController 的按钮,用户可以通过这种方式导航到detailViewController,然后从搜索中启动应用程序。这也会解决这个问题还是我需要做更多?谢谢。
    • 不确定这是否有效,或者我没有正确实施。
    • @RoLYroLLs 我不确定您的设计(用于导航到 splitViewController 的按钮)是如何工作的。考虑到您问题中的情况,该代码应该可以正常工作。您面临什么问题?
    • @RoLYroLLs 如果您的 detailViewController 旧实例在每种情况下都无法正确弹出。尝试在弹出呼叫中设置“动画:否”。如果还是不行,尝试在推送动作中加一点延迟0.1-0.2秒。
    • MainViewController 上的按钮在 IB 中有一个 seguePhoneDirectorySplitViewController[self.navigationController popViewControllerAnimated:YES]; 状态 Property 'navigationController' not found on object of Type 'AppDelegate *'[self performSegueWithIdentifier:@"showPhoneDirectoryDetail" sender:self]; 给了我No visible @interface for 'AppDelegate' declares the selector 'performSegueWithIdentifier:sender:'
    【解决方案2】:

    因此,在查看了许多 SO 问题/答案之后,这就是我想出的。它可能不是普遍的,但它有效。如果有人可以使其通用,请随时提交新答案。

    AppDelegate.m

    - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
        NSString *uid = nil;
        if ([[userActivity activityType] isEqualToString:CSSearchableItemActionType] || [[userActivity activityType] isEqualToString:@"com.myapp.activity"]) {
            uid = [userActivity userInfo][CSSearchableItemActivityIdentifier];
        }
    
        if (uid != nil) {
            NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
            f.numberStyle = NSNumberFormatterDecimalStyle;
            NSNumber *idNum = [f numberFromString:uid];
    
            UIViewController *vc = [[[[UIApplication sharedApplication] keyWindow] rootViewController] presentedViewController];
    
            if ([vc class] == [PhoneDirectorySplitViewController class]) {
                if ([vc respondsToSelector:@selector(dismissViewControllerAnimated:completion:)]) {
                    [vc dismissViewControllerAnimated:NO completion:^{
                        [self showUserActivity:idNum];
                    }];
                }
            } else if ([vc class] == [UINavigationController class]) {
                if ([vc respondsToSelector:@selector(visibleViewController)]) {
                    if ([[(UINavigationController *)vc visibleViewController] respondsToSelector:@selector(dismissViewControllerAnimated:completion:)]) {
                        [[(UINavigationController *)vc visibleViewController] dismissViewControllerAnimated:NO completion:^{
                            [self showUserActivity:idNum];
                        }];
                    }
                }
            } else {
                [self showUserActivity:idNum];
            }
    
            return YES;
        }
    
        return NO;
    }
    
    - (void)showUserActivity:(NSNumber *)idNum {
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        UINavigationController *navController = nil;
        navController = [storyboard instantiateViewControllerWithIdentifier:@"PhoneDirectory"];
    
        if (navController != nil) {
            [[[self window] rootViewController] presentViewController:navController animated:YES completion:nil];
            PhoneDirectoryMasterViewController *master = [[[[navController viewControllers] firstObject] childViewControllers] firstObject];
            [master selectRowWithId:idNum];
        }
    }
    

    【讨论】:

    • 这是完全错误的。你应该删除这个答案。它会误导其他开发人员。您仍然在这里制作重复的实例 - 'navController = [storyboard instantiateViewControllerWithIdentifier:@"PhoneDirectory"];'
    猜你喜欢
    • 1970-01-01
    • 2018-12-31
    • 2016-05-08
    • 1970-01-01
    • 1970-01-01
    • 2021-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多