I'm following Apple's tutorial on SwiftUI and the app has a behavior that I can't understand.
If I active multitasking, the current view is completely lost and go back to the previous one.
As you can see the timer view is completely gone when I active multitasking. This only happens in the third view (the MeetingView) of the view tree.
Does anyone know why this view has this behavior?
You can download the complete project here.
Code:
ScrumsView:
import SwiftUI
struct ScrumsView: View {
@Binding var scrums: [DailyScrum]
@Environment(\.scenePhase) private var scenePhase
@State private var isPresentingNewScrumView = false
@State private var newScrumData = DailyScrum.Data()
let saveAction: ()->Void
@State var navigated = false
var body: some View {
List {
ForEach($scrums) { $scrum in
NavigationLink(destination: DetailView(scrum: $scrum)) {
CardView(scrum: scrum)
}
.listRowBackground(scrum.theme.mainColor)
}
}
.navigationTitle("Daily Scrums")
.toolbar {
Button(action: {
isPresentingNewScrumView = true
}) {
Image(systemName: "plus")
}
.accessibilityLabel("New Scrum")
}
.sheet(isPresented: $isPresentingNewScrumView) {
NavigationView {
DetailEditView(data: $newScrumData)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Dismiss") {
isPresentingNewScrumView = false
newScrumData = DailyScrum.Data()
}
}
ToolbarItem(placement: .confirmationAction) {
Button("Add") {
let newScrum = DailyScrum(data: newScrumData)
scrums.append(newScrum)
isPresentingNewScrumView = false
newScrumData = DailyScrum.Data()
}
}
}
}
}
// .onChange(of: scenePhase) { phase in
// if phase == .inactive { saveAction() }
// }
}
}
struct ScrumsView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
ScrumsView(scrums: .constant(DailyScrum.sampleData), saveAction: {})
}
}
}
DetailView:
import SwiftUI
struct DetailView: View {
@Binding var scrum: DailyScrum
@State private var data = DailyScrum.Data()
@State private var isPresentingEditView = false
@State private var starButton = false
var body: some View {
List {
Section(header: Text("Meeting Info")) {
NavigationLink(destination: MeetingView(scrum: $scrum)) {
Label("Start Meeting", systemImage: "timer")
.font(.headline)
.foregroundColor(.accentColor)
}
HStack {
Label("Length", systemImage: "clock")
Spacer()
Text("\(scrum.lengthInMinutes) minutes")
}
.accessibilityElement(children: .combine)
HStack {
Label("Theme", systemImage: "paintpalette")
Spacer()
Text(scrum.theme.name)
.padding(4)
.foregroundColor(scrum.theme.accentColor)
.background(scrum.theme.mainColor)
.cornerRadius(4)
}
.accessibilityElement(children: .combine)
}
Section(header: Text("Attendees")) {
ForEach(scrum.attendees) { attendee in
Label(attendee.name, systemImage: "person")
}
}
Section(header: Text("History")) {
if scrum.history.isEmpty {
Label("No meetings yet", systemImage: "calendar.badge.exclamationmark")
}
ForEach(scrum.history) { history in
NavigationLink(destination: HistoryView(history: history)) {
HStack {
Image(systemName: "calendar")
Text(history.date, style: .date)
}
}
}
}
}
.navigationTitle(scrum.title)
.toolbar {
Button("Edit") {
isPresentingEditView = true
data = scrum.data
}
}
.sheet(isPresented: $isPresentingEditView) {
NavigationView {
DetailEditView(data: $data)
.navigationTitle(scrum.title)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") {
isPresentingEditView = false
}
}
ToolbarItem(placement: .confirmationAction) {
Button("Done") {
isPresentingEditView = false
scrum.update(from: data)
}
}
}
}
}
VStack {
NavigationLink(destination: MeetingView(scrum: $scrum), isActive: $starButton) {
Button {
starButton.toggle()
} label: {
Text("Start Meeting")
}
.buttonStyle(.borderedProminent)
.foregroundColor(scrum.theme.accentColor)
.tint(scrum.theme.mainColor)
}
}
.padding()
}
}
struct DetailView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
DetailView(scrum: .constant(DailyScrum.sampleData[0]))
}
}
}
MeetingView (It disappears when the app is inactive)
import SwiftUI
import AVFoundation
struct MeetingView: View {
@Binding var scrum: DailyScrum
@StateObject var scrumTimer = ScrumTimer()
@StateObject var speechRecognizer = SpeechRecognizer()
@State private var isRecording = false
private var player: AVPlayer { AVPlayer.sharedDingPlayer }
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 16)
.fill(scrum.theme.mainColor)
VStack {
MeetingHeaderView(secondsElapsed: scrumTimer.secondsElapsed, secondsRemaining: scrumTimer.secondsRemaining, theme: scrum.theme)
MeetingTimerView(speakers: scrumTimer.speakers, isRecording: isRecording, theme: scrum.theme)
MeetingFooterView(speakers: scrumTimer.speakers, skipAction: scrumTimer.skipSpeaker)
}
}
.padding()
.foregroundColor(scrum.theme.accentColor)
.onAppear {
scrumTimer.reset(lengthInMinutes: scrum.lengthInMinutes, attendees: scrum.attendees)
scrumTimer.speakerChangedAction = {
player.seek(to: .zero)
player.play()
}
speechRecognizer.reset()
speechRecognizer.transcribe()
isRecording = true
scrumTimer.startScrum()
}
.onDisappear {
// scrumTimer.stopScrum()
// speechRecognizer.stopTranscribing()
// isRecording = false
let newHistory = History(attendees: scrum.attendees, lengthInMinutes: scrum.timer.secondsElapsed / 60, transcript: speechRecognizer.transcript)
scrum.history.insert(newHistory, at: 0)
}
.navigationBarTitleDisplayMode(.inline)
}
}
struct MeetingView_Previews: PreviewProvider {
static var previews: some View {
MeetingView(scrum: .constant(DailyScrum.sampleData[0]))
}
}
