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
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:
- It watches a collection and monitors movements. Impossible with an unorered dictionary.
- When reusing views (like a
UITableView
reuses cells when cells can be recycled, aList
is backed byUITableViewCells
, and I think aForEach
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
Using Tvposterimage in Tvuikit for Tvos 12
Swiftui: Unexpected Animation When Using a Non @State Var
Interrupted Purchase Not Calling Delegate After Accepting T&C
How to Create Lazy Combinations
Xcode Gm: No Swift Language for Os X Command Line Tool Project
Stanford Calculator App Keeps Crashing
Bulk Fix Hundreds of "#Selector Not Exposed to Objective-C" Errors in Xcode 9 or 9.1
Get Images from Document Directory Not File Path Swift 3
How to Move a Model and Generate Its Collision Shape at the Same Time
Accessing and Manipulating Array Item in an Environmentobject
Firestore Security Rules Breaking with Update Rule
Cfdictionary Get Value for Key in Swift3
Ambiguous Method Overload with Closures in Swift, But Only When Closure Returns a Value
Performing a Completion Handler Before App Launches
Swift: Skspritekit, Using Storyboards, Uiviewcontroller and Uibutton to Set in Game Parameters
How to Decode Utf8-Literals Like "\Xc3\Xa6" in Swift 5
How to Change the Appearance of the Datepicker in the Swiftui Framework to Only Months and Years