0

I recently asked (and got a valid answer) to a question related to this issue.

How do I get my UIViewRepresentable to correctly size for its content?

As I mentioned in the previous post, I want to use the awesome MultiSegmentPicker written by Yonat Sharon in my SwiftUI View.

https://github.com/yonat/MultiSelectSegmentedControl

Implementing the answer showed that I'm not out of the woods, yet.

screenshot

Notice that the bar is behind the "bottom" text view

In my non-demo view it is completely hidden behind other views - so it's not visible at all.

I understand this is a constraints issue, and Xcode view debugging helpfully confirms:

runtime: Layout Issues: Position is ambiguous for MultiSelectSegmentedControl.

Where do I address this problem? Is it in the UIViewRepresentable which is not behaving as I would expect? Since the package has been around for years, and pure UIKit code does not encounter this issue, I'm pretty sure that the issue is in the UIViewRepresentable code.

In the init for MultiSelectSegmentedControl is the following setup code:

   private func setup() {
        addConstrainedSubview(borderView, constrain: .top, .bottom, .left, .right)
        addConstrainedSubview(stackView, constrain: .top, .bottom)
        constrain(stackView, at: .left, to: borderView, diff: 1)
        constrain(stackView, at: .right, to: borderView, diff: -1)
        clipsToBounds = true
        stackView.distribution = .fillEqually
        borderWidth = { borderWidth }()
        borderRadius = { borderRadius }()
        tintColorDidChange()
        borderView.isUserInteractionEnabled = false
        addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTap)))
        accessibilityIdentifier = "MultiSelectSegmentedControl"
    }

In the MultiSegmentPicker: UIViewRepresentable's init I find the following code related to constraints:

   public init(
        selectedSegmentIndexes: Binding<IndexSet>,
        items: [Any],
        allowsMultipleSelection: Bool? = nil,
        borderWidth: CGFloat? = nil,
        borderRadius: CGFloat? = nil,
        isVertical: Bool? = nil,
        isVerticalSegmentContents: Bool? = nil,
        selectedBackgroundColor: UIColor? = nil
    ) {
       _selectedSegmentIndexes = selectedSegmentIndexes
        uiView = MultiSelectSegmentedControl(items: items)
        uiView.translatesAutoresizingMaskIntoConstraints = false

        uiView.allowsMultipleSelection =? allowsMultipleSelection
        uiView.borderWidth =? borderWidth
        uiView.borderRadius =? borderRadius
        uiView.isVertical =? isVertical
        uiView.isVerticalSegmentContents =? isVerticalSegmentContents
        uiView.selectedBackgroundColor =? selectedBackgroundColor
       }

Also, the segment views MultiSelectSegment: UIView use this code:

    private func setup() {
    addConstrainedSubview(stackView, constrain: .topMargin, .bottomMargin, .leftMargin, .rightMargin)
    layoutMargins = UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7)
    stackView.spacing = layoutMargins.left
    stackView.isUserInteractionEnabled = false
    stackView.alignment = .center
    isAccessibilityElement = true
    accessibilityTraits = [.button]
    accessibilityIdentifier = "MultiSelectSegment"
}

Here is the code that demonstrates the problem shown above:

   var body: some View {
        VStack(alignment: .center) {
            Text("top")
            Spacer()
            MultiSegmentPicker(
                selectedSegmentIndexes: $selectedSegmentIndexes,
                items: ["First", "Second", "Third", "Done"]
            ).fixedSize()
            Text("bottom")
        }
    }
}

What I expected from this code is that the bar with "First", "Second", etc. would be centered on the bottom, not pushed to the trailing edge, and that it would be above not behind the Text View displaying "bottom"

=== EDIT === Here's the view with the spacer removed and the .center alignment removed as well.

It might be helpful to see the difference...

enter image description here

=== EDIT 2 ==

I want to show a little more "unexpected" behavior. Once you think about it, it isn't unexpected at all. The Multiselector was positioned correctly, but hidden by the bigger view. When I added a regular Picker() object after the Multiselector, the multiselector peeks through... But these two pictures explain a lot:

enter image description here

enter image description here

== Edit 3 ==

Yonat has fixed this bug, it works as expected now! (thanks!)

enter image description here

Mozahler
  • 3,643
  • 6
  • 24
  • 42

2 Answers2

1

The problem seemed to be with intrinsicContentSize. I changed in in version 2.3.3 of the control, and now fixedSize() works fine.

Yonat
  • 3,298
  • 2
  • 21
  • 29
0

The alignment: .center is centering the leading edge of your picker, remove that from VSTack.

remove the Spacer() and it won't be behind, but since it's set up as a stackView SwiftUI won't automatically position it in relation to other elements.

DckBrd
  • 33
  • 6
  • Those were there to illustrate the problem. I've done as you suggested, and edited the question to show the results. It doesn't solve anything, but maybe the situation is clearer now. Thanks for the suggestion. – Mozahler Apr 16 '20 at 19:40
  • wow that really demonstrates the unexpected behavior. I will check out the git and try to mess with the stackview.alignment and stackView.spacing = layoutMargins.left when I get a chance, but I don't know explicitly the why behind why this is happening. – DckBrd Apr 16 '20 at 21:17
  • I added a couple more screenshots which should be helpful in illustrating current behavior. – Mozahler Apr 16 '20 at 22:40