Сделать наблюдаемым для UIButton свойство isHighlighted

Я пытаюсь создать isHighlighted Observable для моего UIButton, чтобы отправлять последовательность каждый раз, когда isHiglighted для UIButton изменился. И я написал что-то вроде этого

extension Reactive where Base: UIButton {

    var isHighlighted: Observable<Bool> {

        let property = self.base.rx.controlProperty(editingEvents: .allTouchEvents,
                                                    getter: { _ in self.base.isHighlighted },
                                                    setter: { (_, _) in })
        return property
            .distinctUntilChanged()
            .asObservable()
    }
}

Проблема в том, что это не работает для .touchUpInside. Если я проведу пальцем за пределы UIButton, а затем вернусь, он будет работать нормально, но не для действия касания. Я думаю, что сразу после .touchUpInside он все еще выделяется на очень короткое время.


person Michal Rogowski    schedule 02.02.2018    source источник
comment
Я надеялся найти документ Apple, в котором конкретно говорится об этом, но не смог. Я обнаружил, что устанавливает состояние кнопки на highlighted - Например, когда пользователь нажимает кнопку с заголовком, кнопка переходит в выделенное состояние. - но не то, что устанавливает его на ... default? Но имеет смысл (по крайней мере, для меня), что пока что-то еще не получит фокус (если использовать термин MSFT), операционная система может быть спроектирована так, чтобы удерживать фокус на кнопке. Таким образом, задержка при удалении выделенного состояния.   -  person dfd    schedule 03.02.2018
comment
@dfd Есть ли у вас другие идеи, как создать Observable для свойства isHighlighted UIButton? Без подкласса UIButton   -  person Michal Rogowski    schedule 03.02.2018
comment
Я не работаю с [rx-swift], но не могли бы вы создать простое расширение для UIButton и переопределить isHightlighted, делая то, что вам нужно, в didSet. Я просто попробовал это, и он построен без ошибок.   -  person dfd    schedule 03.02.2018


Ответы (2)


Спасибо @iWheelBuy Я создал код, который работает без RxOptional, я частично основал его на вашем ответе, поэтому спасибо! Вот рабочий код:

extension Reactive where Base: UIButton {
    var isHighlighted: Observable<Bool> {
        let anyObservable = self.base.rx.methodInvoked(#selector(setter: self.base.isHighlighted))

        let boolObservable = anyObservable
            .flatMap { Observable.from(optional: $0.first as? Bool) }
            .startWith(self.base.isHighlighted)
            .distinctUntilChanged()
            .share()

        return boolObservable
    }
}
person Michal Rogowski    schedule 03.02.2018
comment
Отлично! Я бы выбрал methodInvoked для реализации по умолчанию вместо sentMessage. Но решать тебе ???? - person iWheelBuy; 04.02.2018
comment
Вы правы, он должен вызываться после вызова метода, спасибо! - person Michal Rogowski; 04.02.2018

Думаю, у меня есть решение. Это можно упростить, но я просто копирую и вставляю полное решение, которое у меня есть.

public extension Reactive where Base: UIButton {

    public func isHighlighted() -> Observable<Bool> {
        let selector = #selector(setter: UIButton.isHighlighted)
        let value: ([Any]) -> Bool? = { $0.first(where: { $0 is Bool }) as? Bool }
        return base
            .observable(selector: selector, value: value)
            .filterNil()
            .startWith(base.isHighlighted)
            .distinctUntilChanged()
            .share(replay: 1, scope: .whileConnected)
    }
}

Кроме того, чтобы он работал. Вам понадобится RxOptional и дополнительный код:

public enum InvocationTime: Int {

    case afterMessageIsInvoked
    case beforeMessageIsInvoked
}

а также

public extension NSObject {

    public func observable<T>(selector: Selector, value: @escaping ([Any]) -> T, when: InvocationTime = .afterMessageIsInvoked) -> Observable<T> {
        let observable: Observable<[Any]> = {
            switch when {
            case .afterMessageIsInvoked:
                return rx.methodInvoked(selector)
            case .beforeMessageIsInvoked:
                return rx.sentMessage(selector)
            }
        }()
        return observable
            .map({ value($0) })
            .share(replay: 1, scope: .whileConnected)
    }
}

Надеюсь, это поможет (^

person iWheelBuy    schedule 03.02.2018