2

I have a horizontal UIStackView with several buttons inside. The number of button is not constant and the length of stack view may exceed screen width.

enter image description here

I use the structure UIScrollView - UIView - UIStackView to right align when the length is shorter than screen width, and left align when the length exceeds.

Right align: enter image description here Left align: enter image description here

When VoiceOver is on, it only read buttons in the screen.

I tried to add all buttons to accessibilityElements. VoiceOver can read all buttons but did not scroll the stack view to show them. And when swipe from the element below the stack view, VoiceOver still ignore buttons not in the screen. So adding to accessibilityElements cannot resolve this issue.

Anybody knows how to scroll stack view to show hiding buttons when user swipes screen? Thanks in advance.

ArgenBarbie
  • 571
  • 1
  • 4
  • 10
  • @XLE_22 I tried the same as your edited answer, but it shows error: Scroll View - Need constraints for: X(Y) position or width(height). – ArgenBarbie Oct 14 '20 at 08:53
  • weird because my screenshots are the exact results of what I coded under Xcode 11 and it worked as desired. I'll take a look when I have more time but take a look at the constraints I showed to be sure you have exactly the same. – XLE_22 Oct 14 '20 at 08:59
  • @XLE_22 Please check the below screenshot focusing on "My scroll view", there's the error icon. – ArgenBarbie Oct 14 '20 at 15:37

2 Answers2

1

Anybody knows how to scroll stack view to show hiding buttons when user swipe screen?

Embed the stack view inside a scroll view to be able to get the elements outside the screen: you can do this with in the Interface Builder or programmatically.

Here's my configuration under Xcode to swipe buttons with VoiceOver inside a UIStackView: enter image description here enter image description here And I finally get the following result on my device: enter image description here I haven't played with alignment in the UIStackView but, with this rationale, you can scroll stack view to show hiding buttons when user swipes screen.

⚠️ ⬛️◼️▪️ EDIT ▪️◼️⬛️ ⚠️ (February 2020: after added new constraints in the original question)

The alignment is my primary need.

As explained in my comment, the layout constraints are the solution to reach your goal without using a dedicated UIView.

I detail the Interface Builder configuration hereunder: enter image description here enter image description here And with few lines of code as follows:

@IBOutlet weak var myScrollView: UIScrollView!
@IBOutlet weak var myStackView: UIStackView!

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    let delta = myScrollView.frame.width - myStackView.frame.width
    let constant = (delta > 0.0) ? delta : 0.0

    myStackView.leadingAnchor.constraint(greaterThanOrEqualTo: myScrollView.contentLayoutGuide.leadingAnchor,
                                         constant: constant).isActive = true
}

... you get this result with 5 buttons: enter image description here ... and the right alignment is kept with few buttons: enter image description here

Now, you can scroll UIStackView horizontally when VoiceOver swipe.

XLE_22
  • 5,124
  • 3
  • 21
  • 72
  • This doesn't work for my case. It won't scroll automatically when there's a UIView between UIScrollView and UIStackView. – ArgenBarbie Feb 06 '20 at 02:25
  • Directly embed the stack view in a scroll view cannot make it right align when its length is shorter than screen width and left align when it's longer. The alignment is my primary need, so I have to put a UIView between to make this possible. – ArgenBarbie Feb 06 '20 at 09:14
0

The solution is to subclass UIButton and override its accessibilityElementDidBecomeFocused method.

MyButton.h

@interface MyButton : UIButton

@property (weak, nonatomic) UIScrollView *scrollView;

@end

MyButton.m

#import "MyButton.h"

@implementation MyButton

- (void)accessibilityElementDidBecomeFocused
{
    [self.scrollView scrollRectToVisible:CGRectMake(self.frame.origin.x, self.frame.origin.y, self.bounds.size.width, self.bounds.size.height) animated:YES];
}

@end
ArgenBarbie
  • 571
  • 1
  • 4
  • 10