Calling a Function to Set Up Body

I have the following lines of code where I have several vertical stacks of horizontal stacks of buttons on top of a push button titled 'Click me.'

Code Block
import SwiftUI
struct ContentView: View {
@State private var eventPresented = false
@State var fillColors: [Color] = Array(repeating: Color.white, count: 38)
@State var buttonTitles: [String?] = Array(repeating: nil, count: 38)
var body: some View {
VStack {
/* click me */
VStack() {
Button("Click me", action: {
buttonTitles.removeAll()
for i in 0..<38 {
buttonTitles.append(String(i + 1))
}
fillColors.removeAll()
for _ in 0..<2 {
fillColors.append(Color.clear)
}
for _ in 0..<36 {
fillColors.append(Color.white)
}
})
}
/* cal buttons */
VStack(spacing: 0.0) {
HStack(spacing: 0.0) {
ForEach((0...6), id: \.self) {
Button(buttonTitles[$0] ?? "") {
eventPresented = true
}
.buttonStyle(BorderlessButtonStyle())
.frame(width: 48, height: 48, alignment: .center)
.background(RoundedRectangle(cornerRadius: 2)
.fill(fillColors[$0])
.shadow(color: Color.black.opacity(0.4), radius: 2, x: 0, y: 0)
)
}
}
...
...
...
HStack(alignment: .top, spacing: 0.0) {
ForEach((35...36), id: \.self) {
Button(buttonTitles[$0] ?? "") {
}
.buttonStyle(BorderlessButtonStyle())
.frame(width: 48, height: 48, alignment: .center)
.background(RoundedRectangle(cornerRadius: 2)
.fill(fillColors[$0])
.shadow(color: Color.black.opacity(0.4), radius: 2, x: 0, y: 0)
)
}
}
.frame(width: 336.0, height: 48.0, alignment: .leading)
}
}
}
}


If I click on 'Click me,' the application will label each button. My question is how I take the function out of this button. If I do something like the following as I used to doing in Cocoa and UIKit, I get an error. (I've created a function named 'makeButtons.')

Code Block
import SwiftUI
struct ContentView: View {
@State private var eventPresented = false
@State var fillColors: [Color] = Array(repeating: Color.white, count: 38)
@State var buttonTitles: [String?] = Array(repeating: nil, count: 38)
var body: some View {
makeButtons()
/* cal buttons */
VStack(spacing: 0.0) {
HStack(spacing: 0.0) {
ForEach((0...6), id: \.self) {
Button(buttonTitles[$0] ?? "") {
eventPresented = true
}
.buttonStyle(BorderlessButtonStyle())
.frame(width: 48, height: 48, alignment: .center)
.background(RoundedRectangle(cornerRadius: 2)
.fill(fillColors[$0])
.shadow(color: Color.black.opacity(0.4), radius: 2, x: 0, y: 0)
)
}
}
...
...
...
HStack(alignment: .top, spacing: 0.0) {
ForEach((35...36), id: \.self) {
Button(buttonTitles[$0] ?? "") {
}
.buttonStyle(BorderlessButtonStyle())
.frame(width: 48, height: 48, alignment: .center)
.background(RoundedRectangle(cornerRadius: 2)
.fill(fillColors[$0])
.shadow(color: Color.black.opacity(0.4), radius: 2, x: 0, y: 0)
)
}
}
.frame(width: 336.0, height: 48.0, alignment: .leading)
}.frame(minWidth: 370, idealWidth: 370, maxWidth: 370, minHeight: 420, idealHeight: 420, maxHeight: 420, alignment: .top)
}
func makeButtons() {
buttonTitles.removeAll()
for i in 0..<38 {
buttonTitles.append(String(i + 1))
}
fillColors.removeAll()
for _ in 0..<2 {
fillColors.append(Color.clear)
}
for _ in 0..<36 {
fillColors.append(Color.white)
}
}
}


Thank you.
Answered by Tomato in 652511022
Do I have to call it with onAppear like the following?

Code Block
import SwiftUI
struct ContentView: View {
@State private var eventPresented = false
@State var fillColors: [Color] = Array(repeating: Color.white, count: 38)
@State var buttonTitles: [String?] = Array(repeating: nil, count: 38)
var body: some View {
VStack {
ZStack() {
}.onAppear { makeButtons() }
/* cal buttons */
VStack(spacing: 0.0) {
HStack(spacing: 0.0) {
ForEach((0...6), id: \.self) {
Button(buttonTitles[$0] ?? "") {
eventPresented = true
}
.buttonStyle(BorderlessButtonStyle())
.frame(width: 48, height: 48, alignment: .center)
.background(RoundedRectangle(cornerRadius: 2)
.fill(fillColors[$0])
.shadow(color: Color.black.opacity(0.4), radius: 2, x: 0, y: 0)
)
}
}
...
...
...
HStack(alignment: .top, spacing: 0.0) {
ForEach((35...36), id: \.self) {
Button(buttonTitles[$0] ?? "") {
}
.buttonStyle(BorderlessButtonStyle())
.frame(width: 48, height: 48, alignment: .center)
.background(RoundedRectangle(cornerRadius: 2)
.fill(fillColors[$0])
.shadow(color: Color.black.opacity(0.4), radius: 2, x: 0, y: 0)
)
}
}
.frame(width: 336.0, height: 48.0, alignment: .leading)
}
}.frame(minWidth: 370, idealWidth: 370, maxWidth: 370, minHeight: 420, idealHeight: 420, maxHeight: 420, alignment: .top)
}
func makeButtons() {
buttonTitles.removeAll()
for i in 0..<38 {
buttonTitles.append(String(i + 1))
}
fillColors.removeAll()
for _ in 0..<2 {
fillColors.append(Color.clear)
}
for _ in 0..<36 {
fillColors.append(Color.white)
}
}
}

Accepted Answer
Do I have to call it with onAppear like the following?

Code Block
import SwiftUI
struct ContentView: View {
@State private var eventPresented = false
@State var fillColors: [Color] = Array(repeating: Color.white, count: 38)
@State var buttonTitles: [String?] = Array(repeating: nil, count: 38)
var body: some View {
VStack {
ZStack() {
}.onAppear { makeButtons() }
/* cal buttons */
VStack(spacing: 0.0) {
HStack(spacing: 0.0) {
ForEach((0...6), id: \.self) {
Button(buttonTitles[$0] ?? "") {
eventPresented = true
}
.buttonStyle(BorderlessButtonStyle())
.frame(width: 48, height: 48, alignment: .center)
.background(RoundedRectangle(cornerRadius: 2)
.fill(fillColors[$0])
.shadow(color: Color.black.opacity(0.4), radius: 2, x: 0, y: 0)
)
}
}
...
...
...
HStack(alignment: .top, spacing: 0.0) {
ForEach((35...36), id: \.self) {
Button(buttonTitles[$0] ?? "") {
}
.buttonStyle(BorderlessButtonStyle())
.frame(width: 48, height: 48, alignment: .center)
.background(RoundedRectangle(cornerRadius: 2)
.fill(fillColors[$0])
.shadow(color: Color.black.opacity(0.4), radius: 2, x: 0, y: 0)
)
}
}
.frame(width: 336.0, height: 48.0, alignment: .leading)
}
}.frame(minWidth: 370, idealWidth: 370, maxWidth: 370, minHeight: 420, idealHeight: 420, maxHeight: 420, alignment: .top)
}
func makeButtons() {
buttonTitles.removeAll()
for i in 0..<38 {
buttonTitles.append(String(i + 1))
}
fillColors.removeAll()
for _ in 0..<2 {
fillColors.append(Color.clear)
}
for _ in 0..<36 {
fillColors.append(Color.white)
}
}
}

Do I have to call it with onAppear like the following?

It is one possible place, where calling makeButtons() is accepted.

What is important is that do you want to invoke makeButtons at each time when the ZStack appears?
what you could do is to make "makeButtons" return a View, such as:
Code Block
func makeButtons() -> some View {
VStack() {
Button("Click me", action: {
buttonTitles.removeAll()
for i in 0..<38 {
buttonTitles.append(String(i + 1))
}
fillColors.removeAll()
for _ in 0..<2 {
fillColors.append(Color.clear)
}
for _ in 0..<36 {
fillColors.append(Color.white)
}
})
}
}

then use it just like any View, such as:
Code Block
var body: some View {
VStack {
makeButtons()
...

The key here is to return a View. This is common practice. And of course you can do the same for cal buttons.

What is important is that do you want to invoke makeButtons at each time when the ZStack appears?

In fact, the answer is Yes,  OOPer. Thanks for asking the question.

Thank you, workingdogintokyo and OOPer. This time, I choose my own answer as a solution.
Calling a Function to Set Up Body
 
 
Q