Cloning Airbnb with SwiftUI — Part 2
It’s been a while! Balancing work, life, and creating content can be difficult. I’m back with a second approach to recreating Airbnb’s design.
First, I reorganized the folders. SwiftUI projects with everything in the same folder were confusing me, so I changed the structure to this:
airbnb-home
|___Model
|___Views
|_____Cells
|_____Sections
|___Navigation
|___Screens
The benefits were immediately clear: separating cells and sections made each screen element easier to identify.
Let’s go through the screen by area, starting with the main photo:
ZStack(alignment: .top) {
ScrollView {
VStack {
HStack {
Spacer()
Image("airbnb-home-cardPrincipal")
.resizable()
.cornerRadius(6)
.frame(width: 315, height: 400)
.foregroundColor(.white)
}
}
.padding(16)
.padding(.trailing, 16)
.padding(.leading, 16)
Since I didn’t have high-quality source photos, I took screenshots from the app. If you build the project linked at the end of the article, you’ll notice extra padding on the left. I still don’t know why! -.-‘
Next, I created a section containing a horizontally scrolling view of the photos.
struct ExperiencesSectionView: View {
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 12) {
ForEach(GenericContent.experiencesData) { item in
ExperiencesCell(data: item)
}
}
.padding()
.padding(.top, -16)
}
}
}
Here I’m simply using ForEach over the ExperiencesCell cells. This encapsulates the code instead of putting everything into HomeView, as I did before.
Inside the cell:
struct ExperiencesCell: View {
var data: GenericContent
var body: some View {
VStack(alignment: .leading) {
Image(data.imageName)
.resizable()
.cornerRadius(4)
.frame(width: 180, height: 130)
Text(data.title)
.foregroundColor(.black)
.fontWeight(.semibold)
.font(.subheadline)
HStack {
Text(data.subtitle ?? "")
.foregroundColor(.black)
.font(.system(size: 14, weight: .light))
.frame(width: 160, alignment: .leading)
}
}
}
}
The title and subtitle look good, but the result still doesn’t quite look like a card, with everything enclosed in one unit. I need to research that further and will try to cover it in the next article.
To finish this revision, I created another section for the app’s black background area. I used SectionTitle for its title and passed the subtitle directly into the view, along with the button I created. I left ForEach commented out because it will use another horizontally scrolling cell layout that I haven’t finished yet.
I also added a tab bar. Since I don’t have the icons yet, I’ll try using some defaults so it isn’t left blank.
Presenting everything is now easier. Previously, almost all of the creation logic lived in the main view:
var body: some View {
ZStack(alignment: .top) {
ScrollView {
VStack {
HStack {
Spacer()
Image("airbnb-home-cardPrincipal")
.resizable()
.cornerRadius(6)
.frame(width: 315, height: 400)
.foregroundColor(.white)
}
}
.padding(16)
.padding(.trailing, 16)
.padding(.leading, 16)
ExperiencesSectionView()
SectionTitle(title: "Online Experiences: Field Tips")
OnlineExperiencesSectionView()
}
.padding(.bottom, 30)
}
}
}
Now I call the elements in their display order and can easily rearrange them. Home no longer creates everything: it only decides what to present and in which order. Much simpler!
If you want to follow this adventure of recreating Airbnb’s interface, watch the commits in this repository. PRs are welcome. For feedback or questions, open an issue or talk to me on Twitter. :)
See you soon.