如何将 [String : Any] 转换为 [NSAttributedStringKey : Any]

How to convert [String : Any] to [NSAttributedStringKey : Any]

处理一些 objC API,我收到一个 NSDictionary<NSString *, id> *>,它在 Swift 中转换为 [String : Any],我将其用于 NSAttributedString.addAttributes:range:

但是,此方法签名现已更改为 Xcode 9,现在需要 [NSAttributedStringKey : Any]

let attr: [String : Any]? = OldPodModule.getMyAttributes()
// Cannot assign value of type '[String : Any]?' to type '[NSAttributedStringKey : Any]?'
let newAttr: [NSAttributedStringKey : Any]? = attr
if let newAttr = newAttr {
    myAttributedString.addAttributes(newAttr, range: range)
}

如何将 [String : Any] 转换为 [NSAttributedStringKey : Any]

NSAttributedStringKeyan initialiser that takes a String, and you can use Dictionary's init(uniqueKeysWithValues:) 初始化器,以便从一系列键值元组构建字典,其中每个键都是唯一的(例如这里的情况)。

我们只需对 attr 应用转换,在调用 Dictionary 的初始化程序之前将每个 String 键转换为 NSAttributedStringKey

例如:

let attributes: [String : Any]? = // ...

let attributedString = NSMutableAttributedString(string: "hello world")
let range = NSRange(location: 0, length: attributedString.string.utf16.count)

if let attributes = attributes {
    let convertedAttributes = Dictionary(uniqueKeysWithValues:
        attributes.lazy.map { (NSAttributedStringKey([=10=].key), [=10=].value) }
    )
    attributedString.addAttributes(convertedAttributes, range: range)
}

我们在这里使用 lazy 以避免创建不必要的中间数组。

您可以使用

`NSAttributedStringKey(rawValue: String)`

初始化程序。但是,即使属性字符串不受影响,它也会创建一个对象。例如,

`NSAttributedStringKey(rawValue: fakeAttribute)` 

仍会为字典创建键。此外,这仅在 iOS 11 中可用,因此请谨慎使用以实现向后兼容性。

虽然 Hamish 提供了完美的 Swift 答案,但请注意最后 I solved 问题直接在 Objective-C API 级别。如果您无法控制源代码,也可以使用小型 Objective-C 包装器来完成。

我们只需将 NSDictionary<NSString *, id> * 替换为 NSDictionary<NSAttributedStringKey, id> * 并添加 typedef 以与早期版本的 Xcode 兼容:

#ifndef NS_EXTENSIBLE_STRING_ENUM
// Compatibility with Xcode 7
#define NS_EXTENSIBLE_STRING_ENUM
#endif

// Testing Xcode version (
#if __clang_major__ < 9
// Compatibility with Xcode 8-
typedef NSString * NSAttributedStringKey NS_EXTENSIBLE_STRING_ENUM;
#endif