在不知道当前视图状态的情况下实现 openURL

Posted

技术标签:

【中文标题】在不知道当前视图状态的情况下实现 openURL【英文标题】:Implementing openURL without knowing the current view state 【发布时间】:2012-12-31 00:03:23 【问题描述】:

我正在实现一个自定义 URL 方案,它将向我的数据模型添加实体。实体的详细信息包含在 URL 中。基本思想是电子邮件链接(或来自另一个应用程序的链接)将打开我的应用程序并添加新实体。

问题是,我永远无法确定我的应用在响应时会处于哪种状态。任何数量的视图控制器都可能在视图中。如果实体列表在视图中,我需要为该实体插入一个新行。如果屏幕上有其他视图,我需要做出不同的反应。有些视图也可能是模态的。

当这种情况发生时,我会对一个简单的模式感到满意 - 中止用户当前正在执行的任何操作,并弹出到根视图控制器。从这里我可能会推送到一个控制器,在那里我将显示正在添加的新实体。

我尝试总是关闭任何显示的模式并弹出到根目录,这样做的好处是不需要知道究竟显示了什么:

[(UINavigationController *)self.window.rootViewController dismissViewControllerAnimated:NO completion:nil];
[(UINavigationController *)self.window.rootViewController popToRootViewControllerAnimated:NO];

这工作相当不错,但至少有两种情况是不够的:

    如果在呈现模式时创建了某个对象(然后使用模式来修改新对象),并且如果用户取消,则委托有责任删除该对象,该实体将保持活动状态。 如果正在显示UIActionSheet,则所有投注均已关闭。如果不知道显示它的控制器,可以访问该控制器并向其发送消息以关闭操作表,我就无法关闭它。如果不这样做,根视图控制器会弹出,但操作表会留在屏幕上。后续点击操作表当然会导致崩溃,因为显示它的控制器已经消失。

我该如何稳健地处理这个问题?我是否应该尝试具体找出当前呈现的视图控制器,并依次处理每个场景?或者是否有一个更具可扩展性的解决方案,每次我添加控制器或更改应用程序的流程时都不需要更新?

【问题讨论】:

【参考方案1】:

听起来您正在尝试做几件事:

    当用户点击您的自定义网址时,您希望向模型添加一个“实体”。 您希望以某种EntityListViewController 的形式显示这个新实体,它可能在也可能不在 ViewController 堆栈上。 您(可能)想要弹出EntityListViewController 上方的所有视图控制器。 您希望用户知道添加了一个新实体(可能只是通过执行第 2 项)。 您想要推送某种EntityViewController,或者如果视图控制器堆栈中当前存在EntityViewController,您想要使用新实体的数据重新加载。

听起来您已经准备好了第 1 项,因为您没有明确询问处理 url 点击和插入新模型对象。

剩下的,一个灵活的 MVC 模式是使用 NSNotificationCenter。

插入新模型对象的代码会“发布”一个通知:

[[NSNotifcationCenter defaultCenter] postNotificationName:@"entity_added" object:myNewEntity];

然后您的各种 UI 元素(例如,UIAlertView 和 UIViewController 子类)将侦听此通知并采取一些有用的操作(例如关闭它们自己,或者在 EntityListViewControllerEntityViewController 的情况下,重新加载它们自己)。

例如,UIViewController 子类可能会这样做:

-(void) viewDidLoad

  [super viewDidLoad];
  [[NSNoticationCenter defaultCenter] addObserver:self selector:@selector(onNewEntity:) name:@"entity_added" object:nil];


-(void) onNewEntity:(MyEntity*)entity

   // close, or redraw or...


-(void) dealloc

  [[NSNoticationCenter defaultCenter] removeObserver:self];
  // if not using ARC, also call [super dealloc];

为了让你的生活保持简单(并且不要太担心所有不同的 UI 状态),我会考虑在通知发生时这样做:

    EntityListViewController 重绘自身(不管上面是否有东西)。 在导航栏中(或您​​知道始终可见的其他位置)显示某种短暂的指示器,或播放声音让用户知道已添加实体。 仅此而已。

如果您采用这种方法,那么对用户正在/正在做的任何事情的影响都会很小,但是当他们确实导航回EntityListViewController 时,它已经显示了所有新实体。

显然,如果单击自定义 URL 可能会删除现有实体,那么弹出与该实体相关的任何视图控制器会更重要。但这也可以使用相同的模式来做——让模型或控制器发布通知,然后让各种 UI 元素监听它并采取适当的行动。

【讨论】:

感谢所有伟大的建议。我很可能会走这条路。这并不理想——我正在改变我想要的 UI 行为以适应技术限制——但似乎很务实。它也具有合理的可扩展性,但我必须确保我添加的任何新控制器都记得在必要时收听通知。我将尝试实施您的建议,看看效果如何。仍然会对一些万无一失的方法感兴趣,只需将我的应用程序踢回它的起始屏幕,从那里我可以做任何我喜欢的事情。 一个中途措施可能是到popToRootViewController,但在你这样做之前,发出你即将这样做的通知。然后,您只需要在您提到的几个“特殊”位置收听该通知,例如应该在取消对象时删除对象的视图控制器,或者您的 UIActionSheet 的子类。 在任何情况下,我通常都继承 UIActionSheetUIAlertView,这是另一个很好的理由。在这些子类中,添加观察者的好地方是 initBlah,然后在 dealloc 中删除观察者。

以上是关于在不知道当前视图状态的情况下实现 openURL的主要内容,如果未能解决你的问题,请参考以下文章

如何在不知道当前/主视图是啥的情况下从后台线程打开新视图?

如何在不使当前视图颤动的情况下实现这种幻灯片效果

在不触发事件的情况下设置虚拟键/鼠标按钮状态

如何在不滚动选择器视图的情况下选择当前时间

在不覆盖 iPhone 上的当前视图的情况下呈现模态视图控制器

在不重新加载 web 视图的情况下更改 html 文本颜色