TIMRTLUtil.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. //
  2. // TIMRTLUtil.m
  3. // TIMCommon
  4. //
  5. // Created by cologne on 2023/7/21.
  6. // Copyright © 2023 Tencent. All rights reserved
  7. //
  8. #import "TIMRTLUtil.h"
  9. #import <objc/runtime.h>
  10. #import <TUICore/TUIGlobalization.h>
  11. @implementation TIMRTLUtil
  12. @end
  13. @interface UIView (TUIRTL)
  14. @end
  15. @implementation UIView (TUIRTL)
  16. - (void)setRTLFrame:(CGRect)frame width:(CGFloat)width {
  17. if (isRTL()) {
  18. if (self.superview == nil) {
  19. NSAssert(0, @"must invoke after have superView");
  20. }
  21. CGFloat x = width - frame.origin.x - frame.size.width;
  22. frame.origin.x = x;
  23. }
  24. self.frame = frame;
  25. }
  26. - (void)setRTLFrame:(CGRect)frame {
  27. [self setRTLFrame:frame width:self.superview.frame.size.width];
  28. }
  29. - (void)resetFrameToFitRTL {
  30. [self setRTLFrame:self.frame];
  31. }
  32. @end
  33. @interface UIImage (TUIRTL)
  34. @end
  35. @implementation UIImage (TUIRTL)
  36. - (UIImage *_Nonnull)checkOverturn{
  37. if (isRTL()) {
  38. UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale);
  39. CGContextRef bitmap = UIGraphicsGetCurrentContext();
  40. CGContextTranslateCTM(bitmap, self.size.width / 2, self.size.height / 2);
  41. CGContextScaleCTM(bitmap, -1.0, -1.0);
  42. CGContextTranslateCTM(bitmap, -self.size.width / 2, -self.size.height / 2);
  43. CGContextDrawImage(bitmap, CGRectMake(0, 0, self.size.width, self.size.height), self.CGImage);
  44. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  45. return image;
  46. }
  47. return self;
  48. }
  49. - (UIImage *)_imageFlippedForRightToLeftLayoutDirection {
  50. if (isRTL()) {
  51. return [UIImage imageWithCGImage:self.CGImage
  52. scale:self.scale
  53. orientation:UIImageOrientationUpMirrored];
  54. }
  55. return self;
  56. }
  57. - (UIImage *)rtl_imageFlippedForRightToLeftLayoutDirection {
  58. if (isRTL()) {
  59. if (@available(iOS 13.0, *)) {
  60. UITraitCollection *const scaleTraitCollection = [UITraitCollection currentTraitCollection];
  61. UITraitCollection *const darkUnscaledTraitCollection = [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
  62. UITraitCollection *const darkScaledTraitCollection =
  63. [UITraitCollection traitCollectionWithTraitsFromCollections:@[ scaleTraitCollection, darkUnscaledTraitCollection ]];
  64. UIImage *lightImg = [[self.imageAsset imageWithTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]]
  65. _imageFlippedForRightToLeftLayoutDirection];
  66. UIImage *darkImage = [[self.imageAsset imageWithTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]]
  67. _imageFlippedForRightToLeftLayoutDirection];
  68. UIImage *image =
  69. [lightImg imageWithConfiguration:[self.configuration
  70. configurationWithTraitCollection:[UITraitCollection
  71. traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]]];
  72. [image.imageAsset registerImage:darkImage withTraitCollection:darkScaledTraitCollection];
  73. return image;
  74. } else {
  75. return [UIImage imageWithCGImage:self.CGImage scale:self.scale orientation:UIImageOrientationUpMirrored];
  76. }
  77. }
  78. return self;
  79. }
  80. @end
  81. @interface UINavigationController (TUIRTL)
  82. @end
  83. @implementation UINavigationController (TUIRTL)
  84. + (void)load {
  85. Method oldMethod = class_getInstanceMethod(self, @selector(initWithRootViewController:));
  86. Method newMethod = class_getInstanceMethod(self, @selector(rtl_initWithRootViewController:));
  87. method_exchangeImplementations(oldMethod, newMethod);
  88. }
  89. - (instancetype)rtl_initWithRootViewController:(UIViewController *)rootViewController {
  90. if ([self rtl_initWithRootViewController:rootViewController]) {
  91. if (@available(iOS 9.0, *)) {
  92. if (isRTL()) {
  93. self.navigationBar.semanticContentAttribute = [UIView appearance].semanticContentAttribute;
  94. self.view.semanticContentAttribute = [UIView appearance].semanticContentAttribute;
  95. }
  96. }
  97. }
  98. return self;
  99. }
  100. @end
  101. UIEdgeInsets rtlEdgeInsetsWithInsets(UIEdgeInsets insets) {
  102. if (insets.left != insets.right && isRTL()) {
  103. CGFloat temp = insets.left;
  104. insets.left = insets.right;
  105. insets.right = temp;
  106. }
  107. return insets;
  108. }
  109. @implementation UIButton (TUIRTL)
  110. void swizzleInstanceMethod(Class cls, SEL originSelector, SEL swizzleSelector){
  111. if (!cls) {
  112. return;
  113. }
  114. /* if current class not exist selector, then get super*/
  115. Method originalMethod = class_getInstanceMethod(cls, originSelector);
  116. Method swizzledMethod = class_getInstanceMethod(cls, swizzleSelector);
  117. /* add selector if not exist, implement append with method */
  118. if (class_addMethod(cls,
  119. originSelector,
  120. method_getImplementation(swizzledMethod),
  121. method_getTypeEncoding(swizzledMethod)) ) {
  122. /* replace class instance method, added if selector not exist */
  123. /* for class cluster , it always add new selector here */
  124. class_replaceMethod(cls,
  125. swizzleSelector,
  126. method_getImplementation(originalMethod),
  127. method_getTypeEncoding(originalMethod));
  128. } else {
  129. /* swizzleMethod maybe belong to super */
  130. class_replaceMethod(cls,
  131. swizzleSelector,
  132. class_replaceMethod(cls,
  133. originSelector,
  134. method_getImplementation(swizzledMethod),
  135. method_getTypeEncoding(swizzledMethod)),
  136. method_getTypeEncoding(originalMethod));
  137. }
  138. }
  139. + (void)load
  140. {
  141. swizzleInstanceMethod(self, @selector(setContentEdgeInsets:), @selector(rtl_setContentEdgeInsets:));
  142. swizzleInstanceMethod(self, @selector(setImageEdgeInsets:), @selector(rtl_setImageEdgeInsets:));
  143. swizzleInstanceMethod(self, @selector(setTitleEdgeInsets:), @selector(rtl_setTitleEdgeInsets:));
  144. }
  145. - (void)rtl_setContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets {
  146. [self rtl_setContentEdgeInsets:rtlEdgeInsetsWithInsets(contentEdgeInsets)];
  147. }
  148. - (void)rtl_setImageEdgeInsets:(UIEdgeInsets)imageEdgeInsets {
  149. [self rtl_setImageEdgeInsets:rtlEdgeInsetsWithInsets(imageEdgeInsets)];
  150. }
  151. - (void)rtl_setTitleEdgeInsets:(UIEdgeInsets)titleEdgeInsets {
  152. [self rtl_setTitleEdgeInsets:rtlEdgeInsetsWithInsets(titleEdgeInsets)];
  153. }
  154. @end
  155. @implementation UILabel (TUIRTL)
  156. - (void)setRtlAlignment:(TUITextRTLAlignment)rtlAlignment {
  157. objc_setAssociatedObject(self, @selector(rtlAlignment), @(rtlAlignment), OBJC_ASSOCIATION_ASSIGN);
  158. switch (rtlAlignment) {
  159. case TUITextRTLAlignmentLeading:
  160. self.textAlignment = (isRTL() ? NSTextAlignmentRight : NSTextAlignmentLeft);
  161. break;
  162. case TUITextRTLAlignmentTrailing:
  163. self.textAlignment = (isRTL() ? NSTextAlignmentLeft : NSTextAlignmentRight);
  164. break;
  165. case TUITextRTLAlignmentCenter:
  166. self.textAlignment = NSTextAlignmentCenter;
  167. case TUITextRTLAlignmentUndefine:
  168. break;
  169. default:
  170. break;
  171. }
  172. }
  173. - (TUITextRTLAlignment)rtlAlignment {
  174. NSNumber *identifier = objc_getAssociatedObject(self, @selector(rtlAlignment));
  175. if (identifier) {
  176. return identifier.integerValue;
  177. }
  178. return TUITextRTLAlignmentUndefine;
  179. }
  180. @end
  181. @implementation NSMutableAttributedString (TUIRTL)
  182. - (void)setRtlAlignment:(TUITextRTLAlignment)rtlAlignment {
  183. switch (rtlAlignment) {
  184. case TUITextRTLAlignmentLeading:
  185. self.rtlAlignment = (isRTL() ? NSTextAlignmentRight : NSTextAlignmentLeft);
  186. break;
  187. case TUITextRTLAlignmentTrailing:
  188. self.rtlAlignment = (isRTL() ? NSTextAlignmentLeft : NSTextAlignmentRight);
  189. break;
  190. case TUITextRTLAlignmentCenter:
  191. self.rtlAlignment = NSTextAlignmentCenter;
  192. case TUITextRTLAlignmentUndefine:
  193. break;
  194. default:
  195. break;
  196. }
  197. }
  198. @end
  199. BOOL isRTLString(NSString *string) {
  200. if ([string hasPrefix:@"\u202B"] || [string hasPrefix:@"\u202A"]) {
  201. return YES;
  202. }
  203. return NO;
  204. }
  205. NSString * rtlString(NSString *string) {
  206. if (string.length == 0 || isRTLString(string)) {
  207. return string;
  208. }
  209. if (isRTL()) {
  210. string = [@"\u202B" stringByAppendingString:string];
  211. } else {
  212. string = [@"\u202A" stringByAppendingString:string];
  213. }
  214. return string;
  215. }
  216. NSAttributedString *rtlAttributeString(NSAttributedString *attributeString ,NSTextAlignment textAlignment ){
  217. if (attributeString.length == 0) {
  218. return attributeString;
  219. }
  220. NSRange range;
  221. NSDictionary *originAttributes = [attributeString attributesAtIndex:0 effectiveRange:&range];
  222. NSParagraphStyle *style = [originAttributes objectForKey:NSParagraphStyleAttributeName];
  223. if (style && isRTLString(attributeString.string)) {
  224. return attributeString;
  225. }
  226. NSMutableDictionary *attributes = originAttributes ? [originAttributes mutableCopy] : [NSMutableDictionary new];
  227. if (!style) {
  228. NSMutableParagraphStyle *mutableParagraphStyle = [[NSMutableParagraphStyle alloc] init];
  229. UILabel *test = [UILabel new];
  230. test.textAlignment = textAlignment;
  231. mutableParagraphStyle.alignment = test.textAlignment;
  232. style = mutableParagraphStyle;
  233. [attributes setValue:mutableParagraphStyle forKey:NSParagraphStyleAttributeName];
  234. }
  235. NSString *string = rtlString(attributeString.string);
  236. return [[NSAttributedString alloc] initWithString:string attributes:attributes];
  237. }
  238. @implementation TUICollectionRTLFitFlowLayout
  239. - (UIUserInterfaceLayoutDirection)effectiveUserInterfaceLayoutDirection {
  240. if (isRTL()) {
  241. return UIUserInterfaceLayoutDirectionRightToLeft;
  242. }
  243. return UIUserInterfaceLayoutDirectionLeftToRight;
  244. }
  245. - (BOOL)flipsHorizontallyInOppositeLayoutDirection{
  246. return isRTL()? YES:NO;
  247. }
  248. @end