How to Disambiguate a Foreach Loop in Xcode with Swiftui

How to disambiguate a foreach loop in Xcode with SwiftUI

ForEach is a structure that computes views on demand from an underlying collection of identified data, NOT a loop.

How to create hexagonal grid? Below is one possible approach where I tried to show you how to use ForEach and SwiftUI declarative syntax.

import SwiftUI

struct Hexagon: Shape {
func path(in rect: CGRect) -> Path {
Path { (path) in
path.move(to: .init(x: rect.midX, y: rect.minY))
path.addLine(to: .init(x: rect.maxX, y: (rect.minY + rect.midY) / 2 ))
path.addLine(to: .init(x: rect.maxX, y: (rect.maxY + rect.midY) / 2 ))
path.addLine(to: .init(x: rect.midX, y: rect.maxY))
path.addLine(to: .init(x: rect.minX, y: (rect.maxY + rect.midY) / 2 ))
path.addLine(to: .init(x: rect.minX, y: (rect.minY + rect.midY) / 2 ))
path.closeSubpath()
}
}
}

struct ContentView: View {
var body: some View {
VStack(spacing: -10) {
ForEach(0 ..< 3) { j in
HStack(spacing: 5) {
ForEach(0 ..< 4) { i in
Hexagon()
.fill(Color.yellow)
.overlay(
Hexagon()
.stroke(Color.red, lineWidth: 5)
)
.frame(width: 50, height: 60)
}
}
HStack(spacing: 5) {
ForEach(0 ..< 3) { i in
Hexagon()
.stroke(Color.blue, lineWidth: 5)
.frame(width: 50, height: 60)
}
}
}
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

and here is the result
Sample Image

Spacing and frame parameters are fixed here for simplicity, you are better to calculate proper numbers.

init(Range<Int>, content: (Int) -> Content)

Available when Data is Range, ID is Int, and Content conforms to
View.

You tried to use

init(Data, id: KeyPath<Data.Element, ID>, content: (Data.Element) -> Content)

Available when Content conforms to View.

and finally we don't have any idea what type returns your hexagon function, it could be Shape, but Shape is protocol, and that could make your trouble. If it is Shape, try define return type as some Shape.

Yes, compiler error reporting is still not very usable with SwiftUI

ForEach cannot use branch

Because view builder expected one return of one type view, but condition does not generate opaque return. To solve - just embed condition in Group

ForEach(1...5, id: \.self) { index in
Group {
if index == 1 {
Text("Cup of coffee.")
} else {
Text("\(index) cups of coffee")
}
}
}

SwiftUI iterating through dictionary with ForEach

Simple answer: no.

As you correctly pointed out, a dictionary is unordered. The ForEach watches its collection for changes. These changes includes inserts, deletions, moves and update. If any of those changes occurs, an update will be triggered. Reference: https://developer.apple.com/videos/play/wwdc2019/204/ at 46:10:

A ForEach automatically watches for changes in his collection

I recommend you watch the talk :)

You can not use a ForEach because:

  1. It watches a collection and monitors movements. Impossible with an unorered dictionary.
  2. When reusing views (like a UITableView reuses cells when cells can be recycled, a List is backed by UITableViewCells, and I think a ForEach is doing the same thing), it needs to compute what cell to show. It does that by querying an index path from the data source. Logically speaking, an index path is useless if the data source is unordered.

How to pass variables functions in forEach loops

Try to separate grid item creation in separate function as below (the code snapshot is not testable, so just scratchy to demo idea)

    ...
ForEach(0 ..< 11, id: \.self) { Xnum in
ForEach(0 ..< 11, id: \.self) { Ynum in
self.createItem(for: Xnum, Ynum: Ynum)
}
}
...

private func createItem(for Xnum: Int, Ynum: Int) -> some View {
self.MyNewPath(in: CGRect(), Xcoord: Double(Xnum), Ycoord: Double(Ynum), Type: 1)
.fill(RadialGradient(
gradient: Gradient(colors: [Color.red, Color.blue]),
center: .init(
x: self.gradientParametersX(Xpos: Double(Xnum)),
y: self.gradientParametersY(Ypos: Double(Ynum), Xpos: Double(Xnum))),
startRadius: CGFloat(2.0),
endRadius: CGFloat(70.0)))
.gesture(
TapGesture().onEnded {_ in
self.dataEditor(Xplace: Int(Xnum), Yplace: Int(Ynum))
}
)
}

How to use Foreach on view in SwiftUI?

ForEach row must be represented by single View, so you need something like the following (still I'm not sure in type of container, but just for example)

ForEach(controller.verses) { w in
ZStack {
Rectangle()
.foregroundColor(Color.white)
.cornerRadius(28)
.opacity(0.4)
.offset(x:0, y:68)
.frame(width:290, height:280)

CardView(date: w.date)
.gesture(DragGesture()
.onChanged({ (value) in

...
}
}

Program crashing using ForEach loop in SwiftUI

The range in your ForEach loop is open, so you will get an out of bounds exception when you try access self.testData[result].name when result is equal to the length of testData.

Changing your range to 0..<testData.count should fix your problem.

SwiftUI, using ForEach's argument causing error Unable to infer closure type

Your issue is that test's parameter is a UInt, while 0..<self.Cells.count's type is Range<Int>, so i is inferred by the compiler to be an Int. When you pass it into the function, the function is expecting a UInt, and the compiler cannot implicitly cast from Int to UInt, so you have to do it. Here is one way to do that:

var body: some View {
ZStack {
ForEach(0..<self.Cells.count, id: \.self) { i in
Text("").position(self.test(UInt(exactly: i) ?? 0))
}
}
}

Another way to fix your problem is to just change the test function's parameter to be of type Int and deal with casting to UInt inside of there if you really need to have it be a UInt and not just an Int.

Get element position in ZStack

Found the solution to this specific case

Background:
The View that is being loaded in the loop is in an array, that I previously created. The array also contain an ID.

What I did now was to find out the max ID, reverse loop through my array while passing the current ID and the current max ID to the new view, where I can handle the data accordingly.
So if the max ID == currentElement ID, I can apply my changes, thus also getting the topmost card.

Problem with code which worked in Xcode11 beta 4 but stop working in beta 5

I simplified your code down into something I could run:

struct ContentView: View {
var rows: [[Int]] = [[0, 1, 2], [3, 4, 5]]

var body: some View {
VStack {
ForEach(rows, id: \.self) { row in
HStack {
ForEach(row) { item in
EmptyView()
}
}
}
}
}
}

...and I got this error:

Referencing initializer 'init(_:content:)' on 'ForEach' requires that 'Int' conform to 'Identifiable'

I'm guessing that in previous betas Int conformed to Identifiable and that beta 5 changed that. So to fix this, just change your second ForEach to ForEach(row, id: \.self).

Update

After removing the parts of your code that I could not run, I managed to get the same error.

Error: Unable to infer complex closure return type; add explicit type to disambiguate

It appears that ForEach is expecting one view to be returned from its body, not multiple as you have here:

ForEach(row) { item in
Reminder(closed: self.list[item].closed, text: self.list[item].text)
self.number % 3 == 0 ? nil : VStack() {
self.number-1 == item ? AddReminder() : nil
}
}

You're trying to return both a Reminder and an optional VStack, so the compiler can't determine what the return type is supposed to be. This may have worked in the past because ForEach could previously handle tuple views and no longer does - I'm not sure. Whatever the case, you need to first change the ForEach to ForEach(row, id: \.self) as I pointed out earlier, and then you have to wrap everything inside the ForEach in a group, like this:

ForEach(row, id: \.self) { item in
Group {
Reminder(closed: self.list[item].closed, text: self.list[item].text)
self.number % 3 == 0 ? nil : VStack {
self.number - 1 == item ? AddReminder() : nil
}
}
}

One last thing that I've just noticed. The name of your struct should not be List. List already exists in SwiftUI, and you shouldn't name your custom views in ways that will conflict with framework defined types. I would suggest that you rename your view to ReminderList if that adequately describes its purpose.



Related Topics



Leave a reply



Submit