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

一 序言
  • 中东国家的人们使用习惯和其他国家不太一致,我们一般是从左往右看,但是中东的人们习惯从右往左看。
  • 随着大屏手机的出现,侧滑返回功能就显得至关重要。但是正常情况下,当进入一个新的页面时,新页面是从右往左慢慢显示。当返回上一个页面时,右滑即可。
  • 但是中东国家恰恰相反,页面时从左往右显示,需要左滑返回上一个页面。
二 本文解决的问题
  • 如何正确处理系统语言和当前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 

@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-中东镜像翻转+添加左滑返回手势)