Conditional view in SwiftUI doesn't compile - view

I'd like to create a settings form with toggles that reveal more toggles for detail customization if turned on. But the bellow simple code doesn't compile: "Segmentation fault: 11". What is wrong with the code?
import SwiftUI
struct Settings: View {
#State var voiceOver = true
#State var voiceOverStart = true
var body: some View {
NavigationView {
Form {
Section(header: Text("Voice Over")) {
Toggle(isOn: $voiceOver) {
Text("Voice Over")
}
if voiceOver == true {
VStack {
Toggle(isOn: $voiceOverStart) {
Text("Start Bell")
}
}
}
}
}.navigationBarTitle(Text("Settings"))
}
}
}
Update: Commenting out the if statements, the code compiles... But of course without the desired behaviour...
// if voiceOver == true {
VStack {
Toggle(isOn: $voiceOverStart) {
Text("Start Bell")
}
}
// }

Related

SwiftUI: animating tab item addition/removal in tab bar

In my app I add/remove a subview to/from a TabView based on some condition. I'd like to animate tab item addition/removal in tab bar. My experiment (see code below) shows it's not working. I read on the net that TabView support for animation is quite limited and some people rolled their own implementation. But just in case, is it possible to implement it?
import SwiftUI
struct ContentView: View {
#State var showBoth: Bool = false
var body: some View {
TabView {
Button("Test") {
withAnimation {
showBoth.toggle()
}
}
.tabItem {
Label("1", systemImage: "1.circle")
}
if showBoth {
Text("2")
.tabItem {
Label("2", systemImage: "2.circle")
}
.transition(.slide)
}
}
}
}
Note: moving transition() call to the Label passed to tabItem() doesn't work either.
As commented Apple wants the TabBar to stay unchanged throughout the App.
But you can simply implement your own Tabbar with full control:
struct ContentView: View {
#State private var currentTab = "One"
#State var showBoth: Bool = false
var body: some View {
VStack {
TabView(selection: $currentTab) {
// Tab 1.
VStack {
Button("Toggle 2. Tab") {
withAnimation {
showBoth.toggle()
}
}
} .tag("One")
// Tab 2.
VStack {
Text("Two")
} .tag("Two")
}
// custom Tabbar buttons
Divider()
HStack {
OwnTabBarButton("One", imageName: "1.circle")
if showBoth {
OwnTabBarButton("Two", imageName: "2.circle")
.transition(.scale)
}
}
}
}
func OwnTabBarButton(_ label: String, imageName: String) -> some View {
Button {
currentTab = label
} label: {
VStack {
Image(systemName: imageName)
Text(label)
}
}
.padding([.horizontal,.top])
}
}

Calling a sheet from a menu item

I have a macOS app that has to display a small dialog with some information when the user presses the menu item "Info".
I've tried calling doing this with a .sheet but can't get it to display the sheet. Code:
#main
struct The_ThingApp: App {
private let dataModel = DataModel()
#State var showsAlert = false
#State private var isShowingSheet = false
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(self.dataModel)
}
.commands {
CommandMenu("Info") {
Button("Get Info") {
print("getting info")
isShowingSheet.toggle()
}
.sheet(isPresented: $isShowingSheet) {
VStack {
Text("Some stuff to be shown")
.font(.title)
.padding(50)
Button("Dismiss",
action: { isShowingSheet.toggle() })
}
}
}
}
}
}
How would I display a sheet from a menu item?
However, if a sheet is not the way to do it (I think given the simplicity of what I need to show, it would be it), how would you suggest I do it? I tried creating a new view, like I did with the preferences window, but I can't call it either from the menu.
put the sheet directly on ContentView:
#main
struct The_ThingApp: App {
#State private var isShowingSheet = false
var body: some Scene {
WindowGroup {
ContentView()
// here VV
.sheet(isPresented: $isShowingSheet) {
VStack {
Text("Some stuff to be shown")
.font(.title)
.padding(50)
Button("Dismiss",
action: { isShowingSheet.toggle() })
}
}
}
.commands {
CommandMenu("Info") {
Button("Get Info") {
print("getting info")
isShowingSheet.toggle()
}
}
}
}
}

SwiftUI for macOS - trigger sheet .onDismiss problem

In a multiplatform app I'm showing a sheet to collect a small amount of user input. On iOS, when the sheet is dismissed, the relevant .onDismiss method is called but not on macOS.
I've read that having the .onDismiss in the List can cause problems so I've attached it to the button itself with no improvement. I've also tried passing the isPresented binding through and toggling that within the sheet itself to dismiss, but again with no success.
I am employing a NavigationView but removing that makes no difference. The following simplified example demonstrates my problem. Any ideas? Should I even be using a sheet for this purpose on macOS?
I just want to make clear that I have no problem closing the sheet. The other questions I found were regarding problems closing the sheet - I can do that fine.
import SwiftUI
#main
struct SheetTestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
ListView()
}
}
}
The List view.
struct ListView: View {
#State private var isPresented: Bool = false
var body: some View {
VStack {
Text("Patterns").font(.title)
Button(action: {
isPresented = true
}, label: {
Text("Add")
})
.sheet(isPresented: $isPresented, onDismiss: {
doSomethingAfter()
}) {
TestSheetView()
}
List {
Text("Bingo")
Text("Bongo")
Text("Banjo")
}
.onAppear(perform: {
doSomethingBefore()
})
}
}
func doSomethingBefore() {
print("Johnny")
}
func doSomethingAfter() {
print("Cash")
}
}
This is the sheet view.
struct TestSheetView: View {
#Environment(\.presentationMode) var presentationMode
#State private var name = ""
var body: some View {
Form {
TextField("Enter name", text: $name)
.padding()
HStack {
Spacer()
Button("Save") {
presentationMode.wrappedValue.dismiss()
}
Spacer()
}
}
.frame(minWidth: 300, minHeight: 300)
.navigationTitle("Jedward")
}
}
Bad issue.. you are right. OnDismiss is not called. Here is a workaround with Proxybinding
var body: some View {
VStack {
Text("Patterns").font(.title)
Button(action: {
isPresented = true
}, label: {
Text("Add")
})
List {
Text("Bingo")
Text("Bongo")
Text("Banjo")
}
.onAppear(perform: {
doSomethingBefore()
})
}
.sheet(isPresented: Binding<Bool>(
get: {
isPresented
}, set: {
isPresented = $0
if !$0 {
doSomethingAfter()
}
})) {
TestSheetView()
}
}

exc_breakpoint while trying to load view in SwiftUI

I'm trying to make barcode scanner app. I want the scanner to load as the app first launches, like a background. Then when a barcode is scanned, load a view on top displaying the product.
In my ContentsView() I load this View, which starts the scanner and then navigates to FoundItemSheet() when a barcode has been found.
import Foundation
import SwiftUI
import CodeScanner
struct barcodeScannerView: View {
#State var isPresentingScanner = false
#State var scannedCode: String?
#State private var isShowingScanner = false
var body: some View {
NavigationView {
if self.scannedCode != nil {
NavigationLink("Next page", destination: FoundItemSheet(scannedCode: scannedCode!), isActive: .constant(true)).hidden()
}
}
.onAppear(perform: {
self.isPresentingScanner = true
})
.sheet(isPresented: $isShowingScanner) {
self.scannerSheet
}
}
var scannerSheet : some View {
CodeScannerView(
codeTypes: [.qr],
completion: { result in
if case let .success(code) = result {
self.scannedCode = code
self.isPresentingScanner = false
}
}
)
}
}
When navigation view is replaced with a button, like this:
VStack(spacing: 10) {
if self.scannedCode != nil {
NavigationLink("Next page", destination: FoundItemSheet(scannedCode: scannedCode!), isActive: .constant(true)).hidden()
}
Button("Scan Code") {
self.isPresentingScanner = true
}
.sheet(isPresented: $isPresentingScanner) {
self.scannerSheet
}
Text("Scan a QR code to begin")
It works with this solution, but I want the scanner to show when the app loads, not on a button press. I tried replacing the button with .onAppear with the same contents as the button, but it doesn't work.
This is foundItemSheet()
struct FoundItemSheet: View {
#State private var bottomSheetShown = false
#State var scannedCode: String?
var body: some View {
GeometryReader { geometry in
BottomSheetView(
scannedCode: self.$scannedCode,
isOpen: self.$bottomSheetShown,
maxHeight: geometry.size.height * 0.7
) {
Color.blue
}
}.edgesIgnoringSafeArea(.all)
}
}
struct FoundItemSheet_Previews: PreviewProvider {
static var previews: some View {
FoundItemSheet()
}
}
I'm getting exc_breakpoint I believe where CodeScanner is declared.
I've been stuck on this for hours, so I'll reply quick to any questions.
Couple of thoughts..
1) change order of modifiers
.sheet(isPresented: $isShowingScanner) {
self.scannerSheet
}
.onAppear(perform: {
self.isPresentingScanner = true
})
2) make delayed sheet activation (as view hierarchy might not ready for that custom scanner view)
.onAppear(perform: {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5)
self.isPresentingScanner = true
}
})

SwiftuUI NavigationLink inside touchBar

I'm trying to create NavigationLink in MacBook touchBar with help of SwiftUI. Actually with my piece of code, the button is shown in touchbar, but unfortunately the link doesn't work.
NavigationView {
.touchBar {
NavigationLink(destination: BookView()) {
Text("GoToBook")
}
}
}
struct BookView: View {
var body: some View {
Text("Hello")
}
}
Try instead with Button in touchBar activating NavigationLink programmatically, like below
#State private var isActive = false
...
// below in body
NavigationView {
SomeView() // << your view here
.background(NavigationLink(destination: BookView(), isActive: $isActive) {
EmptyView()
} // hidden link
)
.touchBar {
Button("GoToBook") { self.isActive.toggle() } // activate link
}
}

Resources