NSString+Lookin.m 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. //
  2. // NSString+Lookin.m
  3. // Lookin
  4. //
  5. // Created by Li Kai on 2019/5/11.
  6. // https://lookin.work
  7. //
  8. #import "NSString+Lookin.h"
  9. @implementation NSString (Lookin)
  10. + (NSString *)lookin_stringFromDouble:(double)doubleValue decimal:(NSUInteger)decimal {
  11. NSString *formatString = [NSString stringWithFormat:@"%%.%@f", @(decimal)];
  12. NSString *string = [NSString stringWithFormat:formatString, doubleValue];
  13. for (int i = 0; i < decimal; i++) {
  14. if ([[string substringFromIndex:string.length - 1] isEqualToString:@"0"]) {
  15. string = [string substringToIndex:string.length - 1];
  16. }
  17. }
  18. if ([[string substringFromIndex:string.length - 1] isEqualToString:@"."]) {
  19. string = [string substringToIndex:string.length - 1];
  20. }
  21. return string;
  22. }
  23. + (NSString *)lookin_stringFromInset:(LookinInsets)insets {
  24. return [NSString stringWithFormat:@"{%@, %@, %@, %@}",
  25. [NSString lookin_stringFromDouble:insets.top decimal:2],
  26. [NSString lookin_stringFromDouble:insets.left decimal:2],
  27. [NSString lookin_stringFromDouble:insets.bottom decimal:2],
  28. [NSString lookin_stringFromDouble:insets.right decimal:2]];
  29. }
  30. + (NSString *)lookin_stringFromSize:(CGSize)size {
  31. return [NSString stringWithFormat:@"{%@, %@}",
  32. [NSString lookin_stringFromDouble:size.width decimal:2],
  33. [NSString lookin_stringFromDouble:size.height decimal:2]];
  34. }
  35. + (NSString *)lookin_stringFromPoint:(CGPoint)point {
  36. return [NSString stringWithFormat:@"{%@, %@}",
  37. [NSString lookin_stringFromDouble:point.x decimal:2],
  38. [NSString lookin_stringFromDouble:point.y decimal:2]];
  39. }
  40. + (NSString *)lookin_stringFromRect:(CGRect)rect {
  41. return [NSString stringWithFormat:@"{%@, %@, %@, %@}",
  42. [NSString lookin_stringFromDouble:rect.origin.x decimal:2],
  43. [NSString lookin_stringFromDouble:rect.origin.y decimal:2],
  44. [NSString lookin_stringFromDouble:rect.size.width decimal:2],
  45. [NSString lookin_stringFromDouble:rect.size.height decimal:2]];
  46. }
  47. + (NSString *)lookin_rgbaStringFromColor:(LookinColor *)color {
  48. if (!color) {
  49. return @"nil";
  50. }
  51. #if TARGET_OS_IPHONE
  52. UIColor *rgbColor = color;
  53. #elif TARGET_OS_MAC
  54. NSColor *rgbColor = [color colorUsingColorSpace:NSColorSpace.sRGBColorSpace];
  55. #endif
  56. CGFloat r, g, b, a;
  57. [rgbColor getRed:&r green:&g blue:&b alpha:&a];
  58. NSString *colorDesc;
  59. if (a >= 1) {
  60. colorDesc = [NSString stringWithFormat:@"(%.0f, %.0f, %.0f)", r * 255, g * 255, b * 255];
  61. } else {
  62. colorDesc = [NSString stringWithFormat:@"(%.0f, %.0f, %.0f, %@)", r * 255, g * 255, b * 255, [NSString lookin_stringFromDouble:a decimal:2]];
  63. }
  64. return colorDesc;
  65. }
  66. - (NSString *)lookin_safeInitWithUTF8String:(const char *)string {
  67. if (NULL != string) {
  68. return [self initWithUTF8String:string];
  69. }
  70. return nil;
  71. }
  72. - (NSString *)lookin_shortClassNameString {
  73. if ([self containsString:@"."]) {
  74. // Swift 中,class 可能会被加前缀,比如 Weread.AvatarView,此时返回后半部分即可
  75. NSString *shortName = [self componentsSeparatedByString:@"."].lastObject;
  76. return shortName;
  77. }
  78. if ([self hasPrefix:@"_Tt"]) {
  79. // Swift 中,private class 可能会被加前缀,比如 MyApp 里一个叫 TestView 的 private class,这里获取到的可能是:_TtC5MyAppP33_6BC07D230B8F25989003315CA1D8100B8TestView
  80. // 这里规则比较复杂,可以参考这里的讨论:https://stackoverflow.com/questions/24062957/swift-objective-c-runtime-class-naming
  81. // _Tt 开头则认为是这种类型
  82. for (NSInteger i = self.length - 1; i >= 0; i--) {
  83. // 比如上面的注释里的例子,末尾的 TestView 即我们想截取的字符串,而它前面的那个数字 8 则是分隔符,8 在这里表示 TestView 字符串的长度是 8
  84. // 这里是倒序从最后一个字母向前遍历,i 为当前被遍历到的字符串的 idx,比如遍历到 "_" 时则 i 为 0
  85. // 当前已经被遍历到的末尾字符串的数量,比如第八次执行该循环时,enumeratedStringLength 为 8
  86. NSInteger enumeratedStringLength = self.length - i;
  87. // 我们此刻检查下一次将会被遍历到的那一个(或两个)字符串是否是对应的数字 8,如果是,则认为当前被遍历过的字符串是我们要找的类名
  88. // 要检查接下来多少位的字符串是否是数字,比如 enumeratedStringLength 为 8 时则检查接下来的 1 位字符串是否是 8,如果 enumeratedStringLength 为 12 则检查接下来的 2 位数字是否是 12
  89. NSInteger numberLengthToCheck = 1;
  90. if (enumeratedStringLength >= 10) {
  91. numberLengthToCheck = 2;
  92. } else if (enumeratedStringLength >= 100) {
  93. numberLengthToCheck = 3;
  94. }
  95. if (i < numberLengthToCheck) {
  96. // 全部检查完了,失败,直接返回自身吧
  97. return self;
  98. }
  99. NSRange checkRange = NSMakeRange(i - numberLengthToCheck, numberLengthToCheck);
  100. NSString *stringToCheck = [self substringWithRange:checkRange];
  101. NSInteger scannedNumber = [stringToCheck integerValue];
  102. if (scannedNumber == enumeratedStringLength) {
  103. // 比如我们已经遍历到了 “TextView”,而下一个字母确实就是数字 8,则这里我们认为找到了目标字符串
  104. NSString *targetString = [self substringFromIndex:i];
  105. return targetString;
  106. }
  107. // 继续遍历
  108. }
  109. }
  110. return self;
  111. }
  112. @end