Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash after providing accessibilityScrollStatus(for scrollView: UIScrollView) #398

Open
Ma-He opened this issue Apr 8, 2021 · 2 comments

Comments

@Ma-He
Copy link

Ma-He commented Apr 8, 2021

We are trying to override the default iOS VoiceOver read out during the scrolling of a UITableView with RxTableViewSectionedReloadDataSource binded to it.
After extending our ViewController to UIScrollViewAccessibilityDelegate and implementing

func accessibilityScrollStatus(for scrollView: UIScrollView) -> String? {
    return "Some status"
}

we will receive a crash when VoiceOver is enabled and we are trying to scroll the list.

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[RxCocoa.RxTableViewDelegateProxy accessibilityScrollStatusForScrollView:]: unrecognized selector sent to instance 0x282a966f0'

To recreate the error, you can simply display this VoiceOverScrollStatusViewController and scroll down the list with VoiceOver enabled.

import Foundation
import UIKit
import RxSwift
import RxCocoa
import RxDataSources

struct CustomData {
    var anInt: Int
    var aString: String
}

struct SectionOfCustomData {
    var header: String
    var items: [Item]
}

extension SectionOfCustomData: SectionModelType {
    typealias Item = CustomData
    
    init(original: SectionOfCustomData, items: [Item]) {
        self = original
        self.items = items
    }
}

class VoiceOverScrollStatusViewController: UIViewController {
    private let tableView: UITableView
    private let disposeBag = DisposeBag()
    
    let rxDataSource = RxTableViewSectionedReloadDataSource<SectionOfCustomData> { (_, tableView, _, itemSource) -> UITableViewCell in
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") else {
            return UITableViewCell()
        }
        
        cell.textLabel?.text = "Cell \(itemSource.anInt): \(itemSource.aString)"
        return cell
    }
    
    init() {
        if #available(iOS 13.0, *) {
            tableView = UITableView(frame: .zero, style: .insetGrouped)
        } else {
            tableView = UITableView(frame: .zero, style: .grouped)
        }
        
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - view lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupTableView()
        
        rxDataSource.titleForHeaderInSection = { dataSource, index in
            return dataSource.sectionModels[index].header
        }
        
        let sections = [
            SectionOfCustomData(header: "First Section", items: [.init(anInt: 1, aString: "First"),
                                                                 .init(anInt: 2, aString: "Zweiter"),
                                                                 .init(anInt: 3, aString: "Third")]),
            SectionOfCustomData(header: "Second Section", items: [.init(anInt: 1, aString: "First"),
                                                                 .init(anInt: 2, aString: "Zweiter")]),
            SectionOfCustomData(header: "Third Section", items: [.init(anInt: 1, aString: "First"),
                                                                 .init(anInt: 2, aString: "Zweiter"),
                                                                 .init(anInt: 3, aString: "Third")]),
            SectionOfCustomData(header: "Fourth Section", items: [.init(anInt: 1, aString: "First")]),
            SectionOfCustomData(header: "Fifth Section", items: [.init(anInt: 1, aString: "First"),
                                                                 .init(anInt: 2, aString: "Second"),
                                                                 .init(anInt: 3, aString: "Third")])
        ]
        
        Observable.just(sections)
            .bind(to: tableView.rx.items(dataSource: rxDataSource))
            .disposed(by: disposeBag)
    }
    
    private func setupTableView() {
        tableView.backgroundColor = UIColor(hex: "#F3F5F8")
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        tableView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tableView)
        
        if #available(iOS 11.0, *) {
            let guide = view.safeAreaLayoutGuide
            tableView.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
            tableView.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true
            tableView.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
            tableView.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
        } else {
            tableView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        }
        
        tableView.delegate = self
    }
}

extension VoiceOverScrollStatusViewController: UITableViewDelegate {
    
}

extension VoiceOverScrollStatusViewController: UIScrollViewAccessibilityDelegate {
    func accessibilityScrollStatus(for scrollView: UIScrollView) -> String? {
        return "Some status"
    }
}
@ddosang
Copy link

ddosang commented Feb 27, 2024

I have same issue.

@i6abesz
Copy link

i6abesz commented Jun 20, 2024

This works:

extension RxScrollViewDelegateProxy: UIScrollViewAccessibilityDelegate {
    func accessibilityScrollStatus(for scrollView: UIScrollView) -> String? {
        return "Some status"
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants