Activity Indicator doesn't show when isAnimating set to TRUE

128 views Asked by At

I've got several activity indicators. The one in the choose subscription screen works ok, but when I do a similar thing when user taps a button that writes some information to a database, the activity indicator doesn't show after the boolean value is set to true for the isAnimating parameter.

When I set the isLoading variable to true in its declaration, the activity indicator appears.

Below is the code that does the database work, and the activity indicator view.

import SwiftUI

struct LanguagePickerWheel: View {

    @State private var selectedLanguage: String = ""
    @State private var isLoading = false

    var availableLanguages: [String] = []

    @Environment(\.presentationMode) var presentation
    @Environment(\.managedObjectContext) private var viewContext
    
    private func dismiss() {
        self.presentation.wrappedValue.dismiss()
    }
    
    var body: some View {
        GeometryReader { geometry in
            VStack {
                HStack {
                    Button("Cancel") { self.dismiss() }
                        .padding(.top)
                        .padding(.trailing, 125)
                    Button("Select") {
                        DispatchQueue.main.async {
                            isLoading = true
                        }
                        let queue = DispatchQueue(label: "work-queue-1")
                        queue.async {
                            if selectedLanguage == "" {
                                selectedLanguage = availableLanguages[0]
                            }
                            // submit language to the addAndSaveLanguage method
                            let newLanguage = Language(context: viewContext)
                            newLanguage.name = selectedLanguage
                            newLanguage.setAsRevision = false
                            PersistenceController.shared.saveDB()
                            do {
                                    // This solution assumes you've got the file in your bundle
                                if let path = Bundle.main.path(forResource: "\(selectedLanguage)_English_Over_2500_Words", ofType: "txt") {
                                    let data = try String(contentsOfFile:path, encoding: String.Encoding.utf8)
                                    var arrayOfStrings: [String]
                                    arrayOfStrings = data.components(separatedBy: ";")

                                    for string in arrayOfStrings {
                                        let newCommonWord = CommonWordThing(context: viewContext)
                                        newCommonWord.native = string.components(separatedBy: "_")[1]
                                        newCommonWord.foreign = string.components(separatedBy: "_")[0]
                                        newCommonWord.ckimage = false
                                        newCommonWord.inUse = false
                                        newCommonWord.typingTestCorrect = 0
                                        newCommonWord.arrangeWordsCorrect = 0
                                        newCommonWord.ckreference = newLanguage.ckrecordname
                                        newCommonWord.attempts = 0
                                        newCommonWord.image = nil
                                        newCommonWord.repetitionInterval = 0
                                        newCommonWord.testsUntilPresented = 0
                                        newCommonWord.setAsRevision = false
                                        newCommonWord.language = newLanguage
                                        
                                        var stringNumber = string.split(separator: "_")[2]
                                        if stringNumber.contains("\r\n") {
                                            stringNumber.removeLast(1)
                                        }
                                        newCommonWord.count = NumberFormatter().number(from: String(stringNumber) as String)?.int64Value ?? 0
                                    }
                                    
                                }
                            } catch let err as NSError {
                                    // do something with Error
                                print("Couldn't save new language to database: \(err)")
                            }
                            PersistenceController.shared.saveDB()
                        }
                        isLoading = false
                        self.dismiss()
                    }
                    .padding(.top)
                    .padding(.leading, 125)
                }
                Text("Choose a language:")
                    .font(.title)
                    .padding(.top, 50)
                Picker("Choose a language:", selection: $selectedLanguage, content: {
                    ForEach(Array(availableLanguages), id: \.self) { language in
                        Text(language)
                    }
                })
                .pickerStyle(WheelPickerStyle())
                .padding(.leading)
                .padding(.trailing)
                
            }
            ActivityIndicatorView(isAnimating: $isLoading, text: "Loading...")
                .position(x: geometry.frame(in: .local).midX, y: geometry.frame(in: .local).midY)
        }
    }
}

Activity Indicator:

struct ActivityIndicatorView: View {
    @Binding var isAnimating:Bool
    var body: some View {
        if isAnimating{
            ZStack{
                RoundedRectangle(cornerRadius: 15).fill(Color.lightGray)
                ProgressView {
                    Text("Loading...")
                        .font(.title2)
                }
            }.frame(width: 120, height: 120, alignment: .center)
            .background(RoundedRectangle(cornerRadius: 25).stroke(.gray,lineWidth: 2))
        }
    }
}
1

There are 1 answers

1
hallo On

Everything that happens between isLoading = true and isLoading = false in your Button("Select") action is running on the main thread and is therefore blocking the UI. The blocked UI cannot be updated so the UI will only update again when it reaches isLoading = false but then it will be hidden again.

You need to look into async/await and/or threading to solve your issue.