mirror of
https://github.com/ReactiveX/RxSwift.git
synced 2024-10-04 05:57:23 +03:00
Merge master into add_rx_tab_bar_controller_delegate_proxy
This commit is contained in:
commit
437c339545
@ -250,6 +250,7 @@ custom_categories:
|
||||
- UITextView+Rx
|
||||
- UIView+Rx
|
||||
- UIViewController+Rx
|
||||
- UIWebView+Rx
|
||||
- name: RxCocoa/iOS/Protocols
|
||||
children:
|
||||
- RxCollectionViewDataSourceType
|
||||
@ -268,6 +269,7 @@ custom_categories:
|
||||
- RxTableViewDelegateProxy
|
||||
- RxTextStorageDelegateProxy
|
||||
- RxTextViewDelegateProxy
|
||||
- RxWebViewDelegateProxy
|
||||
- name: RxCocoa/macOS
|
||||
children:
|
||||
- NSButton+Rx
|
||||
|
13
CHANGELOG.md
13
CHANGELOG.md
@ -7,6 +7,10 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
* Adds `didScroll` and `didZoom` `ControlEvent`s to `UIScrollView+Rx`
|
||||
* Renames `refreshing` to `isRefreshing`.
|
||||
* adds `UIWebView` extensions:
|
||||
* `didStartLoad`
|
||||
* `didFinishLoad`
|
||||
* `didFailLoad`
|
||||
* Adds `willBeginCustomizing`, `willEndCustomizing`, `didEndCustomizing` and `didSelect` to `UITabBarController+Rx`
|
||||
|
||||
## [3.0.1](https://github.com/ReactiveX/RxSwift/releases/tag/3.0.1) (Xcode 8 / Swift 3.0 compatible)
|
||||
@ -44,7 +48,7 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
#### Anomalies
|
||||
|
||||
* Fixes wrong casing in `#import "include/_RXObjCRuntime.h"` (was creating issues for people with
|
||||
* Fixes wrong casing in `#import "include/_RXObjCRuntime.h"` (was creating issues for people with
|
||||
case sensitive file system). #949
|
||||
* Fixes issues with locking strategy for subjects. #936
|
||||
* Fixes code example in comments of RxTableViewExtensions that didn't compile. #947
|
||||
@ -54,7 +58,7 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
* Renames `RxTests` library to `RxTest` because of problems with Swift Package Manager.
|
||||
* Adds Swift Package Manager support
|
||||
* Adds Linux support
|
||||
* Adds Linux support
|
||||
* Replaces `AnyObserver` with `UIBindingObserver` in public interface.
|
||||
* Renames `resourceCount` to `Resources.total`.
|
||||
* Makes `rx.text` type consistent with UIKit `String?` type.
|
||||
@ -99,7 +103,7 @@ text.drive(label.rx.text)
|
||||
|
||||
* Add `rx.` extensions on Types.
|
||||
|
||||
* Moves `UIImagePickerViewController` and `CLLocationManager` out of `RxCocoa` to `RxExample` project because of App Store submissions issues
|
||||
* Moves `UIImagePickerViewController` and `CLLocationManager` out of `RxCocoa` to `RxExample` project because of App Store submissions issues
|
||||
on iOS 10.
|
||||
|
||||
* Adds `sentMessage` got its equivalent sequence `methodInvoked` that produces elements after method is invoked (vs before method is invoked).
|
||||
@ -130,7 +134,7 @@ any observers or `forwardToDelegate` wasn't implementing `UITableViewDataSource.
|
||||
|
||||
* Update Getting Started document, section on creating an observable that performs work to Swift 3.0.
|
||||
|
||||
* Removes stale installation instructions.
|
||||
* Removes stale installation instructions.
|
||||
|
||||
## [3.0.0-beta.1](https://github.com/ReactiveX/RxSwift/releases/tag/3.0.0-beta.1) (Xcode 8 GM compatible 8A218a)
|
||||
|
||||
@ -145,6 +149,7 @@ any observers or `forwardToDelegate` wasn't implementing `UITableViewDataSource.
|
||||
* `textDidEndEditing`
|
||||
* Moves `CLLocationManager` and `UIImagePickerViewController` extensions from RxCocoa to RxExample project. #874
|
||||
* Adds matrix CI builds.
|
||||
=======
|
||||
|
||||
## [3.0.0.alpha.1](https://github.com/ReactiveX/RxSwift/releases/tag/3.0.0.alpha.1) (Xcode 8 beta 6 compatible 8S201h)
|
||||
|
||||
|
@ -16,6 +16,9 @@
|
||||
1AF67DA81CED430100C310FA /* ReplaySubjectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF67DA51CED430100C310FA /* ReplaySubjectTest.swift */; };
|
||||
271A97411CFC996B00D64125 /* UIViewController+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271A97401CFC996B00D64125 /* UIViewController+Rx.swift */; };
|
||||
271A97441CFC9F7B00D64125 /* UIViewControler+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271A97421CFC99FE00D64125 /* UIViewControler+RxTests.swift */; };
|
||||
4613456F1D9A4467001ABAF2 /* UIWebView+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4613456E1D9A4467001ABAF2 /* UIWebView+RxTests.swift */; };
|
||||
461345711D9A4543001ABAF2 /* UIWebView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 461345701D9A4543001ABAF2 /* UIWebView+Rx.swift */; };
|
||||
4613457C1D9A4AEE001ABAF2 /* RxWebViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4613457B1D9A4AEE001ABAF2 /* RxWebViewDelegateProxy.swift */; };
|
||||
46307D4E1CDE77D800E47A1C /* UIAlertAction+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46307D4D1CDE77D800E47A1C /* UIAlertAction+Rx.swift */; };
|
||||
46307D4F1CDE77D800E47A1C /* UIAlertAction+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46307D4D1CDE77D800E47A1C /* UIAlertAction+Rx.swift */; };
|
||||
54700CA01CE37E1800EF3A8F /* UINavigationItem+RxTests.swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54700C9E1CE37D1000EF3A8F /* UINavigationItem+RxTests.swift.swift */; };
|
||||
@ -1459,6 +1462,9 @@
|
||||
1AF67DA51CED430100C310FA /* ReplaySubjectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplaySubjectTest.swift; sourceTree = "<group>"; };
|
||||
271A97401CFC996B00D64125 /* UIViewController+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Rx.swift"; sourceTree = "<group>"; };
|
||||
271A97421CFC99FE00D64125 /* UIViewControler+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewControler+RxTests.swift"; sourceTree = "<group>"; };
|
||||
4613456E1D9A4467001ABAF2 /* UIWebView+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIWebView+RxTests.swift"; sourceTree = "<group>"; };
|
||||
461345701D9A4543001ABAF2 /* UIWebView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIWebView+Rx.swift"; sourceTree = "<group>"; };
|
||||
4613457B1D9A4AEE001ABAF2 /* RxWebViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxWebViewDelegateProxy.swift; sourceTree = "<group>"; };
|
||||
46307D4D1CDE77D800E47A1C /* UIAlertAction+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIAlertAction+Rx.swift"; sourceTree = "<group>"; };
|
||||
54700C9E1CE37D1000EF3A8F /* UINavigationItem+RxTests.swift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINavigationItem+RxTests.swift.swift"; sourceTree = "<group>"; };
|
||||
54D2138C1CE081890028D5B4 /* UINavigationItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINavigationItem+Rx.swift"; sourceTree = "<group>"; };
|
||||
@ -2363,6 +2369,12 @@
|
||||
C8F27DB11CE6711600D5FB4F /* UITextView+RxTests.swift */,
|
||||
C83508F11C38706D0027C24C /* UIView+RxTests.swift */,
|
||||
271A97421CFC99FE00D64125 /* UIViewControler+RxTests.swift */,
|
||||
844BC8B71CE5023200F5C7CB /* UIPickerView+RxTests.swift */,
|
||||
914FCD661CCDB82E0058B304 /* UIPageControl+RxTest.swift */,
|
||||
033C2EF41D081B2A0050C015 /* UIScrollView+RxTests.swift */,
|
||||
C8379EF31D1DD326003EF8FC /* UIButton+RxTests.swift */,
|
||||
C8A9B6F31DAD752200C9B027 /* Observable+BindTests.swift */,
|
||||
4613456E1D9A4467001ABAF2 /* UIWebView+RxTests.swift */,
|
||||
);
|
||||
path = RxCocoaTests;
|
||||
sourceTree = "<group>";
|
||||
@ -2567,6 +2579,7 @@
|
||||
54D2138C1CE081890028D5B4 /* UINavigationItem+Rx.swift */,
|
||||
844BC8B31CE4FD7500F5C7CB /* UIPickerView+Rx.swift */,
|
||||
46307D4D1CDE77D800E47A1C /* UIAlertAction+Rx.swift */,
|
||||
461345701D9A4543001ABAF2 /* UIWebView+Rx.swift */,
|
||||
);
|
||||
path = iOS;
|
||||
sourceTree = "<group>";
|
||||
@ -2612,6 +2625,7 @@
|
||||
84C225A21C33F00B008724EC /* RxTextStorageDelegateProxy.swift */,
|
||||
846436E11C9AF64C0035B40D /* RxSearchControllerDelegateProxy.swift */,
|
||||
844BC8AA1CE4FA5600F5C7CB /* RxPickerViewDelegateProxy.swift */,
|
||||
4613457B1D9A4AEE001ABAF2 /* RxWebViewDelegateProxy.swift */,
|
||||
);
|
||||
path = Proxies;
|
||||
sourceTree = "<group>";
|
||||
@ -3559,6 +3573,7 @@
|
||||
C882542F1B8A752B00B02D69 /* UIScrollView+Rx.swift in Sources */,
|
||||
844BC8B41CE4FD7500F5C7CB /* UIPickerView+Rx.swift in Sources */,
|
||||
C89AB20E1DAAC3350065FBE6 /* Logging.swift in Sources */,
|
||||
461345711D9A4543001ABAF2 /* UIWebView+Rx.swift in Sources */,
|
||||
C8093EE31B8A732E0088E94D /* DelegateProxyType.swift in Sources */,
|
||||
C8093EFD1B8A732E0088E94D /* RxTarget.swift in Sources */,
|
||||
C88254361B8A752B00B02D69 /* UITextView+Rx.swift in Sources */,
|
||||
@ -3636,6 +3651,7 @@
|
||||
C882541A1B8A752B00B02D69 /* RxCollectionViewDataSourceType.swift in Sources */,
|
||||
C88254351B8A752B00B02D69 /* UITextField+Rx.swift in Sources */,
|
||||
C89AB1FE1DAAC3350065FBE6 /* UIBindingObserver.swift in Sources */,
|
||||
4613457C1D9A4AEE001ABAF2 /* RxWebViewDelegateProxy.swift in Sources */,
|
||||
C88254301B8A752B00B02D69 /* UISearchBar+Rx.swift in Sources */,
|
||||
C89AB2121DAAC3350065FBE6 /* NSNotificationCenter+Rx.swift in Sources */,
|
||||
C88254181B8A752B00B02D69 /* ItemEvents.swift in Sources */,
|
||||
@ -3786,6 +3802,8 @@
|
||||
C83509361C38706E0027C24C /* NSLayoutConstraint+RxTests.swift in Sources */,
|
||||
C835095E1C38706E0027C24C /* Observable+StandardSequenceOperatorsTest.swift in Sources */,
|
||||
C8C4F1631DE9D0A800003FA7 /* UIProgressView+RxTests.swift in Sources */,
|
||||
4613456F1D9A4467001ABAF2 /* UIWebView+RxTests.swift in Sources */,
|
||||
C83509691C38706E0027C24C /* Recorded+Timeless.swift in Sources */,
|
||||
C835094C1C38706E0027C24C /* AssumptionsTest.swift in Sources */,
|
||||
C835092D1C38706E0027C24C /* Control+RxTests.swift in Sources */,
|
||||
C83509601C38706E0027C24C /* Observable+TimeTest.swift in Sources */,
|
||||
|
@ -20,20 +20,34 @@ public class RxScrollViewDelegateProxy
|
||||
, UIScrollViewDelegate
|
||||
, DelegateProxyType {
|
||||
|
||||
fileprivate var _contentOffsetSubject: ReplaySubject<CGPoint>?
|
||||
fileprivate var _contentOffsetBehaviorSubject: BehaviorSubject<CGPoint>?
|
||||
fileprivate var _contentOffsetPublishSubject: PublishSubject<()>?
|
||||
|
||||
/// Typed parent object.
|
||||
public weak fileprivate(set) var scrollView: UIScrollView?
|
||||
|
||||
/// Optimized version used for observing content offset changes.
|
||||
internal var contentOffsetSubject: Observable<CGPoint> {
|
||||
if _contentOffsetSubject == nil {
|
||||
let replaySubject = ReplaySubject<CGPoint>.create(bufferSize:1)
|
||||
_contentOffsetSubject = replaySubject
|
||||
replaySubject.on(.next(self.scrollView?.contentOffset ?? CGPoint.zero))
|
||||
internal var contentOffsetBehaviorSubject: BehaviorSubject<CGPoint> {
|
||||
if let subject = _contentOffsetBehaviorSubject {
|
||||
return subject
|
||||
}
|
||||
|
||||
return _contentOffsetSubject!
|
||||
|
||||
let subject = BehaviorSubject<CGPoint>(value: self.scrollView?.contentOffset ?? CGPoint.zero)
|
||||
_contentOffsetBehaviorSubject = subject
|
||||
|
||||
return subject
|
||||
}
|
||||
|
||||
/// Optimized version used for observing content offset changes.
|
||||
internal var contentOffsetPublishSubject: PublishSubject<()> {
|
||||
if let subject = _contentOffsetPublishSubject {
|
||||
return subject
|
||||
}
|
||||
|
||||
let subject = PublishSubject<()>()
|
||||
_contentOffsetPublishSubject = subject
|
||||
|
||||
return subject
|
||||
}
|
||||
|
||||
/// Initializes `RxScrollViewDelegateProxy`
|
||||
@ -48,8 +62,11 @@ public class RxScrollViewDelegateProxy
|
||||
|
||||
/// For more information take a look at `DelegateProxyType`.
|
||||
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if let contentOffset = _contentOffsetSubject {
|
||||
contentOffset.on(.next(scrollView.contentOffset))
|
||||
if let subject = _contentOffsetBehaviorSubject {
|
||||
subject.on(.next(scrollView.contentOffset))
|
||||
}
|
||||
if let subject = _contentOffsetPublishSubject {
|
||||
subject.on(.next())
|
||||
}
|
||||
self._forwardToDelegate?.scrollViewDidScroll?(scrollView)
|
||||
}
|
||||
@ -76,8 +93,12 @@ public class RxScrollViewDelegateProxy
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let contentOffset = _contentOffsetSubject {
|
||||
contentOffset.on(.completed)
|
||||
if let subject = _contentOffsetBehaviorSubject {
|
||||
subject.on(.completed)
|
||||
}
|
||||
|
||||
if let subject = _contentOffsetPublishSubject {
|
||||
subject.on(.completed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
40
RxCocoa/iOS/Proxies/RxWebViewDelegateProxy.swift
Normal file
40
RxCocoa/iOS/Proxies/RxWebViewDelegateProxy.swift
Normal file
@ -0,0 +1,40 @@
|
||||
//
|
||||
// RxWebViewDelegateProxy.swift
|
||||
// RxCocoa
|
||||
//
|
||||
// Created by Andrew Breckenridge on 9/26/16.
|
||||
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
#if !RX_NO_MODULE
|
||||
import RxSwift
|
||||
#endif
|
||||
|
||||
public class RxWebViewDelegateProxy
|
||||
: DelegateProxy
|
||||
, DelegateProxyType
|
||||
, UIWebViewDelegate {
|
||||
|
||||
/**
|
||||
For more information take a look at `DelegateProxyType`.
|
||||
*/
|
||||
public class func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {
|
||||
let webView: UIWebView = castOrFatalError(object)
|
||||
webView.delegate = castOptionalOrFatalError(delegate)
|
||||
}
|
||||
|
||||
/**
|
||||
For more information take a look at `DelegateProxyType`.
|
||||
*/
|
||||
public class func currentDelegateFor(_ object: AnyObject) -> AnyObject? {
|
||||
let webView: UIWebView = castOrFatalError(object)
|
||||
return webView.delegate
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -6,8 +6,6 @@
|
||||
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
|
||||
import Foundation
|
||||
|
@ -23,6 +23,15 @@ extension Reactive where Base: UIBarButtonItem {
|
||||
UIElement.isEnabled = value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Bindable sink for `title` property.
|
||||
*/
|
||||
public var title: UIBindingObserver<Base, String> {
|
||||
return UIBindingObserver(UIElement: self.base) { UIElement, value in
|
||||
UIElement.title = value
|
||||
}
|
||||
}
|
||||
|
||||
/// Reactive wrapper for target action pattern on `self`.
|
||||
public var tap: ControlEvent<Void> {
|
||||
|
@ -42,7 +42,7 @@ extension Reactive where Base: UIScrollView {
|
||||
scrollView.contentOffset = contentOffset
|
||||
}
|
||||
|
||||
return ControlProperty(values: proxy.contentOffsetSubject, valueSink: bindingObserver)
|
||||
return ControlProperty(values: proxy.contentOffsetBehaviorSubject, valueSink: bindingObserver)
|
||||
}
|
||||
|
||||
/// Bindable sink for `scrollEnabled` property.
|
||||
@ -54,7 +54,7 @@ extension Reactive where Base: UIScrollView {
|
||||
|
||||
/// Reactive wrapper for delegate method `scrollViewDidScroll`
|
||||
public var didScroll: ControlEvent<Void> {
|
||||
let source = RxScrollViewDelegateProxy.proxyForObject(base).contentOffsetSubject.map { _ in () }
|
||||
let source = RxScrollViewDelegateProxy.proxyForObject(base).contentOffsetPublishSubject
|
||||
return ControlEvent(events: source)
|
||||
}
|
||||
|
||||
|
55
RxCocoa/iOS/UIWebView+Rx.swift
Normal file
55
RxCocoa/iOS/UIWebView+Rx.swift
Normal file
@ -0,0 +1,55 @@
|
||||
//
|
||||
// UIWebView+Rx.swift
|
||||
// RxCocoa
|
||||
//
|
||||
// Created by Andrew Breckenridge on 8/30/16.
|
||||
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
#if !RX_NO_MODULE
|
||||
import RxSwift
|
||||
#endif
|
||||
|
||||
extension Reactive where Base: UIWebView {
|
||||
|
||||
/**
|
||||
Reactive wrapper for `delegate`.
|
||||
For more information take a look at `DelegateProxyType` protocol documentation.
|
||||
*/
|
||||
public var delegate: DelegateProxy {
|
||||
return RxWebViewDelegateProxy.proxyForObject(base)
|
||||
}
|
||||
/**
|
||||
Reactive wrapper for `delegate` message.
|
||||
*/
|
||||
public var didStartLoad: Observable<Void> {
|
||||
return delegate
|
||||
.methodInvoked(#selector(UIWebViewDelegate.webViewDidStartLoad(_:)))
|
||||
.map {_ in}
|
||||
}
|
||||
/**
|
||||
Reactive wrapper for `delegate` message.
|
||||
*/
|
||||
public var didFinishLoad: Observable<Void> {
|
||||
return delegate
|
||||
.methodInvoked(#selector(UIWebViewDelegate.webViewDidFinishLoad(_:)))
|
||||
.map {_ in}
|
||||
}
|
||||
/**
|
||||
Reactive wrapper for `delegate` message.
|
||||
*/
|
||||
public var didFailLoad: Observable<Error> {
|
||||
return delegate
|
||||
.methodInvoked(#selector(UIWebViewDelegate.webView(_:didFailLoadWithError:)))
|
||||
.map { a in
|
||||
return try castOrThrow(Error.self, a[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,6 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11542" systemVersion="16B2555" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="ew7-Ty-fzC">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11524"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@ -14,53 +18,9 @@
|
||||
<viewControllerLayoutGuide type="bottom" id="Zb9-9p-7u0"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="oOI-v8-i8g">
|
||||
<rect key="frame" x="0.0" y="64" width="375" height="667"/>
|
||||
<rect key="frame" x="0.0" y="64" width="375" height="603"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7Wy-Xx-ged">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="603"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Geolocation is not enabled for this app" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QDz-hL-6ve">
|
||||
<rect key="frame" x="47.5" y="168" width="280" height="67.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="280" id="OAO-CK-RYp"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle1"/>
|
||||
<color key="textColor" red="0.94901960780000005" green="0.32549019610000002" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Please enable it in the app preferences" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cbw-8G-pl6">
|
||||
<rect key="frame" x="47.5" y="243.5" width="280" height="42.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="q8m-1B-H0o">
|
||||
<rect key="frame" x="87.5" y="316" width="200" height="28"/>
|
||||
<color key="backgroundColor" red="0.17483745805369111" green="0.60840499161073824" blue="0.84705882349999995" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="200" id="qDe-NM-H36"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="13"/>
|
||||
<state key="normal" title="OPEN APP PREFERENCES"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
|
||||
<integer key="value" value="3"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="q8m-1B-H0o" firstAttribute="top" secondItem="cbw-8G-pl6" secondAttribute="bottom" constant="30" id="8Pd-KG-yad"/>
|
||||
<constraint firstItem="QDz-hL-6ve" firstAttribute="centerY" secondItem="7Wy-Xx-ged" secondAttribute="centerY" constant="-100" id="CP1-Ta-e39"/>
|
||||
<constraint firstItem="cbw-8G-pl6" firstAttribute="width" secondItem="QDz-hL-6ve" secondAttribute="width" id="OXx-2G-Yaz"/>
|
||||
<constraint firstItem="q8m-1B-H0o" firstAttribute="centerX" secondItem="7Wy-Xx-ged" secondAttribute="centerX" id="Qni-Ne-BEo"/>
|
||||
<constraint firstItem="cbw-8G-pl6" firstAttribute="centerX" secondItem="7Wy-Xx-ged" secondAttribute="centerX" id="Thp-mo-mXr"/>
|
||||
<constraint firstItem="cbw-8G-pl6" firstAttribute="top" secondItem="QDz-hL-6ve" secondAttribute="bottom" constant="8" id="ehi-8n-5Gy"/>
|
||||
<constraint firstItem="QDz-hL-6ve" firstAttribute="centerX" secondItem="7Wy-Xx-ged" secondAttribute="centerX" id="iQe-C8-u67"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JL2-pm-xcV">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="603"/>
|
||||
<subviews>
|
||||
@ -122,14 +82,10 @@
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="7Wy-Xx-ged" firstAttribute="top" secondItem="oOI-v8-i8g" secondAttribute="top" id="0j8-qY-okH"/>
|
||||
<constraint firstItem="7Wy-Xx-ged" firstAttribute="leading" secondItem="oOI-v8-i8g" secondAttribute="leading" id="5KT-7g-gmw"/>
|
||||
<constraint firstItem="JL2-pm-xcV" firstAttribute="leading" secondItem="oOI-v8-i8g" secondAttribute="leading" id="Al3-6E-7TX"/>
|
||||
<constraint firstAttribute="trailing" secondItem="JL2-pm-xcV" secondAttribute="trailing" id="TJu-9q-C3r"/>
|
||||
<constraint firstItem="JL2-pm-xcV" firstAttribute="top" secondItem="ouX-MF-szB" secondAttribute="bottom" id="UH9-TU-sn1"/>
|
||||
<constraint firstItem="Zb9-9p-7u0" firstAttribute="top" secondItem="7Wy-Xx-ged" secondAttribute="bottom" id="X8x-Ij-bEV"/>
|
||||
<constraint firstItem="Zb9-9p-7u0" firstAttribute="top" secondItem="JL2-pm-xcV" secondAttribute="bottom" id="eBw-6B-iSn"/>
|
||||
<constraint firstAttribute="trailing" secondItem="7Wy-Xx-ged" secondAttribute="trailing" id="jPm-IL-4ry"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" id="nwM-mR-Dgg"/>
|
||||
@ -139,10 +95,54 @@
|
||||
<outlet property="button2" destination="3q9-a2-ojh" id="kww-B7-jeL"/>
|
||||
<outlet property="label" destination="Edb-tp-cly" id="Icm-cQ-9QB"/>
|
||||
<outlet property="noGeolocationView" destination="7Wy-Xx-ged" id="tce-XP-hMK"/>
|
||||
<outlet property="view" destination="oOI-v8-i8g" id="g8m-Rt-ZGi"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="yOr-7R-tPd" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<view contentMode="scaleToFill" id="7Wy-Xx-ged">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="603"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Geolocation is not enabled for this app" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QDz-hL-6ve">
|
||||
<rect key="frame" x="48" y="168.5" width="280" height="67.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="280" id="OAO-CK-RYp"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle1"/>
|
||||
<color key="textColor" red="0.94901960780000005" green="0.32549019610000002" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Please enable it in the app preferences" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cbw-8G-pl6">
|
||||
<rect key="frame" x="48" y="244" width="280" height="43"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="q8m-1B-H0o">
|
||||
<rect key="frame" x="88" y="317" width="200" height="28"/>
|
||||
<color key="backgroundColor" red="0.17483745805369111" green="0.60840499161073824" blue="0.84705882349999995" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="200" id="qDe-NM-H36"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="13"/>
|
||||
<state key="normal" title="OPEN APP PREFERENCES"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
|
||||
<integer key="value" value="3"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="q8m-1B-H0o" firstAttribute="top" secondItem="cbw-8G-pl6" secondAttribute="bottom" constant="30" id="8Pd-KG-yad"/>
|
||||
<constraint firstItem="QDz-hL-6ve" firstAttribute="centerY" secondItem="7Wy-Xx-ged" secondAttribute="centerY" constant="-100" id="CP1-Ta-e39"/>
|
||||
<constraint firstItem="cbw-8G-pl6" firstAttribute="width" secondItem="QDz-hL-6ve" secondAttribute="width" id="OXx-2G-Yaz"/>
|
||||
<constraint firstItem="q8m-1B-H0o" firstAttribute="centerX" secondItem="7Wy-Xx-ged" secondAttribute="centerX" id="Qni-Ne-BEo"/>
|
||||
<constraint firstItem="cbw-8G-pl6" firstAttribute="centerX" secondItem="7Wy-Xx-ged" secondAttribute="centerX" id="Thp-mo-mXr"/>
|
||||
<constraint firstItem="cbw-8G-pl6" firstAttribute="top" secondItem="QDz-hL-6ve" secondAttribute="bottom" constant="8" id="ehi-8n-5Gy"/>
|
||||
<constraint firstItem="QDz-hL-6ve" firstAttribute="centerX" secondItem="7Wy-Xx-ged" secondAttribute="centerX" id="iQe-C8-u67"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="805" y="341"/>
|
||||
</scene>
|
||||
|
@ -14,48 +14,35 @@ import CoreLocation
|
||||
#endif
|
||||
|
||||
private extension Reactive where Base: UILabel {
|
||||
var driveCoordinates: UIBindingObserver<Base, CLLocationCoordinate2D> {
|
||||
var coordinates: UIBindingObserver<Base, CLLocationCoordinate2D> {
|
||||
return UIBindingObserver(UIElement: base) { label, location in
|
||||
label.text = "Lat: \(location.latitude)\nLon: \(location.longitude)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension Reactive where Base: UIView {
|
||||
var driveAuthorization: UIBindingObserver<Base, Bool> {
|
||||
return UIBindingObserver(UIElement: base) { view, authorized in
|
||||
if authorized {
|
||||
view.isHidden = true
|
||||
view.superview?.sendSubview(toBack:view)
|
||||
}
|
||||
else {
|
||||
view.isHidden = false
|
||||
view.superview?.bringSubview(toFront:view)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GeolocationViewController: ViewController {
|
||||
|
||||
@IBOutlet weak private var noGeolocationView: UIView!
|
||||
@IBOutlet weak private var button: UIButton!
|
||||
@IBOutlet weak private var button2: UIButton!
|
||||
@IBOutlet weak var label: UILabel!
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.addSubview(noGeolocationView)
|
||||
|
||||
let geolocationService = GeolocationService.instance
|
||||
|
||||
|
||||
geolocationService.authorized
|
||||
.drive(noGeolocationView.rx.driveAuthorization)
|
||||
.drive(noGeolocationView.rx.isHidden)
|
||||
.addDisposableTo(disposeBag)
|
||||
|
||||
geolocationService.location
|
||||
.drive(label.rx.driveCoordinates)
|
||||
.drive(label.rx.coordinates)
|
||||
.addDisposableTo(disposeBag)
|
||||
|
||||
|
||||
button.rx.tap
|
||||
.bindNext { [weak self] in
|
||||
self?.openAppPreferences()
|
||||
@ -72,5 +59,5 @@ class GeolocationViewController: ViewController {
|
||||
private func openAppPreferences() {
|
||||
UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
1
Sources/RxCocoa/RxWebViewDelegateProxy.swift
Symbolic link
1
Sources/RxCocoa/RxWebViewDelegateProxy.swift
Symbolic link
@ -0,0 +1 @@
|
||||
../../RxCocoa/iOS/Proxies/RxWebViewDelegateProxy.swift
|
1
Sources/RxCocoa/UIWebView+Rx.swift
Symbolic link
1
Sources/RxCocoa/UIWebView+Rx.swift
Symbolic link
@ -0,0 +1 @@
|
||||
../../RxCocoa/iOS/UIWebView+Rx.swift
|
@ -62,6 +62,7 @@ extension RxSearchControllerDelegateProxy: TestDelegateProtocol {
|
||||
}
|
||||
extension RxPickerViewDelegateProxy: TestDelegateProtocol {
|
||||
}
|
||||
extension RxWebViewDelegateProxy: TestDelegateProtocol {}
|
||||
#endif
|
||||
|
||||
// MARK: Tests
|
||||
@ -133,6 +134,16 @@ extension DelegateProxyTest {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// MARK: UIWebView
|
||||
#if os(iOS)
|
||||
extension DelegateProxyTest {
|
||||
func test_UIWebViewDelegateExtension() {
|
||||
performDelegateTest(UIWebViewSubclass(frame: CGRect.zero))
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// MARK: Mocks
|
||||
|
||||
class ExtendTableViewDelegateProxy
|
||||
@ -467,5 +478,30 @@ class UIPickerViewSubclass
|
||||
onProxyForObject: self)
|
||||
}
|
||||
}
|
||||
|
||||
class UIWebViewSubclass: UIWebView, TestDelegateControl {
|
||||
|
||||
func doThatTest(_ value: Int) {
|
||||
(delegate as! TestDelegateProtocol).testEventHappened?(value)
|
||||
}
|
||||
|
||||
var testSentMessage: Observable<Int> {
|
||||
return rx.delegate
|
||||
.sentMessage(#selector(TestDelegateProtocol.testEventHappened(_:)))
|
||||
.map { a in (a[0] as! NSNumber).intValue }
|
||||
}
|
||||
var testMethodInvoked: Observable<Int> {
|
||||
return rx.delegate
|
||||
.methodInvoked(#selector(TestDelegateProtocol.testEventHappened(_:)))
|
||||
.map { a in (a[0] as! NSNumber).intValue }
|
||||
}
|
||||
|
||||
func setMineForwardDelegate(_ testDelegate: TestDelegateProtocol) -> Disposable {
|
||||
return RxWebViewDelegateProxy.installForwardDelegate(testDelegate,
|
||||
retainDelegate: false,
|
||||
onProxyForObject: self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -21,4 +21,13 @@ extension UIBarButtonItemTests {
|
||||
func testBarButtonItem_DelegateEventCompletesOnDealloc() {
|
||||
ensureEventDeallocated({ UIBarButtonItem() }) { (view: UIBarButtonItem) in view.rx.tap }
|
||||
}
|
||||
|
||||
func testButton_titleObserver() {
|
||||
let button = UIBarButtonItem()
|
||||
XCTAssertEqual(button.title, nil)
|
||||
let text = "title"
|
||||
_ = Observable.just(text).bindTo(button.rx.title)
|
||||
|
||||
XCTAssertEqual(button.title, text)
|
||||
}
|
||||
}
|
||||
|
@ -41,17 +41,52 @@ extension UIScrollViewTests {
|
||||
}
|
||||
|
||||
func testScrollViewDidScroll() {
|
||||
let scrollView = UIScrollView()
|
||||
var didScroll = false
|
||||
var completed = false
|
||||
|
||||
autoreleasepool {
|
||||
let scrollView = UIScrollView()
|
||||
var didScroll = false
|
||||
|
||||
let subscription = scrollView.rx.didScroll.subscribe(onNext: {
|
||||
didScroll = true
|
||||
})
|
||||
_ = scrollView.rx.didScroll.subscribe(onNext: {
|
||||
didScroll = true
|
||||
}, onCompleted: {
|
||||
completed = true
|
||||
})
|
||||
|
||||
scrollView.delegate!.scrollViewDidScroll!(scrollView)
|
||||
XCTAssertFalse(didScroll)
|
||||
|
||||
XCTAssertTrue(didScroll)
|
||||
subscription.dispose()
|
||||
scrollView.delegate!.scrollViewDidScroll!(scrollView)
|
||||
|
||||
XCTAssertTrue(didScroll)
|
||||
}
|
||||
|
||||
XCTAssertTrue(completed)
|
||||
}
|
||||
|
||||
func testScrollViewContentOffset() {
|
||||
var completed = false
|
||||
|
||||
autoreleasepool {
|
||||
let scrollView = UIScrollView()
|
||||
scrollView.contentOffset = .zero
|
||||
|
||||
var contentOffset = CGPoint(x: -1, y: -1)
|
||||
|
||||
_ = scrollView.rx.contentOffset.subscribe(onNext: { value in
|
||||
contentOffset = value
|
||||
}, onCompleted: {
|
||||
completed = true
|
||||
})
|
||||
|
||||
XCTAssertEqual(contentOffset, .zero)
|
||||
|
||||
scrollView.contentOffset = CGPoint(x: 2, y: 2)
|
||||
scrollView.delegate!.scrollViewDidScroll!(scrollView)
|
||||
|
||||
XCTAssertEqual(contentOffset, CGPoint(x: 2, y: 2))
|
||||
}
|
||||
|
||||
XCTAssertTrue(completed)
|
||||
}
|
||||
|
||||
func testScrollViewDidZoom() {
|
||||
@ -62,6 +97,8 @@ extension UIScrollViewTests {
|
||||
didZoom = true
|
||||
})
|
||||
|
||||
XCTAssertFalse(didZoom)
|
||||
|
||||
scrollView.delegate!.scrollViewDidZoom!(scrollView)
|
||||
|
||||
XCTAssertTrue(didZoom)
|
||||
@ -76,6 +113,8 @@ extension UIScrollViewTests {
|
||||
didScrollToTop = true
|
||||
})
|
||||
|
||||
XCTAssertFalse(didScrollToTop)
|
||||
|
||||
scrollView.delegate!.scrollViewDidScrollToTop!(scrollView)
|
||||
|
||||
XCTAssertTrue(didScrollToTop)
|
||||
|
68
Tests/RxCocoaTests/UIWebView+RxTests.swift
Normal file
68
Tests/RxCocoaTests/UIWebView+RxTests.swift
Normal file
@ -0,0 +1,68 @@
|
||||
//
|
||||
// UIWebView+RxTests.swift
|
||||
// Tests
|
||||
//
|
||||
// Created by Andrew Breckenridge on 8/30/16.
|
||||
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import RxBlocking
|
||||
import XCTest
|
||||
|
||||
class UIWebViewTests: RxTest {}
|
||||
|
||||
fileprivate let testHTMLString = "<html><head></head><body><h1>🔥</h1></body></html>"
|
||||
|
||||
extension UIWebViewTests {
|
||||
|
||||
func testDidStartLoad() {
|
||||
let webView = UIWebView()
|
||||
var didStartLoad = false
|
||||
|
||||
let subscription = webView.rx.didStartLoad.subscribe(onNext: {
|
||||
didStartLoad = true
|
||||
})
|
||||
|
||||
webView.delegate!.webViewDidStartLoad!(webView)
|
||||
|
||||
XCTAssertTrue(didStartLoad)
|
||||
subscription.dispose()
|
||||
}
|
||||
|
||||
func testDidFinishLoad() {
|
||||
let webView = UIWebView()
|
||||
var didFinishLoad = false
|
||||
|
||||
let subscription = webView.rx.didFinishLoad.subscribe(onNext: {
|
||||
didFinishLoad = true
|
||||
})
|
||||
|
||||
webView.delegate!.webViewDidFinishLoad!(webView)
|
||||
|
||||
XCTAssertTrue(didFinishLoad)
|
||||
subscription.dispose()
|
||||
}
|
||||
|
||||
func testDidFailLoad() {
|
||||
let webView = UIWebView()
|
||||
var didFailLoad = false
|
||||
|
||||
let subscription = webView.rx.didFailLoad.subscribe { _ in
|
||||
didFailLoad = true
|
||||
}
|
||||
|
||||
webView.delegate!.webView!(webView, didFailLoadWithError: NSError(domain: "", code: 0, userInfo: .none))
|
||||
|
||||
XCTAssertTrue(didFailLoad)
|
||||
subscription.dispose()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user