Leave a rating/review
So far, every SwiftUI View and every SwiftUI View Modifier that we’ve added into Bullseye is in a single view: ContentView.
But as you might have noticed, since we’re shoving all of this code into a single place, it’s getting pretty hard to read. We already have 100 lines of code, and we’re not even done styling the app yet.
I don’t know about you, but I’m already finding it difficult to figure out what curly brace matches up where, and to just get my mind wrapped about what’s going on. And this is a simple app!
Imagine if we kept going like this for a larger app: before long our code would look like a tangled mess of spaghetti.
Luckily, SwiftUI provides an easy way to keep your code a lot cleaner, and restore your sanity: extract your views!
What this means is instead of creating a single monolithic view like ContentView, we can create multiple views for each bit of our app.
For example, consider this Text View that displays the target value you’re trying to hit. There’s several lines of code here that is cluttering up our ContentView.
So what we can do instead is copy these lines of code to another view, that we could call BigNumberView. And we can make it reusable in other places in the app, by having it take a parameter of the text to display inside that view.
After doing that, we can delete the original lines of code and use the our new BigNumberText view instead.
This makes ContentView shorter and easier to read and understand what’s going on.
And then we can repeat this! For example, we could take this code from the top and move it into a reusable view to show Instruction-styled text.
Now that we hve our shiny new InstructionText view, we can simply use that instead of styling the text view directly in ContentView.
And we can nest views in other views, too. Considering these top two views are related to instructions, we can move these into an InstructionsView.
And that in turn, cleans up our ContentView even more. It’s getting a lot smaller already!
As can see, extracting your views like this will make your code more readable and less spaghetti-like. Plus, now we have nice and reusable views that we can use in multiple areas across our app without copying and pasting code.
Now that you understand the theory of extracting views, let’s put this into practice.
Add a new file SwiftUI View file to Views, name it TextViews.swift.
Start with this:
import SwiftUI
struct InstructionText: View {
var body: some View {
Text("Hello, World!")
}
}
struct TextViews_Previews: PreviewProvider {
static var previews: some View {
InstructionText()
}
}
Copy/paste from ContentView.swift:
Text("🎯🎯🎯\nPut the Bullseye as close as you can to".uppercased())
.bold()
.kerning(2.0)
.multilineTextAlignment(.center)
.lineSpacing(4.0)
.font(.footnote)
.padding(.leading, 30.0)
.padding(.trailing, 30.0)
.foregroundColor(Color("TextColor"))
Show how it shows up in the preview.
Go back to ContentView.swift and replace it:
InstructionText()
Explain it would be better if the text wasn’t hardcoded. Add a property:
var text: String
And use it:
Text(text.uppercased())
Modify the preview accordingly:
InstructionText(text: "Instructions")
And ContentView.swift:
InstructionText(text: "🎯🎯🎯\nPut the Bullseye as close as you can to")
Explain how one small tweak here: you should try to make the views you extract as reusable as possible. The padding we have here is really specific to these particular instructions, so I’m going to pull it out back into the main instructions:
InstructionText(text: "🎯🎯🎯\nPut the Bullseye as close as you can to")
.padding(.leading, 30.0)
.padding(.trailing, 30.0)
Repeat for BigNumberText:
struct BigNumberText: View {
var text: String
var body: some View {
Text(text)
.kerning(-1.0)
.font(.largeTitle)
.fontWeight(.black)
.foregroundColor(Color("TextColor"))
}
}
Modify previews accordingly:
VStack {
InstructionText(text: "Instructions")
BigNumberText(text: "999")
}
And use in ContentView.swift:
BigNumberText(text: String(game.target))
Also show how we can extract even more:
struct InstructionsView: View {
@Binding var game: Game
var body: some View {
VStack {
InstructionText(text: "🎯🎯🎯\nPut the Bullseye as close as you can to")
.padding(.leading, 30.0)
.padding(.trailing, 30.0)
BigNumberText(text: String(game.target))
}
}
}
And in ContentView:
InstructionView(game: $game)