1dolinski
1dolinski

Reputation: 549

ScrollView and .sheet

I have a ScrollView with a ForEach loop, each rendering a View. In the View I have 3 renders of the below ActionItem (a button that displays a sheet). The sheet does not show up with ScrollView but does with List. I'd normally attach the .sheet at the ScrollView layer however, with each button rendering a different view it seems more appropriate to nest it.

How I could get this to work with ScrollView? I'm using Xcode 12

struct ActionItem<Content>: View where Content : View {
    public var text: String
    public var icon: String
    public var content: Content
    @State var isPresented = false
    
    init(text: String, icon: String, @ViewBuilder content: () -> Content) {
        self.text = text
        self.icon = icon
        self.content = content()
    }
    
    var body: some View {
        Button (action: {
           DispatchQueue.main.async {
             withAnimation {
               self.isPresented = true
             }
            }
        }) {
            HStack(spacing: 2) {
                Image(systemName: icon).font(.system(size: 14, weight: .semibold))
                Text(text).fontWeight(.semibold)
                
            }.padding([.top, .bottom], Dimensions.spacing)
            .padding([.leading, .trailing], Dimensions.spacingMedium)
        }.foregroundColor(Color.gray).font(.subheadline).background(Color.grayWhiteTer)
        .cornerRadius(Dimensions.spacing)
        .sheet(isPresented: $isPresented) {
            self.content
        }
    }
}

In the View I'd render ActionItem such as Text, this also occurs if the View is ignored and the ActionItem is just directly in the ForEach. Same issue, sheet does not appear.

ActionItem(text: "", icon: "pencil") {
  Text("ok")
}

The list looks like this

import SwiftUI

struct ItemsList: View {
  @ObservedObject var itemModel: ItemModel
    
  var body: some View {
    VStack(alignment: .center, spacing: 0) {
      ScrollView {
        VStack {
            ForEach(itemModel.items, id: \.self) { item in
              ItemView(item: item)
            }
        }.frame(maxWidth: .infinity, maxHeight: .infinity)
}}

Suggested callback update

struct ActionItem<Content>: View where Content : View {
    public var text: String
    public var icon: String
    public var content: () -> Content
    @State var isPresented = false
    
    init(text: String, icon: String, @ViewBuilder content: @escaping () -> Content) {
        self.text = text
        self.icon = icon
        self.content = content
    }
    
    var body: some View {
        Button (action: {
            DispatchQueue.main.async {
               withAnimation {
                self.isPresented = true
               }
            }
            
        }) {
            HStack(spacing: 2) {
                Image(systemName: icon).font(.system(size: 14, weight: .semibold))
                    Text(text).fontWeight(.semibold)
                
            }.padding([.top, .bottom], Dimensions.spacing)
            .padding([.leading, .trailing], Dimensions.spacingMedium)
        }.foregroundColor(Color.gray).font(.subheadline).background(Color.grayWhiteTer)
        .cornerRadius(Dimensions.spacing)
        .sheet(isPresented: $isPresented) {
            self.content()
        }
    }
}

Upvotes: 0

Views: 492

Answers (1)

BJ Beecher
BJ Beecher

Reputation: 205

Try saving content as a callback (i.e. () -> Content) and call it in the sheet method instead of calling it in the initializer.. This will change when the view is created.

Upvotes: 1

Related Questions