如何以人类可读的形式显示OptionSet值?

时间:2017-03-03 21:02:28

标签: ios swift optionsettype

Swift具有OptionSet类型,它基本上将设置操作添加到C-Style位标志。 Apple在他们的框架中广泛使用它们。示例包括animate(withDuration:delay:options:animations:completion:)中的options参数。

从好的方面来说,它可以让你使用干净的代码,如:

options: [.allowAnimatedContent, .curveEaseIn]

然而,也存在不利因素。

如果我想显示OptionSet的指定值,似乎不是一个干净的方法:

let options: UIViewAnimationOptions = [.allowAnimatedContent, .curveEaseIn]
print("options = " + String(describing: options))

显示非常无用的消息:

  

options = UIViewAnimationOptions(rawValue:65664)

其中一些位字段的文档将常量表示为二次幂值:

flag0    = Flags(rawValue: 1 << 0)

但是我的示例OptionSet UIViewAnimationOptions的文档并没有告诉你关于这些标志的数值的任何信息,并且从十进制数中计算出来的数字并不简单。

问题:

是否有一些干净的方法将OptionSet映射到选定的值?

我想要的输出类似于:

  

options = UIViewAnimationOptions([.allowAnimatedContent,.curveEaseIn])

但是,如果没有一堆杂乱的代码需要我为每个标志维护一个显示名称表,我无法想到这样做的方法。

(我有兴趣为我在自己的代码中创建的系统框架和自定义OptionSet执行此操作。)

枚举让你可以同时拥有枚举的名称和原始值,但是那些不支持使用OptionSets获得的设置功能。<​​/ p>

5 个答案:

答案 0 :(得分:0)

这是我采用的一种方法,使用字典并遍历键。不太好,但是可以。

struct MyOptionSet: OptionSet, Hashable, CustomStringConvertible {

    let rawValue: Int
    static let zero = MyOptionSet(rawValue: 1 << 0)
    static let one = MyOptionSet(rawValue: 1 << 1)
    static let two = MyOptionSet(rawValue: 1 << 2)
    static let three = MyOptionSet(rawValue: 1 << 3)

    var hashValue: Int {
        return self.rawValue
    }

    static var debugDescriptions: [MyOptionSet:String] = {
        var descriptions = [MyOptionSet:String]()
        descriptions[.zero] = "zero"
        descriptions[.one] = "one"
        descriptions[.two] = "two"
        descriptions[.three] = "three"
        return descriptions
    }()

    public var description: String {
        var result = [String]()
        for key in MyOptionSet.debugDescriptions.keys {
            guard self.contains(key),
                let description = MyOptionSet.debugDescriptions[key]
                else { continue }
            result.append(description)
        }
        return "MyOptionSet(rawValue: \(self.rawValue)) \(result)"
    }

}

let myOptionSet = MyOptionSet([.zero, .one, .two])

// prints MyOptionSet(rawValue: 7) ["two", "one", "zero"]

答案 1 :(得分:0)

NSHipster中的本文提供了OptionSet的替代方法,它提供了OptionSet的所有功能以及易于记录的内容:

https://nshipster.com/optionset/

如果仅添加选项类型为CustomStringConvertible的要求,则可以非常清晰地记录此类型的Set。以下是NSHipster网站的代码-唯一的变化是向CustomStringConvertible类添加了Option符合性

protocol Option: RawRepresentable, Hashable, CaseIterable, CustomStringConvertible {}

enum Topping: String, Option {
    case pepperoni, onions, bacon,
    extraCheese, greenPeppers, pineapple

    //I added this computed property to make the class conform to CustomStringConvertible
    var description: String {
        return ".\(self.rawValue)"
    }
}

extension Set where Element == Topping {
    static var meatLovers: Set<Topping> {
        return [.pepperoni, .bacon]
    }

    static var hawaiian: Set<Topping> {
        return [.pineapple, .bacon]
    }

    static var all: Set<Topping> {
        return Set(Element.allCases)
    }
}

typealias Toppings = Set<Topping>

extension Set where Element: Option {
    var rawValue: Int {
        var rawValue = 0
        for (index, element) in Element.allCases.enumerated() {
            if self.contains(element) {
                rawValue |= (1 << index)
            }
        }
        return rawValue
    }
}

然后使用它:

let toppings: Set<Topping> = [.onions, .bacon]

print("toppings = \(toppings), rawValue = \(toppings.rawValue)")

输出

  

浇头= [.onions,.bacon],rawValue = 6

就像您想要的那样。

之所以可行,是因为Set将其成员显示为方括号内的逗号分隔列表,并使用每个Set成员的description属性来显示该成员。 description属性只显示前缀为String的每个项目(枚举名称为.

并且由于Set<Option>的rawValue与具有相同值列表的OptionSet相同,因此您可以轻松地在它们之间进行转换。

我希望Swift可以使其成为OptionSet s的母语功能。

答案 2 :(得分:0)

StrOptionSet 协议:

  • 添加标签集属性,以测试 Self 上的每个标签值。

StrOptionSet 扩展名:

  • 过滤掉未相交的对象。
  • 将标签文本返回为数组
  • 以“,”作为 CustomStringConvertible :: description

以下是代码段:

protocol StrOptionSet : OptionSet, CustomStringConvertible {
    typealias Label = (Self, String)
    static var labels: [Label] { get }
}
extension StrOptionSet {
    var strs: [String] { return Self.labels
                                .filter{ (label: Label) in self.intersection(label.0).isEmpty == false }
                                .map{    (label: Label) in label.1 }
    }
    public var description: String { return strs.joined(separator: ",") }
}

为目标选项集VTDecodeInfoFlags添加标签集。

extension VTDecodeInfoFlags : StrOptionSet {
    static var labels: [Label] { return [
        (.asynchronous, "asynchronous"),
        (.frameDropped, "frameDropped"),
        (.imageBufferModifiable, "imageBufferModifiable")
    ]}
}

使用

let flags: VTDecodeInfoFlags = [.asynchronous, .frameDropped]
print("flags:", flags) // output: flags: .asynchronous,frameDropped

答案 3 :(得分:0)

struct MyOptionSet: OptionSet {
    let rawValue: UInt
    static let healthcare   = MyOptionSet(rawValue: 1 << 0)
    static let worldPeace   = MyOptionSet(rawValue: 1 << 1)
    static let fixClimate   = MyOptionSet(rawValue: 1 << 2)
    static let exploreSpace = MyOptionSet(rawValue: 1 << 3)
}

extension MyOptionSet: CustomStringConvertible {
    static var debugDescriptions: [(Self, String)] = [
        (.healthcare, "healthcare"),
        (.worldPeace, "world peace"),
        (.fixClimate, "fix the climate"),
        (.exploreSpace, "explore space")
    ]

    var description: String {
        let result: [String] = Self.debugDescriptions.filter { contains($0.0) }.map { $0.1 }
        return "MyOptionSet(rawValue: \(self.rawValue)) \(result)"
    }
}

用法

var myOptionSet: MyOptionSet = []
myOptionSet.insert(.healthcare)
print("here is my options: \(myOptionSet)")

答案 4 :(得分:0)

这就是我的方法。

public struct Toppings: OptionSet {
        public let rawValue: Int
        
        public static let cheese = Toppings(rawValue: 1 << 0)
        public static let onion = Toppings(rawValue: 1 << 1)
        public static let lettuce = Toppings(rawValue: 1 << 2)
        public static let pickles = Toppings(rawValue: 1 << 3)
        public static let tomatoes = Toppings(rawValue: 1 << 4)
        
        public init(rawValue: Int) {
            self.rawValue = rawValue
        }
    }
    
    extension Toppings: CustomStringConvertible {
        
        static public var debugDescriptions: [(Self, String)] = [
            (.cheese, "cheese"),
            (.onion, "onion"),
            (.lettuce, "lettuce"),
            (.pickles, "pickles"),
            (.tomatoes, "tomatoes")
        ]
        
        public var description: String {
            let result: [String] = Self.debugDescriptions.filter { contains($0.0) }.map { $0.1 }
            let printable = result.joined(separator: ", ")
            
            return "\(printable)"
        }
    }