Scroll results to top after button click

42 views Asked by At

I have a View where I take input from the user. Once they click the Calculate button, I'll run a function to calculate the results and display the results below the Calculate button. What I would like to see is the results section to scroll up after the user clicks the Calculate button.

I tried to use ScrollViewReader but I don't know how to implement it in this case.

Here is my code:

import SwiftUI

enum ParameterType: String, CaseIterable, Identifiable {
    case Mean, Proportion
    var id: Self { self }
}

struct confidenceValue: Identifiable {
    var id: Int
    var name: String
    var dispName: String
    var isSelected: Bool = false
    var relCoeff: String = ""
    var level: Int
}

extension View {
    //Hide Keyboard
    func hideKeyboard() {
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

struct ContentView: View {
    
    @State var inputtext: String = ""
    @State var standardDeviation: String = "12"
    @State var marginofError: String = "8"
    @State var proportiondata: String = "0.5"
    @State var proportionME: String = "0.1"
    @State var showResults: Bool = false
    @State private var selectedParameterType: ParameterType = .Mean
    @State private var selectedInterval = 1
    
    @State var confidenceValues:[confidenceValue] = [confidenceValue(id: 1, name: "0.90", dispName: "90%", isSelected: true, relCoeff: "1.645", level: 0),confidenceValue(id: 2, name: "0.95", dispName: "95%", isSelected: true, relCoeff: "1.96", level: 1),confidenceValue(id: 3, name: "0.99", dispName: "99%" , isSelected: true, relCoeff: "2.575", level: 2)
    ]
    
    var body: some View {
        ScrollViewReader { proxy in
            NavigationView {
                Form {
                    //Number of Samples - One or Two samples
                    Section(header: Text("Parameter of Interest")) {
                        Picker("ParameterType", selection: $selectedParameterType) {
                            Text("Population Mean").tag(ParameterType.Mean)
                            Text("Population Proportion").tag(ParameterType.Proportion)
                        }
                        .pickerStyle(SegmentedPickerStyle())
                        //Text("selected parameter is : \(selectedParameterType.rawValue)")
                        //.onChange(of: selectedParameterType,  ParameterTypeOnChange())
                    } //Section
                    
                    if (selectedParameterType.rawValue == "Mean") {
                        //This section is applicable only for Population Mean
                        Section(header: Text("Enter Values")) {
                            
                            HStack (alignment: .center) {
                                
                                Text("Population Std. Dev(σ)")
                                    .font(.callout)
                                    .frame(maxWidth: .infinity, alignment: .leading)
                                
                                Spacer(minLength: /*@START_MENU_TOKEN@*/0/*@END_MENU_TOKEN@*/)
                                
                                Divider()
                                
                                TextField("Std. Deviation:", text: $standardDeviation)
                                
                            } //HStack
                            
                            .onTapGesture {
                                self.hideKeyboard()
                            }
                            
                            HStack (alignment: .center) {
                                Text("Margin of Error (E)")
                                    .font(.callout)
                                    .frame(maxWidth: .infinity, alignment: .leading)
                                
                                Spacer(minLength: /*@START_MENU_TOKEN@*/0/*@END_MENU_TOKEN@*/)
                                
                                Divider()
                                
                                TextField("Margin of Error:", text: $marginofError)
                                
                                
                            } //HStack
                            .onTapGesture {
                                self.hideKeyboard()
                            }
                            
                        } // Section
                    } //If condition
                    
                    else if (selectedParameterType.rawValue == ParameterType.Proportion.rawValue) {
                        //This section is applicable only for Population Mean
                        Section(header: Text("Enter Values")) {
                            
                            HStack (alignment: .center) {
                                
                                Text("Target Proportion (p̂)")
                                    .font(.callout)
                                    .frame(maxWidth: .infinity, alignment: .leading)
                                
                                //Spacer(minLength: /*@START_MENU_TOKEN@*/0/*@END_MENU_TOKEN@*/)
                                
                                Divider()
                                
                                TextField("proportion data:", text: $proportiondata)
                                
                            } //HStack
                            .onTapGesture {
                                self.hideKeyboard()
                            }
                            
                            HStack (alignment: .center) {
                                Text("Margin of Error (E)")
                                    .font(.callout)
                                    .frame(maxWidth: .infinity, alignment: .leading)
                                
                                // Spacer(minLength: /*@START_MENU_TOKEN@*/0/*@END_MENU_TOKEN@*/)
                                
                                Divider()
                                
                                TextField("proportion ME:", text: $proportionME)
                                
                            } //HStack
                            .onTapGesture {
                                self.hideKeyboard()
                            }
                        } // Section
                    } //Else condition
                    
                    
                    List{
                        HStack {
                            ForEach(0..<confidenceValues.count, id: \.self) {index in
                                //HStack {
                                Button(action: {
                                    confidenceValues[index].isSelected = confidenceValues[index].isSelected ? false : true
                                }) {
                                    HStack{
                                        if confidenceValues[index].isSelected {
                                            Image(systemName: "checkmark.circle.fill")
                                                .foregroundColor(.green)
                                            
                                        } else {
                                            Image(systemName: "circle")
                                                .foregroundColor(.primary)
                                            
                                        }
                                        Text(confidenceValues[index].name)
                                    }
                                    .padding(10)
                                }.buttonStyle(BorderlessButtonStyle())
                                //} //HStack
                            } //ForEach
                        } //HStack
                    }//List
                    
                    Section(){
                        Button(
                            action: {
                                calculateEstimate()
                            },
                            label: {
                                Text("Calculate")
                                    .frame(minWidth: 0, maxWidth: 300)
                                    .foregroundColor(.blue)
                                    .cornerRadius(40)
                                    .font(.title)
                            }
                        )
                    }
                    .listRowBackground(Color.clear)
                    
                    //Print Results
                    if showResults == true {
                        Section(header: Text("Results")) {
                            //Display the results for each confidence level selected
                            List {
                                ForEach(0..<confidenceValues.count, id: \.self)
                                {nEstimate in
                                    VStack{
                                        Text("The required sample size with a \(confidenceValues[nEstimate].name) confidence level is: ")
                                            .frame(alignment: .topLeading)
                                        //.id(nEstimate.level)
                                        //.foregroundColor(.red)
                                        
                                        
                                        Divider()
                                        
                                        if selectedParameterType.rawValue == "Mean" {
                                            Text("n = ((Zα/2 * σ) / E))^2")
                                                .frame(alignment: .topLeading)
                                            
                                            Spacer()
                                            
                                            Text("n = (1.645 * 12.0) / 8.0)^2 ")
                                                .frame(alignment: .topLeading)
                                            
                                            Spacer()
                                        }
                                        else if selectedParameterType.rawValue == "Proportion" {
                                            Text("n = (p̂ * (1−p̂ )) * (Zα/2 / E)^2")
                                                .frame(alignment: .topLeading)
                                            
                                            Spacer()
                                            
                                            Text("n = (0.5) * 0.5) * (1.645 / 0.1)^2 ")
                                                .frame(alignment: .topLeading)
                                            
                                            Spacer()
                                        }
                                        
                                        
                                        
                                        
                                        Text("n = 67.6506 ≈ 68")
                                            .frame(alignment: .topLeading)
                                        
                                        Spacer()
                                        
                                    } // VStack
                                } //ForEach
                            } //List
                        } //Section
                        
                        //Display Reset button
                        Button(
                            action: {resetDSData()},
                            label: {
                                Text("Reset Values")
                                    .frame(minWidth: 0, maxWidth: 300)
                                    .foregroundColor(.blue)
                                    .cornerRadius(40)
                                    .font(.title)
                            }
                        )
                        .listRowBackground(Color.clear)
                    } //If showResults
                    
                } //Form
            } //Navigation
        } //ScrollViewReader
    } //Body
    
    struct ParameterTypeOnChange {
        var showResults = false
    }
    
    func calculateEstimate() {
        showResults = true
    }
    
    func resetDSData() {
        showResults = false
    }
} //ContentView
2

There are 2 answers

0
Taeeun Kim On

You can set .id(0) to somewhere you want, and then use proxy.scrollTo(0) in the button's action

1
krzysztofk On

Using .id() property and .scrollTo() you can achieve exactly what you need.

Try to always reach for official Apple's documentation

This example might be useful for you.

    ScrollViewReader { proxy in
        ScrollView {
            Button("Press me") {
                //Your calculation actions
                proxy.scrollTo(37)
            }
            .padding()

            ForEach(0..<50) { i in
                Image(systemName: "bookmark.fill")
                    .resizable()
                    .frame(width: 150, height: 200)
                    .id(i)
                    .overlay(
                        Text("#\(i)")
                            .foregroundColor(.white)
                            .font(.caption)
                            .padding(.top, 4)
                        ,alignment: .center)
            }
        }
    }