There are two things I’d like to discuss briefly, both extending what you already learned to help push your skills even further.
First, the #targetEnvironment(simulator)
compiler directive. Swift has several of these, and I want to demonstrate two just briefly: #line
and #if swift
. #line
is easy enough: when your code gets built it automatically gets replaced with the current line number. You can also use #filename
and #function
, and the combination of these are very useful in debugging strings.
The #if swift
directive allows you to conditionally compile code depending on the Swift compiler version being used. So, you could write Swift 4.2 code and Swift 5.0 code in the same file, and have Xcode choose the right version automatically.
Now why, do you think, might you want such functionality? Well, there are two situations that you’re likely to encounter:
Here’s some example code to get you started:
#if swift(>=5.0)
print("Running Swift 5.0 or later")
#else
print("Running Swift 4.2")
#endif
The second thing I’d like to touch on briefly is image rendering. Hacking with Swift is written specifically for the latest and greatest APIs from Apple, because if you’re learning from scratch it’s usually not worth bothering learning older technologies.
However, the case of image rendering is unique because the old technology – i.e., everything before iOS 10.0 – takes only a minute to learn. So, I want to show you just quickly how to render images before iOS 10, because it’s likely you’ll come across it in the wider world.
Here’s the iOS 10 and later code we would use to render a circle:
let renderer = UIGraphicsImageRenderer(size: CGSize(width: 512, height: 512))
let img = renderer.image { ctx in
ctx.cgContext.setFillColor(UIColor.red.cgColor)
ctx.cgContext.setStrokeColor(UIColor.green.cgColor)
ctx.cgContext.setLineWidth(10)
let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512)
ctx.cgContext.addEllipse(in: rectangle)
ctx.cgContext.drawPath(using: .fillStroke)
}
To convert that to pre-iOS 10 rendering, you need to learn four new functions:
UIGraphicsBeginImageContextWithOptions()
. This starts a new Core Graphics rendering pass. Pass it your size, then a rendering scale, and whether the image should be opaque. If you want to use the current device’s scale, use 0 for the scale parameter.UIGraphicsGetCurrentContext()
, which returns a CGContext?
. It’s optional because of course Swift doesn’t know we just started a rendering pass.UIGraphicsGetImageFromCurrentImageContext()
when you want to extract a UIImage
from your rendering. Again, this returns an optional (in this case UIImage?
) because Swift doesn’t know a rendering pass is active.UIGraphicsEndImageContext()
when you’ve finished, to free up the memory from your rendering.As you can see, the older calls are a little more flaky: having extra optionality is never welcome, and you need to remember to both start and end your rendering blocks. Still, if you want to give it a try, here’s the same circle rendering code rewritten for iOS 9.3 and earlier:
UIGraphicsBeginImageContextWithOptions(CGSize(width: 512, height: 512), false, 0)
if let ctx = UIGraphicsGetCurrentContext() {
ctx.setFillColor(UIColor.red.cgColor)
ctx.setStrokeColor(UIColor.green.cgColor)
ctx.setLineWidth(10)
let rectangle = CGRect(x: 5, y: 5, width: 502, height: 502)
ctx.addEllipse(in: rectangle)
ctx.drawPath(using: .fillStroke)
}
if let img = UIGraphicsGetImageFromCurrentImageContext() {
// "img" is now a valid UIImage – use it here!
}
UIGraphicsEndImageContext()
SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's all new Paywall Editor allow you to remotely configure your paywall view without any code changes or app updates.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.