Multiple ui elements break WidgetExtension - swift

I'm trying to dynamically build a view using SwiftUI for my widget. However, when multiple views are stacked, the widget no longer works.
I want to loop my Widget over an array of fetched data, like so:
VStack {
ForEach(entry.layers.indices) { index in
let layer = entry.layers[index]
Text(layer)
}
}
but this doesnt work properly when the forEach loops more than about 5 times (it works fine when only looping once or twice!). I contacted Apple Code Level Support about this and they told me this is a 'bug'. See the example project I provided: https://github.com/swifty-on-me/Widget-Example
This is the data I'm trying to render: https://github.com/swifty-on-me/Widget-Example/blob/main/Data/FakeData.swift
I tried something like converting a view to image but the solution doesn't work in Extensions.
So I found this 'workaround' on the Apple Developer Forums but I'm unsure how to implement it:
https://developer.apple.com/forums/thread/665935
Could someone explain or guide me to this workaround? (I'm not so at home with Swift(UI))
I've been messing around with this for ages now and I think I'm going insane about what seems like such a simple task.
See this example (about 28 seconds)

It looks like you are going against how SwiftUI works. You should not have code inside a view declaration. I'm surprised that code even compiled.
Here's where the problem appears to be:
GeometryReader { geo in
ZStack(alignment: .topLeading) {
Color.init("#bf1b49")
VStack {
ForEach(entry.layers.indices) { index in
let layer = entry.layers[index] // < Here
Text(layer)
}
}
}
}
I'm not entirely sure what you are trying to do here, but you don't need two lines of code to do that. You could probably try moving the entry.layers[index] into the Text, because you don't need to store a variable for that. Like this:
GeometryReader { geo in
ZStack(alignment: .topLeading) {
Color.init("#bf1b49")
VStack {
ForEach(entry.layers.indices) { index in
Text(entry.layers[index])
}
}
}
}
Notice how the let layer is gone and the code that makes up layer is now within the text directly. Hopefully this helps!

I too have the same problem. I try to show month view of a calendar in widget (with additional info from my app). I use ForEach and many VStack and HStack. The widget is very unreliable to load. I still try to figure out what causes this issue but I found out one thing… The widget does load successfully when I test it from around 2PM - midnight my local time. (Both when launching app from Xcode and from TestFlight) I know this is weird but it is what happens to me right now.
And yes removing some elements from widget makes widget loads successfully.
Thank you
Boon

Related

How to grab a path for app.navigationBars

I faced an issue that is combined with UI testing of the view.
I have an issue with an incorrect path to the element that is the text field, which is Search. I would like to achieve the state when the code for app.navigationBars will be resistant for different languages. That's why right now it looks the following way:
var airportsSearchTextField: XCUIElement { app.navigationBars["Airports"].searchFields["Search"] }
This won't work properly for another language like English. I know that it could be possible to add accessibilityIdentifier, but I'm not sure where and maybe for navigationBar is there any other way to implement a better solution to grab this element within UI tests?
func selectFlight() { airportsSearchTextField.tap() }

Issue with NavigationBarTitle when scrolling List with TextFields inside of it

I have a simple checklist app that uses a SwiftUI List view to display sections with list items inside of it.
Because I want the list items to be editable, each list row has a TextField inside of it with the item name in it. However, if I focus into a text field and scroll the list before hitting the Return Key, the large navigationBarTitle has a bug where it remains frozen in place. For it to work properly, I have to back out of the page and come back.
I'm not seeing any errors, so I'm not sure why this occurring. I have a suspicion using a wrapped UITextField might solve this, but I'm not sure.
I was able to fix my own issue while trying to create a reproducible example.
The bug was the result of a background color I was applying to the parent Group that contained the List. Removing the background color fixed my issue!
struct ChecklistView: View {
var body: some View {
Group {
if empty {
EmptyView()
} else {
ListView()
}
}
.background(Color(.tertiarySystemBackground).edgesIgnoringSafeArea(.all)) // removing this line fixed my issue
.navigationBarTitle("My title")
}
}

SwiftUI List updating slowly

I have a SwiftUI List, which has thousands of items. Each item is as such:
class Item: Codable, Identifiable {
var id = UUID()
var prop1 = ""
var prop2 = ""
}
Now I do this:
List(store.items.indices, id: \.self) { index in
DetailView(item: self.$store.items[index])
}.id(UUID())
I iterate the indicies because I have to pass a Binding to the DetailView.
Now the DetailView has TextFields attached to the Item's properties.
When DetailView changes an Item's properties, it causes the List to refresh ALL of the items, which I dont want.
Also, it's still important that when one of those properties changes, the main view is updated, because on macOS, the main view is still visible even when you click on one of the items.
My question is how to make it so that whenever an Item passed in from the List changes, only update that row?
I also tried a solution mentioned where I use a LazyVStack, and it fixes the performance issue, but I use the List inside of a NavigationView, and the LazyVStack doesn't seem to support that.
Any suggestions are greatly appreciated.
Remove .id(UUID()) from the List.
By default, List diffs changes and only updates the affected rows. For very large data sets, that .id(UUID()) trick is used to force the whole List to reload when something changes, which may be faster than trying to diff the large dataset. It doesn’t sound like you need it, so it’s actually worse to include it unnecessarily.
Edit: I don’t think binding to the array by index is the problem, but it is unusual and may bite you eventually.
You could use a LazyVStack in a ScrollView as long as you don't need List specific functionality. Lists aren't as efficient as LazyVStacks yet (Apple bug).
ScrollView {
LazyVStack {
ForEach(store.items.indices) { index in
DetailView(item: self.$store.items[index])
}
}
}

Swiftui list padding

I have a list of list items in swiftui - and all I want to do is get rid of the list padding. I've found a large number of stack overflow posts covering this. There are many solutions identified. I've tried them all - particularly :
List( data.treeLines ) {
item in
TreeLineView( line: item, selectedId: $selectedItem )
.listRowInsets(EdgeInsets()).background(Color.yellow)
})
Some people say that doesn't work because you need foreach - I've tried
List() {
ForEach( data.treeLines ) {
item in
TreeLineView( line: item, selectedId: $selectedItem ).listRowInsets(EdgeInsets()).background(Color.yellow)
}.listRowInsets(EdgeInsets()).background(Color.green)
}
I've tried .none instead of edge insets.. I've tried EdgeInsets( row:0, top:0, leading:0:, trailing:0) - I've tried -20 leading and trailing. I've tried putting the directive on the list itself. I've even tried .listStyle - planliststyle did nothing and the other one I found just gave me an error saying it was only available on iOS.
In every case, the list looks like:
where I want it to go from edge to edge (and in fact, I also want to get rid of the spacing in between the list items, but that's extra credit)
the one way I did manage to get a change is by doing the following on the list itself:
.padding(.horizontal, -40)
which does actually get rid of some of the padding, but also starts putting some of my actual content underneath the border, and still seems to have only gotten rid of half of the padding. Making that number bigger just shifts my content worse, it doesn't get rid of any more border - it looks like this:
so basically - I've read everything I can find, tried every solution, and am now at a bit of a loss :(
note - this is a MacOS app running under macOS 11 - not an IOS app.

Make SwiftUI Form wrap contents properly

Swift 5.1
There's a View that contains a Form. When I put that View in any other View, the Form takes up way more space than it needs. For example:
VStack {
Form {
Text("Form")
}
Text("Not form")
Text("Also not form")
}
This results in the entire screen being taken up by the Form (containing only the one Text), save for the two Text squashed way down at the bottom. How do I get Form to shrink-wrap to the size of its contents? .frame(height: 100) (from https://stackoverflow.com/a/59635380/513038) vaguely works, but you have to know how big the contents are, beforehand, which isn't always the case.