2017-05-11 5 views
1

Ich brauche ein Swift-Wörterbuch, das beliebige Objekte speichern kann. Einige der Werte sind CGColor Referenzen. Ich habe kein Problem, das Wörterbuch zu erstellen und die CGColor Referenzen zu speichern. Das Problem ist, sie sicher zurück zu bekommen.Problem beim Abrufen eines CGColor aus einem Swift-Wörterbuch

let color = CGColor(gray: 0.5, alpha: 1) 
var things = [String:Any]() 
things["color"] = color 
things["date"] = Date() 
print(things) 

Das funktioniert und ich bekomme vernünftige Ergebnisse. Später mag ich die Farbe erhalten (die so natürlich kann oder auch nicht im Wörterbuch vorhanden Ich versuche folgend:.

if let color = things["color"] as? CGColor { 
    print(color) 
} 

Aber dies führt zu dem Fehler:

error: conditional downcast to CoreFoundation type 'CGColor' will always succeed

Am Ende ich kam mit:

if let val = things["color"] { 
    if val is CGColor { 
     let color = val as! CGColor 
     print(color) 
    } 
} 

Dies ohne Warnung auf einem Spielplatz funktioniert, aber in meinem eigentlichen Xcode Projekt erhalte ich eine Warnung auf der if val is CGColor Linie:

'is' test always true because 'CGColor' is a Core Foundation type

Gibt es eine gute Lösung für dieses Problem?

Ich arbeite mit Core-Grafik und Ebenen und der Code muss mit iOS und MacOS arbeiten, also versuche ich UIColor und NSColor zu vermeiden.

Ich habe Casting from AnyObject to CGColor? without errors or warnings gefunden, die verwandt ist, aber scheint nicht mehr relevant, da ich die Klammern nicht brauche, um die Warnung zu beseitigen, plus ich versuche, optionale Bindung zu verwenden, die von dieser Frage nicht abgedeckt wird.

+0

Was ist die Swift-Version? – iMuzahid

+0

Vergleichen [Wie kann ich CGColor/CGPath checken?] (Http: // stackoverflow.com/q/38252330/2976878) - Sie können einen Core Foundation-Typ nicht mit 'is' /' as? 'eintippen, da es sich eigentlich nur um einen undurchsichtigen Zeiger handelt, Sie können jedoch' CFGetTypeID' verwenden. – Hamish

+0

@ Md.MuzahidulIslam Swift 3.1 (Xcode 8.3.2) – rmaddy

Antwort

1

Das Problem ist, dass Core Foundation Objekte sind undurchsichtig, daher ist ein Wert vom Typ CGColor nichts anderes als ein undurchsichtiger Zeiger - Swift selbst weiß nichts über das zugrunde liegende Objekt. Das bedeutet also, dass Sie is oder as? nicht verwenden können, um es zu prüfen, Swift muss immer nur die gegebene Umwandlung erfolgreich durchführen lassen.

Eine Lösung, as shown by Martin in this Q&A ist CFGetTypeID zu verwenden, um den Typ des Core Foundation-Objekt zu überprüfen - was würde ich in einer Verlängerung CGColor für Bequemlichkeit setzen empfehlen:

extension CGColor { 

    static func conditionallyCast<T>(_ value: T) -> CGColor? { 

    guard CFGetTypeID(value as CFTypeRef) == CGColor.typeID else { 
     return nil 
    } 

    return (value as! CGColor) 
    } 
} 
// ... 

if let color = CGColor.conditionallyCast(things["color"]) { 
    print(color) 
} else { 
    print("nil, or not a color") 
} 

Und Sie an andere Core Foundation Typen könnten auch diese Rolle unter Verwendung eines Protokolls:

protocol CFTypeProtocol { 
    static var typeID: CFTypeID { get } 
} 

extension CFTypeProtocol { 

    static func conditionallyCast<T>(_ value: T) -> Self? { 

    guard CFGetTypeID(value as CFTypeRef) == typeID else { 
     return nil 
    } 

    return (value as! Self) 
    } 
} 

extension CGColor : CFTypeProtocol {} 
extension CGPath : CFTypeProtocol {} 

// ... 
+0

Danke für die gute Erklärung des zugrunde liegenden Problems. Ich mag den Vorschlag der CGColor-Erweiterung sehr. Das macht den bedingten Bindungscode viel einfacher. – rmaddy

2

Für Swift 3:

if let val = things["color"], CFGetTypeID(val as CFTypeRef) == CGColor.typeID { 
     let color = val as! CGColor 
     print(color) 
    } 

Sie könnten auch halten es einfacher und speichern UIColor in Ihrem Wörterbuch statt CGColor - dies ermöglicht es Ihnen, Standard as? UIImage Methoden verwenden

+0

Perfekt (gut, so gut wie es in diesem Fall geht). Das gleiche habe ich nach dem Link von Hamish herausgefunden. – rmaddy

+0

BTW - wie in meiner Frage angegeben, brauche ich 'CGColor', da der Code in einem gemeinsamen Rahmen von iOS und MacOS verwendet wird. Die Verwendung von 'UIColor' ist also keine Option. – rmaddy

+0

Ugh. Das ist hässlich, aber ich verstehe, warum es erforderlich ist. Rmaddy, du solltest es akzeptieren, da es die erste Antwort ist, die dein Problem löst. –

1

Die anderen Antworten sind interessant, aber ich würde nur einen Wrapper verwenden. Wie dieser Code zeigt, können Sie auf ein Jedes zuweisen und es wieder mit einem is oder as? Test zurück:

struct ColorWrapper { 
    let color:CGColor 
} 
let c = ColorWrapper(color:UIColor.red.cgColor) 
let any : Any = c 
if let c2 = any as? ColorWrapper { 
    let result = c2.color 
} 
+0

Sneaky Lösung. Lösen Sie das Problem, indem Sie alles zusammen vermeiden. :) – rmaddy

+0

@rmaddy Das ist was Programmieren ist _is_. :) – matt

Verwandte Themen