Return a view from a closure in SwiftUI

Viewed 1769

I have a generic section view component, that should be initialised to return some view for edit of the section and another one for adding elements to the section.

import SwiftUI

enum ProfileSectionType {
    case editable
    case addable
}

struct ProfileSection<Content> : View where Content : View {

    var model:ProfileSectionModel? = nil
    var sectionType:ProfileSectionType = .editable
    var onClick:(() -> View)? = nil
    var content: Content

    @inlinable public init(_ model:ProfileSectionModel? = nil, sectionType:ProfileSectionType = .editable, @ViewBuilder content: () -> Content) {
        self.model = model
        self.content = content()
        self.sectionType = sectionType
    }

    var body : some View {
        switch sectionType {
        case .editable:
            return AnyView(editable())
        case .addable:
            return AnyView(addable())
        }
    }
}

But it is not possible to use a closure like (() -> View). I could have passed it as an parameter to the init, but then I would not get lazy loading of the view.

I have also tried to use generic in the enum. My first attempt were something like:

enum ProfileSectionType<T : View> {
    case editable
    case addable(viewForAdding:T)
}

But that seemed to make it more complex. How can get this section to create two different views for edit and add?

I want to use it like this:

ProfileSection(m.identifications(), sectionType: .addable, onClick: ^{
    AddIdentificationView()
}){
    ForEach( 0 ..< m.identifiers().count ) {
        ProfileEditableItem(key: m.identifiers()[$0].title, value: m.identifiers()[$0].value)
    }
}
1 Answers

I have solved it by setting:

struct ProfileSection<Content, T> : View where Content : View, T : View {

    var model:ProfileSectionModel? = nil
    var sectionType:ProfileSectionType = .editable
    var onClickAdd:(() -> T)? = nil
    var content: Content

I can then use it like this:

ProfileSection<ForEach, AddIdentificationView>(m.identifications(), sectionType: .addable, editDialog: {
    AddIdentificationView()
}){
    ForEach( 0 ..< m.identifiers().count ) {
        ProfileEditableItem(key: m.identifiers()[$0].title, value: m.identifiers()[$0].value)
    }
}

The AddIdentificationView is then presented in a modal view. It would be cleaner if I got rid of <ForEach, but it will do for now.

Related