If you cloned ConsoleUI
, at this point you should be able to run the following commands:
swift build # build project
swift run && open test.png # rasterize image and open in Preview app
swift test # run tests
This is why we are here, right? Let’s draw some cool app icons for iOS.
App Icon Design Guidelines state icons should be a PNG image with the largest size of 1024 x 1024 pixels (@1x) for the App Store.
We will update our view to match those dimensions, both on the rasterization code and the SwiftUI preview:
let wrapper = NSHostingView(rootView: MySwiftUIView())
wrapper.frame = CGRect(x: 0, y: 0, width: 1024, height: 1024)
#if DEBUG
struct MySwiftUIView_Previews : PreviewProvider {
static var previews: some View {
MySwiftUIView()
.frame(width: 1024, height: 1024)
}
}
#endif
We now should have a nice square to draw on.
Equally, running swift run
from the terminal should now generate a 1024 x 1204 PNG image as output.
It’s pretty common for app icons to have a gradient background. Gradients are pretty easy to do in SwiftUI, lets add some:
struct MySwiftUIView : View {
let gradientStart = Color(#colorLiteral(red: 0.9098039269, green: 0.4784313738, blue: 0.6431372762, alpha: 1))
let gradientEnd = Color(#colorLiteral(red: 0.3647058904, green: 0.06666667014, blue: 0.9686274529, alpha: 1))
var body: some View {
ZStack {
LinearGradient(gradient: Gradient(colors: [gradientStart, gradientEnd]),
startPoint: .top, endPoint: .bottom)
Text("Hello, world!")
.foregroundColor(.white)
.font(.largeTitle)
}
}
}
Drawing paths in SwiftUI is a pleasure. There are pretty good articles with great examples online (some examples: Paths in SwiftUI, SwiftUI: Paths vs. Shapes).
Here is an example of drawing overlapping squares, as illustrated by Paul Hudson on How to draw a custom path.
struct SpiroSquare: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
let rotations = 5
let amount = .pi / CGFloat(rotations)
let transform = CGAffineTransform(rotationAngle: amount)
for _ in 0 ..< rotations {
path = path.applying(transform)
path.addRect(CGRect(x: -rect.width / 2, y: -rect.height / 2, width: rect.width, height: rect.height))
}
return path
}
}
struct MySwiftUIView : View {
let gradientStart = Color(#colorLiteral(red: 0.9098039269, green: 0.4784313738, blue: 0.6431372762, alpha: 1))
let gradientEnd = Color(#colorLiteral(red: 0.3647058904, green: 0.06666667014, blue: 0.9686274529, alpha: 1))
var body: some View {
ZStack {
LinearGradient(gradient: Gradient(colors: [gradientStart, gradientEnd]),
startPoint: .top, endPoint: .bottom)
SpiroSquare()
.stroke(lineWidth: 8)
.frame(width: 600, height: 600)
.offset(x: 300, y: 300)
}
}
}
When we run our rasterizer from the command line with swift run
, we get this pretty icon:
Besides paths, we can also use shapes to compose our icon.
For example, using rounded rectangle, we can replicate (to some extent) the Photos app icon shape:
struct MySwiftUIView : View {
let gradientStart = Color(#colorLiteral(red: 0.9098039269, green: 0.4784313738, blue: 0.6431372762, alpha: 1))
let gradientEnd = Color(#colorLiteral(red: 0.3647058904, green: 0.06666667014, blue: 0.9686274529, alpha: 1))
let petalLength: CGFloat = 400
var body: some View {
ZStack {
LinearGradient(gradient: Gradient(colors: [gradientStart, gradientEnd]),
startPoint: .top, endPoint: .bottom)
ForEach(0..<8) { index in
RoundedRectangle(cornerRadius: petalLength / 2)
.frame(width: petalLength, height: petalLength / 2)
.offset(x: petalLength / 2, y: 0)
.opacity(0.7)
.rotationEffect(Angle(degrees: Double(index) * 45))
}
}
}
}
Live preview:
And generated PNG icon image:
There you go, hope you found this useful. If you do and end up using this technique, please ping me on Twitter, I cannot wait to see the app icons you create in SwiftUI.
This article was written as an issue on my Blog repository on GitHub (see Issue #24)
First draft: 2021-01-23
Published on: 2021-01-23
Last update: 2021-01-23