0%

字符串

搜索

在一个字符串中搜索子字符串

  • 最灵活的方法

    • (NSRange)rangeOfString:(NSString *)aString options:(NSStringCompareOptions)mask range:(NSRange)searchRange locale:(NSLocale *)locale

格式化字符串

  • 3个方法
-initWithFormat:

-initWithFormat:arguments:

+stringWithFormat:

整数

  • 可以同时工作在32位和64位的

    uint64_t p = 2305843009213693951;

    NSString *s = [NSString stringWithFormat:@”The ninth Mersenne prime is %llu”, (unsigned long long) p];

    // “The ninth Mersenne prime is 2305843009213693951”

Modifier d, i o, u, x, X
hh signed char unsigned char
h short unsigned short
(none) int unsigned int
l(ell) long unsigned long
j intmax_t uintmax_t
t ptrdiff_t
z size_t
  • 转换规则

    int m = -150004021;

    uint n = 150004021U;

    NSString *s = [NSString stringWithFormat:@”d:%d i:%i o:%o u:%u x:%x X:%X”, m, m, n, n, n, n];

    // “d:-150004021 i:-150004021 o:1074160465 u:150004021 x:8f0e135 X:8F0E135”

    //o是八进制

  • 设置最小字段宽度和最小数字位数

    int m = 42;

    NSString *s = [NSString stringWithFormat:@”‘%4d’ ‘%-4d’ ‘%+4d’ ‘%4.3d’ ‘%04d’”, m, m, m, m, m];

    // “[ 42] [42 ] [ +42] [ 042] [0042]”

    m = -42;

    NSString *s = [NSString stringWithFormat:@”‘%4d’ ‘%-4d’ ‘%+4d’ ‘%4.3d’ ‘%04d’”, m, m, m, m, m];

    // “[ -42] [-42 ] [ -42] [-042] [-042]”

  • %p可打印指针,和%#x不同的是它可以同时在32位和64位执行

浮点数

  • 使用%f和%g

    double v[5] = {12345, 12, 0.12, 0.12345678901234, 0.0000012345678901234};

    NSString *s = [NSString stringWithFormat:@”%g %g %g %g %g”, v[0], v[1], v[2], v[3], v[4]];

    // “12345 12 0.12 0.123457 1.23457e-06”

    NSString *s = [NSString stringWithFormat:@”%f %f %f %f %f”, v[0], v[1], v[2], v[3], v[4]];

    // “12345.000000 12.000000 0.120000 0.123457 0.000001”

多行文字

  • 使用\n来

    NSString *limerick = @”A lively young damsel named Menzies\n”

    @”Inquired: «Do you know what this thenzies?»\n”

    @”Her aunt, with a gasp,\n”

    @”Replied: “It’s a wasp,\n”

    @”And you’re holding the end where the stenzies.\n”;

  • 等价写法

    NSString *limerick = @”A lively young damsel named Menzies\nInquired: «Do you know what this thenzies?»\nHer aunt, with a gasp,\nReplied: “It’s a wasp,\nAnd you’re holding the end where the stenzies.\n”;

  • 更简洁的方法

    NSString * string = @”The man “ @”who knows everything “ @”learns nothing” @”.”;

替换字符串

  • NSMutableString的四个方法

    -deleteCharactersInRange:

    -insertString:atIndex:

    -replaceCharactersInRange:withString:

    -replaceOccurrencesOfString:withString:options:range:

  • NSString的方法

    -stringByReplacingOccurrencesOfString:withString:

    -stringByReplacingOccurrencesOfString:withString:options:range:

    -stringByReplacingCharactersInRange:withString:

  • NSMutableString不会创建新字符串,性能会好点

    NSMutableString *string; // 假设我们已经有了一个名为 string 的字符串

    // 现在要去掉它的一个前缀,做法如下:

    NSString *prefix = @”WeDon’tWantThisPrefix”

    NSRange r = [string rangeOfString:prefix options:NSAnchoredSearch range:NSMakeRange(0, string.length) locale:nil];

    if (r.location != NSNotFound) {

     [string deleteCharactersInRange:r];
    

    }

连接字符串

1
2
3
NSArray *names = @["Hildr", @"Heidrun", @"Gerd", @"Guðrún", @"Freya", @"Nanna", @"Siv", @"Skaði", @"Gróa"];

NSString *result = [names componentsJoinedByString:@", "];

字符串解析

正则表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

NSError *error = nil;

NSString *pattern = @"(\\w+) = #(\\p{Hex_Digit}{6})";

NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:pattern

options:0

error:&error];

NSTextCheckingResult *result = [expression firstMatchInString:string

options:0

range:NSMakeRange(0, string.length)];

NSString *key = [string substringWithRange:[result rangeAtIndex:1]];

NSString *value = [string substringWithRange:[result rangeAtIndex:2]];

将字符串分解成数组,使用componentsSeparatedByString:这个方法,或者enumerateSubstringsInRange:options:usingBlock:。如果是按照行来进行分解可以使用option这个参数传NSStringEnumerationByLines

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

NSString *input = @“

backgroundColor = #ff0000

textColor = #0000ff

"

NSString *pattern = @"(\\w+) = #([\\da-f]{6})";

NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:pattern

options:0

error:NULL];

NSArray *lines = [input componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];

NSMutableDictionary *result = [NSMutableDictionary dictionary];

for (NSString *line in lines) {

NSTextCheckingResult *textCheckingResult = [expression firstMatchInString:line

options:0

range:NSMakeRange(0, line.length)];

NSString* key = [line substringWithRange:[textCheckingResult rangeAtIndex:1]];

NSString* value = [line substringWithRange:[textCheckingResult rangeAtIndex:2]];

result[key] = value;

}

return result;

扫描

  • NSScanner

    NSScanner *scanner = [NSScanner scannerWithString:string];

    //默认情况下,扫描器会跳过所有空格符和换行符。但这里我们只希望跳过空格符

    scanner.charactersToBeSkipped = [NSCharacterSet whitespaceCharacterSet];

    //定义一个十六进制字符集

    NSCharacterSet *hexadecimalCharacterSet =

    [NSCharacterSet characterSetWithCharactersInString:@”0123456789abcdefABCDEF”];

    NSMutableDictionary *result = [NSMutableDictionary dictionary];

    while (!scanner.isAtEnd) {

     NSString *key = nil;
    
     NSString *value = nil;
    
     NSCharacterSet *letters = [NSCharacterSet letterCharacterSet];
    
     BOOL didScan = [scanner scanCharactersFromSet:letters intoString:&key] &&
    
          [scanner scanString:@"=" intoString:NULL] &&
    
          [scanner scanString:@"#" intoString:NULL] &&
    
          [scanner scanCharactersFromSet:hexadecimalCharacterSet intoString:&value] &&
    
     value.length == 6;
    
     result[key] = value;
    
     [scanner scanCharactersFromSet:[NSCharacterSet newlineCharacterSet]
    
          intoString:NULL]; // 继续扫描下一行
    

    }

    return result;

解析器

  • 设计一个能够用(100,0,255)或者#ff0000这样的字符来定义颜色的方法。

    • (NSDictionary *)parse:(NSString *)string error:(NSError **)error

    {

     self.scanner = [NSScanner scannerWithString:string];
    
     self.scanner.charactersToBeSkipped = [NSCharacterSet whitespaceCharacterSet];
    
     NSMutableDictionary *result = [NSMutableDictionary dictionary];
    
     NSCharacterSet *letters = [NSCharacterSet letterCharacterSet]
    
     while (!self.scanner.isAtEnd) {
    
          NSString *key = nil;
    
          UIColor *value = nil;
    
          BOOL didScan = [self.scanner scanCharactersFromSet:letters intoString:&key] &&
    
               [self.scanner scanString:@"=" intoString:NULL] &&
    
               [self scanColor:&value];
    
          result[key] = value;
    
          [self.scanner scanCharactersFromSet:[NSCharacterSet newlineCharacterSet]
    
               intoString:NULL]; // 继续扫描下一行
    
     }
    

    }

    • (BOOL)scanColor:(UIColor **)out

    {

     return [self scanHexColorIntoColor:out] || [self scanTupleColorIntoColor:out];
    

    }

    //扫描设置#ff0000这样的

    • (BOOL)scanHexColorIntoColor:(UIColor **)out

    {

     NSCharacterSet *hexadecimalCharacterSet =
    
          [NSCharacterSet characterSetWithCharactersInString:@"0123456789abcdefABCDEF"];
    
     NSString *colorString = NULL;
    
     if ([self.scanner scanString:@"#" intoString:NULL] &&
    
          [self.scanner scanCharactersFromSet:hexadecimalCharacterSet
    
          intoString:&colorString] &&
    
          colorString.length == 6) {
    
          *out = [UIColor colorWithHexString:colorString];
    
          return YES;
    
     }
    
     return NO;
    

    }

    • (BOOL)scanTupleColorIntoColor:(UIColor **)out

    {

     NSInteger red, green, blue = 0;
    
     BOOL didScan = [self.scanner scanString:@"(" intoString:NULL] &&
    
          [self.scanner scanInteger:&red] &&
    
          [self.scanner scanString:@"," intoString:NULL] &&
    
          [self.scanner scanInteger:&green] &&
    
          [self.scanner scanString:@"," intoString:NULL] &&
    
          [self.scanner scanInteger:&blue] &&
    
          [self.scanner scanString:@")" intoString:NULL];
    
     if (didScan) {
    
          *out = [UIColor colorWithRed:(CGFloat)red/255.
    
               green:(CGFloat)green/255.
    
               blue:(CGFloat)blue/255.
    
               alpha:1];
    
          return YES;
    
     } else {
    
          return NO;
    
     }
    

    }

符号化处理

先进星扫描,使用NSScanner来解析这个表达式

1
2
3
4
5

myView.left = otherView.right * 2 + 10

viewController.view.centerX + myConstant <= self.view.centerX

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

NSScanner *scanner = [NSScanner scannerWithString:contents];

NSMutableArray *tokens = [NSMutableArray array];

while (![scanner isAtEnd]) {

for (NSString *operator in @[@"=", @"+", @"*", @">=", @"<=", @"."]) {

if ([scanner scanString:operator intoString:NULL]) {

[tokens addObject:operator];

}

}

}

//接下来识别非符号的只包含字母的string

NSString *result = nil;

if ([scanner scanCharactersFromSet:[NSCharacterSet letterCharacterSet]

intoString:&result]) {

[tokens addObject:result];

}

//NSScanner有scanDouble:来扫描double

double doubleResult = 0;

if ([scanner scanDouble:&doubleResult]) {

[tokens addObject:@(doubleResult)];

}

//完成后用将需要解析的表达式放入试试

NSString* example = @"myConstant = 100\n"

@"\nmyView.left = otherView.right * 2 + 10\n"

@"viewController.view.centerX + myConstant <= self.view.centerX";

NSArray *result = [self.scanner tokenize:example];

NSArray *expected = @[@"myConstant", @"=", @100, @"myView", @".", @"left",

@"=", @"otherView", @".", @"right", @"*", @2, @"+",

@10, @"viewController", @".", @"view", @".",

@"centerX", @"+", @"myConstant", @"<=", @"self",

@".", @"view", @".", @"centerX"];

XCTAssertEqualObjects(result, expected);

进行语法解析,需要语法分析库描述我们的语言。下面代码就是为那个布局约束语言写的解析语法,用的扩展的巴科斯范式EBNF写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

constraint = expression comparator expression

comparator = "=" | ">=" | "<="

expression = keyPath "." attribute addMultiplier addConstant

keyPath = identifier | identifier "." keyPath

attribute = "left" | "right" | "top" | "bottom" | "leading" | "trailing" | "width" | "height" | "centerX" | "centerY" | "baseline"

addMultiplier = "*" atom

addConstant = "+" atom

atom = number | identifier

还有很多Objective-C的语法解析,更多的可以在CocoaPods上找到:http://cocoapods.org/?q=parse。比较好的就是CoreParse,地址:https://github.com/beelsebob/CoreParse,但是需要使用它支持的语法。下面就是CoreParse支持的格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
NSString* grammarString = [@[

@"Atom ::= num@'Number' | ident@'Identifier';",

@"Constant ::= name@'Identifier' '=' value@<Atom>;",

@"Relation ::= '=' | '>=' | '<=';",

@"Attribute ::= 'left' | 'right' | 'top' | 'bottom' | 'leading' | 'trailing' | 'width' | 'height' | 'centerX' | 'centerY' | 'baseline';",

@"Multiplier ::= '*' num@'Number';",

@"AddConstant ::= '+' num@'Number';",

@"KeypathAndAttribute ::= 'Identifier' '.' <AttributeOrRest>;",

@"AttributeOrRest ::= att@<Attribute> | 'Identifier' '.' <AttributeOrRest>;",

@"Expression ::= <KeypathAndAttribute> <Multiplier>? <AddConstant>?;",

@"LayoutConstraint ::= lhs@<Expression> rel@<Relation> rhs@<Expression>;",

@"Rule ::= <Atom> | <LayoutConstraint>;",

] componentsJoinedByString:@"\n"];

一个规则匹配后解析器就找到同样名称的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

- (id)parser:(CPParser *)parser didProduceSyntaxTree:(CPSyntaxTree *)syntaxTree

NSString *ruleName = syntaxTree.rule.name;

if ([ruleName isEqualToString:@"Attribute"]) {

return self.layoutAttributes[[[syntaxTree childAtIndex:0] keyword]];

}

...

}

完整的解析器代码在:https://github.com/objcio/issue-9-string-parsing。里面有个解析类可以用来解析复杂的布局约束,如下:

1
2
3

viewController.view.centerX + 20 <= self.view.centerX * 0.5

可以得到如下结果,方便转换成NSLayoutConstraint对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

(<Expression: self.keyPath=(viewController, view),

self.attribute=9,

self.multiplier=1,

self.constant=20>

-1

<Expression: self.keyPath=(self, view),

self.attribute=9,

self.multiplier=0.5,

self.constant=0>)

字符串的渲染

UILabel

  • label默认显示一行,如果设置numberOfLines为大于1的话可以显示指定行数,如果设置为0,则多少行都显示
  • attributedText属性可以显示富文本
  • label的font,textColor,textAlignment,shadowColor和shadowOffset属性可以改变外观。
  • 改变程序内所有Label的风格,可以使用[UILabel appearance]方法

UITextField

  • text field只限于单行
  • UITextfield实现了UITextInputTraits协议,这个协议需要指定键盘外观和操作等细节。比如显示什么键盘和返回按键响应等
  • 可以通过设置左右辅助视图,或者设置背景来自定义输入框风格了。

UITextView

TableView中显示动态文本

Table view的Delegate有个方法用来计算高度:tableView:heightForRowAtIndexPath:。自定义一个UITableViewCell的子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

- (void)layoutSubviews

{

[super layoutSubviews];

self.textLabel.frame = CGRectInset(self.bounds,

MyTableViewCellInset,

MyTableViewCellInset);

}

计算真实高度需要使用boundingRectWithSize:options:context: 这个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

{

CGFloat labelWidth = self.tableView.bounds.size.width - MyTableViewCellInset*2;

NSAttributedString *text = [self attributedBodyTextAtIndexPath:indexPath];

NSStringDrawingOptions options = NSStringDrawingUsesLineFragmentOrigin |

NSStringDrawingUsesFontLeading;

CGRect boundingRect = [text boundingRectWithSize:CGSizeMake(labelWidth, CGFLOAT_MAX)

options:options

context:nil];

return (CGFloat) (ceil(boundingRect.size.height) + MyTableViewCellInset*2);

}

使用Text Kit和NSAttributedString进行布局

先设置attributes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

CGFloat const fontSize = 15;

NSMutableDictionary *body1stAttributes = [NSMutableDictionary dictionary];

body1stAttributes[NSFontAttributeName] = [UIFont fontWithName:@"BodoniSvtyTwoITCTT-Book"

size:fontSize];

NSMutableParagraphStyle *body1stParagraph = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];

body1stParagraph.alignment = NSTextAlignmentJustified;

body1stParagraph.minimumLineHeight = fontSize + 3;

body1stParagraph.maximumLineHeight = body1stParagraph.minimumLineHeight;

body1stParagraph.hyphenationFactor = 0.97;

body1stAttributes[NSParagraphStyleAttributeName] = body1stParag

raph;

这里字体为BodoniSvtyTwoITCTT,如果需要查看更多字体可以使用 +[UIFont familyNames]这个方法。为了得到字体的名字,可以使用 +[UIFont fontNamesForFamilyName:]。接下来创建段落的属性

1
2
3
4
5
6
7
8
9
10
NSMutableDictionary *bodyAttributes = [body1stAttributes mutableCopy];

NSMutableParagraphStyle *bodyParagraph =

[bodyAttributes[NSParagraphStyleAttributeName] mutableCopy];

bodyParagraph.firstLineHeadIndent = fontSize;

bodyAttributes[NSParagraphStyleAttributeName] = bodyParagraph;

装饰段落风格,使用装饰字体将文本居中对齐,装饰字符的前后加上空白段落

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

NSMutableDictionary *ornamentAttributes = [NSMutableDictionary dictionary];

ornamentAttributes[NSFontAttributeName] = [UIFont fontWithName:@"BodoniOrnamentsITCTT"

size:36];

NSMutableParagraphStyle *ornamentParagraph = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];

ornamentParagraph.alignment = NSTextAlignmentCenter;

ornamentParagraph.paragraphSpacingBefore = fontSize;

ornamentParagraph.paragraphSpacing = fontSize;

ornamentAttributes[NSParagraphStyleAttributeName] = ornamentParagraph;

显示数字表格table,表格布局示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

NSCharacterSet *decimalTerminator = [NSCharacterSet

characterSetWithCharactersInString:decimalFormatter.decimalSeparator];

NSTextTab *decimalTab = [[NSTextTab alloc]

initWithTextAlignment:NSTextAlignmentCenter

location:100

options:@{NSTabColumnTerminatorsAttributeName:decimalTerminator}];

NSTextTab *percentTab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight

location:200

options:nil];

NSMutableParagraphStyle *tableParagraphStyle =

[[NSParagraphStyle defaultParagraphStyle] mutableCopy];

tableParagraphStyle.tabStops = @[decimalTab, percentTab];

显示列表的属性设置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

NSMutableDictionary *listAttributes = [bodyAttributes mutableCopy];

NSMutableParagraphStyle *listParagraph =

[listAttributes[NSParagraphStyleAttributeName] mutableCopy];

listParagraph.headIndent = fontSize * 3;

listParagraph.firstLineHeadIndent = fontSize;

NSTextTab *listTab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentNatural

location:fontSize * 3

options:nil];

listParagraph.tabStops = @[listTab];

listAttributes[NSParagraphStyleAttributeName] = listParagraph;

字符串本地化