skip to Main Content

I have a List in SwiftUI that I populate with a custom SwiftUI cell, the issue is that on tap I need to do some stuff and the tap only works when you click the text in the cell, if you click any empty space it will not work. How can I fix this?

struct SelectDraftView: View {
    @Environment(.presentationMode) var presentationMode

    @ObservedObject var viewModel = SelectDraftViewModel()
    
    var body: some View {
        VStack {
                List {
                    ForEach(viewModel.drafts.indices, id: .self) { index in
                        DraftPostCell(draft: viewModel.drafts[index])
                            .contentShape(Rectangle())
                            .onTapGesture {
                                presentationMode.wrappedValue.dismiss()
                            }
                    }
                    .onDelete { indexSet in
                        guard let delete = indexSet.map({ viewModel.drafts[$0] }).first else { return }
                        viewModel.delete(draft: delete)
                    }
                
            }
                .background(Color.white)
            Spacer()

        }
    }
}

struct DraftPostCell: View {
    var draft: CDDraftPost
    
    var body: some View {

        VStack(alignment: .leading) {
            Text(draft.title ?? "")
                .frame(alignment: .leading)
                .font(Font(UIFont.uStadium.helvetica(ofSize: 14)))
                .padding(.bottom, 10)
            if let body = draft.body {
                Text(body)
                    .frame(alignment: .leading)
                    .multilineTextAlignment(.leading)
                    .frame(maxHeight: 40)
                    .font(Font(UIFont.uStadium.helvetica(ofSize: 14)))
            }
            Text(draft.date?.toString(format: "EEEE, MMM d, yyyy") ?? "")
                .frame(alignment: .leading)
                .font(Font(UIFont.uStadium.helvetica(ofSize: 12)))
        }
        .padding(.horizontal, 16)
    }
}

2

Answers


  1. try adding .frame(idealWidth: .infinity, maxWidth: .infinity) just after DraftPostCell(...). You can also use a minWidth: if required.

    EDIT-1: the code I use for testing (on real devices ios 15.6, macCatalyst, not Previews):

    import Foundation
    import SwiftUI
    
    
    struct ContentView: View {
        var body: some View {
            SelectDraftView()
        }
    }
    
    class SelectDraftViewModel: ObservableObject {
        @Published var drafts: [
            CDDraftPost] = [
                CDDraftPost(title: "item 1", date: Date(), body: "body 1"),
                CDDraftPost(title: "item 2", date: Date(), body: "body 4"),
                CDDraftPost(title: "item 3", date: Date(), body: "body 3")]
        
        func delete(draft: CDDraftPost) {  }
    }
    
    struct CDDraftPost: Codable {
        var title: String?
        var date: Date?
        var body: String?
    }
    
    struct SelectDraftView: View {
        @Environment(.presentationMode) var presentationMode
        
        @ObservedObject var viewModel = SelectDraftViewModel()
        
        var body: some View {
            VStack {
                List {
                    ForEach(viewModel.drafts.indices, id: .self) { index in
                        DraftPostCell(draft: viewModel.drafts[index])
                            .frame(idealWidth: .infinity, maxWidth: .infinity, alignment: .leading)
                            .contentShape(Rectangle())
                            .border(.red)  // <-- for testing
                            .onTapGesture {
                                print("----> onTapGesture")
                                //  presentationMode.wrappedValue.dismiss()
                            }
                    }
                    .onDelete { indexSet in
                        guard let delete = indexSet.map({ viewModel.drafts[$0] }).first else { return }
                        viewModel.delete(draft: delete)
                    }
                }
                .background(Color.white)
                Spacer()
                
            }
        }
    }
    
    struct DraftPostCell: View {
        var draft: CDDraftPost
        
        var body: some View {
            VStack(alignment: .leading) {
                Text(draft.title ?? "")
                    .frame(alignment: .leading)
                //    .font(Font(UIFont.uStadium.helvetica(ofSize: 14)))
                    .padding(.bottom, 10)
                if let body = draft.body {
                    Text(body)
                        .frame(alignment: .leading)
                        .multilineTextAlignment(.leading)
                        .frame(maxHeight: 40)
                    //   .font(Font(UIFont.uStadium.helvetica(ofSize: 14)))
                }
                Text(draft.date?.formatted() ?? "")
                    .frame(alignment: .leading)
                //  .font(Font(UIFont.uStadium.helvetica(ofSize: 12)))
            }
            .padding(.horizontal, 16)
        }
    }
    
    Login or Signup to reply.
  2. I’m probably late but this will be useful for anyone checking this in the future.
    You need to add .background() modifier to your view before you do .onTapGesture{...}

    so in your ForEach code would be modified like this:

    ForEach(viewModel.drafts.indices, id: .self) { index in
        DraftPostCell(draft: viewModel.drafts[index])
            .contentShape(Rectangle())
            .frame(maxWidth: .infinity) // you should use the frame parameter according to your needs, but if you want your cell to occupy the whole width of your scroll view, use this one
            .background()  // this makes the empty portions of view 'non-transparent', so those portions also receive the tap gesture
            .onTapGesture {
                presentationMode.wrappedValue.dismiss()
            }
    }
    

    P.S if you need the whole portion of your scroll view cell to receive the tap gesture you’ll also need to add .frame(...) modifier, so it has the exact background you want

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search