stevengharris / SplitView

A flexible way to split SwiftUI views with a draggable splitter

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Feature: minimum split size

aehlke opened this issue · comments

To mimic system components, the split should collapse when it reaches a size threshold and should only reappear when dragged back beyond that threshold or toggled back on. See NavigationSplitView behavior for example.

You can do something like this using the fraction reported by onDrag. Using the DemoSplitter in the README as an example, here the right side hides itself when the fraction exceeds 80%.

struct DemoSlider: View {
    @State private var privateFraction: CGFloat = 0.5
    @State private var hideRed = SideHolder()
    var body: some View {
        HSplit(
            left: {
                ZStack {
                    Color.green
                    Text(percentString(for: .left))
                }
            },
            right: {
                ZStack {
                    Color.red
                    Text(percentString(for: .right))
                }
            }
        )
        .hide(hideRed)
        .onDrag { fraction in
            if fraction > 0.8 {
                hideRed.toggle()
            } else {
                privateFraction = fraction
            }
        }
        .frame(width: 400, height: 30)
    }

    /// Return a string indicating the percentage occupied by `side`
    func percentString(for side: SplitSide) -> String {
        var percent: Int
        if side.isPrimary {
            percent = Int(round(100 * privateFraction))
        } else {
            percent = Int(round(100 * (1 - privateFraction)))
        }
        // Empty string if the side will be too small to show it
        return percent < 10 ? "" : "\(percent)%"
    }
}

Thanks, I will try this! Note that the standard OS behavior is to allow snapping back to visible if dragged back without letting go; the hidden state is only finalized once the drag is completed.

I was thinking about this more, and I think both this issue and the other open one would best be handled with a couple more styling modifiers. I won't be able to work on them for another 10 days or so, but I hope to do something to address them at that time. Thanks for bringing them up.

This PR (#15) is what I came up with. I decided it was better to add arguments to the constraints modifier, since the behavior really needs to be coupled with the existing minPFraction and minSFraction. I also added a section to the README to describe it. If you pull this branch down, you can see the behavior in the Invisible Splitter demo. Let me know what you think!

Hide-On-Drag

When you constrain the fraction of the primary or secondary side, you may want the
side to hide automatically when you drag past the constraint. This is a kind of
shortcut to avoid having to press a button to hide a side. You can see an example of
it in Xcode when you drag the splitter between the editor area in the middle and the
Inspector on the right beyond the constraint Xcode puts on the Inspector width. Because
in Xcode the splitter between the editor area and the Inspector is
[invisible](### Invisible Splitters), once it's hidden, you cannot drag it back out.
You need a button to invoke the hide/show action, as discussed
[earlier](### Modifying and Constraining the Default Splitter).

To use hide-on-drag, add hideAtMinP and/or hideAtMinS to your constraints definition.
For example, the following will constrain dragging between 20% and 80% of the width, but
when the drag gesture ends at or beyond the 80% mark on the right, the secondary side will
hide. Note also that in this case, the primary side doesn't hide when the drag gesture ends
at or beyond the 20% mark on the left:

HSplit(left: { Color.red }, right: { Color.green })
    .constraints(minPFraction: 0.2, minSFraction: 0.2, hideAtMinS: true)

This really isn't behaving like it should. It basically makes it impossible to drag the splitter to the constraint location, because when you stop dragging, it hides. Also, like Xcode, it should hide while you're dragging when it will leave the side hidden, so you have some visual feedback of what will happen when you let go. I'm reopening the issue and will have to work on it some more.

@aehlke This pull request (or the "previewHide" branch) should, I think, implement "drag-to-hide" properly. Instead of hiding the side at the minPFraction or minSFraction, you have to stop dragging beyond the halfway point of the constrained side. When you do drag past that point, it now shows the side hidden, so you can tell it will be hidden if you stop dragging, and if you drag back, it unhides it again. This actually behaves like the Inspector view hiding in Xcode now. I updated the DemoApp so it uses it for the right/bottom side in the 'Sidebars" demo, and I'm consuming it in my app. I'm going to hold off for a day or two merging the PR if you have a chance to look at it.

FWIW, I renamed the hideAtMinP and hideAtMinS parameters in constraints to dragToHideP and dragToHideS, since it's not actually hiding at the contraint point anymore, and the README now covers Drag-To-Hide as a feature.

I won't be able to look within a couple days but what you described sounds perfect!

Okay, well I will go ahead and merge it. Reopen or file another issue if you see a problem. Thanks again for raising the issue. I really like this feature and am glad I put time into it.