KnightSama‘s Blog

vuePress-theme-reco KnightSama    2021
KnightSama‘s Blog KnightSama‘s Blog

Choose mode

  • dark
  • auto
  • light
首页
分类
  • iOS
  • 集锦
  • JavaScript
  • Github
  • Python
标签
时间线
GitHub
author-avatar

KnightSama

27

文章

14

标签

首页
分类
  • iOS
  • 集锦
  • JavaScript
  • Github
  • Python
标签
时间线
GitHub
  • Swift 5 字符串插值增强

    • 扩展 String 的插值功能
      • 原始输出
      • 格式化增强
    • 扩展自定义类型
      • 创建自定义类型
      • 遵循 ExpressibleByStringLiteral 协议
      • 遵循 ExpressibleByStringInterpolation 协议
      • 使用

Swift 5 字符串插值增强

vuePress-theme-reco KnightSama    2021

Swift 5 字符串插值增强


KnightSama 2020-03-18 Swift

# 扩展 String 的插值功能

在 Swift 中我们一般通过 \(Any) 的方式在 String 中插值,而在 Swift 5 中我们可以通过扩展 String.StringInterpolation 并实现带有不同参数的 appendInterpolation 方法来增强这一功能,这里以格式化输出时间为例

# 原始输出

    print("\(Date())")
    
    1

    2020-03-18 07:15:51 +0000

    # 格式化增强

      /// 扩展 String.StringInterpolation
      extension String.StringInterpolation {
          /// 实现 appendInterpolation
          /// 此处入参为 Date 类型,只对 Date 类型有效
          mutating func appendInterpolation(_ value: Date) {
              let formatter = DateFormatter()
              formatter.dateFormat = "yyyy-MM-dd HH:mm"
              appendLiteral(formatter.string(from: value))
          }
      
          /// 可以带有多个参数
          mutating func appendInterpolation(_ value: Date, formatterStr: String) {
              let formatter = DateFormatter()
              formatter.dateFormat = formatterStr
              appendLiteral(formatter.string(from: value))
          }
      }
      
      print("\(Date())")
      print("\(Date(), formatterStr:  "HH:mm:ss")")
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20

      2020-03-18 15:20 15:20:18

      提示

      通过在扩展中实现多个不同的 appendInterpolation 方法,我们就可以实现对不同数据进行格式化输出,方法中的 appendLiteral 将最终处理的结果添加到原始字符串上,如果遗漏则会变成插入空字符串

      # 扩展自定义类型

      实际上我们不止可以扩展原始的 String , 通过遵循 ExpressibleByStringLiteral 与 ExpressibleByStringInterpolation 协议可以让自定义的类型具备通过字符串初始化并插值的功能

      # 创建自定义类型

      首先我们创建一个用来进行扩展的类型,例如构建一个可以通过插值方式生成属性字符串的新类型 AttributedString

      /// 创建一个用来测试的类型,里面仅有一个属性
      struct AttributedString {
          let attributedString: NSAttributedString
      }
      
      1
      2
      3
      4

      现在 AttributedString 只是一个普通的结构体,接下来我们让它可以通过字符串初始化

      # 遵循 ExpressibleByStringLiteral 协议

      首先我们通过扩展方法遵循 ExpressibleByStringLiteral 协议并实现 ExpressibleByStringLiteral 协议中的初始化方法 init(stringLiteral: String) ,当我们自定义类在通过没有插值的字符串初始化时会执行这个方法来初始化

      /// 实现协议并实现其中的初始化方法
      extension AttributedString: ExpressibleByStringLiteral {
          /// 没有插值的字符串会执行这个方法初始化
          init(stringLiteral: String) {
              attributedString = NSAttributedString(string: stringLiteral)
          }
      }
      
      1
      2
      3
      4
      5
      6
      7

      # 遵循 ExpressibleByStringInterpolation 协议

      遵循 ExpressibleByStringInterpolation 协议需要我们实现其初始化方法 init(stringInterpolation: StringInterpolation) ,当使用带有插值的字符串初始化时会执行这个方法来初始化。同时我们还要添加一个类型为 StringInterpolation 的子类型并让其遵循 StringInterpolationProtocol 协议,这个类型用来对插值进行处理,初始化方法的参数就是这个类型

      在 StringInterpolation 子类型中我们需要实现其初始化方法 init(literalCapacity: Int, interpolationCount: Int) 在这里可以进行初始化等操作,实现方法 appendLiteral(_ literal:String) , 这个方法用来处理初始化字符串中非插值的部分,同时实现一个或多个不同的 appendInterpolation 方法,这个方法就如同上面扩展 Stirng 一样用来对不同类型的插值进行处理

      在利用带有插值的字符串初始化时,会首先调用 StringInterpolation 子类型中的初始化方法,然后按字符串顺序依次解析,根据是否是插值以及插值的类型来调用 appendLiteral 和 appendInterpolation 方法,最后调用 init(stringInterpolation: StringInterpolation) 构建完成

      /// 实现协议并实现其中的方法
      extension AttributedString: ExpressibleByStringInterpolation {
          /// `StringInterpolation` 子类型,用来处理插值
          struct StringInterpolation: StringInterpolationProtocol {
          
              /// 这里创建可变属性字符串组成最后的结果
              let attributedString:NSMutableAttributedString
              
              /// 实现初始化方法
              init(literalCapacity: Int, interpolationCount: Int) {
                  attributedString = NSMutableAttributedString()
              }
              
              /// 实现 `appendLiteral` 方法,当遇到非插值部分时会调用该方法进行组合
              func appendLiteral(_ literal:String) {
                  /// 把不是插值部分的字符串拼接到结果上
                  /// 此处可以对非插值部分进行处理
                  attributedString.append(NSAttributedString(string: literal))
              }
              
              /// 实现 `appendInterpolation` 方法,当遇到插值部分时会调用该方法进行处理
              /// 可以实现多个来对不同类型的插值进行处理
              func appendInterpolation(_ value: String) {
                  /// 添加插值部分到结果上
                  attributedString.append(NSAttributedString(string: value))
              }
          }
          
          /// 插值字符串会执行该初始化方法
          init(stringInterpolation: StringInterpolation) {
              attributedString = NSAttributedString(attributedString: stringInterpolation.attributedString)
          }
      }
      
      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

      # 使用

      通过上面的几个步骤我们构建了一个简单的结构体类型 AttributedString , 这个类型可以通过插值字符串初始化并可以返回一个属性字符串

      let result: AttributedString = "测试\("插入一个值")"
      print(result.attributedString)
      
      1
      2

      通过添加更多的 appendInterpolation 方法可以处理更多不同类型的插值

      欢迎来到 KnightSama‘s Blog
      看板娘