This question already has answers here:
Are there maximum limits to VStack?
(4 answers)
Closed 1 year ago.
I'm finishing an app that needs user data entry fields. I modeled it with a small number of data elements to streamline the development. Today I attempted to add additional elements and was astounded to find that I could only add 10 views to a view. So I tried the simplest of designs (below). If I added 11 "things" to the view it immediately presented the error on the top item, whatever it was:
"Argument passed to call that takes no arguments"
Doesn't matter whether I call the outside container a ScrollView, VStack, List or Form. Same behavior. Doesn't matter whether the Text/TextField sub units are in a VStack or not.
So I went back to basics - just added ten Text views. No problem. Add an eleventh and it blows up. Here's one of the variants - but all I need to do was add 10 simple Text views to get it to break.
I must be missing something really basic here. I checked for a newer release of Xcodebut I have Version 11.2 beta 2 (11B44), the latest.
#State private var textField1: String = "Pass to the ListCell"
#State private var textField2: String = "2"
//more of these
var body: some View {
NavigationView {
VStack {
//extract the VStack and create a separate struct
ListCell(tfString: textField1)
VStack {
Text("Text Field")
TextField("Placeholder", text: $textField2)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
VStack {
Text("Text Field")
TextField("Placeholder", text: $textField3)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
//more of the above VStacks
Text("6")
Text("7")
Text("8")
Text("9")
Text("10")
//Spacer()
//Text("11")
}
}
}
Any guidance would be appreciated.
Use Group {...} https://developer.apple.com/documentation/swiftui/group
var body: some View {
NavigationView {
VStack {
//extract the VStack and create a separate struct
ListCell(tfString: textField1)
Group {
VStack {
Text("Text Field")
TextField("Placeholder", text: $textField2)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
VStack {
Text("Text Field")
TextField("Placeholder", text: $textField3)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
}
//more of the above VStacks
Group {
Text("6")
Text("7")
Text("8")
Text("9")
Text("10")
}
//Spacer()
//Text("11")
}
}
}
ViewBuilders in SwiftUI take between 0 and 10 elements into their initializer anything more than that and you have to start grouping them using Group, VStack, HStack, List, ForEach and so on.
The best approach is to start extracting a few elements that belong together into separate Views, for example:
struct FormCell: View {
#Binding var inputString: String
var body: some View {
VStack {
Text("Text Field")
TextField("Placeholder", text: $inputString)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
}
}
Next if you have a few of them, you can group them using ForEach and List or VStack.
Use Group as people suggest.
However, you can extend ViewBuilder to make builders for more than 10 views. I created an article that walk you through it at More than 10 views in SwiftUI extending ViewBuilder.
Related
I'd like to hide/show the details split view of a NavigationSplitView on macOS.
However NavigationSplitViewVisibility does not seem to have such option. Changing .navigationSplitViewColumnWidth() or .frame() has no effect on the details view although it works well with the content and list view.
NavigationSplitView {
List(selection: $selection)
} content: {
content(for: selection)
} detail: {
Text("Detail")
}
Did Apple forget to implement such a feature? :/
Trying to figure out an answer to the same question for myself, I have come to this conclusion:
A NavigationSplitView is meant to display a hierarchy where each next level (sidebar, content, detail) is a sub-level of of the previous one. In such a structure you might always want to show a detail view, even it is empty.
In any case, even if that is not the logic, the way to make the "detail" part hidable would be by implementing a two-column navigation with NavigationSplitView and adding a DetailView, enclosing all of these in an HStack and making the DetailView visibility conditional:
struct MyView: View {
#State var showingDetail: Bool = true
var body: some View {
HStack {
NavigationSplitView {
SidebarView()
} detail: {
ContentView()
}
if showingDetail {
DetailView()
}
}
.toolbar {
Toggle(isOn: $showingDetail) {
Image(systemName: "sidebar.trailing")
}
.toggleStyle(.button)
}
}
}
I'm trying to replicate a common view that I've seen in the iPhone settings where you can see some settings field and underneath it, there's an explanation text:
I can't find a way to add the explanation text below and make it look neat like Apple are doing:
struct SomeView: View {
#State private var someBool = true
var body: some View {
Form {
Toggle("I am a toggle", isOn: $someBool)
Text("This is not formatted well :(")
.font(.caption)
.foregroundColor(.gray)
}
.navigationBarTitle(Text("Some form"))
.navigationBarTitleDisplayMode(.inline)
}
}
Here's the result:
Use section footer for that, like
Form {
Section {
Toggle("I am a toggle", isOn: $someBool)
} footer: {
Text("This is not formatted well :(") // << here !!
.font(.caption)
.foregroundColor(.gray)
}
}
Tested with Xcode 13.4 / iOS 15.5
I have this model where I have a list of boat, and a list of works (which are linked to a boat). All are linked to the user. My data is stored in Firestore by my repository.
struct boat: Codable, Identifiable {
#DocumentID var id : String?
var name: String
var description: String
#ServerTimestamp var createdtime: Timestamp?
var userId: String?
}
struct Work: Identifiable, Codable {
#DocumentID var id : String?
var title: String
var descriptionpb: String
var urgent: Bool
#ServerTimestamp var createdtime: Timestamp?
var userId: String?
var boatId: String // (boatId = id of struct boat just above)
}
I have a view in which I want to display (and let the user edit) the details of the work (such as the title and descriptionpb), I manage to display the boatId (see below), however I want to display the boat name. How should I go about it?
import SwiftUI
struct WorkDetails: View {
#ObservedObject var wcvm: WorkCellVM
#ObservedObject var wlvm = WorklistVM()
#State var presentaddwork = false
var onCommit: (work) -> (Void) = { _ in }
var body: some View {
ScrollView {
VStack(alignment: .leading) {
Text(wcvm.work.boatId) // <-- THIS IS WHAT I WANT TO CHANGE INTO boat name instead of boatId
.padding()
TextField("Enter work title", text: $wcvm.work.title, onCommit: {
self.onCommit(self.wcvm.work)
})
.font(.title)
.padding()
HStack {
TextField("Enter problem description", text: $wcvm.work.descriptionpb, onCommit: {
self.onCommit(self.wcvm.work)
})
}
.font(.subheadline)
.foregroundColor(.secondary)
.padding()
}
}
}
}
Essentially you have a Data Model problem, not a SwiftUI problem. I would be keeping all of this in Core Data and linking the various models(Entities in Core Data) with relationships. So your Work(Essentially a work order) would link to the boat that the work was being performed on.
Otherwise, you need to add a Boat as a parameter to Work. Either way would give you the desired syntax, but Core Data is much more efficient. It is also your data persistence model so you would kill two birds with one stone.
Solution found: when creating a work order, I was assigning the boat id, I am now assigning the boat name as well (and calling it in the work order display). Essentially keeping the same code as above, tweaking it a little bit so that it does what I want to do.
I'm just starting to learn SwiftUI, so I decided to work thru Apple's tutorial, using the latest Xcode (12.5). One line of code immediately got me a semantic error: "Value of type 'Color' has no member 'accessibleFontColor'"Here's the entire source module:
//
// CardView.swift
// Scrumdinger
//
// Created by Vacuumhead on 6/4/21.
//
import SwiftUI
struct CardView: View {
let scrum: DailyScrum
var body: some View {
VStack(alignment: .leading) {
Text(scrum.title).font(.headline)
Spacer()
HStack {
Label("\(scrum.attendees.count)", systemImage: "person.3")
.accessibilityElement(children: .ignore)
.accessibilityLabel(Text("Attendees"))
.accessibilityValue(Text("\(scrum.attendees.count)"))
Spacer()
Label("\(scrum.lengthInMinutes)", systemImage: "clock")
.padding(.trailing, 20)
.accessibilityElement(children: .ignore)
.accessibilityLabel(Text("Meeting length"))
.accessibilityValue(Text("\(scrum.lengthInMinutes) minutes"))
}
.font(.caption)
}
.padding()
.foregroundColor(scrum.color.accessibleFontColor)
}
}
struct CardView_Previews: PreviewProvider {
static var scrum = DailyScrum.data[0]
static var previews: some View {
CardView(scrum: scrum)
.background(scrum.color)
.previewLayout(.fixed(width: 400, height: 60))
}
}
The line that gets the error is the last line of body:
.foregroundColor(scrum.color.accessibleFontColor)
The message is "Value of type 'Color' has no member 'accessibleFontColor'".The program compiles and runs just fine when I comment out that line, of course without any color. I've been writing C++ since the age of dinosaurs but I'm new to SwiftUI and don't even know where I should look to fix this.
Any suggestions are welcome.
There's a file that you're probably missing in the sample project called Color+Codable.swift that defines some extensions on Color. One is accessibleFontColor:
extension Color {
var accesibleFontColor : Color {
//etc.
}
}
Download the files from https://developer.apple.com/tutorials/app-dev-training/managing-state-and-life-cycle and make sure that you're using Color+Codable.swift in your project.
Working with SwiftUI:
I have a list of views in a ScrollView that I am creating using a ForEach loop. I want to show or hide a number of little flags depending on 4 different Bool properties in the struct I am using as the model for the objects in the list. The problem I'm having is the more If-statements I add, the worse the performance gets. With no If-statements the list loads without a hitch. I'm running into this problem with only 120 items in the list. I would love help figuring out what I'm doing wrong!
Here is an example of the Content View with the ScrollView and loop:
struct ContentView: View {
#ObservedObject var model = Model()
var body: some View {
ScrollView(.vertical, showsIndicators: true){
VStack(spacing: 10){
ForEach(model.list){ item in
ItemView(item: item)
}
}
}
}// end body
} // end struct
And the Item Views and Struct I'm using as the model.
struct ListData: Identifiable {
var id = UUID()
var title: String
var subtitle: String
var isTrue: Bool
var isAlsoTrue:Bool
}
struct ItemView: View {
var item: ListData
var body: some View {
VStack(alignment: .leading){
Text(item.title)
HStack{
if item.isTrue{
FlagView(text: "True")
}
if item.isAlsoTrue{
FlagView(text: "Also")
}
Text(item.subtitle)
Spacer()
}
.font(.system(size: 12, weight: .semibold))
}
.font(.system(size: 14, weight: .semibold))
}
}
struct FlagView: View {
var text: String
var body: some View {
Text(text)
.padding(2)
.foregroundColor(.white)
.background(RoundedRectangle(cornerRadius: 2).foregroundColor(.gray))
}
}
The Actual objects have 4 Bool properties I want to use. Is there a better way to hide or show these flags in my list? Any help figuring out what's wrong with the performance is greatly appreciated!