Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RxSwiftを導入したくても、できなかったら?

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

 RxSwiftを導入したくても、できなかったら?

Avatar for Taiki Suzuki

Taiki Suzuki

March 01, 2017
Tweet

More Decks by Taiki Suzuki

Other Decks in Programming

Transcript

  1. UISearchBar + Timer class ViewController: UIViewController, UISearchBarDelegate { @IBOutlet weak

    var searchBar: UISearchBar! private lazy var timer: Timer = { return self.createTimer() }() override func viewDidLoad() { super.viewDidLoad() searchBar.delegate = self } func createTimer() -> Timer { return Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(ViewController.handleTimer(_:)), userInfo: nil, repeats: false) } func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { timer.invalidate() timer = createTimer() } @objc private func handleTimer(_ timer: Timer) { print(self.searchBar.text) } }
  2. UISearchBar + DispatchQueue class ViewController: UIViewController, UISearchBarDelegate { @IBOutlet weak

    var searchBar: UISearchBar! //0.5ඵ͝ͱʹॲཧΛglobalQueueͰߦ͏ClosureΛPropertyͱͯ͠อ࣋ let debounce = DispatchQueue.global().debounce(delay: .milliseconds(500)) override func viewDidLoad() { super.viewDidLoad() searchBar.delegate = self } func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { debounce { print(searchBar.text) } } }
  3. Implementation extension DispatchQueue { func debounce(delay: DispatchTimeInterval) -> (_ action:

    @escaping () -> ()) -> () { var lastFireTime: DispatchTime = .now() return { action in let deadline: DispatchTime = .now() + delay lastFireTime = .now() self.asyncAfter(deadline: deadline) { let now: DispatchTime = .now() let when: DispatchTime = lastFireTime + delay if now < when { return } lastFireTime = .now() action() } } } }
  4. NotificationCenter + RxSwift class ViewController: UIViewController { //some properties private

    var disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.rx.notification(.UIKeyboardWillShow).asDriver(onErrorDriveWith: .empty()) .flatMap { notification -> Driver<UIKeyboardNotificationInfo> in guard let keyboardInfo = UIKeyboardNotificationInfo(from: notification) else { return Driver.empty() } return Driver.just(keyboardInfo) } .drive(onNext: { [unowned self] info in self.keyboardWillAnimate(toDefault: false, with: info) }) .addDisposableTo(disposeBag) } func keyboardWillAnimate(toDefault: Bool, with info: UIKeyboardNotificationInfo) { //Keyboard animation } }
  5. NoticeObserveKit https://github.com/marty-suzuki/NoticeObserveKit class ViewController: UIViewController { //some properties private var

    pool = NoticeObserverPool() override func viewDidLoad() { super.viewDidLoad() UIKeyboardWillShow.observe { [unowned self] in self.keyboardWillAnimate(toDefault: false, with: $0) }.addObserverTo(pool) } }
  6. NoticeObserveKit.NoticeType struct TalkObjectDidUpdate: NoticeType { typealias InfoType = TalkObject static

    let name: Notification.Name = Notification.Name("TalkObjectDidUpdate") } let talk: TalkObject = //... TalkObjectDidUpdate.post(info: talk)
  7. RxPagination https://github.com/tryswift/RxPagination extension Reactive where Base: UIScrollView { var reachedBottom:

    ControlEvent<Void> { let observable = contentOffset .flatMap { [weak base] contentOffset -> Observable<Void> in guard let scrollView = base else { return Observable.empty() } let visibleHeight = scrollView.frame.height - scrollView.contentInset.top - scrollView.contentInset.bottom let y = contentOffset.y + scrollView.contentInset.top let threshold = max(0.0, scrollView.contentSize.height - visibleHeight) return y > threshold ? Observable.just() : Observable.empty() } return ControlEvent(events: observable) } }
  8. Paging + UITableViewDelegate class ViewController: UIViewController, UITableViewDelegate { @IBOutlet weak

    var tableView: UITableView! private var reachedBottom: Bool = false { didSet { if reachedBottom == oldValue { return } guard reachedBottom else { return } didReachBottom() } } override func viewDidLoad() { super.viewDidLoad() tableView.delegate = self } private func didReachBottom() { print("scrollViewDidReachBottom!") } func scrollViewDidScroll(_ scrollView: UIScrollView) { let maxDistance = max(0, scrollView.contentSize.height - scrollView.bounds.size.height) reachedBottom = maxDistance < scrollView.contentOffset.y } }
  9. Paging + ReachExtension class ViewController: UIViewController { @IBOutlet weak var

    tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() tableView.re.delegate = self tableView.re.scrollViewDidReachBottom = { scrollView in print("scrollViewDidReachBottom!") } } } extension ViewController: UITableViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { print("scrollViewDidScroll = \(scrollView.contentOffset.y)") } }
  10. DelegateTransporter.m @interface DelegateTransporter () @property (nonnull, nonatomic, strong) NSHashTable<NSObject *>

    *delegates; @end @implementation DelegateTransporter - (instancetype)initWithDelegates:(NSArray<id> *)delegates { self = [super init]; if (self != nil) { self.delegates = [NSHashTable weakObjectsHashTable]; for (id delegate in delegates) { if (![delegates isKindOfClass:[NSObject class]]) { continue; } [self.delegates addObject:delegate]; } } return self; } //...etc @end
  11. DelegateTransporter.m - (BOOL)respondsToSelector:(SEL)aSelector { for (NSObject *delegate in self.delegates) {

    if ([delegate isKindOfClass:[NSNull class]]) { continue; } if ([delegate respondsToSelector:aSelector]) { return YES; } } return NO; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { for (NSObject *delegate in self.delegates) { if ([delegate isKindOfClass:[NSNull class]]) { continue; } if ([delegate respondsToSelector:aSelector]) { return [delegate methodSignatureForSelector:aSelector]; } } return nil; } - (void)forwardInvocation:(NSInvocation *)anInvocation { for (NSObject *delegate in self.delegates) { if ([delegate isKindOfClass:[NSNull class]]) { continue; } if ([delegate respondsToSelector:anInvocation.selector]) { [anInvocation invokeWithTarget:delegate]; } } }
  12. UITableView.ReachExtension extension UITableView { final class ReachExtension: NSObject { private

    var delegateTransporter: UITableViewDelegateTransporter? { didSet { base?.delegate = delegateTransporter } } weak var delegate: UITableViewDelegate? { didSet { guard let delegate = delegate else { delegateTransporter = nil return } delegateTransporter = UITableViewDelegateTransporter(delegates: [delegate, self]) } } fileprivate var reachedBottom: Bool = false { didSet { if reachedBottom == oldValue { return } guard let base = base, reachedBottom else { return } scrollViewDidReachBottom?(base) } } var scrollViewDidReachBottom: ((UIScrollView) -> ())? private weak var base: UITableView? init(_ base: UITableView) { self.base = base super.init() } } }
  13. UITableView.ReachExtension extension UITableView.ReachExtension: UITableViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) {

    let maxDistance = max(0, scrollView.contentSize.height - scrollView.bounds.size.height) reachedBottom = maxDistance < scrollView.contentOffset.y } }
  14. UITableView extension UITableView { private struct AssociatedKey { static var

    re: UInt8 = 0 } var re: ReachExtension { guard let re = objc_getAssociatedObject(self, &AssociatedKey.re) as? ReachExtension else { let re = ReachExtension(self) objc_setAssociatedObject(self, &AssociatedKey.re, re, .OBJC_ASSOCIATION_RETAIN) return re } return re } }
  15. UITableView.ReverseExtension https://github.com/marty-suzuki/ReverseExtension extension UITableView { final class ReverseExtension: NSObject {

    //some implementation //... private var lastScrollIndicatorInsets: UIEdgeInsets? private var lastContentInset: UIEdgeInsets? private var mutex = pthread_mutex_t() init(_ base: UITableView) { self.base = base super.init() configureTableView(base) } //some implementation //... } }
  16. UITableView.ReverseExtension private func configureTableView(_ tableView: UITableView) { guard let base

    = self.base else { return } if base.transform == CGAffineTransform.identity { UIView.setAnimationsEnabled(false) base.transform = CGAffineTransform.identity.rotated(by: .pi) UIView.setAnimationsEnabled(true) } tableView.addObserver(self, forKeyPath: #keyPath(UITableView.contentInset), options: [.new, .old], context: nil) } public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { switch keyPath { case (#keyPath(UITableView.contentInset))?: DispatchQueue.global().async { [weak self] in DispatchQueue.main.async { self?.configureTableViewInsets() } } default: break } }
  17. UITableView.ReverseExtension private func configureTableViewInsets() { defer { pthread_mutex_unlock(&mutex) } pthread_mutex_lock(&mutex)

    guard let base = base else { return } if let _ = self.lastContentInset, let _ = self.lastScrollIndicatorInsets { return } let contentInset = base.contentInset base.contentInset.bottom = contentInset.top base.contentInset.top = contentInset.bottom self.lastContentInset = base.contentInset let scrollIndicatorInsets = base.scrollIndicatorInsets base.scrollIndicatorInsets.bottom = scrollIndicatorInsets.top base.scrollIndicatorInsets.top = scrollIndicatorInsets.bottom base.scrollIndicatorInsets.right = base.bounds.size.width - 8 self.lastScrollIndicatorInsets = base.scrollIndicatorInsets }
  18. UITableView.ReverseExtension extension UITableView.ReverseExtension: UITableViewDelegate { func tableView(_ tableView: UITableView, willDisplay

    cell: UITableViewCell, forRowAt indexPath: IndexPath) { if cell.transform == CGAffineTransform.identity { UIView.setAnimationsEnabled(false) cell.transform = CGAffineTransform.identity.rotated(by: .pi) UIView.setAnimationsEnabled(true) } } }