i-swift

Master the Art of Swift Programming

Mastering ScrollView, ScrollViewReader, and ScrollViewProxy in SwiftUI

SwiftUI makes it easy to create smooth and interactive scrolling in your apps. You can use tools like ScrollViewScrollViewReader, and ScrollViewProxy to handle large amounts of data, create unique scrolling effects, and even programmatically jump to specific parts of your content.

In this article, we’ll dive deep into how these tools work and build a simple app to show you what they can do.

ScrollView: The Basics

A ScrollView lets you see more content than fits on the screen by letting you scroll up and down or side-to-side. Here’s a quick example of how it works.

import SwiftUI

struct SimpleScrollView: View {
    var body: some View {
        ScrollView {
            VStack(spacing: 20) {
                ForEach(1...50, id: \.self) { index in
                    Text("Item \(index)")
                        .frame(maxWidth: .infinity)
                        .padding()
                        .background(Color.blue.opacity(0.2))
                        .cornerRadius(10)
                        .padding(.horizontal)
                }
            }
        }
    }
}
Swift

Key Features:

  • You can easily adjust a ScrollView to scroll horizontally, vertically, or in both directions.
  • It’s smart enough to automatically adjust to fit perfectly on any screen size

ScollViewReader: Enabling Programmatic Scrolling

Imagine you have a long list of items on your screen. ScrollViewReader lets you easily jump directly to the one you’re looking for.

It works by teaming up with ScrollViewProxy. This little helper gives you the tools to quickly move the screen to show the exact item you want to see.

Example: Scrolling to a Specific Item

import SwiftUI

struct ScrollViewReaderExample: View {
    var body: some View {
        ScrollViewReader { proxy in
            VStack {
                Button("Scroll to Item 25") {
                    proxy.scrollTo(25, anchor: .center)
                }
                .padding()

                ScrollView {
                    VStack(spacing: 20) {
                        ForEach(1...50, id: \.self) { index in
                            Text("Item \(index)")
                                .id(index) // Assign a unique ID for each item
                                .frame(maxWidth: .infinity)
                                .padding()
                                .background(Color.green.opacity(0.2))
                                .cornerRadius(10)
                                .padding(.horizontal)
                        }
                    }
                }
            }
        }
    }
}
Swift

How It Works:

  1. id Modifier : Each item in your scrolling list has a unique identifier within the ScrollView
  2. scrollTo Method : You can tell the ScrollView to jump directly to the item with the name you specify.

ScrollViewProxy: The Core of Programmatic Control

ScrollViewProxy is a powerful tool that lets you control how your scrolling content behaves. It gives you the power to:

  • Jump directly to specific items: Think of it like choosing a chapter in a book.
  • Position content perfectly: You can easily make sure the most important part is always at the top, center, or bottom of the screen.
proxy.scrollTo(itemID, anchor: .top)
Swift

Sample Project: A Beautiful ScrollView Page

Now let’s put these ideas together! We’ll build a beautiful page where users can easily jump between different sections by simply tapping a button.

//
//  ContentView.swift
//  scrollviewTest
//
//  Created by mohsen on 12/24/24.
//

import SwiftUI

struct ContentView: View {
    var body: some View {
        ScrollViewReader { proxy in
            VStack(spacing: 20) {
                // Navigation Bar
                HStack(spacing: 10) {
                    Button(action: {
                        withAnimation {
                            proxy.scrollTo("section1", anchor: .top)
                        }
                        
                        
                    }) {
                        Text("Section  1")
                            .padding()
                            .frame(maxWidth: .infinity)
                            .background(LinearGradient(gradient: Gradient(colors: [.red, .pink]), startPoint: .leading, endPoint: .trailing))
                            .cornerRadius(10)
                            .foregroundColor(.white)
                            .font(.headline)
                            .shadow(color: .pink.opacity(0.5), radius: 5, x: 0, y: 3)
                    }
                    
                    Button(action: {
                        withAnimation {
                            proxy.scrollTo("section2", anchor: .top)
                        }
                        
                    }) {
                        Text("Section 2")
                            .padding()
                            .frame(maxWidth: .infinity)
                            .background(LinearGradient(gradient: Gradient(colors: [.blue, .purple]), startPoint: .leading, endPoint: .trailing))
                            .cornerRadius(10)
                            .foregroundColor(.white)
                            .font(.headline)
                            .shadow(color: .blue.opacity(0.5), radius: 5, x: 0, y: 3)
                    }
                    
                    Button(action: {
                        withAnimation {
                            proxy.scrollTo("section3", anchor: .top)
                        }
                        
                    }) {
                        Text("Section 3")
                            .padding()
                            .frame(maxWidth: .infinity)
                            .background(LinearGradient(gradient: Gradient(colors: [.green, .teal]), startPoint: .leading, endPoint: .trailing))
                            .cornerRadius(10)
                            .foregroundColor(.white)
                            .font(.headline)
                            .shadow(color: .green.opacity(0.5), radius: 5, x: 0, y: 3)
                    }
                }
                .padding()
                .background(Color.white.opacity(0.8))
                .cornerRadius(15)
                .shadow(color: Color.black.opacity(0.1), radius: 10, x: 0, y: 5)
                
                // Scrollable Content
                ScrollView {
                    VStack(spacing: 50) {
                        BeautifulSectionView(title: "Section 1", color: .red, gradient: Gradient(colors: [.red, .orange]), id: "section1")
                        BeautifulSectionView(title: "Section 2", color: .blue, gradient: Gradient(colors: [.blue, .purple]), id: "section2")
                        BeautifulSectionView(title: "Section 3", color: .green, gradient: Gradient(colors: [.green, .teal]), id: "section3")
                    }
                }
            }
            .padding(.horizontal)
            .background(
                LinearGradient(gradient: Gradient(colors: [.white, .gray.opacity(0.2)]), startPoint: .top, endPoint: .bottom)
                    .edgesIgnoringSafeArea(.all)
            )
        }
    }
}

#Preview {
    ContentView()
}



// Custom Section View
struct BeautifulSectionView: View {
    let title: String
    let color: Color
    let gradient: Gradient
    let id: String
    
    var body: some View {
        VStack(spacing: 20) {
            Text(title)
                .font(.largeTitle)
                .bold()
                .foregroundColor(.white)
                .padding()
                .frame(maxWidth: .infinity)
                .background(LinearGradient(gradient: gradient, startPoint: .leading, endPoint: .trailing))
                .cornerRadius(15)
                .shadow(color: color.opacity(0.5), radius: 10, x: 0, y: 5)
            
            Text("""
                 This is \(title). 
                 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor
                  incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
                   nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
                      On the other hand, we denounce with righteous indignation and dislike men who
                       are so beguiled and demoralized by the charms of pleasure of the moment, so 
                       blinded by desire, that they cannot foresee the pain and trouble that are 
                       bound to ensue; and equal blame belongs to those who fail in their duty 
                       through weakness of will, which is the same as saying through shrinking
                        from toil and pain. These cases are perfectly simple and easy to 
                        distinguish. In a free hour, when our power of choice is untrammelled and 
                        when nothing prevents our being able to do what we like best, every 
                        pleasure is to be welcomed and every pain avoided. But in certain 
                        circumstances and owing to the claims of duty or the obligations of 
                        business it will frequently occur that pleasures have to be repudiated
                         and annoyances accepted. The wise man therefore always holds in these 
                         matters to this principle of selection: he rejects pleasures to secure
                          other greater pleasures, or else he endures pains to avoid worse pains.
                 """)
            .font(.body)
            .padding()
            .background(Color.white.opacity(0.9))
            .cornerRadius(15)
            .shadow(color: .black.opacity(0.1), radius: 5, x: 0, y: 5)
        }
        .id(id) // Assign ID for ScrollViewProxy
        .padding(.horizontal)
    }
}
Swift

Here’s what makes this page special:

  • Easy Navigation: Users can quickly jump between different parts of the page with just a tap.
  • Stylish Sections: Each section has its own unique look and feel with different colors and designs.
  • Smooth Scrolling: When you tap a button, the page smoothly glides to the section you want to see.

Conclusion

SwiftUI gives you all the tools you need to make great scrolling experiences with ScrollView, ScrollViewReader, and ScrollViewProxy. Whether you’re making a basic scrolling list or a more complicated interface, these tools make it easy and powerful.

Leave a Reply

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

Quick contact info

© 2024 i-Swift. All rights reserved
Your go-to source for Swift tutorials, tips, and insights. Empowering developers with expert content since 2024.