The WWDC 2020 SwiftUI talks made mention of a new ".toolbar" modifier. Toolbars vary depending on the device being used and the toolbar modifier should create the proper type of toolbar for the device. Moreover, toolbars vary depending on how a view gets displayed - i.e. did you navigate to the scene or was the scene modally presented?
In the playground code, below, there are two View structs. The first is called MainToolbarView
and gets displayed (I believe) as if the playground had navigated to it. The second is called PresentedView
and is presented modally from MainToolbarView
.
In the MainToolbarView the toolbar has positional placement for its Save
and Cancel
buttons (.navigationBarLeading
and .navigationBarTrailing
). This works fine. Tapping either button causes a text field to display either "Save" or "Cancel".
When the Present Sheet
button is tapped the second view is modally presented. There is a problem in toolbar for the presented sheet. The toolbar is using semantic placement for its Confirm
and Deny
buttons (.confirmationAction
and .cancellationAction
). However, only the Confirm
button is displayed.
Can anyone tell me why both buttons are shown on the MainToolbarView
but only one button is on the PresentedView
toolbar? I've tried changing the order of the buttons, making the two buttons into an HStack, or even trying to use positional placement. None of those options have worked for me.
import SwiftUI
import PlaygroundSupport
struct MainToolbarView: View {
@State private var buttonIdentifier = "initial state"
@State private var showSheet = false
var body: some View {
NavigationView {
VStack {
Spacer()
Text( $buttonIdentifier.wrappedValue )
Spacer()
Button( "Present Sheet" )
{ showSheet = true }
Spacer()
}
.toolbar {
ToolbarItem( placement: .navigationBarLeading )
{ Button( "Save", action: reportSave ) }
ToolbarItem( placement: .navigationBarTrailing )
{ Button( "Cancel", action: reportCancel ) }
}
.navigationBarTitle( "MAIN" )
}
.sheet(isPresented: $showSheet )
{ PresentedView(buttonIdentifier: $buttonIdentifier ) }
}
private func reportSave()
{
buttonIdentifier = "save"
}
private func reportCancel()
{
buttonIdentifier = "cancel"
}
}
struct PresentedView: View {
// MARK: API
@Binding var buttonIdentifier: String
// MARK: instance variables
@Environment( \.presentationMode ) var mode
//.confirmationAction
var body: some View {
NavigationView {
Text( "Tap action buttons" )
.toolbar {
ToolbarItem( placement: .confirmationAction )
{ Button( "Confirm", action: reportConfirm ) }
ToolbarItem( placement: .cancellationAction )
{ Button( "Deny", action: reportDenied ) }
}
.navigationBarTitle( "SHEET" )
}
}
private func reportDenied() {
buttonIdentifier = "sheet denied"
self.mode.wrappedValue.dismiss()
}
private func reportConfirm() {
buttonIdentifier = "sheet confirmed"
self.mode.wrappedValue.dismiss()
}
}
PlaygroundPage.current.setLiveView( MainToolbarView() )
`
I think the trick (and for me is a bug) is using the
.navigationBarBackButtonHidden(true)
modifier. After using it (add it below.navigationBarTitle( "SHEET" ))
, I see both buttons.