'coerced to Any' but property is of type UIColor

前端 未结 4 832
深忆病人
深忆病人 2021-01-24 10:42

This

NSAttributedString.Key.foregroundColor: view.tintColor

Triggers this warning

Exp         


        
相关标签:
4条回答
  • 2021-01-24 10:46

    setTitleTextAttributes expects a dictionary of [NSAttributedString.Key : Any]

    Everything can be treated as Any and that's why this warning doesn't appear in any other case. The only time it appears is when you do it with Optional. Compiler just wants you to make sure that you know what you're doing with your optional :)

    You asked why it happens in Xcode 10.2 and Swift 5?

    It always worked well for optionals that were declared this way:

    let optionalNumber: Int? = 5 
    

    and never for optionals that were declared this way:

    let implicitlyUnwrappedOptionalNumber: Int! = 5 
    

    look at your example: view.tintColor is

    Why it didn't worked for implicitly unwrapped optionals? Because before Swift 5, ImplicitlyUnwrappedOptional and Optional were two different types! And as I wrote before: It always worked well for optionals (and didn't work for ImplicitlyUnwrappedOptional).

    Right now they are the same type, but implicitly unwrapped optionals have special @_autounwrapped mechanism to differentiate the two.

    Thay started removal of this type in Swift 4.2:

    https://github.com/apple/swift-evolution/blob/master/proposals/0054-abolish-iuo.md

    This proposal seeks to limit the adoption of IUOs to places where they are actually required, and put the Swift language on the path to removing implicitly unwrapped optionals from the system entirely when other technologies render them unnecessary. It also completely abolishes any notion of IUOs below the type-checker level of the compiler, which will substantially simplify the compiler implementation.

    but apparently they completed it in Swift 5:

    https://forums.swift.org/t/possible-misdiagnosis-of-se-0054/9546

    ImplicitlyUnwrappedOptional isn’t going to be a type at all anymore. We put the warnings specifically around the use of ! because that’s easier to detect, but yes, using it in any position that isn’t the top-level type of a variable, parameter, or return value is deprecated and will be removed. (@rudkx has already done a lot of work to actually do that removing in Swift 5, some of which will start showing up even in Swift 4.1.)

    0 讨论(0)
  • 2021-01-24 10:50

    This has nothing to do with .foregroundColor. It has everything to do with .tintColor and setTitleTextAttributes.

    This parameter is of type [NSAttributedString.Key : Any]. It is not in any way considering the documentation for each key. It doesn't know or care that this should be a UIColor. If you passed "squid", this would compile without warning (it wouldn't work, but it would compile):

    UIBarButtonItem.appearance().setTitleTextAttributes(
        [
            .font: UIFont.systemFont(ofSize: 40),
            .foregroundColor: "squid",
        ], for: .normal)
    

    All it's looking at is that you're assigning view.tintColor to a value of type Any.

    The problem is that view.tintColor is not UIColor, it's UIColor!. It's not actually possible for .tintColor to be nil, but it's possible to set it to nil:

    view.tintColor        // r 0.0 g 0.478 b 1.0 a 1.0
    view.tintColor = .red
    view.tintColor        // r 1.0 g 0.0 b 0.0 a 1.0
    view.tintColor = nil
    view.tintColor        // r 0.0 g 0.478 b 1.0 a 1.0
    

    That makes sense in ObjC, but the only way to express it in Swift is to use a ! type. When you assign ! types to other things, they become ? types. And that means that you're using UIColor? in a place that accepts Any (the value of the dictionary).

    Using an optional as Any can be dangerous because it creates a lot of weird corner cases. For example, you can't round-trip an optional through Any; it gets squashed into its base type:

    let x: Any = Optional(1)
    x as? Int? // Cannot downcast from 'Any' to a more optional type 'Int?'
    x as? Int  // 1
    

    There are a lot of these kinds of little sharp-edges when working with Any.

    Of course you didn't mean to work with Any. It's not your fault. But this is why Swift is complaining.

    There are several solutions, depending on what you like. You can use a !:

        .foregroundColor: view.tintColor!
    

    You can add as Any to silence the warning:

        .foregroundColor: view.tintColor as Any
    

    Personally I'd use as Any.

    Or you can be elaborate and unload the value earlier (I don't recommend this):

    let tintColor = view.tintColor ?? .blue
    
    UIBarButtonItem.appearance().setTitleTextAttributes(
        [
            .font: UIFont.systemFont(ofSize: 40),
            .foregroundColor: tintColor,
        ], for: .normal)
    
    0 讨论(0)
  • 2021-01-24 10:56

    Just do this:

    guard let viewTint = view.tintColor else { return }
    NSAttributedString.Key.foregroundColor: viewTint
    

    Hope this helps!

    0 讨论(0)
  • 2021-01-24 11:00

    Adding to what Rob Napier wrote in his reply, here is Apple's response to my Bug Report:

    Engineering has provided the following information regarding this issue:

    This is an unintended result of an intentional change in Swift 5. tintColor is of type UIColor!, an implicitly unwrapped optional, and it needs to be converted to Any so it can be added to the dictionary passed to setTitleTextAttributes(_:for:). When faced with this situation, Swift 4.2 would insert a force unwrap to convert the value to UIColor, which would cause a difficult-to-debug crash if the value was nil. Swift 5.0 instead converts the value to UIColor?, which never crashes but emits a warning when implicitly converted to Any.

    In your specific case, the tintColor property is documented to never return nil, so it’s safe to use the force unwrapping operator by writing “view.tintColor!” instead of “view.tintColor”. (This workaround is specific to the tintColor property—in other situations where you get this warning, force-unwrapping may crash your app at runtime. Only force-unwrap when you are certain that the value can never be nil.)

    If your project has a strict style guide which absolutely forbids force unwrapping, you could instead use the nil-coalescing operator by writing something like “view.tintColor ?? UIColor.black”."

    0 讨论(0)
提交回复
热议问题