Reputation:
Experienced Swift UIKit coder but definitely not with SwiftUI. Let's say I have the following code:
HStack {
Button(
action: { self.createNode() },
label: {
Text("ADD").bold().font(.system(size: 40)).frame(width: 200, height: 80).background(Color.yellow).cornerRadius(5)
})
Spacer()
Button(
action: { self.deleteNode() },
label: {
Text("DELETE").bold().font(.system(size: 40)).frame(width: 200, height: 80).background(Color.yellow).cornerRadius(5)
})
}
A very simple toolbar. But now, let's add (a) four more Buttons
and fit this into a "root" view. Makes for what in UIKit we call a massive view controller.
I'm trying to move this entire HStack
out of the root view, but I'm having issues with the action
. I'm aware I can create ViewModifiers
, custom views, and - at least to some degree - move some things into extensions. But I haven't been able to move this extension "lock, stock, and barrel" anywhere else. I'm stuck trying to move createNode()
and deleteNode()
anywhere else.
I'm sure it's me trying to fit a square peg (UIKit
) into a round hole (SwiftUI
), but none of the WWDC sessions or other resources I've found seem to be pointing me in the right direction. What am I missing?
EDIT:
Here's what the two actions are - they work properly.
@State var nodes: [Node] = []
func createNode() {
let newNodeName = nodes.count + 1
let newNode = Node(name: newNodeName)
nodes.append(newNode)
}
func deleteNode() {
if nodes.count != 0 {
nodes.remove(at: nodes.count - 1)
}
}
My question is not about Swift, or about maintaining the array. It's about SwiftUI, and how to "refactor" my current 140 line file into smaller things - in this case by removing the "top bar" HStack into it's own file in Xcode.
This horizontal stack of Buttons will eventually number 5 (it's an iPad only app) and my issue is how to move the Button "actions", type () -> Void
correctly.
More, here's a simplified view of the entire file (forgive the markdown use, I couldn't find a better way to represent this):
HStack
VStack
HStack <-- Contains the Buttons that add/delete the nodes
HStack
HStack <-- Contains the nodes themselves, will soon contain thumbnails
List
If my HStack consists of a horizontal row of Text
views, it works fine. But when I try to move a Button
(be it nested in an HStack or standalone) into a separate file, I get build issues.
Upvotes: 1
Views: 658
Reputation: 22856
I suggest refactoring actions and @State
into a view model.
final class ViewModel: BindableObject {
let didChange = PassthroughSubject<Void, Never>()
var nodes: [Node] = [] {
didSet {
didChange.send(())
}
}
func createNode() {
let newNodeName = nodes.count + 1
let newNode = Node(name: newNodeName)
nodes.append(newNode)
}
func deleteNode() {
if nodes.count != 0 {
nodes.remove(at: nodes.count - 1)
}
}
}
All the views that need access to the actions, need to declare this property:
@EnvironmentObject var viewModel: ViewModel
More info about @EnvironmentObject
here.
Whenever the nodes changes, all subviews that declare the environment object will be redrawn.
You only need to set the environment object in the container view, for it to be passed down all the subviews.
e.g.
ContentView().environmentObject(ViewModel())
Your view hierarchy:
HStack
VStack
HStack <- @EnvironmentObject (will call the actions)
HStack
HStack <- @EnvironmentObject (will use viewModel.nodes to display nodes)
List
Upvotes: 2