ScrollViewReader/Positioning scroll view, GeometryReader/Getting view update parameters, MultipleSheets/Multiple working views, Mask/Usage of five-star evaluation view

1. ScrollViewReader positions the scroll view

1.1 Implementation

/// Position the scroll view
struct ScrollViewReaderBootcamp: View {
    @State var scrollToIndex: Int = 0
    @State var textFiledText: String = ""
    
    var body: some View {
        VStack {
            TextField("Enter a # here...", text: $textFiledText)
                .padding(.horizontal)
                .frame(height: 55)
                .border(Color.gray)
                .padding(.horizontal)
                .keyboardType(.numberPad)
            
            Button("Scroll now") {
                // add animation
                if let index = Int(textFiledText){
                    scrollToIndex = index
                }
            }
            
            ScrollView {
                ScrollViewReader { proxy in
                    /// Loop
                    ForEach(0..<50) { index in
                        Text("This is item #\(index)")
                            .font(.headline)
                            .frame(height: 200)
                            .frame(maxWidth: .infinity)
                            .background(Color.white)
                            .cornerRadius(10)
                            .shadow(radius: 10)
                            .padding()
                            .id(index)
                    }
                    .onChange(of: scrollToIndex) { value in
                        withAnimation(.spring()){
                            // anchor anchor point .top/.center
                            proxy.scrollTo(value, anchor: .top)
                        }
                    }
                }
            }
        }
    }
}

1.2 Rendering:

2. GeometryReader geometry layout obtains view update parameters

2.1 Implementation

/// Geometric layout
struct GeometryReaderBootcamp: View {
    var body: some View {
        //geometryReader1
        geometryReader2
    }
    
    /// Get the percentage
    func getPercentage(geometry: GeometryProxy) -> Double{
        //maximum distance
        let maxDistance = UIScreen.main.bounds.size.width / 2
        print("maxDistance: \(maxDistance)")
        //Current X point .global: the position of global coordinates
        let currentX = geometry.frame(in: .global).midX
        return Double(1 - (currentX / maxDistance))
    }
    
    /// Example 2
    var geometryReader2: some View{
        ScrollView(.horizontal, showsIndicators: false) {
            HStack{
                ForEach(0..<20) { index in
                    GeometryReader { geometry in
                        RoundedRectangle(cornerRadius: 20)
                            .rotation3DEffect(
                                Angle(degrees: getPercentage(geometry: geometry) * 40),
                                axis: (x: 0.0, y: 1.0, z: 0.0))
                    }
                    .frame(width: 300, height: 250)
                    .padding()
                }
            }
        }
    }
    
    /// Example 1
    var geometryReader1: some View{
        //Geometry reader updates the width of the screen when solving horizontal and vertical screens
        GeometryReader { geometry in
            HStack(spacing: 0) {
                Rectangle().fill(Color.red)
                //UIScreen.main.bounds.width
                    .frame(width: geometry.size.width * 0.66666)
                Rectangle().fill(Color.blue)
            }
            .ignoresSafeArea()
        }
    }
}

2.2 Rendering:

3. Use of MultipleSheets multiple worksheet views

3.1 Implementation

//Model
struct RandomModel: Identifiable{
    var id = UUID().uuidString
    let title: String
}
// 1 - use a binding
// 2 - use multiple .sheets
// 3 - use $item

/// Multiple work form views pass Model
struct MultipleSheetsBootcamp: View {
    //@State var selectedModel: RandomModel = RandomModel(title: "Starting Title")
    @State var selectedModel: RandomModel? = nil
    @State var showSheet:Bool = false
    @State var showSheet2: Bool = false
    
    var body: some View {
        //useBinding
        //useMultipleSheets
        useItem
    }
    
    /// Method 3: Use Item
    private var useItem: some View{
        ScrollView {
            VStack(spacing: 20){
                ForEach(0..<50) { index in
                    Button(action: {
                        selectedModel = RandomModel(title: "\(index)")
                    }, label: {
                        Text("Button \(index)")
                            .frame(height: 150)
                            .frame(maxWidth: .infinity)
                            .background(Color.white.cornerRadius(10))
                            .padding(.horizontal)
                            .shadow(radius: 10)
                            .padding(.horizontal)
                    })
                }
            }
            .sheet(item: $selectedModel) { model in
                NextScreen(selectedModel: model)
            }
        }
    }
    
    // Method 2 Use multiple form views
    private var useMultipleSheets: some View{
        VStack(spacing: 20){
            Button("Button 1") {
                showSheet.toggle()
            }
            .sheet(isPresented: $showSheet) {
                NextScreen(selectedModel: RandomModel(title: "One1"))
            }
            Button("Button 2") {
                showSheet2.toggle()
            }
            .sheet(isPresented: $showSheet2) {
                NextScreen(selectedModel: RandomModel(title: "Two2"))
            }
        }
    }
    
    // Method 1 Use @Binding
    private var useBinding: some View{
        VStack(spacing: 20){
            Button("Button 1") {
                selectedModel = RandomModel(title: "One")
                showSheet.toggle()
            }
            Button("Button 2") {
                selectedModel = RandomModel(title: "Two")
                showSheet.toggle()
            }
        }
        .sheet(isPresented: $showSheet) {
            //NextScreen(selectedModel: $selectedModel)
        }
    }
}

// next view
struct NextScreen: View{
    // Use Binding to simply fix the problem of passing values
    // @Binding var selectedModel: RandomModel
    @State var selectedModel: RandomModel
    
    init(selectedModel: RandomModel) {
        self.selectedModel = selectedModel
        print(selectedModel.title)
    }
    
    var body: some View{
        Text("\(selectedModel.title)")
            .font(.largeTitle)
    }
}

3.2 Rendering:

4. Mask creates a five-star review view

4.1 Implementation

//Five-star evaluation production mask modifier/mask component
struct MaskBootcamp: View {
    @State var rating: Int = 2
    
    var body: some View {
        ZStack{
            satrtView
               .overlay(overlayView.mask(satrtView))
        }
    }
    
    // Overwrite View
    private var overlayView: some View{
        // Use geometry reader to get width
        GeometryReader{ geometryProxy in
            ZStack(alignment: .leading) {
                Rectangle()
                    //.foregroundColor(.yellow)
                    .fill(LinearGradient(gradient: Gradient(colors: [Color.red, Color.blue]), startPoint: .leading, endPoint: .trailing))
                    .frame(width: CGFloat(rating) / 5 * geometryProxy.size.width)
            }
        }
        // User cannot click on overlay/remove user click ability
        .allowsHitTesting(false)
    }
    
    //Start View
    private var satrtView : some View{
        HStack{
            ForEach(1 ..< 6) { index in
                Image(systemName: "star.fill")
                    .font(.largeTitle)
                    .foregroundColor(Color.gray)
                    .onTapGesture {
                        withAnimation(.easeInOut){
                            rating=index
                        }
                    }
            }
        }
    }
}

4.2 Rendering: