Overriding or extending UIColor to support certain protocols

南楼画角 提交于 2020-01-24 19:39:06

问题


I'm trying to subclass or extend UIColor to support a few protocols.

Let's say my protocol looks like this:

public protocol MyProtocol {
    init(myValue: Any) throws
}

For some reason, I'm unable to implement it, and I don't know why.

This works for all other classes:

class MyTestClass:SomeOtherClass, MyProtocol{
    required init(myValue: Any) throws{
        super.init(someOtherClassInitializer:Any)
    }
}

No issues. But if I try to do this with UIColor, I get errors.

class MyColor:UIColor, MyProtocol{

    required init(myValue: Any) throws {
        super.init(red: 0, green: 0, blue: 0, alpha: 1)
    }
}

first off, it complains about the required:Coder-init. Fine, provide that one. fatalError is fine. Then it complains about another init. It says

'required' initializer 'init(_colorLiteralRed:green:blue:alpha:)' must be provided by subclass of 'UIColor'

Weird flex, but ok. Let's add that too. I click "Fix", and it adds this stub:

@nonobjc required convenience init(_colorLiteralRed red: Float, green: Float, blue: Float, alpha: Float) {
    fatalError("init(_colorLiteralRed:green:blue:alpha:) has not been implemented")
}

Then it gives me two errors, one identical as the one I just clicked "Fix" on (which adds another identical init, and again and again), and another which says:

Overriding non-@objc declarations from extensions is not supported

I'm not in an extension, but I assume this convenience initializer might be? But then why is it required for me to implement - and also impossible to implement?


I know, "you shouldn't subclass UIColor", but I think I have to if I want this to work. This is a weird request, so some background info; I'm using the Apollo library to perform GraphQL-network tasks, which uses a script to convert the expected response into strongly typed swift objects so I can use them in code without manually deserializing them. It's working perfectly.

Most of the values are the standard and primitive types, like String, Int etc., but sometimes the server tries to send me an object of a class that's foreign to Swift, and these will default to String. Which is perfectly fine. But I want more fancy. Example; the value "2020-01-14T10:00:00" might be returned as a class named DateTime from the server, but since "DateTime" doesn't exist in my project or in Swift, the auto-generated classes will contain them as a String-value, and I will have to treat it as a String.

Since I want to use these autogenerated classes all the way down to the view, that means I have to convert it from String to Date everywhere it's used. Another option is to create my own versions of all classes, and convert all foreign classes to my own, like String->Date, which is boring. I want this done automatically for me.

The good thing is - Apollo allows me to create my own Custom Scalars. So with this "DateTime -> Date"-example, I can simply say

typealias DateTime = Date
extension DateTime, JSONDecodable, JSONEncodable{ ... }

This lets Apollo know that theres a corresponding class that the objects of class "DateTime" can be converted to. And the protocols JSONDecodable and JSONEncodable tells it how (which I implement myself). Using this, the autogenerated code will generate so that any date-value is now a DateTime (e.g a Date) instead of a String. Nice!

So I thought, why not use this to our advantage? We also receive hex-colors from this API. So we've made it so the API returns a hex code ("#FFFFFF") as class HexColorCode. By default, this will just turn into a String so that I have to initialize a UIColor with hex everywhere I want to use it. But I'm now trying to use the same logic so that the autogenerated classes actually have a UIColor that I can use directly everywhere. But the above happens.

I assume that Date, being a public struct from Foundation, has some freedom that UIColor, an open class from UIKit inheriting from NSObject, doesn't have. But what and why?


I think it's possible to create a "wrapper"-object, so that I can say "HexColorCode" is a standalone class which has a single color-field, and I'd have to say myView.backgroundColor = apiModel.color.color instead of just apiModel.color. But I really wanted this to work..


回答1:


I checked your situation, and I have also the impression that the compiler gets confused.
But there might be a solution. The following code compiles for me without problems:

public protocol MyProtocol {
    init(myValue: Any) throws
}

class MyColor:UIColor {
    convenience init(myValue: Any) throws {
    self.init(red: 0, green: 0, blue: 0, alpha: 1)
    }
}

EDIT:

I am sorry: Although this code compiles, it is missing the protocol.
Here is (hopefully) the correct code:

class MyColor:UIColor, MyProtocol {
    required convenience init(myValue: Any) throws {
    self.init(red: 0, green: 0, blue: 0, alpha: 1)
    }
}


来源:https://stackoverflow.com/questions/59739137/overriding-or-extending-uicolor-to-support-certain-protocols

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!