Value of type 'UIColor' has no member 'adjust' - swift5

I am trying to get a lighter version of a UIColor defined by user in my custom view like-
public var borderColor : UIColor?{ //needs to be set by user
didSet{
setNeedsLayout()
}
}
private var backgroundColor : UIColor{ // computed property
if let color = borderColor{
return color.adjust(by: abs(20))
}
}
I searched for possible solution and got this
Get lighter and darker color variations for a given UIColor
However, I am getting a compile time error stating-
Value of type 'UIColor' has no member 'adjust'
Also in the Apple documentation of UIColor, I don't find any method named "adjust(by:)".
Can anyone please help me with this.

Related

Xcode - Unit Testing UIColor from asset catalog

I wrote an extension for my Theme class that returns my mainColor based on my current theme. My unit tests cover theme changes but I still have problem with UIColor optional value. I can't cover the situation that UIColor could not able returns my specific color from my color catalog.
extension Theme {
var mainColor: UIColor {
switch self {
case .system:
if let color = UIColor(named: "MainColor") {
return color
} else {
return .clear
}
case .custom:
return .yellow
}
}
}
I also used this line instead of unwrapping the optional but it didn't work too.
return UIColor(named: "MainColor") ?? .clear
How I unit test that the correct color is retrieved from the asset catalog?
I would personally just force unwrap UIColor(named: "MainColor")! the color in my tests to assert the correct behaviour is occurring (that we are indeed able to access the desired color in the asset catalog). If we cannot access this color we will get a crash which, in a unit testing context, is actually desirable to isolate any problems.
I would also abstract the name of the color into an enum to ensure that the name is only defined in 1 place, then the possibility of naming inconsistencies between your app target and test target are removed.
enum Colors: String {
case main = "MainColor"
}
Then use UIColor(named: Colors.main.rawValue) to access the color.
The problem is that the name MainColor is hard coded, and either this color is in your asset catalog or it isn’t, and you cannot control for that in a test. You need to abstract this into a method to which you can pass the color name from the test.

What's the difference between an Extension func, Extension static func & Extension class func in Swift?

I was trying to create an extension function on the UIColor which could take a parameter of type Card.Colour and return a UIColor back to the caller.
button.backgroundColor = UIColor.getColour(cardColour: cardToDeal.colour)
extension UIColor {
func getColour(cardColour: Card.Colour) -> UIColor {
switch cardColour {
case .Red:
return UIColor.red
case .Green:
return UIColor.green
case .Blue:
return UIColor.blue
}
}
}
When I tried to do this, the extension function of UIColor.getColour required me to enter a parameter of type UIColor rather than the specified type of Card.Colour in the extension method.
However, when I changed the extension function of getColour to either:
static func getColour(cardColour: Card.Colour) -> UIColor {
class func getColour(cardColour: Card.Colour) -> UIColor {
It allowed me to pass a parameter of type Card.Colour
Why is this? Why does changing the function to either a static function or a class function change the type required to be passed in?
Thanks in advance!
(A detailed answer would be much appreciated)
Remember that UIColor is a class. A class is kind of like a blueprint that you use to create instances or objects that conform to that class. UIColor.red is an example of an instance of the UIColor class.
When you define a func inside a class (in your case, as an extension), Swift assumes that you want to add that func to the blueprint, which will in turn be available in all instances of the UIColor class, like UIColor.red.
You could also define your func outside of all classes, by just placing it on the top level of the module, instead of inside an extension.
However, to keep your functions organized, you can place functions like that inside the class name. You just have to tell Swift that you're not trying to add the function to the blueprint that will be applied to all instances, and that all you want instead is to have a function whose name is prefixed with the name of the class.
Here's an example to illustrate the difference in usage:
class Test {
func notStatic() {
print("called from an instance")
}
static func thisIsStatic() {
print("called on class name directly")
}
}
let instance = Test() // this is an *instance* of Test
instance.notStatic() // we can call a non static func on instance
Test.thisIsStatic() // we can call a static func directly on the class only
Now, let's go back to your specific example for a second. Notice that in your example, you're starting with a instance of Card.Colour and trying to create a new instance of UIColor.
In other words, adding a func to UIColor instances (i.e., a non-static or class) is useless to you, because you don't have an instance of UIColor yet.
The idiomatic way of creating a new instance of a class is using an initializer (init). So you could turn your function into an initializer on UIColor like this:
extension UIColor {
convenience init(cardColour: Card.Colour) {
switch cardColour {
case .Red: self.init(cgColor: UIColor.red.cgColor)
case .Blue: self.init(cgColor: UIColor.blue.cgColor)
case .Green: self.init(cgColor: UIColor.green.cgColor)
}
}
}
Now you just call UIColor(cardColour: .Red) to get what you want. Note that in the implementation, I'm converting UIColor.red to cgColor and back as a quick hack. Feel free to use the initializer you see fit on UIColor for each case of Card.Colour.
But there's another way, which I think is even more elegant. Since you already have an instance of Card.Colour, you can extend Card.Colour with a function that gives you the UIColor corresponding to the instance. Within that function, you can refer to the Card.Colour instance using the keyword self.
Since you already have the Card.Colour instance through self, you don't need to pass any arguments to that function. This allows you to use a cool feature called computed properties to make the usage even nicer.
This is how you'd add such an extension to Card.Colour:
extension Card.Colour {
var uiColor: UIColor {
switch self {
case .Red: return .red
case .Blue: return .blue
case .Green: return .green
}
}
}
And you can then get a UIColor from a Card.Colour like this Card.Colour.Red.uiColor or mainColour.uiColor, where mainColour is of type Card.Colour.
Finally, as Leo Dabus noted in a comment, Swift's naming conventions is that cases should start with a lowercase letter. You should be using Card.Colour.red instead of Card.Colour.Red, etc. Those conventions came out around Swift 3 time. It was common to capitalize case names before then.
An extension method operates on an instance of provided type. You can use all of the internal properties and methods of an instance inside the method block.
static methods are methods that are namespaced by the class name and do not operate on any specific instance of your class. class methods are pretty much the same, just the difference between class and static is, that you can override class methods in a subclass.

Swift - adding a constant, accepting argument and returning value

I'm working through the Swift book from Apple and I have a scenario where I have this code
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
The above code works fine in the playground.
The question then asks - Add a constant property with let, and add another method that takes an argument. and so I do the following:
class Shape {
var numberOfSides = 0
let color = "red"
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
func colorDescription() -> String {
return "This shape is of color \(color)."
}
}
My string "This shape is...." is not returned at all.
There is no error thrown and there is no change from what is returned in the first piece of code.
I have obviously searched GitHub for solutions and tried to understand but in this case, the problem is I don't understand why my own specific solution doesn't work.
What do I need to change here and why?
Your code is correct and returns This shape is of color red. using this
let shape = Shape()
let colorDescription = shape.colorDescription()
The Swift Tour provides this sample code for simpleDescription()
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
which does the same: create the instance and call the method.

sharing variables between classes in Swift

I am just getting into Swift and struggling how to simply set a text property of a label in one class based on a variable in another. In one swift file I have:
Class GameController:
var instructsText = String()
func gameControl()
instructsText = array[2] as string //this gets the instructions text from a Plist file.
In another swift file I have
Class HelpViews: UIView {
override init(frame: CGRect) {
// add the instructions label
var instructsLabel = UILabel(frame: CGRectMake(ScreenWidth/2-300, ScreenHeight-150, 600, 100))
instructsLabel.text = instructsText - ERROR 'instructs is not a subtype of 'NSString'
self.addSubview(instructsLabel)
any help very much appreciated!
It is unclear why you would be getting the error you say you are. At no point, (at least in the code above) do you make an object of the GameController Class and thus the compiler should be telling you that it does not recognize the variable instructsText. However, I would suggest using a computed class property. An example of one would be:
class MyClass {
class var pi: Double { return 3.1415926 }
// ...
}
println(MyClass.pi)
Which would print out 3.1415926. In you code, one way that might work is as follows:
Class GameController:
class var instructsText = String {return array[2] as String}
And in the HelpView Class:
Class HelpViews: UIView {
override init(frame: CGRect) {
// add the instructions label
var instructsLabel = UILabel(frame: CGRectMake(ScreenWidth/2-300, ScreenHeight-150, 600, 100))
instructsLabel.text = GameController.instructsText
self.addSubview(instructsLabel)
The Apple Swift documentation is here.
Try first to println(instructsText) variable, to see what you try to set.
Here is a few cases:
Not declared variable
It could be nil
It's not String

Swift class without initializer working?

Could you please tell me why that code (class) is working in playground?
If I understand properly there should be init or something that can be used by "blank initializer"?
class Shape{
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
var shapeA = Shape()
shapeA.simpleDescription()
shapeA.numberOfSides = 7
var shapeDescription = shapeA.simpleDescription()
shapeA.simpleDescription()
Thank you for your help
If all the stored properties are given default values, as here, the class does not need to have an explicit initializer.
In Swift an init override is only required in certain cases.
For instance, if you hadn't put a default value on that numberOfSides var then you would have to have an init to provide the default value.
Or you would have had to make it optional which gives it a nil default value.
This is all explained in the iBook by apple.