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))
}
}
}
Everything that happens between
isLoading = trueandisLoading = falsein 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 reachesisLoading = falsebut then it will be hidden again.You need to look into async/await and/or threading to solve your issue.