如何“取消” UIStoryBoardSegue

Posted

技术标签:

【中文标题】如何“取消” UIStoryBoardSegue【英文标题】:How to "cancel" a UIStoryBoardSegue 【发布时间】:2011-12-10 19:29:27 【问题描述】:

有谁知道如何有条件地“停止”转场:

我的表格视图单元格表示可以在向下钻取“详细信息”视图中查看的产品......或者不能! (这取决于几件事)

现在我的应用认为所有产品都“解锁”了:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

    NSIndexPath *selectedRowIndex = [self.tableView indexPathForSelectedRow];
    ListaProdottiController *prodottiViewController = [segue destinationViewController];
    prodottiViewController.blocco = [self.fetchedResultsController objectAtIndexPath:selectedRowIndex];

此时如何取消行选择 => 向下钻取?

【问题讨论】:

如果您使用的是 ios 6 或更高版本,请参阅下面的my answer 了解处理此问题的正确方法。 【参考方案1】:

如果您的目标是 iOS 6 或更高版本,那么我所知道的最简洁的方法如下:

-(BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender

    if([identifier isEqualToString:@"show"])
    
        NSIndexPath *selectedRowIndex = [self.tableView indexPathForSelectedRow];
        Blocco *blocco = [self.fetchedResultsController objectAtIndexPath:selectedRowIndex];
        return [blocco meetRequiredConditions];
    
    return YES;

哪里有方法

-(BOOL) meetsRequiredConditions;

如果允许向下钻取的“几件事”有效,则在 Blocco 类上定义返回 YES。

【讨论】:

查看 Apple 参考资料,似乎这就是它的用途。在我的情况下,如果 segue 被阻止,我想做一个对话框。不过一次只做一件事。【参考方案2】:

我不知道这样做是否正确,但我发现了一种解决方法。

从情节提要中,我从视图控制器的状态栏中关联(控制+单击)一个 segue。给 segue 一个 ID(例如:switchSegue)。

现在,从您代码中的一个操作(在我的代码中我使用一个按钮),我调用:

 [self performSegueWithIdentifier:@"switchSegue" sender:sender];

这样你就可以控制你的segue是否被执行。 尝试从 here 和 here 获得帮助的教程

希望这会有所帮助。

【讨论】:

这是我发现的最简单的方法 :) 酷!谢谢! 一个简单快速的固定,很好的解释和来源——我希望每个答案都这么棒【参考方案3】:

我正在使用一种更简单、更整洁的方法。

故事板

    创建两个具有不同标识符的相同单元格。例如:“cellWithSegue”和“cellWithoutSegue”。 将第一个单元格(“cellWithSegue”)与要显示的 segue 连接起来。 不要将第二个单元与任何 segue 连接。

表格视图

    在 cellForRowAtIndexPath 上,实现一个逻辑来确定单元格是否应链接到 segue。 对于应该与 segue 链接的单元格,使用“cellWithSegue”标识符,其余的使用“cellWithoutSegue”。

这种方式看起来更容易实现,也不会改变 segues 的工作方式。

【讨论】:

这是迄今为止最强大的解决方案,而且仍然非常简单。非常感谢。如果可以的话,我会再次投票。 哇,这是最乏味的方法 - 现在您必须维护两种单元格类型的外观,即确定显示哪一种而不是 if (this) doSegue() 之类的代码。跨度> -1 这是一个可怕的解决方案。 “创建两个相同的单元格”直接违反了 DRY 原则。 是什么让你觉得这很“整洁”? 我不再这样做了。我当时不知道接受的解决方案。【参考方案4】:

我在这里可能错了,但是在为此苦苦挣扎之后,我只是在不希望触发序列的单元格上禁用了单元格的用户交互(在 cellForRowAtIndexPath 中:)。似乎完美运行,而且只有 1 行代码!

cell.userInteractionEnabled = NO;

【讨论】:

很棒的提示!就我而言,完美的解决方案。 这也是我的首选解决方案。谢谢! 请注意,此解决方案适用于 cellForRowAtIndexPath:,但在用于其他方法时还不够,例如在尝试防止 prepareForSegue:sender: 中的双重触发时,因为可能会重新创建单元格而无需用户交互被禁用。【参考方案5】:

最简单的解决方案是在故事板中创建手动转场并使用它,如下所示。

[self performSegueWithIdentifier:@"loginSuccessSegue" sender:self];

或者


@Fabio:我试图为相同类型的用例找到解决方案,但我几乎找到了解决方案。

用例 1.有条件地停止segue转换 2.有条件地改变目标viewController

解决方案:

使用“自定义”转场。 按照以下步骤创建自定义 segue 1.创建UIStoryboardSegue的子类 "MyCustomSegue.h"

@interface MyCustomSegue : UIStoryboardSegue
@end

“MyCustomSegue.m”

重写 initWithIdentifier 以实现用例 1 和 2 如果你返回 nil,segue 将被取消/不采取任何行动 您实例化您的 ViewController 并将其设置为目标。 您也可以将目标设置为您的旧 xib 文件。该代码已被注释,但我确保它会起作用。

@implementation MyCustomSegue
- (id)initWithIdentifier:(NSString *)identifier source:(UIViewController *)source destination:(UIViewController *)destination

    UIStoryboard *storyBoard= [UIStoryboard storyboardWithName:@"MainStoryboard_iPhone" bundle:nil];

    UIViewController *viewController = [storyBoard instantiateViewControllerWithIdentifier:@"testIdentifier"];

    // MyViewController* viewController= [[MyViewController alloc]initWithNibName:@"MyViewController" bundle:nil];
    return [super initWithIdentifier:identifier source:source destination:viewController];

    您必须覆盖“执行”。

您也可以在这里实现用例 1..

- (void)perform 
    // if either source or destination is nil, stop
    if (nil == self.sourceViewController || nil == self.destinationViewController) return;
    // return; //No Action. Segue will be cancelled
    UINavigationController *ctrl = [self.sourceViewController navigationController];
    [ctrl
     pushViewController:self.destinationViewController
     animated:YES];

希望这会有所帮助。如果你不清楚,请写出来。

【讨论】:

这似乎比需要的要多得多。根据选择的表格单元格来选择要调用(或不调用)的序列不是更容易吗?您不会创建自定义 segue,并且您的视图控制器将失去对其他视图控制器的依赖。您可以使用以下命令请求在代码中执行序列:[self performSegueWithIdentifier:@"SegueIdentiferName" sender:self];。 同意这不是必需的。只需手动调用 performSegueWithIdentifier 就不需要覆盖 UIStoryboardSegue 在我看来这不是很多代码,它只是一个类,你可以通过使用类别和协议来重用其他 segue 和控制器。我发现它比编写大量 it-then-else 案例来决定在 xib 中按下了哪个按钮或单元格要好得多。 更好的方法是将触发 segue 的事物连接到 IBAction,该 IBAction 确定是否应该发生 segue 并触发 [self performSegueWithIdentifier:@"Whatever" sender:self]【参考方案6】:

在 UITableViewDelegate 方法tableView:willSelectRowAtIndexPath: 中执行此操作的一种不错且轻量级的方法——如果适用的话。 YMMV。

这就是我的做法(iOS 5、ARC)。我在视图控制器中使用了一个 BOOL 实例变量,最初在 viewDidLoad 中设置为 False。在 Interface Builder 中将表格单元格设置为 segue 的目标视图控制器依赖于从服务器加载的一些数据,所以我不希望 segue 在我拥有数据之前发生。

简单来说,就是这个样子:

@implementation SomeViewController 
    BOOL okayToSegue;


...

- (void)viewDidLoad

    [super viewDidLoad];
    okayToSegue = NO;
    // The success block for the data retrieval
    void(^successBlock)(void) = ^
        // Other code...
        okayToSegue = YES;
    
    [[ServerClient sharedClient] getDataFromServerSuccess:successBlock];


...

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath

    if (!okayToSegue) 
        return nil;
    
    return indexPath;

忽略那里的 sharedClient 之类的细节,这就是我用成功块调用我的 AFHTTPClient 子类的方式,在现实生活中我也会有一个失败块和其他东西。

tableView:willSelectRowAtIndexPath: 中返回nil 会导致点击表格单元格什么都不做。只有在我从我的 AFHTTPClient 实例(通过 successBlock)中得知 segued-to 视图控制器所需的数据已准备好并等待之后,我才更改实例变量,以后的点击将正常工作。在现实生活中的代码中,您会希望有一些用户可见的通知或视觉上明显的迹象表明 segueing 尚不可能。

因此,在许多情况下,您需要确定从表格单元格中分割是否可以的任何逻辑都可以以这种方式进行。

【讨论】:

可能是最通用的解决方案。 尽管如果您仍然希望在不执行 segue 的情况下选择单元格,这将无法按预期工作。 jweyrich:是的。我认为,以编程方式在 willSelectRow... 中进行单元格选择(可能是定时取消选择)仍然是可能的,并且无论如何,您都希望向用户提供某种类型的指示,说明为什么单元格点击没有做什么他们期待。 想象一个选择器,但在它自己的屏幕上。用户点击桌子上的一个项目,它调用一个传递所选项目的委托,然后处理它并弹出控制器。这需要我扩展 UITableViewController,添加属性 inSelectionMode 并有条件地将 accessoryType 从 Disclosure 更改为 Mark。因此,我希望在不执行 segue 的情况下直观地选择单元格。两者似乎都需要调用didSelectRowAtIndexPath。我找到的最简单的解决方案是将 segue 直接链接到控制器并有条件地调用 performSegueWithIdentifier【参考方案7】:

Apple 的模板为 iPad popOver 执行此操作的方式是使用手动 segue,而不是在触摸时触发手动 segue 的自动 segue,需要使用 performSegueWithIdentifier 触发:

要创建手动转场而不是从您想到的元素中按 ctrl-drag 从视图的控制器图标中按住 ctrl-drag,请为 segue 设置一个标识符,然后在 IB 中完成。

【讨论】:

【参考方案8】:

这是我刚刚找到的另一个解决方案。

在 Mainstoryboard TableView 中,当您使用从单元格 (identifier=Cell) 到目标视图的自动 segue 时,您还可以添加另一个具有另一个标识符的单元格 (identifier=CellWithoutSegue)。因此,在 cellForRowAtIndexPath 中创建新单元格时,只需重用您想要的单元格标识符(带或不带 segue)。

希望对你有帮助!

(如果您需要一些代码源示例,请告诉我)。

问候,

【讨论】:

【参考方案9】:

你可以通过返回 false in 来取消 segue

override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool 
    let someCondition = ...
    return someCondition

每当转场即将发生时,都会调用此方法。 当 performSegue(withIdentifier: ..., sender: ...) 显式触发 segue 时,调用此方法。

【讨论】:

【参考方案10】:

在您的- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender; 方法中,您可以添加一些代码,例如:

if ([[segue identifier] isEqualToString:@"DemoSegue"] && !self.canOpenDemo) 
    id *nc = [segue destinationViewController]; // UIViewController, UINavigationController, etc. (keeping "id" will return warning)
    [nc dismissModalViewControllerAnimated:NO];

这将阻止视图打开,但是我没有检查,但它似乎已经调用了你的目标视图控制器初始化函数(再次,我没有检查 Xcode,所以我不完全当然)。

【讨论】:

这根本行不通。你不能关闭视图控制器,因为你在 segue 发生之前就在运行。 由于某种原因,当 segue 以模态方式呈现时它仍然有效,即使它看起来不像。它在我停止的应用程序中有效。现在发布了 5.0.1 和 5.1,它可能无法正常工作。 iOS 6 中有一个shouldPerformSegueWithIdentifier:sender: 方法,但由于 NDA,无法解释更多内容。

以上是关于如何“取消” UIStoryBoardSegue的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Angular 4+ 中取消/取消订阅所有挂起的 HTTP 请求

如何取消2003系统关机计划

如何取消mySql 5.0 密码

如何取消WORD中的自动更新时间

如何使用取消令牌Azure Service Bus(SubscriptionClient)

如何根据条件行值对 pandas 数据框进行取消堆叠或取消透视?