Sorry for the cryptic title.
I would like to have part of a row inside a list of rows to to animate downwards without moving the views vertically above it.
Here is a minimal example, showing what I mean:
import SwiftUI
struct ContentView: View {
#State var display = false
var body: some View {
List {
Button("Toggle") {
display.toggle()
}
Text("Text")
row
Text("Text")
}
.listStyle(.plain)
.animation(.linear, value: display)
}
var row: some View {
VStack {
Text("Title").bold()
if display {
Text("Lorem")
Text("ipsum")
Text("dolor")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.previewDevice(.init(rawValue: "iPhone 13 mini"))
}
}
The lorem ipsum block is supposed to animate into view without moving the Title text.
I imagine there's no way around GeometryReader here, but wanted to make sure. All suggestions are much appreciated 🙏
I want to add the possibility to select items in a list when edit mode is selected, additionally to the delete and move option. Ideally I want to use the existing edit, delete and move buttons instead of writing my own. I tried the example from the documentation. It's not working for me. The value of editMode is always .inactive. I'm using XCode 14. The deployment target of my app is iOS 16.0.
This is my source code:
import SwiftUI
struct ContentView: View {
#Environment(\.editMode)
private var editMode
#State
private var name = "Maria Ruiz"
var body: some View {
NavigationView {
Form {
if editMode?.wrappedValue.isEditing == true {
TextField("Name", text: $name)
} else {
Text("test")
}
}
.animation(nil, value: editMode?.wrappedValue)
.toolbar { // Assumes embedding this view in a NavigationView.
EditButton()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
It always shows the test text. I also tried a variant with the .onChange modifier, with the same result.
Forwarding the fix from https://developer.apple.com/forums/thread/716434:
Try extracting the parts that access the editMode property from the
container that changes based on it, like List/Form.
struct ContentView: View {
var body: some View {
NavigationView {
Form {
MyForm()
}
.toolbar { // Assumes embedding this view in a NavigationView.
EditButton()
}
}
}
}
and
struct MyForm: View {
#Environment(\.editMode)
private var editMode
#State
private var name = "Maria Ruiz"
var body: some View {
Text(String(editMode!.wrappedValue.isEditing))
if editMode?.wrappedValue.isEditing == true {
TextField("Name", text: $name)
} else {
Text("test")
}
}
}
hi am having issues with the picker view in swiftui
i have created one file with just a class like this
import Foundation
import SwiftUI
class something: ObservableObject {
#Published var sel = 0
}
and then I created 2 views
import SwiftUI
struct ContentView: View {
#StateObject var hihi: something
var characters = ["Makima", "Ryuk", "Itachi", "Gojou", "Goku", "Eren", "Levi", "Jiraya", "Ichigo", "Sukuna"]
var body: some View {
VStack {
Section{
Picker("Please choose a character", selection: $hihi.sel) {
ForEach(characters, id: \.self) { name in
Text(name)
}
}
Text(characters[hihi.sel])
}
now(hihi: something())
}
}
}
struct now: View {
#StateObject var hihi: something
var body: some View {
Text("\(hihi.sel)")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(hihi: something())
}
}
now the problem am facing is that the code compiles but the picker ain't working it won't change to any other value in the array I have provided it recoils back to its original value provided that is 0th index "Makima" and it won't select any other option, why so?
please help
There are three problems, the main one being the mismatching selection.
In the Picker, your selection is based on the string value for each character. This is because the ForEach identifies each Text by the name string, since you used id: \.self.
However, your something model (which ideally should start with a capital letter by convention) has a numeric selection. Because the items in the Picker have String IDs, and this is an Int, the selection can't be set.
You can change your model to this:
class something: ObservableObject {
#Published var sel = "Makima"
}
Which also requires a slight change in the body:
VStack {
Section{
Picker("Please choose a character", selection: $hihi.sel) {
ForEach(characters, id: \.self) { name in
Text(name)
}
}
Text(hihi.sel) // Now getting string directly
}
now(hihi: something())
}
Notice we now have two views showing the selected character - but only the top one updates. The bottom one may now be redundant (the now view), but I'll show you how you can get it working anyway. This is where we encounter the 2nd problem:
You are creating a new instance of something() when passing it to now (again, should start with a capital). This means that the current instance of hihi stored in ContentView is not passed along. You are just creating a new instance of something, which uses the default value. This is completely independent from the hihi instance.
Replace:
now(hihi: something())
With:
now(hihi: hihi)
The final problem, which may not be as visible, is that you shouldn't be using #StateObject in now, since it doesn't own the object/data. Instead, the object is passed in, so you should use #ObservedObject instead. Although the example now works even without this change, you will have issues later on when trying to change the object within the now view.
Replace #StateObject in now with #ObservedObject.
Full answer (something is initialized in ContentView only for convenience of testing):
struct ContentView: View {
#StateObject var hihi: something = something()
var characters = ["Makima", "Ryuk", "Itachi", "Gojou", "Goku", "Eren", "Levi", "Jiraya", "Ichigo", "Sukuna"]
var body: some View {
VStack {
Section{
Picker("Please choose a character", selection: $hihi.sel) {
ForEach(characters, id: \.self) { name in
Text(name)
}
}
Text(hihi.sel)
}
now(hihi: hihi)
}
}
}
struct now: View {
#ObservedObject var hihi: something
var body: some View {
Text(hihi.sel)
}
}
class something: ObservableObject {
#Published var sel = "Makima"
}
Swiftui: I can show the data from db in list ,but it shown nothing in picker, how can I fix it
here is my ContentView:
import SwiftUI
struct ContentView: View {
#ObservedObject var model = PostListViewModel()
#State private var selectedStrength = 0
var body: some View {
// List(model.posts) { post in
// Text(post.name)
// }
Picker(selection: $selectedStrength, label: Text("picker")) {
ForEach(model.posts) { post in
Text(post.name)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
try this or something similar:
ForEach(model.posts, id: \.self) { post in
Text(post.name)
}
Consider the following code:
import SwiftUI
class ViewModel: ObservableObject {
}
struct TestView: View {
#ObservedObject var vm = ViewModel()
var body: some View {
// self.sample
GeometryReader { _ in
self.sample
}
}
var sample: some View {
Text("Hello, World!")
}
}
struct Tabs : View {
#State var selection: Int = 0
var body: some View {
TabView(selection: $selection) {
TestView().tabItem {
Text("First Tab")
}
.tag(0)
Text(String(selection))
.tabItem {
Text("Second Tab")
}
.tag(1)
}
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
There are two tabs and selection is referenced in body therefore body will be called when selection is changed.
TestView is using GeometryReader.
When I switch from "First Tab" to "Second Tab" ViewModel is created again and never dereferenced. This is unexpected.
If I switch 100 times I will have 100 ViewModels referenced from SwiftUI internals.
Though if i remove GeometryReader it works as expected.
Did someone experience it? Are there any workarounds?
I simply want this ViewModel lifetime to be bound to TestView lifetime.
UPDATE:
XCode 11.3.1 iOS 13.3
Ok, let's make the following changes in ViewModel
class ViewModel: ObservableObject {
init() {
print(">> inited") // you can put breakpoint here in Debug Preview
}
}
so now it seen that because View is value type
struct TestView: View {
#ObservedObject var vm = ViewModel() // << new instance on each creation
...
and it is originated from
var body: some View {
TabView(selection: $selection) {
TestView().tabItem { // << created on each tab switch
...
so, the solution would be to ViewModel creation out of TestView and inject outer instance either via .environmentObject or via constructor arguments.
Btw, it does not depend on GeometryReader. Tested with Xcode 11.2.1 / iOS 13.2