Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI: Random "Extra argument in call" error

So I'm trying to learn SwiftUI and Combine. I usually start new tech by making a simple tip calculator.

I seem to be getting a random "Extra argument in call." error while coding Here is my SwiftUI File

import SwiftUI

internal enum ReceiptRowType {
    case subtotal
    case tax
    case total
    case tip
    case grandTotal
}

struct TipView: View {
    @ObservedObject internal var adBannerView: BannerAdView = BannerAdView()
    @ObservedObject internal var receiptViewModel: ReceiptViewModel

    private let percentageFormatter: NumberFormatter = {
        let f = NumberFormatter()
        f.numberStyle = .percent
        return f
    }()

    var body: some View {
        ZStack {
            Color.white
                .scaledToFit()

            VStack {
                if adBannerView.adHasLoaded {
                    adBannerView
                        .frame(maxHeight: adBannerView.adHeight)
                        .animation(.easeInOut(duration: 2.0))
                }

                BorderView()

                Text(ARCHLocalizedStrings.receipt)
                    .foregroundColor(Color.gray)

                BorderView()

                HStack {
                    Spacer()

                    Button(action: {
                        self.receiptViewModel.addNewReceiptItem()
                    }) {
                        Text(ARCHLocalizedStrings.buttonTitleAddItem)
                    }
                }

                BorderView()

                ScrollView {
                    ForEach(receiptViewModel.receiptItems) { receiptItem in
                        ItemView(receiptItem: receiptItem)

                        if receiptItem != self.receiptViewModel.receiptItems.last {
                            Divider()
                        }
                    }
                }

                BorderView()

                BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                                   type: ReceiptRowType.subtotal,
                                   title: ARCHLocalizedStrings.subtotal)

                BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                                   type: ReceiptRowType.tax,
                                   title: ARCHLocalizedStrings.tax)
            }
            .padding(.horizontal, ARCHSwiftUILayoutConstants.defaultPaddingAndSpacing)
        }
    }
}

struct BorderView: View {
    var body: some View {
        Text("================================")
            .lineLimit(1)
            .foregroundColor(Color.gray)
            .minimumScaleFactor(0.5)
    }
}

struct ItemView: View {
    @ObservedObject var receiptItem: ReceiptItemViewModel

    var body: some View {
        HStack {
            TextField(receiptItem.name, text: $receiptItem.name)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .foregroundColor(Color.gray)
                .multilineTextAlignment(TextAlignment.leading)

            TextField("Price", value: $receiptItem.price, formatter: ARCHUtilities.currencyFormatter)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .foregroundColor(Color.gray)
                .multilineTextAlignment(TextAlignment.trailing)
                .minimumScaleFactor(0.5)
                .frame(width: ARCHSwiftUILayoutConstants.widthForCurrency)
        }
    }
}

struct BottomOfReceiptRow: View {
    @ObservedObject internal var receiptViewModel: ReceiptViewModel

    internal var type: ReceiptRowType
    internal var title: String

    var body: some View {
        HStack {
            Spacer()

            Text(title)
                .foregroundColor(Color.gray)

            if type == ReceiptRowType.subtotal {
                Text("\(receiptViewModel.subtotal)")
                    .foregroundColor(Color.gray)
                    .frame(width: ARCHSwiftUILayoutConstants.widthForCurrency)
            } else if type == ReceiptRowType.tax {
                Text("\(receiptViewModel.taxRate)")
                    .foregroundColor(Color.gray)
                    .frame(width: ARCHSwiftUILayoutConstants.widthForCurrency)
            } else if type == ReceiptRowType.total {
                Text("\(receiptViewModel.total)")
                    .foregroundColor(Color.gray)
                    .frame(width: ARCHSwiftUILayoutConstants.widthForCurrency)
            } else if type == ReceiptRowType.tip {

            } else if type == ReceiptRowType.grandTotal {
                Text("\(receiptViewModel.grandTotal)")
                    .foregroundColor(Color.gray)
                    .frame(width: ARCHSwiftUILayoutConstants.widthForCurrency)
            }
        }
    }
}

struct TipView_Previews: PreviewProvider {
    static var previews: some View {
        TipView(receiptViewModel: ReceiptViewModel())
    }
}

However, if I add another view on the TipView body (Any View) I seem to get a "Extra argument in call" error.

Picture of error here

Does anyone know what's going on?

like image 331
user2593831 Avatar asked Apr 12 '20 22:04

user2593831


2 Answers

try making a Group { } around your views. just 10 are allowed in Swiftui...with group you can add more. or use subviews...(would be cleaner code too)

like image 80
Chris Avatar answered Nov 10 '22 15:11

Chris


The @ViewBuilder system in SwiftUI is limited to 10 views within any given view container. There's no argument for an 11th view, so you get that error.

Basic solution:

The issue here is that your VStack is at maximum capacity, however you can wrap those existing views inside of other containers. As a very basic example that would allow you to show 10 BottomReceiptRow views:

    var body: some View {
    ZStack {
        Color.white
            .scaledToFit()

// Modifications start here

        VStack {
            if adBannerView.adHasLoaded {
                adBannerView
                    .frame(maxHeight: adBannerView.adHeight)
                    .animation(.easeInOut(duration: 2.0))
            }

            BorderView()

            Text(ARCHLocalizedStrings.receipt)
                .foregroundColor(Color.gray)

            BorderView()

            HStack {
                Spacer()

                Button(action: {
                    self.receiptViewModel.addNewReceiptItem()
                }) {
                    Text(ARCHLocalizedStrings.buttonTitleAddItem)
                }
            }

            BorderView()

            ScrollView {
                ForEach(receiptViewModel.receiptItems) { receiptItem in
                    ItemView(receiptItem: receiptItem)

                    if receiptItem != self.receiptViewModel.receiptItems.last {
                        Divider()
                    }
                }
            }

            BorderView()

            Group {
                BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                                   type: ReceiptRowType.subtotal,
                                   title: ARCHLocalizedStrings.subtotal)

                BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                                   type: ReceiptRowType.tax,
                                   title: ARCHLocalizedStrings.tax)

                BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                                   type: ReceiptRowType.someValue,
                                   title: ARCHLocalizedStrings.someValue)

                BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                                   type: ReceiptRowType.someValue2,
                                   title: ARCHLocalizedStrings.someValue2)

                BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                                   type: ReceiptRowType.someValue3,
                                   title: ARCHLocalizedStrings.someValue3)

                BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                                   type: ReceiptRowType.someValue4,
                                   title: ARCHLocalizedStrings.someValue4)

                BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                                   type: ReceiptRowType.someValue5,
                                   title: ARCHLocalizedStrings.someValue5)
            }
// Modifications end here

        }
        .padding(.horizontal, ARCHSwiftUILayoutConstants.defaultPaddingAndSpacing)
    }
}

Alternatively:

You may prefer to completely refactor these sections into their own View as composition makes the code easier to read. It's not strictly necessary here though.

If you did want to do that, you could consider an example where those receipt rows are all in their own view like:

    var body: some View {
    ZStack {
        Color.white
            .scaledToFit()

        VStack {
            if adBannerView.adHasLoaded {
                adBannerView
                    .frame(maxHeight: adBannerView.adHeight)
                    .animation(.easeInOut(duration: 2.0))
            }

            BorderView()

            Text(ARCHLocalizedStrings.receipt)
                .foregroundColor(Color.gray)

            BorderView()

            HStack {
                Spacer()

                Button(action: {
                    self.receiptViewModel.addNewReceiptItem()
                }) {
                    Text(ARCHLocalizedStrings.buttonTitleAddItem)
                }
            }

            BorderView()

            ScrollView {
                ForEach(receiptViewModel.receiptItems) { receiptItem in
                    ItemView(receiptItem: receiptItem)

                    if receiptItem != self.receiptViewModel.receiptItems.last {
                        Divider()
                    }
                }
            }

            BorderView()

            bottomRow
        }
        .padding(.horizontal, ARCHSwiftUILayoutConstants.defaultPaddingAndSpacing)
    }
}

// Additional computed property in TipView
var bottomRow: some View {
    Group {
        BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                           type: ReceiptRowType.subtotal,
                           title: ARCHLocalizedStrings.subtotal)

        BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                           type: ReceiptRowType.tax,
                           title: ARCHLocalizedStrings.tax)

        BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                           type: ReceiptRowType.someValue,
                           title: ARCHLocalizedStrings.someValue)

        BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                           type: ReceiptRowType.someValue2,
                           title: ARCHLocalizedStrings.someValue2)

        BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                           type: ReceiptRowType.someValue3,
                           title: ARCHLocalizedStrings.someValue3)

        BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                           type: ReceiptRowType.someValue4,
                           title: ARCHLocalizedStrings.someValue4)

        BottomOfReceiptRow(receiptViewModel: receiptViewModel,
                           type: ReceiptRowType.someValue5,
                           title: ARCHLocalizedStrings.someValue5)
    }
} // end bottomRow
like image 21
Sparklebeard Avatar answered Nov 10 '22 15:11

Sparklebeard