2017-05-11 3 views
1

J'ai besoin d'un dictionnaire Swift pouvant stocker n'importe quel type d'objet. Certaines des valeurs seront CGColor références. Je n'ai aucun problème à créer le dictionnaire et à stocker les références CGColor. Le problème est d'essayer de les récupérer en toute sécurité.Problème lors de la récupération d'une CGColor à partir d'un dictionnaire Swift

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

Cela fonctionne et j'obtiens une sortie raisonnable. Plus tard, je souhaite obtenir la couleur (qui peut ou peut ne pas exister dans le dictionnaire Alors, naturellement, je les opérations suivantes:.

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

Mais cela se traduit par l'erreur:

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

En fin de compte Je suis venu avec:

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

Cela fonctionne sans aucun avertissement dans une aire de jeux, mais dans mon projet Xcode réelle, je reçois un avertissement sur la ligne if val is CGColor:

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

Y a-t-il une bonne solution à ce problème? Je travaille avec des graphiques et des calques de base et le code doit fonctionner avec iOS et macOS, donc j'essaie d'éviter UIColor et NSColor.

J'ai trouvé Casting from AnyObject to CGColor? without errors or warnings qui est lié mais ne semble plus pertinent puisque je n'ai pas besoin des parenthèses pour éliminer l'avertissement plus j'essaye d'utiliser la liaison facultative qui n'est pas couverte par cette question.

+0

Quelle est la version Swift? – iMuzahid

+0

Compare [Comment puis-je taper check CGColor/CGPath?] (Http: // stackoverflow.com/q/38252330/2976878) - vous ne pouvez pas vérifier un type de base de base avec 'is' /' as? 'parce que c'est vraiment un pointeur opaque, vous pouvez cependant utiliser' CFGetTypeID'. – Hamish

+0

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

Répondre

1

Le problème est que Core Foundation les objets sont opaques, donc une valeur de type CGColor n'est rien de plus qu'un pointeur opaque - Swift lui-même ne sait rien de l'objet sous-jacent. Cela signifie donc que vous ne pouvez pas utiliser is ou as? pour le vérifier, Swift doit toujours permettre à la distribution donnée de réussir.

Une solution, as shown by Martin dans this Q&A, est d'utiliser CFGetTypeID afin de vérifier le type de l'objet de base Foundation - qui, je recommande de mettre dans une extension de CGColor pour plus de commodité:

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") 
} 

Et vous pourrait même rouler ceci à d'autres types de base de la Fondation en utilisant un protocole:

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

Merci pour la bonne explication du problème sous-jacent. J'aime beaucoup la suggestion de l'extension 'CGColor'. Cela rend le code de liaison conditionnelle beaucoup plus simple. – rmaddy

2

Pour Swift 3:

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

Vous pouvez aussi le garder simple et stocker UIColor dans votre dictionnaire au lieu de CGColor - cela vous permettra d'utiliser as? UIImage méthodes standard

+0

Parfait (eh bien, aussi bien qu'il obtient dans ce cas). Je suis arrivé avec la même chose après avoir vu le lien posté par Hamish. – rmaddy

+0

BTW - comme indiqué dans ma question, j'ai besoin de 'CGColor' puisque le code sera dans un cadre commun utilisé par iOS et macOS. Utiliser 'UIColor' n'est donc pas une option. – rmaddy

+0

Pouce. C'est moche, mais je vois pourquoi c'est nécessaire. Rmaddy, vous devriez l'accepter car c'est la première réponse qui résout votre problème. –

1

Les autres réponses sont intéressantes mais je voudrais simplement utiliser un emballage. Comme ce code indique, vous pouvez attribuer à ANY et le récupérer à nouveau avec un is ou as? test:

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

Solution sournoise. Résolvez le problème en évitant tout cela ensemble. :) – rmaddy

+0

@rmaddy C'est ce que la programmation _is_. :) – matt