EurekaCommunity / SplitRow

A row for Eureka to put two rows side by side into the same UITableViewCell
MIT License
56 stars 27 forks source link

rowLeft / rowRight flexible row type? #10

Closed tvanes closed 6 years ago

tvanes commented 6 years ago

SplitRow is an awesome addition to Eureka. But I don't suppose it's possible yet to change the row type of rowRight based on (for example) a selection from a PushRow at the rowLeft, is it?

So take your basic usage example, imagine you select "Work" from the selector at your left. Based on this selected value the rowRight changes from a TextRow into a PushRow where a corresponding value can be selected for Work, or perhaps a switch row, etc.

Any pointers to how something like this could be implemented?

marbetschar commented 6 years ago

You're right, thats indeed not possible using SplitRow. The main reason for this is, you have to define the rowTypes before initialization (SplitRow<PushRow<String>,SwitchRow>) - and the type is therefore not changeable at runtime.

The easiest workaround I can see for this is, that you initialize multiple SplitRows and depending on the user selection, you change the row which is displayed at this specific position.

Something like this:

let options = [
    "name",
    "switch"
]

let switchSplitRow = SplitRow<PushRow<String>,SwitchRow>(){
    $0.tag = "name"
    $0.rowLeft?.options = options
}
let nameSplitRow = SplitRow<PushRow<String>,NameRow>(){
    $0.tag = "switch"
    $0.rowLeft?.options = options
}

switchSplitRow.onChange{
    if $0.rowLeft?.value == "name" {
        //hide/remove this row here, and show `nameSplitRow`
    }
}

nameSplitRow.onChange{
    if $0.rowLeft?.value == "switch" {
        //hide/remove this row here, and show `switchSplitRow`
    }
}
tvanes commented 6 years ago

Thank you for that idea, though I was hoping to use something like this within a MultivaluedSection, which would make the swapping a bit more complicated I'm afraid... :)

marbetschar commented 6 years ago

Yeah, within a MultivaluedSection this will get a bit trickier :)

May I ask you what exactly your use case is? Do you really have completely different row types, or are we talking about having input fields which should provide different keyboards (DecimalRow, NameRow, ...) or anything similar?

tvanes commented 6 years ago

In my case I'd need something where users can fill out optional metadata. So the field count is flexible, the data names can be selected from a list on the left and their corresponding values are to be specified on the right, with the type of input depending on the selection. This would be mostly text fields (NameRow) or sublists (like PushRow). So yes, we are talking very different row types here.

marbetschar commented 6 years ago

I see. Well, it seems you'll have to go the tricky path I guess ;)

For this, I would suggest using something like the following code. Note this is untested, but it should provide you with the basic idea to get you going:

struct MetaRowTag {
    static let name = "name"
    static let yesOrNo = "yesOrNo"
}

MultivaluedSection(){ section in
    var metaRowTypes = [String: BaseRow]()

    let nameRow = SplitRow<PushRow<String>,NameRow>{
        $0.tag = MetaRowTag.name
        $0.rowLeft = PushRow<String>{
            $0.options = [MetaRowTag.name,.yesOrNo]
        }
        ...
    }.onChange{
        if let rowType = $0.tag,
            let selectedRowType = $0.rowLeft?.value,
            rowType != selectedRowType,
            let newRow = metaRowTypes[selectedRowType],
            let indexPath = $0.indexPath {

            section.remove(at: indexPath.row)
            section.insert(newRow, at: indexPath.row)
        }
    }
    metaRowTypes[MetaRowTag.name] = nameRow

    let yesOrNoRow = SplitRow<PushRow<String>,SwitchRow>{
        $0.rowLeft = PushRow<String>{
            $0.options = [MetaRowTag.name,.yesOrNo]
        }
        ...
    }.onChange{
        //see above
    }
    metaRowTypes[MetaRowTag.yesOrNo] = yesOrNoRow

     section.addButtonProvider = { section in
         return ButtonRow(){
             $0.title = "Add New Meta"
         }.cellUpdate { cell, row in
             cell.textLabel?.textAlignment = .left
         }
     }
     section.multivaluedRowToInsertAt = { index in
         return metaRowTypes[MetaRowTag.name]
     }

     section <<< metaRowTypes[MetaRowTag.name]
 }
marbetschar commented 6 years ago

PS: You might want to use some kind of metaRowProvider instead of the metaRowTypes object in the example above. This way you always receive a new row instance:

let metaRowProvider: ((String) -> BaseRow?) = { rowType in
        switch rowType{
        case MetaRowTag.name: return SplitRow<PushRow<String>,NameRow>{
                $0.tag = MetaRowTag.name
                $0.rowLeft = PushRow<String>{
                    $0.options = [MetaRowTag.name,.yesOrNo]
                }
            }
        case MetaRowTag.yesOrNo: return SplitRow<PushRow<String>,SwitchRow>{
                $0.tag = MetaRowTag.yesOrNo
                $0.rowLeft = PushRow<String>{
                    $0.options = [MetaRowTag.name,.yesOrNo]
                }
            }
        default: return nil
    }

and then use it like this:

let nameRow = metaRowProvider(MetaRowTag.name)
marbetschar commented 6 years ago

I'll close this for now. Feel free to ping me if you have any other questions.