generic param Label cannot be inferred in SwiftUI - swiftui

enter image description hereI am new to SwiftUI. When I embed a toggle switch inside Vstack, it says "generic param Label cannot be inferred" in SwiftUI. May I know the cause for this error and how to fix it.Thank you

Looks like you might have missed specifying the label or have an error there. I have created the below code that has a toggle button inside VStack.
import SwiftUI
struct ContentView: View {
#State private var isOn: Bool = false
var body: some View {
VStack (alignment: .leading, spacing: 10){
Toggle(isOn: $isOn){
Text("Toggle me to set values")
}
Text("Toggle Value: \(isOn.description)")
Spacer()
}.padding()
}
}
Let me know if this helps.
Toggle Screenshot

I have embedded a text, Two toggles and stepper and a button in Vstack and here I have missed to provide action to that button, which resulted in the issue "generic param Label cannot be inferred".previous Code with issue. After adding the button action

Related

Swiftui: How to Close one Tab in a TabView?

I'm learning SwiftUI and having trouble with closing Each Tab View Element.
My App shows photos from user's album by TabView with pageViewStyle one by one.
And What I want to make is user can click save button in each view, and when button is clicked, save that photo and close only that view while other photos are still displayed. So unless all photos are saved or discarded, if user clicks save button, TabView should automatically move to another one.
However, I don't know how to close only one Tab Element. I've tried to use dismiss() and dynamically changing vm.images element. Latter one actually works, but it displays awkward movement and it also requires quite messy code. How could I solve this issue?
Here is my code.
TabView {
ForEach(vm.images, id: \.self) { image in
TestView(image: image)
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
struct TestView: View {
#ObservedObject var vm: TestviewModel
...
var body: some View {
VStack(spacing: 10) {
Image(...)
Spacer()
Button {
...
} label: {
Text("Save")
}
}
You need actually to remove saved image from the viewModel container, and UI will be updated automatically
literally
Button {
vm.images.removeAll { $0.id == image.id } // << here !!
} label: {
Text("Save")
}
You need to use the selection initializer of TabView in order to control what it displays. So replace TabView with:
TabView(selection: $selection)
Than add a new property: #State var selection: YourIdType = someDefaultValue, and in the Button action you set selection to whatever you want to display.
Also add .tag(TheIdTheViewWillUse) remember that whatever Id you use must be the same as your selection variable. I recommend you use Int for the simple use.

SwiftUI: Update height of an existing row in list

I'm new to programming in SwiftUI. I created a list of a custom view that expands on tap. However, whenever I tap the custom view, the height of the list row doesn't update. Here's what my code looks like:
List(){
ForEach(ingredients){ ingredient in
IngredientList(ingredient: ingredient)
}
}
struct IngredientList: View {
let ingredient: Ingredient
#State private var showBody = true
var body: some View {
Hstack{
VStack(alignment: .leading){
Text("Text 1")
if(showBody){
Divider()
Text("Text 2")
.font(.caption)
}
}
}
.contentShape(Rectangle())
.onTapGesture {
showBody.toggle()
}
}
}
I think it has something to do with state of the list but I can't seem to figure it out.
I found an ugly but working solution. On the expanded item change, check whether it is a selection mode, remember the current data set, clear it, and set it back with DispatchQueue.main.async. It works on iOS 15.4.1. I hope Apple will fix the issue in the subsequent releases.
Update: try introspect. You will be able to retrieve the underlying table view and call UITableView.reloadData() on the expanded row change

Swipe back when using a custom navigation bar and a TabView SwiftUI

I have a SwiftUI app which uses a custom navigation bar. Because of that, I need to handle the back navigation separately (both the back button and the swipe gesture). Everything went fine up until now, when I need to use a TabView to swipe between pages. The code below illustrates what I'm trying to achieve:
ContentView.swift
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination: SecondView()) {
Text("Go to second view")
}
}
}
}
SecondView.swift
import SwiftUI
// Being able to go back by swiping
// https://stackoverflow.com/questions/59921239/hide-navigation-bar-without-losing-swipe-back-gesture-in-swiftui
extension UINavigationController: UIGestureRecognizerDelegate {
override open func viewDidLoad() {
super.viewDidLoad()
interactivePopGestureRecognizer?.delegate = self
}
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return viewControllers.count > 1
}
}
struct SecondView: View {
#Environment(\.presentationMode) var presentationMode
var body: some View {
VStack(alignment: .leading) {
Image(systemName: "chevron.left")
.foregroundColor(Color(.systemBlue))
.font(.title2)
.onTapGesture {
self.presentationMode.wrappedValue.dismiss()
}
.padding()
TabView {
Text("Test 1")
.tag(1)
Text("Test 2")
.tag(1)
Text("Test 3")
.tag(3)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
.background(Color(.systemGray6))
.navigationBarHidden(true)
}
}
Note that I didn't add the custom navigation bar for the second view, I've just hidden the default navigation bar, as the custom bar is not needed to solve this problem.
When the user is inside the SecondView and presses the back button, everything works as expected. The problem appears when he tries to swipe back, as the swipe back gesture is captured by the TabView. I want to keep the 'swipe-between-pages' functionality of the TabView while being able to go back to ContentView when the user swipes right from the leftmost part of the screen.
This problem only appears when using TabViews, other types of content handle the swipe back gesture without problems.
To solve this problem, I could add a horizontal padding to the TabView like this:
TabView {
// content
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.padding(.horizontal)
and then the user would have some space to swipe back, but this solution is not so good when some views inside the TabView need to take the whole width of the screen.
Is there any way to handle the swipe back gesture in this particular case? Maybe another possible solution would be customizing the TabView to ignore the drag gesture when the first view is presented and a swipe right gesture is captured (I don't know how to implement that).
I ran into this same problem. I solved it by wrapping my first tabview in with a geometryReader. When the bounds of that view is more than a quarter off the screen, I dismiss the view.

NavigationLink doesn't fire after FullScreenCover is dismissed

I have a button in a view (inside a NavigationView) that opens a full screen cover - a loading screen while some data is processing. When the cover is dismissed, I want to automatically route to the next view programmatically. I'm using a NavigationLink with a tag and selection binding, and the binding value updates when the cover is dismissed, but the routing doesn't happen unless I tap that same "open modal" button again.
import SwiftUI
struct OpenerView: View {
#EnvironmentObject var viewModel: OpenerViewModel
#State private var selection: Int? = nil
#State private var presentLoadingScreen = false
var body: some View {
VStack {
NavigationLink(destination: SecondScreen(), tag: 1, selection: $selection) { EmptyView() }
Button(action: {
viewModel.frequency = 0
self.presentLoadingScreen.toggle()
}, label: {
Text("Open cover")
}).buttonStyle(PlainButtonStyle())
}
.navigationBarTitle("Nav title", displayMode: .inline)
.fullScreenCover(isPresented: $presentLoadingScreen, onDismiss: {
self.selection = 1
}, content: ModalView.init)
}
}
struct ModalView: View {
#Environment(\.presentationMode) var presentationMode
var body: some View {
Text("Howdy")
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
presentationMode.wrappedValue.dismiss()
}
}
}
}
The first time I hit the Button, the cover opens. Inside the cover is just a DispatchQueue.main.asyncAfter which dismisses it after 2 seconds. When it's dismissed, the onDismiss fires, but then I have to hit the button again to route to SecondScreen.
Any ideas?
Edit: Added the modal's View
I got it working with some changes to the code, and I'm sharing here along with what I think is happening.
I believe the problem is a race condition using a #State boolean to toggle the cover and the navigation. When the cover is being dismissed, my main OpenerView is being recreated - to be expected with state changes. Because of this, I try to set the #State var selection to trigger the navigation change, but before it can do so, the view is recreated with selection = nil.
There seem to be two ways to solve it in my case:
Move the cover boolean to my view model - this worked, but I didn't want it there because it only applied to this view and it's a shared view model for this user flow. Plus, when the modal is dismissed, you see the current OpenerView for a brief flash and then get routed to the SecondScreen.
Keep the cover boolean in #State, but trigger the navigation change in the button immediately after setting the boolean to open the modal. This worked better for my use case because the modal opens, and when it closes, the user is already on the next screen.
I had a similar problem where I was trying to draw a view after dismissing a fullScreenCover. I kept getting an error that said that the view had been deallocated since it was trying to draw to the fullScreenCover.
I used Joe's hints above to make this work. Specifically:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
viewToShow()
}
I had previously tried onChange, onDisappear, onAppear - but none of those fit the use case I needed.

Rename 'Cancel' on modal sheet in SwiftUI

The Apple documentation gives some user interface design tips on modal sheets including renaming Cancel to something like Dismiss where relevant. But there's no guide whether this is possible in SwiftUI.
There's a link that takes you to setTitle for Watchkit: https://developer.apple.com/documentation/watchkit/wkinterfacecontroller/1619570-settitle
But is there something equivalent for a SwiftUI Watch app?
Adding a ToolbarItem with the .cancellationAction placement works:
struct MySheet: View {
#Environment(\.dismiss) private var dismiss
var body: some View {
Text("The Content Of Sheet")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Custom") {
dismiss()
}
}
}
}
}
The result looks like this: