Подтвердить что ты не робот

Как создать перечисление с необработанным типом CGPoint?

Вдохновленный этот вопрос. Быстрая поддержка для создания перечисления с любым сырым типом, поэтому было бы неплохо создать enum с исходным типом CGPoint.

Но этот код не будет компилировать

enum MyEnum : CGPoint {
    case Zero
}

со следующей ошибкой

<REPL>:50:15: error: raw type 'CGPoint' is not convertible from any literal
enum MyEnum : CGPoint {
              ^
<REPL>:51:10: error: enum cases require explicit raw values when the raw type is not integer literal convertible
    case Zero
     ^

Итак, как объявить перечисление с исходным типом CGPoint?

4b9b3361

Ответ 1

В данном коде есть две ошибки.

Первый

error: raw type 'CGPoint' is not convertible from any literal
    enum MyEnum : CGPoint {

Итак, нам нужно сделать кабриолет CGPoint из литерала

Один из способов его решения - расширить CGPoint, чтобы сделать его конвертируемым из строкового литерала с помощью StringLiteralConvertible

extension CGPoint : StringLiteralConvertible {
    static func convertFromStringLiteral(value: String) -> CGPoint {
        return NSPointFromString(value) // CGPointFromString on iOS
    }

    static func convertFromExtendedGraphemeClusterLiteral(value: String) -> CGPoint {
        return NSPointFromString(value) // CGPointFromString on iOS
    }
}

мы можем создать CGPoint из строкового литерала!

var p : CGPoint = "2,3"
println(p) // print (2.0,3.0)

Вторая ошибка:

error: enum cases require explicit raw values when the raw type is not integer literal convertible
        case Zero
         ^

который теперь легко исправить, просто назначьте ему строковый литерал

enum MyEnum : CGPoint {
    case Zero = "0, 0"
    case One = "1, 1"
    case MagicPoint = "0, 42"
}

println(MyEnum.Zero.toRaw()) // (0.0,0.0)
println(MyEnum.One.toRaw()) // (1.0,1.0)
println(MyEnum.MagicPoint.toRaw()) // (0.0,42.0)

и теперь у вас есть перечисление с исходным типом CGPoint


чтобы использовать его

if let p = MyEnum.fromRaw(CGPoint(x:0, y:42)) {
    switch (p) {
    case .Zero:
        println("p is (0, 0)")
        break
    case .One:
        println("p is (1, 1)")
        break
    case .MagicPoint:
        println("p is magic point")
        break
    }
}

// print "p is magic point"

Лучше создать CGPoint из кортежа, но похоже, что это невозможно.

Из grammar

literal → integer-literal­  floating-point-literal­  string-literal­

существует только три типа литерала, поэтому строковый литерал является единственным вариантом здесь (если вы не хотите, чтобы 1.2 был (1, 2))

Ответ 2

На самом деле вы можете иметь правильный путь. Это код, который позволяет иметь CGPoint как RawValue enum:

enum MyPointEnum {
    case zero
}

extension MyPointEnum: RawRepresentable {
    typealias RawValue = CGPoint

    init?(rawValue: CGPoint) {
        if rawValue == CGPoint.zero {
            self = .zero
        } else {
            return nil
        }
    }

    var rawValue: CGPoint {
        switch self {
        case .zero:
            return CGPoint.zero
        }
    }
}

print(MyPointEnum.zero.rawValue) //prints "(0.0, 0.0)\n"

Ответ 3

Мне очень нравится решение Bryan Chen, но я пришел с одной альтернативой. Он действительно не использует перечисления:

extension CGPoint : RawRepresentable, Equatable {
    typealias RawType = (CGFloat, CGFloat)

    static let Zero = CGPointZero
    static let One = CGPoint(x: 1.0, y: 1.0)
    static let MagicPoint = CGPoint(x: 42.0, y: 0.0)

    static func fromRaw(raw: RawType) -> CGPoint? {
        return CGPoint(x: raw.0, y: raw.1)
    }

    func toRaw() -> RawType {
        return (x, y)
    }
}

Теперь мы можем выполнять все "необработанные" операции:

var p = CGPoint.fromRaw((10, 20)) //from tuple

Мы также можем использовать switch:

switch (p) {
case CGPoint.Zero:
    println("p is (0, 0)")
case CGPoint.One:
    println("p is (1, 1)")
case CGPoint.MagicPoint:
    println("p is (42, 0)")
case CGPoint(x: 0, y: 10):
    println("p is (0, 10)")
default:
    println("Something else")
}

Однако вам нужен случай default, и вы не можете использовать короткие имена .Zero.