一 序言
- 中东国家的人们使用习惯和其他国家不太一致,我们一般是从左往右看,但是中东的人们习惯从右往左看。
- 随着大屏手机的出现,侧滑返回功能就显得至关重要。但是正常情况下,当进入一个新的页面时,新页面是从右往左慢慢显示。当返回上一个页面时,右滑即可。
- 但是中东国家恰恰相反,页面时从左往右显示,需要左滑返回上一个页面。
二 本文解决的问题
- 如何正确处理系统语言和当前APP语言问题
- 如何做到中东镜像
- 添加全面左滑手势
三 准备工作
- 本项目是通过第三方库
FDFullscreenPopGesture
实现的,所以阅读本文之前需要先熟悉该第三方库的原理。传输地址 iOS-FDFullscreenPopGesture详解
先看看效果图
四 开始讲解啦
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.设置push
和pop
时的方向。
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 添加左滑手势
- 定义一个左滑的手势的类
- 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语言不是阿语,则需要把操作习惯变成正常的使用习惯
- 新增左滑手势的代理
_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语言是阿语,就添加阿语手势
- 到此为止,工作完成,我们就给中东镜像添加了一个左滑手势,是不是很爽啊。
- 如有错误,欢迎指正,多多点赞,打赏更佳,您的支持是我写作的动力。