美文网首页PerhapYs的OC学习日记
iOS-中东镜像翻转+添加左滑返回手势

iOS-中东镜像翻转+添加左滑返回手势

作者: 路飞_Luck | 来源:发表于2018-10-21 17:17 被阅读222次
    一 序言
    • 中东国家的人们使用习惯和其他国家不太一致,我们一般是从左往右看,但是中东的人们习惯从右往左看。
    • 随着大屏手机的出现,侧滑返回功能就显得至关重要。但是正常情况下,当进入一个新的页面时,新页面是从右往左慢慢显示。当返回上一个页面时,右滑即可。
    • 但是中东国家恰恰相反,页面时从左往右显示,需要左滑返回上一个页面。
    二 本文解决的问题
    • 如何正确处理系统语言和当前APP语言问题
    • 如何做到中东镜像
    • 添加全面左滑手势
    三 准备工作
    • 本项目是通过第三方库FDFullscreenPopGesture实现的,所以阅读本文之前需要先熟悉该第三方库的原理。传输地址 iOS-FDFullscreenPopGesture详解

    先看看效果图


    aribc.gif
    四 开始讲解啦
    4.1 如何正确处理系统语言和当前APP语言关系

    本项目写了一个类RTLHelper,用于处理该问题

    #define AppLanguage @"AppLanguage"
    #define ArabicLanguage @"ArabicLanguage"
    #define EnglishLanguage @"EnglishLanguage"
    
    // 判断用户当前系统语言是否是阿语
    + (bool)isArabicSystemLanguage {
        if (!isArabicKay) {
            NSArray *languages = [NSLocale preferredLanguages];
            NSString *currentLanguage = [languages objectAtIndex:0];
            
            NSString *system_prefix_language = @"";
            if ([currentLanguage containsString:@"-"]) {
                NSArray *arr = [currentLanguage componentsSeparatedByString:@"-"];
                system_prefix_language = arr[0];
            } else {
                system_prefix_language = currentLanguage;
            }
            isArabicKay = [NSNumber numberWithBool:[system_prefix_language isEqualToString:@"ar"]];
        }
        return [isArabicKay boolValue];
    }
    
    // 判断当前APP是否是阿语
    + (bool)isRTL {
        NSString *language = [[NSUserDefaults standardUserDefaults] valueForKey:AppLanguage];
        if ([language isEqualToString:ArabicLanguage]) {
            return YES;
        }
        return NO;
    }
    
    4.2 如何做到中东镜像翻转

    苹果已经帮我们做好了UI布局变动的事项,只要你的app是建立在约束环境中,并且实现了Leading以及Trailing两个约束条件而不是Left和Right,那么,只要输入几句短短的代码就可以轻松不费力地完成整个UI布局的变动。

    /**
     设置视图方向
     1.包括 push 和 pop 时的方向
     2.包括视图布局的方向
     */
    + (void)initRTL {
        //APP阿拉伯语言
        bool isRTL = [RTLHelper isRTL];
        if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"9.0") && isRTL) {
            [[UIView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceRightToLeft];
        } else if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"9.0") && !isRTL) {
            [[UIView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceLeftToRight];
        }
    }
    

    只要在AppDelegate中调用该方法或者切换APP语言后调用方法即可。

    1.该方法会设置视图的布局方向,比如从右往左布局,还是从左往右布局
    2.设置pushpop时的方向。

    4.3 切换APP语言后需要做什么事情

    1.保存当前APP语言为新设置的语言(保存一个key)
    2.设置当前APP的布局方向,即semanticContentAttribute参数
    3.移除栈中所有控制器,并且给window设置一个新的rootVC,然后回到根控制器即可

    相关代码如下

    • 保存当前APP语言为新设置的语言(保存一个key)
    [[NSUserDefaults standardUserDefaults] setValue:self.appLanguage forKey:AppLanguage];
    [[NSUserDefaults standardUserDefaults] synchronize];
    
    • 设置当前APP的布局方向,即semanticContentAttribute参数
    /**
     设置视图方向
     1.包括 push 和 pop 时的方向
     2.包括视图布局的方向
     */
    + (void)initRTL {
        //APP阿拉伯语言
        bool isRTL = [RTLHelper isRTL];
        if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"9.0") && isRTL) {
            [[UIView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceRightToLeft];
        } else if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"9.0") && !isRTL) {
            [[UIView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceLeftToRight];
        }
    }
    
    • 移除栈中所有控制器,并且给window设置一个新的rootVC,然后回到根控制器即可
    // 新增TabBar后直接处理为重设APP界面
    + (void)setController {
        NSMutableArray *tbViewControllers = [NSMutableArray arrayWithArray:[[IContext getCtx].rootTabBarController viewControllers]];
        // 移除栈中所有控制器
        for (BaseNavigationController *navVc in tbViewControllers) {
            for (BaseViewController *vc in navVc.childViewControllers) {
                [vc removeFromParentViewController];
            }
        }
        [tbViewControllers removeAllObjects];
        [[IContext getCtx].rootTabBarController setViewControllers:tbViewControllers];
    
        // 给window设置一个新的根控制器
        MainViewController *tb = [[MainViewController alloc] init];
        [IContext getCtx].rootTabBarController = tb;
        [[IContext getCtx].rootWindow setRootViewController:tb];
        [[IContext getCtx].rootWindow makeKeyWindow];
    }
    
    [self.navigationController popToRootViewControllerAnimated:YES];
    
    4.4 添加左滑手势
    1. 定义一个左滑的手势的类
    • RTLEdgePanGesture
    @interface RTLEdgePanGesture : UIScreenEdgePanGestureRecognizer
    @end
    
    @implementation RTLEdgePanGesture
    
    // 当手势侧滑时会调用该方法,取到的是手指移动后,在相对坐标中的偏移量
    // 注意:如果系统是阿语,手势已经做了特殊处理,所以这个时候使用系统默认的就好
    - (CGPoint)translationInView:(UIView *)view {
        if ([RTLHelper isArabicSystemLanguage] && [RTLHelper isRTL]) {
            // APP 为阿语
            return [super translationInView:view];
        }
        if ([UIDevice currentDevice].systemVersion.doubleValue > 9.0) {
            CGPoint oldP = [super translationInView:view];
            // app为阿语  系统不是  反向处理
            return CGPointMake(-oldP.x, oldP.y);
        }
        return [super translationInView:view];
    }
    @end
    

    1.如果系统是阿语言,并且APP也是阿语,则直接调用系统的方法即可,因为系统以及替我们做好了。这是一个大坑
    2.当手势在屏幕拖拽时,会调用该方法,从而返回当前点击区域的坐标

    添加一个左滑手势的实例

    • fd_rtlFullscreenPopGestureRecognizer
    - (UIScreenEdgePanGestureRecognizer *)fd_rtlFullscreenPopGestureRecognizer {
        UIScreenEdgePanGestureRecognizer *rtlPanGestureRecognizer = objc_getAssociatedObject(self, _cmd);
        if (!rtlPanGestureRecognizer) {
            rtlPanGestureRecognizer = [[RTLEdgePanGesture alloc] init];
            rtlPanGestureRecognizer.edges = UIRectEdgeRight;
            if ([RTLHelper isArabicSystemLanguage] && ![RTLHelper isRTL]) {
                // APP 不是阿语,强制把 APP 换成原来的
                rtlPanGestureRecognizer.edges = UIRectEdgeLeft;
            }
            objc_setAssociatedObject(self, _cmd, rtlPanGestureRecognizer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        }
        return rtlPanGestureRecognizer;
    }
    

    注意:如果系统是阿语,但是APP语言不是阿语,则需要把操作习惯变成正常的使用习惯

    1. 新增左滑手势的代理
    • _FDFullRTLScreenPopGestureRecognizerDelegate
    @interface _FDFullRTLScreenPopGestureRecognizerDelegate : NSObject <UIGestureRecognizerDelegate>
    
    @property (nonatomic, weak) UINavigationController *navigationController;
    
    @end
    
    @implementation _FDFullRTLScreenPopGestureRecognizerDelegate
    
    // 判断当前界面是否支持手势滑动返回
    - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
        // Ignore when no view controller is pushed into the navigation stack.
        if (self.navigationController.viewControllers.count <= 1) {
            return NO;
        }
        // Ignore when the active view controller doesn't allow interactive pop.
        UIViewController *topViewController = self.navigationController.viewControllers.lastObject;
        if (topViewController.fd_interactivePopDisabled) {
            return NO;
        }
        
        // Ignore when the beginning location is beyond max allowed initial distance to left edge.
        CGPoint beginningLocation = [gestureRecognizer locationInView:gestureRecognizer.view];
        CGFloat maxAllowedInitialDistance = topViewController.fd_interactivePopMaxAllowedInitialDistanceToLeftEdge;
        if (maxAllowedInitialDistance > 0 && beginningLocation.x > maxAllowedInitialDistance) {
            return NO;
        }
        
        // Ignore pan gesture when the navigation controller is currently in transition.
        if ([[self.navigationController valueForKey:@"_isTransitioning"] boolValue]) {
            return NO;
        }
        
        return YES;
    }
    
    • 定义左滑代理的实例
    - (_FDFullRTLScreenPopGestureRecognizerDelegate *)fd_popRTLGestureRecognizerDelegate {
        _FDFullRTLScreenPopGestureRecognizerDelegate *rtlDelegate = objc_getAssociatedObject(self, _cmd);
        
        if (!rtlDelegate) {
            rtlDelegate = [[_FDFullRTLScreenPopGestureRecognizerDelegate alloc] init];
            rtlDelegate.navigationController = self;
            
            objc_setAssociatedObject(self, _cmd, rtlDelegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        }
        return rtlDelegate;
    }
    
    3. 在fd_pushViewController:方法中替换系统自带的手势
    - (void)fd_pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
        // 自定义的手势直接添加到interactivePopGestureRecognizer对应的View上。
        // 实际上就是将系统的手势事件转发为自定义的手势,触发的事件不变
        // doc:http://t.cn/RssK6mz 处理阿语翻转手势
        // 当前APP语言为阿语 || 系统语言为阿语
        if ([RTLHelper isArabicSystemLanguage] || [RTLHelper isRTL]) {
            if (![self.interactivePopGestureRecognizer.view.gestureRecognizers containsObject:self.fd_rtlFullscreenPopGestureRecognizer]) {
                // Add our own gesture recognizer to where the onboard screen edge pan gesture recognizer is attached to.
                [self.interactivePopGestureRecognizer.view addGestureRecognizer:self.fd_rtlFullscreenPopGestureRecognizer];
                
                // Forward the gesture events to the private handler of the onboard gesture recognizer.
                // interactivePopGestureRecognizer会操作一个指定的target , action “handleNavigationTransition”,
                // 通过Runtime动态获取到指定的target, 及action添加到自定义的手势上。
                NSArray *internalTargets = [self.interactivePopGestureRecognizer valueForKey:@"targets"];
                // get releate target
                id internalTarget = [internalTargets.firstObject valueForKey:@"target"];
                // get handleNavigationTransition sel
                SEL internalAction = NSSelectorFromString(@"handleNavigationTransition:");
                self.fd_rtlFullscreenPopGestureRecognizer.delegate = self.fd_popRTLGestureRecognizerDelegate;
                // add target and action to this gesture
                [self.fd_rtlFullscreenPopGestureRecognizer addTarget:internalTarget action:internalAction];
                
                // Disable the onboard gesture recognizer.
                self.interactivePopGestureRecognizer.enabled = NO;
            }
        } else {
            // 添加一个正常的手势即可
        }
    }
    

    只要系统语言为阿语或者APP语言是阿语,就添加阿语手势

    • 到此为止,工作完成,我们就给中东镜像添加了一个左滑手势,是不是很爽啊。

    • 如有错误,欢迎指正,多多点赞,打赏更佳,您的支持是我写作的动力。

    项目连接地址 - FDFullScreenPopGestureDemo

    相关文章

      网友评论

        本文标题:iOS-中东镜像翻转+添加左滑返回手势

        本文链接:https://www.haomeiwen.com/subject/xxbbzftx.html