I have a list of messages that are stored in an array and then displayed to the screen via a loop, which is within a ScrollView. What i want is when the view is loaded the scroll view is scrolled all the way to the bottom, which i attempted to do with the scrollTo function, however the scroll view stayed in the same position no matter what value i passed into it. Can someone help point me in the right direction on how to do this.
struct MessageList2: View {
#Binding var messages: [String]
var body: some View {
ScrollViewReader { value in
ScrollView() {
VStack(alignment: .leading) {
ForEach(0..<messages.count, id: \.self) { index in
Text(messages[index])
}
}
.onAppear{
value.scrollTo(messages.count)
}
}
}
}
}
Note the 'messages array receives value from another function and works just fine
I tried following examples online that showed how to use the ScrollView and scrollTo() and tried to implement it myself however the view stayed the same. Im assuming that im using the function wrong.
I also tried adding a .id(index) under Text() but that didnt seem to work.
This is an off-by-one error. Note the id's you are assigning to each items are the same the index of the elements. The max index that can appear in messages is messages.count - 1, which means the max possible id is messages.count - 1.
So instead of scrollTo(messages.count), you probably meant to scrollTo(messages.count - 1).
Related
I have an array of objects (employees) that I am displaying in a navigationview. Each object has a boolean property called "Active". If the employee is active I want the navigationlink for that list item to work as it normally would but if the employee is not active then I want that list item to be disabled so that the navigationlink or any swipe actions do not work. This is my code:
NavigationView {
List {
CustomSearchBar(searchText: $searchText, searchCategory: $searchCategory)
ForEach(Employees) { person in
ZStack {
NavigationLink(destination: DisplayDetails().environmentObject(person)) {
ListItem().environmentObject(person)
}
}
.swipeActions(edge: .leading, content: {
Button {
Employees.toggleArchiveFlag(for: person.id)
} label: {
Label("Archive", systemImage: !person.archived ? "square.and.arrow.down" : "trash")
}
.tint(!person.archived ? .blue : .red)
})
.disabled(!person.active)
}
}
.navigationTitle("Current Employees")
.padding(.horizontal,-15) // remove the padding that the list adds to the screen
}
What ends up happing is that when the view initially loads everything is enabled regardless of each employee's active status. But if I click any of the navigationlinks to load the "DisplayDetails" detailed view and then return back to the main navigationview OR if I click on any of the searchbar toggles or use the searchbar to filter my list of people then the view updates and the correct people are disabled.
It is almost as if the statement ".disabled(!person.active)" is being called too late. If that is the case then where should I be calling it? I have tried moving that statement in the following places:
The closing bracket of the Zstack. But this does nothing
Right below the "ListItem().environmentObject(person)" statement but this still shows the same behavior as mentioned earlier and when the navigationlink is eventually disabled then the swipeactions are still enabled.
Any help at all would be appreciated!
Figured out that the issue was with the logic that set the person.active boolean value not with the presentation of the navigation view items. Thanks.
If you create a new project on Xcode 12.5 and use a SwiftUI List combined to a foreach and .onmove, you will notice 2 annoying things:
1/ Even though ondelete is unset, and even though you explicitly specify .deleteDisabled(true), a space appears on the left side of the list row when in EditMode. Is it possible to get rid of it?
2/ The entrance of the move icon creates some kind of an animation glitch on each list row. The divider is highlighting that. Why is there a space after the divider when EditMode is triggered? It seems that back in the past the divider width stay unchanged during this transition.
If you ask me, the previous behavior was making me think that the move button was embedded in a ZStack wrapping both the move button and the list. Whereas now it looks like it's in an HStack. Whatever, none of that explains the glitch which doesn't appear on the left side of the row even though it gets pushed too.
I am so confused.
I am surprised I am not able to find anything about that issue via Google. I am the one doing something wrong?
A video of the problem: Glitch and here is a simple code to try it out yourself.
Thank you for your time.
struct ContentView: View {
#State var listItems = ["Item 1", "Item 2", "Item 3"]
var body: some View {
NavigationView {
List {
ForEach(listItems, id: \.self) { (item) in
Text(item)
}.onMove { (indexSet, index) in
self.listItems.move(fromOffsets: indexSet,
toOffset: index)
}
}
.navigationBarTitle(Text("Nav Title"))
.deleteDisabled(true)
.navigationBarItems(trailing: EditButton())
}
}
}
I am using Core data and Swiftui and everything have been working fine but in this one view I have a List, ForEach that is not working.
So for testing purpose my code currently look like this:
#ObservedObject var viewModel = NewLearningWhyViewModel()
VStack {
ForEach(viewModel.whys, id: \.self) { why in
Text(why.why)
}
List {
ForEach(viewModel.whys, id: \.self) { why in
Text(why.why)
}
}
Button(action: {
viewModel.createWhy(why: "Test", count: viewModel.whys.count, learning: learn)
viewModel.fetchWhy(predicate: NSPredicate(format: "parentLearning == %#", learn))
}){
Text("Add")
.font(.title)
.foregroundColor(.white)
}
}
The problem is my List { ForEach, first time I press add button it shows the new why, second time i press the button the whole list goes away, the third time I press the button the list shows again with all 3 items.
To test the problem I added that first ForEach part and that shows the correct item at all times, so there is not a problem with the viewmodel or adding the items, the items are added and it is published from the viewmodel since that part is updated.
Does anyone have any clue why my List { ForEach only show every other time?
I have gotten this problem. I figure it out by adding objectWillChange in ViewModel, and send() it manually when your why is changed. Actually I don't know your NewLearningWhyViewModel clearly, so this is just an example, you should try it out.
class NewLearningWhyViewModel: ObservableObject {
let objectWillChange: ObservableObjectPublisher = ObservableObjectPublisher()
#Published var whys: Why = Why() {
didSet {
objectWillChange.send()
}
}
}
Ok the post from Becky Hansmeyer solved it, adding .id(UUID()) to the list solved it and it started working correctly...
because of "viewModel.whys" is set of classes.
SwiftUI does not work with classes directly.
There is 2 solutions:
make it struct instead of class + add #Published modifier inside of view
leave it as is + do it observable object and in init of your view assign into observed object.
More details here:
https://stackoverflow.com/a/62919526/4423545
This is my example that I am trying to get to work:
struct ContentView: View {
let links = ["Item 1", "Item 2", "Item 3", "Item 4"]
var body: some View {
NavigationView {
ScrollView {
Text("My Title")
List(links, id: \.self) {
link in
NavigationLink(destination: TestView()) {
Text(link)
.padding(.vertical, 4)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
.frame(height: 178)
Text("Some more content here")
}
}
}
}
Note: TestView is just some view with the text hello world on it.
I am trying to copy Apple Music's style of navigation. I tried putting a Button in the NavigationLink but tapping it on the text wouldn't change views, and I couldn't find a way to reliably change the color of the row when tapped, at the same time. Also in some approach, I managed to make it work, but the way the colors animate is different, i.e. it fades from A to B, over ~100ms whereas what I'm trying to achieve is to animate between the states instantly (like in Apple Music).
My current approach is using a List, putting NavigationLinks inside it and then cutting off the whole view by giving it a height. This way I can put it alongside other content.
It's working fine for now, but whenever I click on an row and go back, the row is still highlighted, when it shouldn't. Is there a way to make it so that it deselects when going back to the screen somehow?
I think this bug is being caused by the List being inside a ScrollView, since when I removed ScrollView, the list worked properly, and there wasn't this highlight bug. But I need to be able to put my content with the list, and I don't intend to have a list take up the whole screen.
Is there any way to fix this bug with this approach? I'm also willing for other ways to achieve the same result without using List.
Trying to use ForEach instead ofList?
With a view for row (CustomRow) where you can pass link item and set custom dividing line, background etc ...
ForEach(links, id: \.self) { link in
NavigationLink(destination: TestView()) {
CustomRow(item: link)
}
}
.frame(height: 178)
I have reduced my problem to the following views which are embedded in two WKHostingControllers paging between each other.
struct FirstContentView: View {
var body: some View {
Text("FirstContentView")
.contextMenu {
Text("FirstContextMenu")
}
}
}
struct SecondContentView: View {
var body: some View {
List {
Text("SecondContentView")
}.contextMenu {
Text("SecondContextMenu")
}
}
}
When I run it this happens. As soon as page 2 is loaded the context menu of page 1 remains replaced by the one of page 2. This issue only happens if a List view is anywhere in the body of SecondContentView, even if the .contextMenu is not attached to it or the List is encapsulated into another View. Replacing List for example with Group works, but I need the list behaviour for my items.
Has anybody also run into this issue? How can I fix it so the context menu works as intended (?) on both pages?