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 <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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    X先生_未知数的X阅读 15,937评论 3 118
  • 1、精细化管理;2、激情教育;3、高效课堂 1、精细化管理:领导值班,班主任值班,家长参与。骨干教师引领,陪伴,做...
    清澈与明亮阅读 126评论 0 0
  • 刚毕业,在上海,暂时买不起房,所以和基友共三人合租一套三室一厅的房子。三只程序员,三个汉子,一起撸串、一起王者荣耀...
    栾呱呱阅读 735评论 6 9