SwiftUI tutorials (part 4)

Understanding ScrollView and creating a carousel UI

link to part 3

In this lesson we will deal with such an element as ScrollView for creating long scrolling canvases. It often happens that our content is too large but at the same time static, because of this, the use of tables or collections UITableView or UICollectionView is simply impractical, so their parent UIScrollView is used, in our particular case we will use the SUI analogue – ScrollView, and we will create such a carousel :

1-1.png

Let's start by creating a new project and calling it SUIScrollView, of course you can name it whatever you like. Select a folder to store the project and, as usual, Xcode will generate a ContentView for us, which we will not work with in the first place.

1.png

Let's start by adding the pictures that I prepared for you at this link https://disk.yandex.ru/d/WeyQ0sotD8Lq0g download and unzip the archive with pictures and transfer them to the assets directory as we already did in previous lessons.

2.png

Now our task is to use the knowledge from past lessons to create a product card

2-2.png

Try to create such a card yourself; if something doesn’t work out, it’s okay, now we will do it together with you.

Let's create a new SwiftUIView file, in which we will make our card

3.png

Select SwiftUI View and name the file CardView

3-3.png

Xcode has generated a new file for us in which we will work

Screenshot 2024-02-28 at 10.32.02.png

Let's first of all decide that our picture and text will actually be located in a stackview that arranges the elements vertically inside itself, add a stackview and a picture

struct CardView: View {
    var body: some View {
		VStack {
			Image("1")
				.resizable()
		}
    }
}

We added resizable because we want our image to subsequently adapt to the containers in which we will place it; do not be alarmed that the image will stretch across the entire device.

Screenshot 2024-02-28 at 10.35.48.png

Next we will add text, we will have three text elements (headline, title, caption) and update our code under the image

struct CardView: View {
    var body: some View {
		VStack {
			Image("1")
				.resizable()
			Text("MacBook D")
				.font(.headline)
				.foregroundStyle(.gray)
			Text("Doubled MacBook Concept")
				.font(.title)
			Text("Created by Oliver Thompson")
				.font(.caption)
				.foregroundStyle(.gray)
		}
    }
}
Screenshot 2024-02-28 at 11.29.14.png

Now we need to align our text from the left border, I remind you that this is done through the property of the stack itself

		VStack(alignment: .leading) {
			Image("1")
				.resizable()
			Text("MacBook D")
				.font(.headline)
				.foregroundStyle(.gray)
			Text("Doubled MacBook Concept")
				.font(.title)
			Text("Created by Oliver Thompson")
				.font(.caption)
				.foregroundStyle(.gray)
		}

We also need to round the edges of our card, but keep in mind that when rounding, the lowest text may be cut off, so we need to somehow get around this situation

struct CardView: View {
    var body: some View {
		VStack(alignment: .leading) {
			Image("1")
				.resizable()
			Text("MacBook D")
				.font(.headline)
				.foregroundStyle(.gray)
			Text("Doubled MacBook Concept")
				.font(.title)
			Text("Created by Oliver Thompson")
				.font(.caption)
				.foregroundStyle(.gray)
				.padding(.bottom)
		}
		.clipShape(RoundedRectangle(cornerRadius: 20))
    }
}
Screenshot 2024-02-28 at 11.35.51.png

I added a clipShape to round out our entire VStack and added a bottom padding .padding(.bottom) for our last text in the list to achieve the desired effect. In principle, our product card is ready, but now it is not universal, let's create properties for this structure with which we will subsequently fill out our cards in the ContentView

struct CardView: View {
	var imageName: String
	var productType: String
	var productName: String
	var creatorName: String
    var body: some View {
		VStack(alignment: .leading) {
			Image(imageName)
				.resizable()
			Text(productType)
				.font(.headline)
				.foregroundStyle(.gray)
			Text(productName)
				.font(.title)
			Text("Created by \(creatorName)")
				.font(.caption)
				.foregroundStyle(.gray)
				.padding(.bottom)
		}
		.clipShape(RoundedRectangle(cornerRadius: 20))
    }
}

After adding these properties, our preview complains that it does not have enough data to render

Screenshot 2024-02-28 at 11.40.20.png

Let's add this data

#Preview {
	CardView(imageName: "1", productType: "MacBook D", productName: "Doubled MacBook Concept", creatorName: "Oliver Thompson")
}
Screenshot 2024-02-28 at 11.41.32.png

Great, our food card is ready, let's add four copies of these cards to our ContentView

struct ContentView: View {
    var body: some View {
        VStack {
            CardView(imageName: "1",
					 productType: "MacBook D",
					 productName: "Doubled MacBook Concep",
					 creatorName: "Oliver Thompson")
			CardView(imageName: "2",
					 productType: "MacBook S",
					 productName: "MacBook S Concept",
					 creatorName: "Emily Bennett")
			CardView(imageName: "3",
					 productType: "MacBook Steam",
					 productName: "SteamPunk MacBook Concept",
					 creatorName: "Haruto Tanaka")
			CardView(imageName: "4",
					 productType: "MacBook S",
					 productName: "Full Sensored MacBook Concept",
					 creatorName: "Yui Sato")
        }
        .padding()
    }
}
Screenshot 2024-02-28 at 11.54.04.png

Overall not bad, but we definitely need to force the images to be displayed in their resolution, let's go back to CardView and add aspectRatio

struct CardView: View {
	var imageName: String
	var productType: String
	var productName: String
	var creatorName: String
    var body: some View {
		VStack(alignment: .leading) {
			Image(imageName)
				.resizable()
				.aspectRatio(contentMode: .fit)
			Text(productType)
				.font(.headline)
				.foregroundStyle(.gray)
			Text(productName)
				.font(.title)
			Text("Created by \(creatorName)")
				.font(.caption)
				.foregroundStyle(.gray)
				.padding(.bottom)
		}
		.clipShape(RoundedRectangle(cornerRadius: 20))
    }
}
Screenshot 2024-02-28 at 11.58.59.png

Our content has moved out again, but don’t worry – this is due to the fact that in SUI all content tries to fill the space that we give it to fill, at the current moment in time we have a vertical stack that contains four complex cards, it compresses the content as much as possible this is possible, as you can see in some places he even shortened the lines of text. Let's add our VStack to ScrollView

struct ContentView: View {
    var body: some View {
		ScrollView {
			VStack {
				CardView(imageName: "1",
						 productType: "MacBook D",
						 productName: "Doubled MacBook Concept",
						 creatorName: "Oliver Thompson")
				CardView(imageName: "2",
						 productType: "MacBook S",
						 productName: "MacBook S Concept",
						 creatorName: "Emily Bennett")
				CardView(imageName: "3",
						 productType: "MacBook Steam",
						 productName: "SteamPunk MacBook Concept",
						 creatorName: "Haruto Tanaka")
				CardView(imageName: "4",
						 productType: "MacBook S",
						 productName: "Full Sensored MacBook Concept",
						 creatorName: "Yui Sato")
			}
			.padding()
		}
    }
}
Screenshot 2024-02-28 at 12.11.42.png

It may seem strange, but in principle we are here only for this reason, and that seems to be all.

But let’s first try to see what will happen if we make our scrollview horizontal, and secondly, you noticed that we have a scroll indicator, it can also be turned off using the scrollIndicators modifier. Of course, if we want our content to be displayed horizontally, then it should be placed in HStack

struct ContentView: View {
    var body: some View {
		ScrollView(.horizontal) {
			HStack {
				CardView(imageName: "1",
						 productType: "MacBook D",
						 productName: "Doubled MacBook Concept",
						 creatorName: "Oliver Thompson")
				CardView(imageName: "2",
						 productType: "MacBook S",
						 productName: "MacBook S Concept",
						 creatorName: "Emily Bennett")
				CardView(imageName: "3",
						 productType: "MacBook Steam",
						 productName: "SteamPunk MacBook Concept",
						 creatorName: "Haruto Tanaka")
				CardView(imageName: "4",
						 productType: "MacBook S",
						 productName: "Full Sensored MacBook Concept",
						 creatorName: "Yui Sato")
			}
			.padding()
		}
		.scrollIndicators(.hidden)
    }
}
Screenshot 2024-02-28 at 12.19.12.png

We have achieved the desired effect and our scroll works horizontally, but you must admit that now our pictures have taken up too much space and it does not look as convenient as it could be for our conditional user. There is another element in SUI that allows you to combine other elements into a group, it’s called Group, let’s put our CardViews in Group and set the frame to 400 in width, this way we will get a much cleaner design, plus additional space will be freed up if you want to add something else here.

struct ContentView: View {
    var body: some View {
		ScrollView(.horizontal) {
			HStack {
				Group {
					CardView(imageName: "1",
							 productType: "MacBook D",
							 productName: "Doubled MacBook Concept",
							 creatorName: "Oliver Thompson")
					CardView(imageName: "2",
							 productType: "MacBook S",
							 productName: "MacBook S Concept",
							 creatorName: "Emily Bennett")
					CardView(imageName: "3",
							 productType: "MacBook Steam",
							 productName: "SteamPunk MacBook Concept",
							 creatorName: "Haruto Tanaka")
					CardView(imageName: "4",
							 productType: "MacBook S",
							 productName: "Full Sensored MacBook Concept",
							 creatorName: "Yui Sato")
				}
				.frame(width: 400)
				.padding()
			}
		}
		.scrollIndicators(.hidden)
    }
}
Screenshot 2024-02-28 at 12.30.04.png

We could end here, but as you know, ScrollView has the ability to scroll content not only vertically and horizontally, but also along both axes at once; for this we just need to pass an array of both axes to the initializer – so you can delete all the code which was written above and replace it with the following just to test this behavior:

struct ContentView: View {
	var body: some View {
		ScrollView([.horizontal, .vertical]) {
			Image(systemName: "macbook")
				.font(.system(size: 999))
		}
	}
}
Screenshot 2024-02-28 at 12.35.15.png

Now the content can be scrolled as you please along all axes available to us at the same time.

Results

In this part, we worked with ScrollView and consolidated the work with stacks to a greater extent, and also learned about such an element as Group. I recommend practicing with the ScrollView element, putting together different layouts or complex versions of them when some part of the screen scrolls only horizontally, and some vertically, and once again I want to remind you that this element is great for static screens with already prepared data, but here’s how to add We will look at dynamic data that can also be scrolled in the next parts, so stay in touch.

As before, subscribe to my telegram channel – https://t.me/swiftexplorer

I will be glad to see your comments and likes!

Thanks for reading!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *