如何在 ios 11 中使用主详细信息视图禁用向后滑动手势?

Posted

技术标签:

【中文标题】如何在 ios 11 中使用主详细信息视图禁用向后滑动手势?【英文标题】:How to disable swipe back gesture with master-detail view in ios 11? 【发布时间】:2018-04-22 17:52:35 【问题描述】:

我正在尝试禁用向后滑动手势,以便在我的详细视图中向后滑动不会打开主视图。

我知道this question 和this question 存在,但我已经尝试了所有建议的解决方案,但没有一个对我有用。可能是因为我使用的是 ios 11。

为了演示这个问题,我在 Xcode 中创建了一个新的主从项目,并且只添加了我在链接问题中看到的解决方案,即添加以下内容:

self.splitViewController.presentsWithGesture = NO;

到 MasterViewController 的 viewDidLoad 并添加这个

- (void)viewWillLayoutSubviews 
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) 
        self.navigationController.navigationController.interactivePopGestureRecognizer.enabled = NO;
    

到 DetailViewController

主视图控制器

#import "MasterViewController.h"
#import "DetailViewController.h"

@interface MasterViewController ()

@property NSMutableArray *objects;
@end

@implementation MasterViewController

- (void)viewDidLoad 
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.navigationItem.leftBarButtonItem = self.editButtonItem;

    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];
    self.navigationItem.rightBarButtonItem = addButton;
    self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
    self.splitViewController.presentsWithGesture = NO;



- (void)viewWillAppear:(BOOL)animated 
    self.clearsSelectionOnViewWillAppear = self.splitViewController.isCollapsed;
    [super viewWillAppear:animated];



- (void)didReceiveMemoryWarning 
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.



- (void)insertNewObject:(id)sender 
    if (!self.objects) 
        self.objects = [[NSMutableArray alloc] init];
    
    [self.objects insertObject:[NSDate date] atIndex:0];
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];



#pragma mark - Segues

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 
    if ([[segue identifier] isEqualToString:@"showDetail"]) 
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        NSDate *object = self.objects[indexPath.row];
        DetailViewController *controller = (DetailViewController *)[[segue destinationViewController] topViewController];
        [controller setDetailItem:object];
        controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
        controller.navigationItem.leftItemsSupplementBackButton = YES;
    



#pragma mark - Table View

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
    return 1;



- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
    return self.objects.count;



- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    NSDate *object = self.objects[indexPath.row];
    cell.textLabel.text = [object description];
    return cell;



- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath 
    // Return NO if you do not want the specified item to be editable.
    return YES;



- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 
    if (editingStyle == UITableViewCellEditingStyleDelete) 
        [self.objects removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
     else if (editingStyle == UITableViewCellEditingStyleInsert) 
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
    



@end

DetailViewController

#import "DetailViewController.h"

@interface DetailViewController ()

@end

@implementation DetailViewController

- (void)configureView 
    // Update the user interface for the detail item.
    if (self.detailItem) 
        self.detailDescriptionLabel.text = [self.detailItem description];
    



- (void)viewDidLoad 
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self configureView];



- (void)viewWillLayoutSubviews 
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) 
        self.navigationController.navigationController.interactivePopGestureRecognizer.enabled = NO;
        self.navigationController.interactivePopGestureRecognizer.enabled = NO;
    



#pragma mark - Managing the detail item

- (void)setDetailItem:(NSDate *)newDetailItem 
    if (_detailItem != newDetailItem) 
        _detailItem = newDetailItem;

        // Update the view.
        [self configureView];
    



@end

AppDelegate

#import "AppDelegate.h"
#import "DetailViewController.h"

@interface AppDelegate () <UISplitViewControllerDelegate>

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
    // Override point for customization after application launch.
    UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
    UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
    navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem;
    splitViewController.delegate = self;
    return YES;



- (void)applicationWillResignActive:(UIApplication *)application 
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.



- (void)applicationDidEnterBackground:(UIApplication *)application 
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.



- (void)applicationWillEnterForeground:(UIApplication *)application 
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.



- (void)applicationDidBecomeActive:(UIApplication *)application 
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.



- (void)applicationWillTerminate:(UIApplication *)application 
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.



#pragma mark - Split view

- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController 
    if ([secondaryViewController isKindOfClass:[UINavigationController class]] && [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[DetailViewController class]] && ([(DetailViewController *)[(UINavigationController *)secondaryViewController topViewController] detailItem] == nil)) 
        // Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return YES;
     else 
        return NO;
    


@end

如何禁用向后滑动功能?

【问题讨论】:

【参考方案1】:
self.navigationController.interactivePopGestureRecognizer.enabled = NO;

这条线肯定对我有用。不管怎样,我建议你把它移到 viewDidLoad 函数中,这样它就只执行一次。

【讨论】:

这不起作用,我的项目中没有任何东西可以使它独一无二。它实际上只是自动生成的 xcode 主从应用程序,其中一行添加到 viewDidLoad 函数。在 iphone 7 模拟器中运行。 等等,你把那行放在 DetailViewController 中,对吧? 有什么方法可以让我查看您正在构建的项目吗? 就在DetailViewController中。抱歉,我无法分享该项目。【参考方案2】:

对于 Objective-C:

- (void)viewWillAppear:(BOOL)animated 
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) 
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;

对于斯威夫特:

self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

但不推荐。 查找similar question

【讨论】:

【参考方案3】:

我想通了。我必须将此添加到我的 DetailViewController

id savedGestureRecognizerDelegate1;
id savedGestureRecognizerDelegate2;

- (void)viewDidAppear:(BOOL)animated 
    [super viewDidAppear:animated];
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) 
        savedGestureRecognizerDelegate1 = self.navigationController.interactivePopGestureRecognizer.delegate;
        self.navigationController.interactivePopGestureRecognizer.delegate = self;
    
    if ([self.navigationController.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) 
        savedGestureRecognizerDelegate2 = self.navigationController.navigationController.interactivePopGestureRecognizer.delegate;
        self.navigationController.navigationController.interactivePopGestureRecognizer.delegate = self;
    


- (void)viewWillDisappear:(BOOL)animated 
    [super viewWillDisappear:animated];
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) 
        self.navigationController.interactivePopGestureRecognizer.delegate = savedGestureRecognizerDelegate1;
    
    if ([self.navigationController.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) 
        self.navigationController.navigationController.interactivePopGestureRecognizer.delegate = savedGestureRecognizerDelegate2;
    


- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer

    if (gestureRecognizer == self.navigationController.interactivePopGestureRecognizer || gestureRecognizer == self.navigationController.navigationController.interactivePopGestureRecognizer) 
        return NO;
    
    return YES;

这是因为拆分视图控制器中有两个导航控制器。保存的手势识别器委托是因为this。

【讨论】:

以上是关于如何在 ios 11 中使用主详细信息视图禁用向后滑动手势?的主要内容,如果未能解决你的问题,请参考以下文章

GetOrgChart:如何禁用单击转到详细信息视图的默认行为?

如何在 iOS 7 上的 UINavigationController 中禁用向后滑动手势

在 iOS 中禁用 uiwebview 上的某些触摸事件

如何在整个应用程序中禁用 iOS 11 拖动?

Xamarin Forms 在 iOS 的主详细信息页面中重复的工具栏

如何在 React Navigation 中禁用向后滑动选项或弹出路线?