├── Cartfile
├── Cartfile.resolved
├── Gemfile
├── Example
├── Sources
│ ├── Models
│ │ ├── User.swift
│ │ └── Message.swift
│ ├── AppDelegate.swift
│ ├── Views
│ │ ├── MessageCell.swift
│ │ └── MessageInputBar.swift
│ └── ViewControllers
│ │ └── MessageListViewController.swift
├── RxKeyboardExample.xcodeproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── project.pbxproj
├── RxKeyboardExample.xcworkspace
│ └── contents.xcworkspacedata
├── Podfile
├── README.md
├── Resources
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ └── Base.lproj
│ │ └── LaunchScreen.storyboard
├── Supporting Files
│ └── Info.plist
└── Podfile.lock
├── Package.resolved
├── .gitignore
├── Package.swift
├── RxKeyboard.podspec
├── LICENSE
├── scripts
└── carthage.sh
├── RxKeyboard.json
├── .travis.yml
├── Gemfile.lock
├── README.md
└── Sources
└── RxKeyboard
└── RxKeyboard.swift
/Cartfile:
--------------------------------------------------------------------------------
1 | github "ReactiveX/RxSwift" ~> 6.0.0
2 |
--------------------------------------------------------------------------------
/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "ReactiveX/RxSwift" "6.0.0"
2 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'cocoapods', '~> 1.10'
4 | gem 'swiftproj'
5 |
--------------------------------------------------------------------------------
/Example/Sources/Models/User.swift:
--------------------------------------------------------------------------------
1 | //
2 | // User.swift
3 | // RxKeyboard
4 | //
5 | // Created by Suyeol Jeon on 18/01/2017.
6 | // Copyright © 2017 Suyeol Jeon. All rights reserved.
7 | //
8 |
9 | enum User {
10 | case other
11 | case me
12 | }
13 |
--------------------------------------------------------------------------------
/Example/Sources/Models/Message.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Message.swift
3 | // RxKeyboard
4 | //
5 | // Created by Suyeol Jeon on 18/01/2017.
6 | // Copyright © 2017 Suyeol Jeon. All rights reserved.
7 | //
8 |
9 | struct Message {
10 | var user: User
11 | var text: String
12 | }
13 |
--------------------------------------------------------------------------------
/Example/RxKeyboardExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/RxKeyboardExample.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Example/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '10.0'
2 |
3 | target 'RxKeyboardExample' do
4 | use_frameworks!
5 |
6 | pod 'RxKeyboard', :path => '../'
7 | pod 'Then'
8 | pod 'UITextView+Placeholder'
9 | pod 'ReusableKit'
10 | pod 'SnapKit'
11 | pod 'ManualLayout'
12 | pod 'SwiftyColor'
13 | pod 'SwiftyImage'
14 | pod 'CGFloatLiteral'
15 | end
16 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "RxSwift",
6 | "repositoryURL": "https://github.com/ReactiveX/RxSwift.git",
7 | "state": {
8 | "branch": null,
9 | "revision": "c8742ed97fc2f0c015a5ea5eddefb064cd7532d2",
10 | "version": "6.0.0"
11 | }
12 | }
13 | ]
14 | },
15 | "version": 1
16 | }
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 |
3 | sourcekitten-output.json
4 | docs/
5 | /*.xcodeproj
6 | **/xcuserdata
7 | **/xcshareddata
8 | *.xcconfig
9 | *.framework.zip
10 |
11 | # CocoaPods
12 |
13 | Pods/
14 | Examples/**/Podfile.lock
15 |
16 | # Carthage
17 |
18 | Carthage/
19 |
20 | # Various
21 |
22 | .DS_Store
23 |
24 | # Swift Package Manager
25 |
26 | .build/
27 | Packages/
28 | .swiftpm
29 |
30 | # AppCode
31 |
32 | .idea/
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.1
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "RxKeyboard",
7 | products: [
8 | .library(name: "RxKeyboard", targets: ["RxKeyboard"]),
9 | ],
10 | dependencies: [
11 | .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "6.0.0")),
12 | ],
13 | targets: [
14 | .target(name: "RxKeyboard", dependencies: ["RxSwift", "RxCocoa"]),
15 | ]
16 | )
17 |
--------------------------------------------------------------------------------
/Example/README.md:
--------------------------------------------------------------------------------
1 | # RxKeyboardExample
2 |
3 | This is an example messenger application using RxKeyboard.
4 |
5 | 
6 |
7 | ## Building Project
8 |
9 | 1. Install CocoaPods libraries.
10 |
11 | ```console
12 | $ pod install
13 | ```
14 |
15 | 2. Open **`RxKeyboardExample.xcworkspace`** file.
16 | 3. Press ⌘ + R to build and run the project.
17 |
--------------------------------------------------------------------------------
/RxKeyboard.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'RxKeyboard'
3 | s.version = '2.0.0'
4 | s.summary = 'Reactive Keyboard in iOS'
5 | s.homepage = 'https://github.com/RxSwiftCommunity/RxKeyboard'
6 | s.license = { :type => 'MIT', :file => 'LICENSE' }
7 | s.author = { 'Suyeol Jeon' => 'devxoul@gmail.com' }
8 | s.source = { :git => 'https://github.com/RxSwiftCommunity/RxKeyboard.git',
9 | :tag => s.version.to_s }
10 | s.source_files = 'Sources/**/*.swift'
11 | s.frameworks = 'UIKit'
12 | s.requires_arc = true
13 | s.swift_version = "5.1"
14 |
15 | s.dependency 'RxSwift', '~> 6.0'
16 | s.dependency 'RxCocoa', '~> 6.0'
17 |
18 | s.ios.deployment_target = '9.0'
19 | end
20 |
--------------------------------------------------------------------------------
/Example/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Suyeol Jeon (xoul.kr)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/scripts/carthage.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # carthage.sh
4 | # Usage example: ./carthage.sh build --platform iOS
5 |
6 | set -euo pipefail
7 |
8 | xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX)
9 | trap 'rm -f "$xcconfig"' INT TERM HUP EXIT
10 |
11 | # For Xcode 12 make sure EXCLUDED_ARCHS is set to arm architectures otherwise
12 | # the build will fail on lipo due to duplicate architectures.
13 |
14 | CURRENT_XCODE_VERSION=$(xcodebuild -version | grep "Build version" | cut -d' ' -f3)
15 | echo "EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200__BUILD_$CURRENT_XCODE_VERSION = arm64 arm64e armv7 armv7s armv6 armv8" >> $xcconfig
16 |
17 | echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200 = $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200__BUILD_$(XCODE_PRODUCT_BUILD_VERSION))' >> $xcconfig
18 | echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' >> $xcconfig
19 |
20 | export XCODE_XCCONFIG_FILE="$xcconfig"
21 | carthage build "$@"
--------------------------------------------------------------------------------
/RxKeyboard.json:
--------------------------------------------------------------------------------
1 | {
2 | "0.7.0": "https://github.com/RxSwiftCommunity/RxKeyboard/releases/download/0.7.0/RxKeyboard.framework.zip",
3 | "0.7.1": "https://github.com/RxSwiftCommunity/RxKeyboard/releases/download/0.7.1/RxKeyboard.framework.zip",
4 | "0.8.0": "https://github.com/RxSwiftCommunity/RxKeyboard/releases/download/0.8.0/RxKeyboard.framework.zip",
5 | "0.8.1": "https://github.com/RxSwiftCommunity/RxKeyboard/releases/download/0.8.1/RxKeyboard.framework.zip",
6 | "0.8.2": "https://github.com/RxSwiftCommunity/RxKeyboard/releases/download/0.8.2/RxKeyboard.framework.zip",
7 | "0.8.3": "https://github.com/RxSwiftCommunity/RxKeyboard/releases/download/0.8.3/RxKeyboard.framework.zip",
8 | "0.9.0": "https://github.com/RxSwiftCommunity/RxKeyboard/releases/download/0.9.0/RxKeyboard.framework.zip",
9 | "0.9.1": "https://github.com/RxSwiftCommunity/RxKeyboard/releases/download/0.9.1/RxKeyboard.framework.zip",
10 | "0.9.2": "https://github.com/RxSwiftCommunity/RxKeyboard/releases/download/0.9.2/RxKeyboard.framework.zip",
11 | "1.0.0": "https://github.com/RxSwiftCommunity/RxKeyboard/releases/download/1.0.0/RxKeyboard.framework.zip",
12 | "2.0.0": "https://github.com/RxSwiftCommunity/RxKeyboard/releases/download/2.0.0/RxKeyboard.framework.zip",
13 | }
14 |
--------------------------------------------------------------------------------
/Example/Supporting Files/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIRequiredDeviceCapabilities
26 |
27 | armv7
28 |
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/Example/Sources/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // RxKeyboardExample
4 | //
5 | // Created by Suyeol Jeon on 09/10/2016.
6 | // Copyright © 2016 Suyeol Jeon. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | import CGFloatLiteral
12 | import ManualLayout
13 | import SnapKit
14 | import SwiftyColor
15 | import Then
16 | import UITextView_Placeholder
17 |
18 | @UIApplicationMain
19 | final class AppDelegate: UIResponder, UIApplicationDelegate {
20 |
21 | var window: UIWindow?
22 |
23 | #if swift(>=4.2)
24 | typealias ApplicationLaunchOptionsKey = UIApplication.LaunchOptionsKey
25 | #else
26 | typealias ApplicationLaunchOptionsKey = UIApplicationLaunchOptionsKey
27 | #endif
28 |
29 | func application(
30 | _ application: UIApplication,
31 | didFinishLaunchingWithOptions launchOptions: [ApplicationLaunchOptionsKey: Any]?
32 | ) -> Bool {
33 | let window = UIWindow(frame: UIScreen.main.bounds)
34 | window.backgroundColor = .white
35 | window.makeKeyAndVisible()
36 |
37 | let messageListViewController = MessageListViewController()
38 | let navigationController = UINavigationController(rootViewController: messageListViewController)
39 | window.rootViewController = navigationController
40 |
41 | self.window = window
42 | return true
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/Example/Resources/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Example/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - CGFloatLiteral (0.5.0)
3 | - ManualLayout (1.3.0)
4 | - ReusableKit (3.0.0):
5 | - ReusableKit/Core (= 3.0.0)
6 | - ReusableKit/Core (3.0.0)
7 | - RxCocoa (6.0.0):
8 | - RxRelay (= 6.0.0)
9 | - RxSwift (= 6.0.0)
10 | - RxKeyboard (2.0.0):
11 | - RxCocoa (~> 6.0)
12 | - RxSwift (~> 6.0)
13 | - RxRelay (6.0.0):
14 | - RxSwift (= 6.0.0)
15 | - RxSwift (6.0.0)
16 | - SnapKit (5.0.1)
17 | - SwiftyColor (1.2.1)
18 | - SwiftyImage (1.6.0)
19 | - Then (2.7.0)
20 | - "UITextView+Placeholder (1.4.0)"
21 |
22 | DEPENDENCIES:
23 | - CGFloatLiteral
24 | - ManualLayout
25 | - ReusableKit
26 | - RxKeyboard (from `../`)
27 | - SnapKit
28 | - SwiftyColor
29 | - SwiftyImage
30 | - Then
31 | - "UITextView+Placeholder"
32 |
33 | SPEC REPOS:
34 | trunk:
35 | - CGFloatLiteral
36 | - ManualLayout
37 | - ReusableKit
38 | - RxCocoa
39 | - RxRelay
40 | - RxSwift
41 | - SnapKit
42 | - SwiftyColor
43 | - SwiftyImage
44 | - Then
45 | - "UITextView+Placeholder"
46 |
47 | EXTERNAL SOURCES:
48 | RxKeyboard:
49 | :path: "../"
50 |
51 | SPEC CHECKSUMS:
52 | CGFloatLiteral: 0328648f666e3cb2263d5ee3972df9d320786d25
53 | ManualLayout: 68ac8cfa6b5f656f7a9fadec3730208b95986880
54 | ReusableKit: e5f853ad4652e411f96b6119b2488afa12929be6
55 | RxCocoa: 3f79328fafa3645b34600f37c31e64c73ae3a80e
56 | RxKeyboard: aefd4787ca8be28a4470cb871141fb50e105f900
57 | RxRelay: 8d593be109c06ea850df027351beba614b012ffb
58 | RxSwift: c14e798c59b9f6e9a2df8fd235602e85cc044295
59 | SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb
60 | SwiftyColor: a2f468071c1b96be1ba41a0be7cf70d645cd7dcc
61 | SwiftyImage: 65c71a147f417fc0fe137d17b135aaa666279a09
62 | Then: acfe0be7e98221c6204e12f8161459606d5d822d
63 | "UITextView+Placeholder": d7b0c400921f66523f3a85d74f774512e14f6502
64 |
65 | PODFILE CHECKSUM: 217b0b1c7b2faddc54272a5bde63843c54f48190
66 |
67 | COCOAPODS: 1.10.0
68 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | osx_image: xcode12.3
2 | language: objective-c
3 | sudo: required
4 | env:
5 | global:
6 | - PROJECT="RxKeyboard.xcodeproj"
7 | - SCHEME="RxKeyboard-Package"
8 | - IOS_SDK="iphonesimulator"
9 | - MACOS_SDK="macosx11.0"
10 | - TVOS_SDK="appletvsimulator9.0"
11 | - WATCHOS_SDK="watchsimulator3.0"
12 | - FRAMEWORK="RxKeyboard"
13 | matrix:
14 | - SDK="$IOS_SDK" TEST=0 SWIFT_VERSION=5.1 DESTINATION="platform=iOS Simulator,name=iPhone 8"
15 |
16 | install:
17 | - swift --version
18 | - bundle install
19 |
20 | before_script:
21 | - set -o pipefail
22 | - swift package generate-xcodeproj
23 |
24 | script:
25 | - if [ $TEST == 1 ]; then
26 | xcodebuild clean SWIFT_VERSION=${SWIFT_VERSION} build test
27 | -project "$PROJECT"
28 | -scheme "$SCHEME"
29 | -sdk "$SDK"
30 | -destination "$DESTINATION"
31 | -configuration Debug
32 | -enableCodeCoverage YES
33 | CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty -c;
34 | else
35 | xcodebuild clean SWIFT_VERSION=${SWIFT_VERSION} build
36 | -project "$PROJECT"
37 | -scheme "$SCHEME"
38 | -sdk "$SDK"
39 | -destination "$DESTINATION"
40 | -configuration Debug
41 | CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty -c;
42 | fi
43 | - bundle exec pod repo update
44 | - bundle exec pod lib lint --swift-version=${SWIFT_VERSION} --verbose --allow-warnings --fail-fast
45 |
46 | before_deploy:
47 | - bundle exec swiftproj generate-xcconfig --podspec RxKeyboard.podspec
48 | - bundle exec swiftproj generate-xcodeproj --xcconfig-overrides Config.xcconfig
49 | - bundle exec swiftproj configure-scheme --project RxKeyboard.xcodeproj --scheme RxKeyboard-Package --buildable-targets RxKeyboard
50 | - bundle exec swiftproj remove-framework --project RxKeyboard.xcodeproj --target RxKeyboard --framework RxCocoa.framework
51 | - bundle exec swiftproj remove-framework --project RxKeyboard.xcodeproj --target RxKeyboard --framework RxCocoaRuntime.framework
52 | - ./carthage.sh bootstrap
53 | - carthage build --no-skip-current --verbose | xcpretty -c
54 | - carthage archive RxKeyboard
55 |
56 | deploy:
57 | provider: releases
58 | api_key: $GITHUB_ACCESS_TOKEN
59 | file: $FRAMEWORK.framework.zip
60 | skip_cleanup: true
61 | on:
62 | repo: RxSwiftCommunity/RxKeyboard
63 | tags: true
64 | condition: $SDK = $IOS_SDK
65 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | CFPropertyList (3.0.3)
5 | activesupport (5.2.4.4)
6 | concurrent-ruby (~> 1.0, >= 1.0.2)
7 | i18n (>= 0.7, < 2)
8 | minitest (~> 5.1)
9 | tzinfo (~> 1.1)
10 | addressable (2.8.0)
11 | public_suffix (>= 2.0.2, < 5.0)
12 | algoliasearch (1.27.5)
13 | httpclient (~> 2.8, >= 2.8.3)
14 | json (>= 1.5.1)
15 | atomos (0.1.3)
16 | claide (1.0.3)
17 | cocoapods (1.10.0)
18 | addressable (~> 2.6)
19 | claide (>= 1.0.2, < 2.0)
20 | cocoapods-core (= 1.10.0)
21 | cocoapods-deintegrate (>= 1.0.3, < 2.0)
22 | cocoapods-downloader (>= 1.4.0, < 2.0)
23 | cocoapods-plugins (>= 1.0.0, < 2.0)
24 | cocoapods-search (>= 1.0.0, < 2.0)
25 | cocoapods-trunk (>= 1.4.0, < 2.0)
26 | cocoapods-try (>= 1.1.0, < 2.0)
27 | colored2 (~> 3.1)
28 | escape (~> 0.0.4)
29 | fourflusher (>= 2.3.0, < 3.0)
30 | gh_inspector (~> 1.0)
31 | molinillo (~> 0.6.6)
32 | nap (~> 1.0)
33 | ruby-macho (~> 1.4)
34 | xcodeproj (>= 1.19.0, < 2.0)
35 | cocoapods-core (1.10.0)
36 | activesupport (> 5.0, < 6)
37 | addressable (~> 2.6)
38 | algoliasearch (~> 1.0)
39 | concurrent-ruby (~> 1.1)
40 | fuzzy_match (~> 2.0.4)
41 | nap (~> 1.0)
42 | netrc (~> 0.11)
43 | public_suffix
44 | typhoeus (~> 1.0)
45 | cocoapods-deintegrate (1.0.4)
46 | cocoapods-downloader (1.4.0)
47 | cocoapods-plugins (1.0.0)
48 | nap
49 | cocoapods-search (1.0.0)
50 | cocoapods-trunk (1.5.0)
51 | nap (>= 0.8, < 2.0)
52 | netrc (~> 0.11)
53 | cocoapods-try (1.2.0)
54 | colored2 (3.1.2)
55 | concurrent-ruby (1.1.7)
56 | escape (0.0.4)
57 | ethon (0.12.0)
58 | ffi (>= 1.3.0)
59 | ffi (1.14.2)
60 | fourflusher (2.3.1)
61 | fuzzy_match (2.0.4)
62 | gh_inspector (1.1.3)
63 | httpclient (2.8.3)
64 | i18n (1.8.6)
65 | concurrent-ruby (~> 1.0)
66 | json (2.5.1)
67 | minitest (5.14.2)
68 | molinillo (0.6.6)
69 | nanaimo (0.3.0)
70 | nap (1.1.0)
71 | netrc (0.11.0)
72 | public_suffix (4.0.6)
73 | ruby-macho (1.4.0)
74 | swiftproj (0.1.0)
75 | colored2 (>= 3.0)
76 | xcodeproj (>= 1.5)
77 | thread_safe (0.3.6)
78 | typhoeus (1.4.0)
79 | ethon (>= 0.9.0)
80 | tzinfo (1.2.9)
81 | thread_safe (~> 0.1)
82 | xcodeproj (1.19.0)
83 | CFPropertyList (>= 2.3.3, < 4.0)
84 | atomos (~> 0.1.3)
85 | claide (>= 1.0.2, < 2.0)
86 | colored2 (~> 3.1)
87 | nanaimo (~> 0.3.0)
88 |
89 | PLATFORMS
90 | ruby
91 |
92 | DEPENDENCIES
93 | cocoapods (~> 1.10)
94 | swiftproj
95 |
96 | BUNDLED WITH
97 | 2.2.4
98 |
--------------------------------------------------------------------------------
/Example/Sources/Views/MessageCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MessageCell.swift
3 | // RxKeyboard
4 | //
5 | // Created by Suyeol Jeon on 18/01/2017.
6 | // Copyright © 2017 Suyeol Jeon. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | import SwiftyImage
12 |
13 | final class MessageCell: UICollectionViewCell {
14 |
15 | // MARK: Types
16 |
17 | fileprivate enum BalloonAlignment {
18 | case left
19 | case right
20 | }
21 |
22 |
23 | // MARK: Constants
24 |
25 | struct Metric {
26 | static let maximumBalloonWidth = 240.f
27 | static let balloonViewInset = 10.f
28 | }
29 |
30 | struct Font {
31 | static let label = UIFont.systemFont(ofSize: 14)
32 | }
33 |
34 |
35 | // MARK: Properties
36 |
37 | fileprivate var balloonAlignment: BalloonAlignment = .left
38 |
39 |
40 | // MARK: UI
41 |
42 | fileprivate let otherBalloonViewImage = UIImage.resizable()
43 | .corner(radius: 5)
44 | .color(0xD9D9D9.color)
45 | .image
46 | fileprivate let myBalloonViewImage = UIImage.resizable()
47 | .corner(radius: 5)
48 | .color(0x1680FA.color)
49 | .image
50 |
51 | let balloonView = UIImageView()
52 | let label = UILabel().then {
53 | $0.font = Font.label
54 | $0.numberOfLines = 0
55 | }
56 |
57 |
58 | // MARK: Initializing
59 |
60 | override init(frame: CGRect) {
61 | super.init(frame: frame)
62 | self.contentView.addSubview(self.balloonView)
63 | self.contentView.addSubview(self.label)
64 | }
65 |
66 | required init?(coder aDecoder: NSCoder) {
67 | fatalError("init(coder:) has not been implemented")
68 | }
69 |
70 |
71 | // MARK: Configuring
72 |
73 | func configure(message: Message) {
74 | self.label.text = message.text
75 |
76 | switch message.user {
77 | case .other:
78 | self.balloonAlignment = .left
79 | self.balloonView.image = self.otherBalloonViewImage
80 | self.label.textColor = .black
81 |
82 | case .me:
83 | self.balloonAlignment = .right
84 | self.balloonView.image = self.myBalloonViewImage
85 | self.label.textColor = .white
86 | }
87 |
88 | self.setNeedsLayout()
89 | }
90 |
91 |
92 | // MARK: Size
93 |
94 | class func size(thatFitsWidth width: CGFloat, forMessage message: Message) -> CGSize {
95 | let labelWidth = Metric.maximumBalloonWidth - Metric.balloonViewInset * 2
96 | let constraintSize = CGSize(width: labelWidth, height: CGFloat.greatestFiniteMagnitude)
97 | let options: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading]
98 | let rect = message.text.boundingRect(with: constraintSize,
99 | options: options,
100 | attributes: [.font: Font.label],
101 | context: nil)
102 | let labelHeight = ceil(rect.height)
103 | return CGSize(width: width, height: labelHeight + Metric.balloonViewInset * 2)
104 | }
105 |
106 |
107 | // MARK: Layout
108 |
109 | override func layoutSubviews() {
110 | super.layoutSubviews()
111 |
112 | self.label.width = Metric.maximumBalloonWidth - Metric.balloonViewInset * 2
113 | self.label.sizeToFit()
114 |
115 | self.balloonView.width = self.label.width + Metric.balloonViewInset * 2
116 | self.balloonView.height = self.label.height + Metric.balloonViewInset * 2
117 |
118 | switch self.balloonAlignment {
119 | case .left:
120 | self.balloonView.left = 10
121 | case .right:
122 | self.balloonView.right = self.contentView.width - 10
123 | }
124 |
125 | self.label.top = self.balloonView.top + Metric.balloonViewInset
126 | self.label.left = self.balloonView.left + Metric.balloonViewInset
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/Example/Sources/Views/MessageInputBar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MessageInputBar.swift
3 | // RxKeyboard
4 | //
5 | // Created by Suyeol Jeon on 18/01/2017.
6 | // Copyright © 2017 Suyeol Jeon. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | import RxCocoa
12 | import RxSwift
13 | import RxKeyboard
14 |
15 | final class MessageInputBar: UIView {
16 |
17 | // MARK: Properties
18 |
19 | private let disposeBag = DisposeBag()
20 |
21 |
22 | // MARK: UI
23 |
24 | let toolbar = UIToolbar()
25 | let textView = UITextView().then {
26 | $0.placeholder = "Say Hi!"
27 | $0.isEditable = true
28 | $0.showsVerticalScrollIndicator = false
29 | $0.layer.borderColor = UIColor.lightGray.withAlphaComponent(0.6).cgColor
30 | $0.layer.borderWidth = 1 / UIScreen.main.scale
31 | $0.layer.cornerRadius = 3
32 | }
33 | let sendButton = UIButton(type: .system).then {
34 | $0.titleLabel?.font = UIFont.boldSystemFont(ofSize: UIFont.systemFontSize)
35 | $0.setTitle("Send", for: .normal)
36 | }
37 |
38 |
39 | // MARK: Initializing
40 |
41 | override init(frame: CGRect) {
42 | super.init(frame: frame)
43 | self.addSubview(self.toolbar)
44 | self.addSubview(self.textView)
45 | self.addSubview(self.sendButton)
46 |
47 | self.toolbar.snp.makeConstraints { make in
48 | make.edges.equalTo(0)
49 | }
50 |
51 | self.textView.snp.makeConstraints { make in
52 | make.top.left.equalTo(7)
53 | make.right.equalTo(self.sendButton.snp.left).offset(-7)
54 | make.bottom.equalTo(-7)
55 | }
56 |
57 | self.sendButton.snp.makeConstraints { make in
58 | make.top.equalTo(7)
59 | make.bottom.equalTo(-7)
60 | make.right.equalTo(-7)
61 | }
62 |
63 | self.textView.rx.text
64 | .map { text in text?.isEmpty == false }
65 | .bind(to: self.sendButton.rx.isEnabled)
66 | .disposed(by: self.disposeBag)
67 |
68 | RxKeyboard.instance.visibleHeight
69 | .map { $0 > 0 }
70 | .distinctUntilChanged().drive(onNext: { [weak self] (visible) in
71 | guard let `self` = self else {
72 | return
73 | }
74 |
75 | var bottomInset = 0.f
76 | if #available(iOS 11.0, *), !visible, let bottom = self.superview?.safeAreaInsets.bottom {
77 | bottomInset = bottom
78 | }
79 |
80 | self.toolbar.snp.remakeConstraints({ (make) in
81 | make.left.right.top.equalTo(0)
82 | make.bottom.equalTo(bottomInset)
83 | })
84 | })
85 | .disposed(by: self.disposeBag)
86 | }
87 |
88 | @available(iOS 11.0, *)
89 | override func safeAreaInsetsDidChange() {
90 | super.safeAreaInsetsDidChange()
91 | guard let bottomInset = self.superview?.safeAreaInsets.bottom else {
92 | return
93 | }
94 |
95 | self.toolbar.snp.remakeConstraints { make in
96 | make.top.left.right.equalTo(0)
97 | make.bottom.equalTo(bottomInset)
98 | }
99 | }
100 |
101 | required init?(coder aDecoder: NSCoder) {
102 | fatalError("init(coder:) has not been implemented")
103 | }
104 |
105 |
106 | // MARK: Size
107 |
108 | override var intrinsicContentSize: CGSize {
109 | return CGSize(width: self.width, height: 44)
110 | }
111 |
112 | }
113 |
114 |
115 | // MARK: - Reactive
116 |
117 | extension Reactive where Base: MessageInputBar {
118 |
119 | var sendButtonTap: ControlEvent {
120 | let source: Observable = self.base.sendButton.rx.tap
121 | .withLatestFrom(self.base.textView.rx.text.asObservable())
122 | .flatMap { text -> Observable in
123 | if let text = text, !text.isEmpty {
124 | return .just(text)
125 | } else {
126 | return .empty()
127 | }
128 | }
129 | .do(onNext: { [weak base = self.base] _ in
130 | base?.textView.text = nil
131 | })
132 | return ControlEvent(events: source)
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RxKeyboard
2 |
3 | 
4 | [](https://cocoapods.org/pods/RxKeyboard)
5 | [](https://travis-ci.org/RxSwiftCommunity/RxKeyboard)
6 | [](https://github.com/Carthage/Carthage)
7 |
8 | RxKeyboard provides a reactive way of observing keyboard frame changes. Forget about keyboard notifications. It also perfectly works with `UIScrollViewKeyboardDismissMode.interactive`.
9 |
10 | 
11 | 
12 |
13 | ## Getting Started
14 |
15 | RxKeyboard provides two [`Driver`](https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Traits.md#driver)s.
16 |
17 | ```swift
18 | /// An observable keyboard frame.
19 | let frame: Driver
20 |
21 | /// An observable visible height of keyboard. Emits keyboard height if the keyboard is visible
22 | /// or `0` if the keyboard is not visible.
23 | let visibleHeight: Driver
24 |
25 | /// Same with `visibleHeight` but only emits values when keyboard is about to show. This is
26 | /// useful when adjusting scroll view content offset.
27 | let willShowVisibleHeight: Driver
28 | ```
29 |
30 | Use `RxKeyboard.instance` to get singleton instance.
31 |
32 | ```swift
33 | RxKeyboard.instance
34 | ```
35 |
36 | Subscribe `RxKeyboard.instance.frame` to observe keyboard frame changes.
37 |
38 | ```swift
39 | RxKeyboard.instance.frame
40 | .drive(onNext: { frame in
41 | print(frame)
42 | })
43 | .disposed(by: disposeBag)
44 | ```
45 |
46 | ## Tips and Tricks
47 |
48 | - 🔗 **I want to adjust `UIScrollView`'s `contentInset` to fit keyboard height.**
49 |
50 | ```swift
51 | RxKeyboard.instance.visibleHeight
52 | .drive(onNext: { [scrollView] keyboardVisibleHeight in
53 | scrollView.contentInset.bottom = keyboardVisibleHeight
54 | })
55 | .disposed(by: disposeBag)
56 | ```
57 |
58 | - 🔗 **I want to adjust `UIScrollView`'s `contentOffset` to fit keyboard height.**
59 |
60 | ```swift
61 | RxKeyboard.instance.willShowVisibleHeight
62 | .drive(onNext: { [scrollView] keyboardVisibleHeight in
63 | scrollView.contentOffset.y += keyboardVisibleHeight
64 | })
65 | .disposed(by: disposeBag)
66 | ```
67 |
68 | - 🔗 **I want to make `UIToolbar` move along with the keyboard in an interactive dismiss mode. (Just like the wonderful GIF above!)**
69 |
70 | If you're not using Auto Layout:
71 |
72 | ```swift
73 | RxKeyboard.instance.visibleHeight
74 | .drive(onNext: { [toolbar, view] keyboardVisibleHeight in
75 | toolbar.frame.origin.y = view.frame.height - toolbar.frame.height - keyboardVisibleHeight
76 | })
77 | .disposed(by: disposeBag)
78 | ```
79 |
80 | If you're using Auto Layout, you have to capture the toolbar's bottom constraint and set `constant` to keyboard visible height.
81 |
82 | ```swift
83 | RxKeyboard.instance.visibleHeight
84 | .drive(onNext: { [toolbarBottomConstraint] keyboardVisibleHeight in
85 | toolbarBottomConstraint.constant = -1 * keyboardVisibleHeight
86 | })
87 | .disposed(by: disposeBag)
88 | ```
89 |
90 | > **Note**: In real world, you should use `setNeedsLayout()` and `layoutIfNeeded()` with animation block. See the [example project](https://github.com/RxSwiftCommunity/RxKeyboard/blob/master/Example/Sources/ViewControllers/MessageListViewController.swift#L92-L105) for example.
91 |
92 | - Anything else? Please open an issue or make a Pull Request.
93 |
94 | ## Dependencies
95 |
96 | - [RxSwift](https://github.com/ReactiveX/RxSwift) (>= 6.0)
97 | - [RxCocoa](https://github.com/ReactiveX/RxSwift) (>=6.0)
98 |
99 | ## Requirements
100 |
101 | - Swift 5.1
102 | - iOS 9+
103 |
104 | ## Contributing
105 |
106 | In development, RxKeyboard manages dependencies with Swift Package Manager. Use the command below in order to generate a Xcode project file. Note that `.xcodeproj` file changes are not tracked via git.
107 |
108 | ```console
109 | $ swift package generate-xcodeproj
110 | ```
111 |
112 | ## Installation
113 |
114 | - **Using [CocoaPods](https://cocoapods.org)**:
115 |
116 | ```ruby
117 | pod 'RxKeyboard'
118 | ```
119 |
120 | - **Using [Carthage](https://github.com/Carthage/Carthage)**:
121 |
122 | ```
123 | binary "https://raw.githubusercontent.com/RxSwiftCommunity/RxKeyboard/master/RxKeyboard.json"
124 | ```
125 |
126 | ⚠️ With Carthage, RxKeyboard only supports binary installation:
127 | * 0.9.2
128 | * Xcode 10.1 (10B61)
129 | * Swift 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1)
130 | * 0.9.0
131 | * Xcode 10 (10A255)
132 | * Swift 4.2 (swiftlang-1000.11.37.1 clang-1000.11.45.1)
133 | * 0.8.2
134 | * Xcode 9.3 (9E145)
135 | * Swift 4.1.0 (swiftlang-902.0.48 clang-902.0.37.1)
136 | * 0.7.1
137 | * Xcode 9.1 (9B55)
138 | * Swift 4.0.2 (swiftlang-900.0.69.2 clang-900.0.38)
139 | * 0.7.0
140 | * 9.0.1 (9A1004)
141 | * Swift 4.0 (swiftlang-900.0.65.2 clang-900.0.37)
142 |
143 | ## License
144 |
145 | RxKeyboard is under MIT license.
146 |
--------------------------------------------------------------------------------
/Sources/RxKeyboard/RxKeyboard.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RxKeyboard.swift
3 | // RxKeyboard
4 | //
5 | // Created by Suyeol Jeon on 09/10/2016.
6 | // Copyright © 2016 Suyeol Jeon. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 | import UIKit
11 |
12 | import RxCocoa
13 | import RxSwift
14 |
15 | public protocol RxKeyboardType {
16 | var frame: Driver { get }
17 | var visibleHeight: Driver { get }
18 | var willShowVisibleHeight: Driver { get }
19 | var isHidden: Driver { get }
20 | }
21 |
22 | /// RxKeyboard provides a reactive way of observing keyboard frame changes.
23 | public class RxKeyboard: NSObject, RxKeyboardType {
24 |
25 | // MARK: Public
26 |
27 | /// Get a singleton instance.
28 | public static let instance = RxKeyboard()
29 |
30 | /// An observable keyboard frame.
31 | public let frame: Driver
32 |
33 | /// An observable visible height of keyboard. Emits keyboard height if the keyboard is visible
34 | /// or `0` if the keyboard is not visible.
35 | public let visibleHeight: Driver
36 |
37 | /// Same with `visibleHeight` but only emits values when keyboard is about to show. This is
38 | /// useful when adjusting scroll view content offset.
39 | public let willShowVisibleHeight: Driver
40 |
41 | /// An observable visibility of keyboard. Emits keyboard visibility
42 | /// when changed keyboard show and hide.
43 | public let isHidden: Driver
44 |
45 | // MARK: Private
46 |
47 | private let disposeBag = DisposeBag()
48 | private let panRecognizer = UIPanGestureRecognizer()
49 |
50 | // MARK: Initializing
51 |
52 | override init() {
53 |
54 | let keyboardWillChangeFrame = UIResponder.keyboardWillChangeFrameNotification
55 | let keyboardWillHide = UIResponder.keyboardWillHideNotification
56 | let keyboardFrameEndKey = UIResponder.keyboardFrameEndUserInfoKey
57 |
58 | let defaultFrame = CGRect(
59 | x: 0,
60 | y: UIScreen.main.bounds.height,
61 | width: UIScreen.main.bounds.width,
62 | height: 0
63 | )
64 | let frameVariable = BehaviorRelay(value: defaultFrame)
65 | self.frame = frameVariable.asDriver().distinctUntilChanged()
66 | self.visibleHeight = self.frame.map { UIScreen.main.bounds.intersection($0).height }
67 | self.willShowVisibleHeight = self.visibleHeight
68 | .scan((visibleHeight: 0, isShowing: false)) { lastState, newVisibleHeight in
69 | return (visibleHeight: newVisibleHeight, isShowing: lastState.visibleHeight == 0 && newVisibleHeight > 0)
70 | }
71 | .filter { state in state.isShowing }
72 | .map { state in state.visibleHeight }
73 | self.isHidden = self.visibleHeight.map({ $0 == 0.0 }).distinctUntilChanged()
74 | super.init()
75 |
76 | // keyboard will change frame
77 | let willChangeFrame = NotificationCenter.default.rx.notification(keyboardWillChangeFrame)
78 | .map { notification -> CGRect in
79 | let rectValue = notification.userInfo?[keyboardFrameEndKey] as? NSValue
80 | return rectValue?.cgRectValue ?? defaultFrame
81 | }
82 | .map { frame -> CGRect in
83 | if frame.origin.y < 0 { // if went to wrong frame
84 | var newFrame = frame
85 | newFrame.origin.y = UIScreen.main.bounds.height - newFrame.height
86 | return newFrame
87 | }
88 | return frame
89 | }
90 |
91 | // keyboard will hide
92 | let willHide = NotificationCenter.default.rx.notification(keyboardWillHide)
93 | .map { notification -> CGRect in
94 | let rectValue = notification.userInfo?[keyboardFrameEndKey] as? NSValue
95 | return rectValue?.cgRectValue ?? defaultFrame
96 | }
97 | .map { frame -> CGRect in
98 | if frame.origin.y < 0 { // if went to wrong frame
99 | var newFrame = frame
100 | newFrame.origin.y = UIScreen.main.bounds.height
101 | return newFrame
102 | }
103 | return frame
104 | }
105 |
106 | // pan gesture
107 | let didPan = self.panRecognizer.rx.event
108 | .withLatestFrom(frameVariable.asObservable()) { ($0, $1) }
109 | .flatMap { (gestureRecognizer, frame) -> Observable in
110 | guard case .changed = gestureRecognizer.state,
111 | let window = UIApplication.shared.windows.first,
112 | frame.origin.y < UIScreen.main.bounds.height
113 | else { return .empty() }
114 | let origin = gestureRecognizer.location(in: window)
115 | var newFrame = frame
116 | newFrame.origin.y = max(origin.y, UIScreen.main.bounds.height - frame.height)
117 | return .just(newFrame)
118 | }
119 |
120 | // merge into single sequence
121 | Observable.of(didPan, willChangeFrame, willHide).merge()
122 | .bind(to: frameVariable)
123 | .disposed(by: self.disposeBag)
124 |
125 | // gesture recognizer
126 | self.panRecognizer.delegate = self
127 | self.panRecognizer.maximumNumberOfTouches = 1
128 |
129 | UIApplication.rx.didFinishLaunching // when RxKeyboard is initialized before UIApplication.window is created
130 | .subscribe(onNext: { _ in
131 | UIApplication.shared.windows.first?.addGestureRecognizer(self.panRecognizer)
132 | })
133 | .disposed(by: self.disposeBag)
134 | }
135 |
136 | }
137 |
138 |
139 | // MARK: - UIGestureRecognizerDelegate
140 |
141 | extension RxKeyboard: UIGestureRecognizerDelegate {
142 |
143 | public func gestureRecognizer(
144 | _ gestureRecognizer: UIGestureRecognizer,
145 | shouldReceive touch: UITouch
146 | ) -> Bool {
147 | let point = touch.location(in: gestureRecognizer.view)
148 | var view = gestureRecognizer.view?.hitTest(point, with: nil)
149 | while let candidate = view {
150 | if let scrollView = candidate as? UIScrollView,
151 | case .interactive = scrollView.keyboardDismissMode {
152 | return true
153 | }
154 | view = candidate.superview
155 | }
156 | return false
157 | }
158 |
159 | public func gestureRecognizer(
160 | _ gestureRecognizer: UIGestureRecognizer,
161 | shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
162 | ) -> Bool {
163 | gestureRecognizer === self.panRecognizer
164 | }
165 |
166 | }
167 | #endif
168 |
169 |
--------------------------------------------------------------------------------
/Example/Sources/ViewControllers/MessageListViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MessageListViewController.swift
3 | // RxKeyboardExample
4 | //
5 | // Created by Suyeol Jeon on 09/10/2016.
6 | // Copyright © 2016 Suyeol Jeon. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | import ReusableKit
12 | import RxKeyboard
13 | import RxSwift
14 |
15 | class MessageListViewController: UIViewController {
16 |
17 | // MARK: Constants
18 |
19 | struct Reusable {
20 | static let messageCell = ReusableCell()
21 | }
22 |
23 |
24 | // MARK: Properties
25 |
26 | private var didSetupViewConstraints = false
27 | private let disposeBag = DisposeBag()
28 |
29 | fileprivate var messages: [Message] = [
30 | Message(user: .other, text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
31 | Message(user: .other, text: "Morbi et eros elementum, semper massa eu, pellentesque sapien."),
32 | Message(user: .me, text: "Aenean sollicitudin justo scelerisque tincidunt venenatis."),
33 | Message(user: .me, text: "Ut mollis magna nec interdum pellentesque."),
34 | Message(user: .me, text: "Aliquam semper nibh nec quam dapibus, a congue odio consequat."),
35 | Message(user: .other, text: "Nullam iaculis nisi in justo feugiat, at pharetra nulla dignissim."),
36 | Message(user: .me, text: "Fusce at nulla luctus, posuere mauris ut, viverra nunc."),
37 | Message(user: .other, text: "Nam feugiat urna non tortor ornare viverra."),
38 | Message(user: .other, text: "Donec vitae metus maximus, efficitur urna ac, blandit erat."),
39 | Message(user: .other, text: "Pellentesque luctus eros ac nisi ullamcorper pharetra nec vel felis."),
40 | Message(user: .me, text: "Duis vulputate magna quis urna porttitor, tempor malesuada metus volutpat."),
41 | Message(user: .me, text: "Duis aliquam urna quis metus tristique eleifend."),
42 | Message(user: .other, text: "Cras quis orci quis nisi vulputate mollis ut vitae magna."),
43 | Message(user: .other, text: "Fusce eu urna eu ipsum laoreet lobortis."),
44 | Message(user: .other, text: "Proin vitae tellus nec odio consequat varius ac non orci."),
45 | Message(user: .me, text: "Maecenas gravida arcu ut consectetur tincidunt."),
46 | Message(user: .me, text: "Quisque accumsan nisl ut ipsum rutrum, nec rutrum magna lobortis."),
47 | Message(user: .other, text: "Integer ac sem eu velit tincidunt hendrerit a in dui."),
48 | Message(user: .other, text: "Duis posuere arcu convallis tincidunt faucibus."),
49 | ]
50 |
51 |
52 | // MARK: UI
53 |
54 | let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()).then {
55 | $0.alwaysBounceVertical = true
56 | $0.keyboardDismissMode = .interactive
57 | $0.backgroundColor = .clear
58 | $0.register(Reusable.messageCell)
59 | ($0.collectionViewLayout as? UICollectionViewFlowLayout)?.do {
60 | $0.minimumLineSpacing = 6
61 | $0.sectionInset.top = 10
62 | $0.sectionInset.bottom = 10
63 | }
64 | }
65 | let messageInputBar = MessageInputBar()
66 |
67 |
68 | // MARK: Initializing
69 |
70 | init() {
71 | super.init(nibName: nil, bundle: nil)
72 | self.title = "RxKeyboard Example"
73 | }
74 |
75 | required init?(coder aDecoder: NSCoder) {
76 | fatalError("init(coder:) has not been implemented")
77 | }
78 |
79 | override func viewDidLoad() {
80 | super.viewDidLoad()
81 | self.view.addSubview(self.collectionView)
82 | self.view.addSubview(self.messageInputBar)
83 |
84 | self.collectionView.dataSource = self
85 | self.collectionView.delegate = self
86 |
87 | DispatchQueue.main.async {
88 | let indexPath = IndexPath(item: self.messages.count - 1, section: 0)
89 | self.collectionView.scrollToItem(at: indexPath, at: [], animated: true)
90 | }
91 |
92 | RxKeyboard.instance.visibleHeight
93 | .drive(onNext: { [weak self] keyboardVisibleHeight in
94 | guard let `self` = self, self.didSetupViewConstraints else { return }
95 | self.messageInputBar.snp.updateConstraints { make in
96 | if #available(iOS 11.0, *) {
97 | make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).offset(-keyboardVisibleHeight)
98 | } else {
99 | make.bottom.equalTo(self.bottomLayoutGuide.snp.top).offset(-keyboardVisibleHeight)
100 | }
101 | }
102 | self.view.setNeedsLayout()
103 | UIView.animate(withDuration: 0) {
104 | self.collectionView.contentInset.bottom = keyboardVisibleHeight + self.messageInputBar.height
105 | self.collectionView.scrollIndicatorInsets.bottom = self.collectionView.contentInset.bottom
106 | self.view.layoutIfNeeded()
107 | }
108 | })
109 | .disposed(by: self.disposeBag)
110 |
111 | RxKeyboard.instance.willShowVisibleHeight
112 | .drive(onNext: { keyboardVisibleHeight in
113 | self.collectionView.contentOffset.y += keyboardVisibleHeight
114 | })
115 | .disposed(by: self.disposeBag)
116 |
117 | self.messageInputBar.rx.sendButtonTap
118 | .subscribe(onNext: { [weak self] text in
119 | guard let `self` = self else { return }
120 | let message = Message(user: .me, text: text)
121 | self.messages.append(message)
122 | let indexPath = IndexPath(item: self.messages.count - 1, section: 0)
123 | self.collectionView.insertItems(at: [indexPath])
124 | self.collectionView.scrollToItem(at: indexPath, at: [], animated: true)
125 | })
126 | .disposed(by: self.disposeBag)
127 | }
128 |
129 |
130 | // MARK: Auto Layout
131 |
132 | override func updateViewConstraints() {
133 | super.updateViewConstraints()
134 | guard !self.didSetupViewConstraints else { return }
135 | self.didSetupViewConstraints = true
136 |
137 | self.collectionView.snp.makeConstraints { make in
138 | make.edges.equalTo(0)
139 | }
140 | self.messageInputBar.snp.makeConstraints { make in
141 | make.left.right.equalTo(0)
142 | if #available(iOS 11.0, *) {
143 | make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom)
144 | } else {
145 | make.bottom.equalTo(self.bottomLayoutGuide.snp.top)
146 | }
147 | }
148 | }
149 |
150 | override func viewDidLayoutSubviews() {
151 | super.viewDidLayoutSubviews()
152 | if self.collectionView.contentInset.bottom == 0 {
153 | self.collectionView.contentInset.bottom = self.messageInputBar.height
154 | self.collectionView.scrollIndicatorInsets.bottom = self.collectionView.contentInset.bottom
155 | }
156 | }
157 |
158 | }
159 |
160 |
161 | // MARK: - UICollectionViewDataSource
162 |
163 | extension MessageListViewController: UICollectionViewDataSource {
164 |
165 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
166 | return self.messages.count
167 | }
168 |
169 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
170 | let cell = collectionView.dequeue(Reusable.messageCell, for: indexPath)
171 | cell.configure(message: self.messages[indexPath.item])
172 | return cell
173 | }
174 |
175 | }
176 |
177 |
178 | // MARK: - UICollectionViewDelegateFlowLayout
179 |
180 | extension MessageListViewController: UICollectionViewDelegateFlowLayout {
181 |
182 | func collectionView(
183 | _ collectionView: UICollectionView,
184 | layout collectionViewLayout: UICollectionViewLayout,
185 | sizeForItemAt indexPath: IndexPath
186 | ) -> CGSize {
187 | let message = self.messages[indexPath.item]
188 | return MessageCell.size(thatFitsWidth: collectionView.width, forMessage: message)
189 | }
190 |
191 | }
192 |
--------------------------------------------------------------------------------
/Example/RxKeyboardExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 032ABB8A1E35A9F200A3A5D4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 032ABB7B1E35A9F200A3A5D4 /* Assets.xcassets */; };
11 | 032ABB8B1E35A9F200A3A5D4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 032ABB7C1E35A9F200A3A5D4 /* LaunchScreen.storyboard */; };
12 | 032ABB8C1E35A9F200A3A5D4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032ABB7F1E35A9F200A3A5D4 /* AppDelegate.swift */; };
13 | 032ABB8D1E35A9F200A3A5D4 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032ABB811E35A9F200A3A5D4 /* Message.swift */; };
14 | 032ABB8E1E35A9F200A3A5D4 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032ABB821E35A9F200A3A5D4 /* User.swift */; };
15 | 032ABB8F1E35A9F200A3A5D4 /* MessageListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032ABB841E35A9F200A3A5D4 /* MessageListViewController.swift */; };
16 | 032ABB901E35A9F200A3A5D4 /* MessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032ABB861E35A9F200A3A5D4 /* MessageCell.swift */; };
17 | 032ABB911E35A9F200A3A5D4 /* MessageInputBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032ABB871E35A9F200A3A5D4 /* MessageInputBar.swift */; };
18 | B2F98E4D127A1E3F5A712D85 /* Pods_RxKeyboardExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DB8FDC47FB67FE136E7357D /* Pods_RxKeyboardExample.framework */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXFileReference section */
22 | 032ABB7B1E35A9F200A3A5D4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
23 | 032ABB7D1E35A9F200A3A5D4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
24 | 032ABB7F1E35A9F200A3A5D4 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; tabWidth = 2; };
25 | 032ABB811E35A9F200A3A5D4 /* Message.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = ""; };
26 | 032ABB821E35A9F200A3A5D4 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; };
27 | 032ABB841E35A9F200A3A5D4 /* MessageListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageListViewController.swift; sourceTree = ""; };
28 | 032ABB861E35A9F200A3A5D4 /* MessageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageCell.swift; sourceTree = ""; };
29 | 032ABB871E35A9F200A3A5D4 /* MessageInputBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageInputBar.swift; sourceTree = ""; };
30 | 032ABB891E35A9F200A3A5D4 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
31 | 03E8A71E1E35A95B00F7A3EC /* RxKeyboardRxKeyboardExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RxKeyboardRxKeyboardExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
32 | 7DB8FDC47FB67FE136E7357D /* Pods_RxKeyboardExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RxKeyboardExample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
33 | C2DC646E6858079F25D2BFF4 /* Pods-RxKeyboardExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxKeyboardExample.release.xcconfig"; path = "Pods/Target Support Files/Pods-RxKeyboardExample/Pods-RxKeyboardExample.release.xcconfig"; sourceTree = ""; };
34 | FF066ACB76646CE5837B517B /* Pods-RxKeyboardExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxKeyboardExample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RxKeyboardExample/Pods-RxKeyboardExample.debug.xcconfig"; sourceTree = ""; };
35 | /* End PBXFileReference section */
36 |
37 | /* Begin PBXFrameworksBuildPhase section */
38 | 03E8A71B1E35A95B00F7A3EC /* Frameworks */ = {
39 | isa = PBXFrameworksBuildPhase;
40 | buildActionMask = 2147483647;
41 | files = (
42 | B2F98E4D127A1E3F5A712D85 /* Pods_RxKeyboardExample.framework in Frameworks */,
43 | );
44 | runOnlyForDeploymentPostprocessing = 0;
45 | };
46 | /* End PBXFrameworksBuildPhase section */
47 |
48 | /* Begin PBXGroup section */
49 | 032ABB7A1E35A9F200A3A5D4 /* Resources */ = {
50 | isa = PBXGroup;
51 | children = (
52 | 032ABB7B1E35A9F200A3A5D4 /* Assets.xcassets */,
53 | 032ABB7C1E35A9F200A3A5D4 /* LaunchScreen.storyboard */,
54 | );
55 | path = Resources;
56 | sourceTree = "";
57 | };
58 | 032ABB7E1E35A9F200A3A5D4 /* Sources */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 032ABB7F1E35A9F200A3A5D4 /* AppDelegate.swift */,
62 | 032ABB801E35A9F200A3A5D4 /* Models */,
63 | 032ABB831E35A9F200A3A5D4 /* ViewControllers */,
64 | 032ABB851E35A9F200A3A5D4 /* Views */,
65 | );
66 | path = Sources;
67 | sourceTree = "";
68 | };
69 | 032ABB801E35A9F200A3A5D4 /* Models */ = {
70 | isa = PBXGroup;
71 | children = (
72 | 032ABB811E35A9F200A3A5D4 /* Message.swift */,
73 | 032ABB821E35A9F200A3A5D4 /* User.swift */,
74 | );
75 | path = Models;
76 | sourceTree = "";
77 | };
78 | 032ABB831E35A9F200A3A5D4 /* ViewControllers */ = {
79 | isa = PBXGroup;
80 | children = (
81 | 032ABB841E35A9F200A3A5D4 /* MessageListViewController.swift */,
82 | );
83 | path = ViewControllers;
84 | sourceTree = "";
85 | };
86 | 032ABB851E35A9F200A3A5D4 /* Views */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 032ABB861E35A9F200A3A5D4 /* MessageCell.swift */,
90 | 032ABB871E35A9F200A3A5D4 /* MessageInputBar.swift */,
91 | );
92 | path = Views;
93 | sourceTree = "";
94 | };
95 | 032ABB881E35A9F200A3A5D4 /* Supporting Files */ = {
96 | isa = PBXGroup;
97 | children = (
98 | 032ABB891E35A9F200A3A5D4 /* Info.plist */,
99 | );
100 | path = "Supporting Files";
101 | sourceTree = "";
102 | };
103 | 03E8A7151E35A95B00F7A3EC = {
104 | isa = PBXGroup;
105 | children = (
106 | 032ABB7E1E35A9F200A3A5D4 /* Sources */,
107 | 032ABB881E35A9F200A3A5D4 /* Supporting Files */,
108 | 032ABB7A1E35A9F200A3A5D4 /* Resources */,
109 | 03E8A71F1E35A95B00F7A3EC /* Products */,
110 | A5BB4D314040F3C02A825487 /* Pods */,
111 | 88C32DB01AFB62C918A6B8F0 /* Frameworks */,
112 | );
113 | sourceTree = "";
114 | };
115 | 03E8A71F1E35A95B00F7A3EC /* Products */ = {
116 | isa = PBXGroup;
117 | children = (
118 | 03E8A71E1E35A95B00F7A3EC /* RxKeyboardRxKeyboardExample.app */,
119 | );
120 | name = Products;
121 | sourceTree = "";
122 | };
123 | 88C32DB01AFB62C918A6B8F0 /* Frameworks */ = {
124 | isa = PBXGroup;
125 | children = (
126 | 7DB8FDC47FB67FE136E7357D /* Pods_RxKeyboardExample.framework */,
127 | );
128 | name = Frameworks;
129 | sourceTree = "";
130 | };
131 | A5BB4D314040F3C02A825487 /* Pods */ = {
132 | isa = PBXGroup;
133 | children = (
134 | FF066ACB76646CE5837B517B /* Pods-RxKeyboardExample.debug.xcconfig */,
135 | C2DC646E6858079F25D2BFF4 /* Pods-RxKeyboardExample.release.xcconfig */,
136 | );
137 | name = Pods;
138 | sourceTree = "";
139 | };
140 | /* End PBXGroup section */
141 |
142 | /* Begin PBXNativeTarget section */
143 | 03E8A71D1E35A95B00F7A3EC /* RxKeyboardExample */ = {
144 | isa = PBXNativeTarget;
145 | buildConfigurationList = 03E8A7301E35A95B00F7A3EC /* Build configuration list for PBXNativeTarget "RxKeyboardExample" */;
146 | buildPhases = (
147 | D103E37D22DD7DDCE369FBBB /* [CP] Check Pods Manifest.lock */,
148 | 03E8A71A1E35A95B00F7A3EC /* Sources */,
149 | 03E8A71B1E35A95B00F7A3EC /* Frameworks */,
150 | 03E8A71C1E35A95B00F7A3EC /* Resources */,
151 | E857CEA8F03D723AC93BD7A1 /* [CP] Embed Pods Frameworks */,
152 | );
153 | buildRules = (
154 | );
155 | dependencies = (
156 | );
157 | name = RxKeyboardExample;
158 | productName = Example;
159 | productReference = 03E8A71E1E35A95B00F7A3EC /* RxKeyboardRxKeyboardExample.app */;
160 | productType = "com.apple.product-type.application";
161 | };
162 | /* End PBXNativeTarget section */
163 |
164 | /* Begin PBXProject section */
165 | 03E8A7161E35A95B00F7A3EC /* Project object */ = {
166 | isa = PBXProject;
167 | attributes = {
168 | LastSwiftUpdateCheck = 0820;
169 | LastUpgradeCheck = 1020;
170 | ORGANIZATIONNAME = "Suyeol Jeon";
171 | TargetAttributes = {
172 | 03E8A71D1E35A95B00F7A3EC = {
173 | CreatedOnToolsVersion = 8.2.1;
174 | ProvisioningStyle = Automatic;
175 | };
176 | };
177 | };
178 | buildConfigurationList = 03E8A7191E35A95B00F7A3EC /* Build configuration list for PBXProject "RxKeyboardExample" */;
179 | compatibilityVersion = "Xcode 3.2";
180 | developmentRegion = en;
181 | hasScannedForEncodings = 0;
182 | knownRegions = (
183 | en,
184 | Base,
185 | );
186 | mainGroup = 03E8A7151E35A95B00F7A3EC;
187 | productRefGroup = 03E8A71F1E35A95B00F7A3EC /* Products */;
188 | projectDirPath = "";
189 | projectRoot = "";
190 | targets = (
191 | 03E8A71D1E35A95B00F7A3EC /* RxKeyboardExample */,
192 | );
193 | };
194 | /* End PBXProject section */
195 |
196 | /* Begin PBXResourcesBuildPhase section */
197 | 03E8A71C1E35A95B00F7A3EC /* Resources */ = {
198 | isa = PBXResourcesBuildPhase;
199 | buildActionMask = 2147483647;
200 | files = (
201 | 032ABB8A1E35A9F200A3A5D4 /* Assets.xcassets in Resources */,
202 | 032ABB8B1E35A9F200A3A5D4 /* LaunchScreen.storyboard in Resources */,
203 | );
204 | runOnlyForDeploymentPostprocessing = 0;
205 | };
206 | /* End PBXResourcesBuildPhase section */
207 |
208 | /* Begin PBXShellScriptBuildPhase section */
209 | D103E37D22DD7DDCE369FBBB /* [CP] Check Pods Manifest.lock */ = {
210 | isa = PBXShellScriptBuildPhase;
211 | buildActionMask = 2147483647;
212 | files = (
213 | );
214 | inputPaths = (
215 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
216 | "${PODS_ROOT}/Manifest.lock",
217 | );
218 | name = "[CP] Check Pods Manifest.lock";
219 | outputPaths = (
220 | "$(DERIVED_FILE_DIR)/Pods-RxKeyboardExample-checkManifestLockResult.txt",
221 | );
222 | runOnlyForDeploymentPostprocessing = 0;
223 | shellPath = /bin/sh;
224 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
225 | showEnvVarsInLog = 0;
226 | };
227 | E857CEA8F03D723AC93BD7A1 /* [CP] Embed Pods Frameworks */ = {
228 | isa = PBXShellScriptBuildPhase;
229 | buildActionMask = 2147483647;
230 | files = (
231 | );
232 | inputPaths = (
233 | "${PODS_ROOT}/Target Support Files/Pods-RxKeyboardExample/Pods-RxKeyboardExample-frameworks.sh",
234 | "${BUILT_PRODUCTS_DIR}/CGFloatLiteral/CGFloatLiteral.framework",
235 | "${BUILT_PRODUCTS_DIR}/ManualLayout/ManualLayout.framework",
236 | "${BUILT_PRODUCTS_DIR}/ReusableKit/ReusableKit.framework",
237 | "${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework",
238 | "${BUILT_PRODUCTS_DIR}/RxKeyboard/RxKeyboard.framework",
239 | "${BUILT_PRODUCTS_DIR}/RxRelay/RxRelay.framework",
240 | "${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework",
241 | "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework",
242 | "${BUILT_PRODUCTS_DIR}/SwiftyColor/SwiftyColor.framework",
243 | "${BUILT_PRODUCTS_DIR}/SwiftyImage/SwiftyImage.framework",
244 | "${BUILT_PRODUCTS_DIR}/Then/Then.framework",
245 | "${BUILT_PRODUCTS_DIR}/UITextView+Placeholder/UITextView_Placeholder.framework",
246 | );
247 | name = "[CP] Embed Pods Frameworks";
248 | outputPaths = (
249 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CGFloatLiteral.framework",
250 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ManualLayout.framework",
251 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ReusableKit.framework",
252 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework",
253 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxKeyboard.framework",
254 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxRelay.framework",
255 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework",
256 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework",
257 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyColor.framework",
258 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyImage.framework",
259 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Then.framework",
260 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UITextView_Placeholder.framework",
261 | );
262 | runOnlyForDeploymentPostprocessing = 0;
263 | shellPath = /bin/sh;
264 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RxKeyboardExample/Pods-RxKeyboardExample-frameworks.sh\"\n";
265 | showEnvVarsInLog = 0;
266 | };
267 | /* End PBXShellScriptBuildPhase section */
268 |
269 | /* Begin PBXSourcesBuildPhase section */
270 | 03E8A71A1E35A95B00F7A3EC /* Sources */ = {
271 | isa = PBXSourcesBuildPhase;
272 | buildActionMask = 2147483647;
273 | files = (
274 | 032ABB8C1E35A9F200A3A5D4 /* AppDelegate.swift in Sources */,
275 | 032ABB901E35A9F200A3A5D4 /* MessageCell.swift in Sources */,
276 | 032ABB8D1E35A9F200A3A5D4 /* Message.swift in Sources */,
277 | 032ABB8F1E35A9F200A3A5D4 /* MessageListViewController.swift in Sources */,
278 | 032ABB911E35A9F200A3A5D4 /* MessageInputBar.swift in Sources */,
279 | 032ABB8E1E35A9F200A3A5D4 /* User.swift in Sources */,
280 | );
281 | runOnlyForDeploymentPostprocessing = 0;
282 | };
283 | /* End PBXSourcesBuildPhase section */
284 |
285 | /* Begin PBXVariantGroup section */
286 | 032ABB7C1E35A9F200A3A5D4 /* LaunchScreen.storyboard */ = {
287 | isa = PBXVariantGroup;
288 | children = (
289 | 032ABB7D1E35A9F200A3A5D4 /* Base */,
290 | );
291 | name = LaunchScreen.storyboard;
292 | sourceTree = "";
293 | };
294 | /* End PBXVariantGroup section */
295 |
296 | /* Begin XCBuildConfiguration section */
297 | 03E8A72E1E35A95B00F7A3EC /* Debug */ = {
298 | isa = XCBuildConfiguration;
299 | buildSettings = {
300 | ALWAYS_SEARCH_USER_PATHS = NO;
301 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
302 | CLANG_ANALYZER_NONNULL = YES;
303 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
304 | CLANG_CXX_LIBRARY = "libc++";
305 | CLANG_ENABLE_MODULES = YES;
306 | CLANG_ENABLE_OBJC_ARC = YES;
307 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
308 | CLANG_WARN_BOOL_CONVERSION = YES;
309 | CLANG_WARN_COMMA = YES;
310 | CLANG_WARN_CONSTANT_CONVERSION = YES;
311 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
312 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
313 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
314 | CLANG_WARN_EMPTY_BODY = YES;
315 | CLANG_WARN_ENUM_CONVERSION = YES;
316 | CLANG_WARN_INFINITE_RECURSION = YES;
317 | CLANG_WARN_INT_CONVERSION = YES;
318 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
319 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
320 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
321 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
322 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
323 | CLANG_WARN_STRICT_PROTOTYPES = YES;
324 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
325 | CLANG_WARN_UNREACHABLE_CODE = YES;
326 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
327 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
328 | COPY_PHASE_STRIP = NO;
329 | DEBUG_INFORMATION_FORMAT = dwarf;
330 | ENABLE_STRICT_OBJC_MSGSEND = YES;
331 | ENABLE_TESTABILITY = YES;
332 | GCC_C_LANGUAGE_STANDARD = gnu99;
333 | GCC_DYNAMIC_NO_PIC = NO;
334 | GCC_NO_COMMON_BLOCKS = YES;
335 | GCC_OPTIMIZATION_LEVEL = 0;
336 | GCC_PREPROCESSOR_DEFINITIONS = (
337 | "DEBUG=1",
338 | "$(inherited)",
339 | );
340 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
341 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
342 | GCC_WARN_UNDECLARED_SELECTOR = YES;
343 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
344 | GCC_WARN_UNUSED_FUNCTION = YES;
345 | GCC_WARN_UNUSED_VARIABLE = YES;
346 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
347 | MTL_ENABLE_DEBUG_INFO = YES;
348 | ONLY_ACTIVE_ARCH = YES;
349 | SDKROOT = iphoneos;
350 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
351 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
352 | SWIFT_VERSION = 5.0;
353 | };
354 | name = Debug;
355 | };
356 | 03E8A72F1E35A95B00F7A3EC /* Release */ = {
357 | isa = XCBuildConfiguration;
358 | buildSettings = {
359 | ALWAYS_SEARCH_USER_PATHS = NO;
360 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
361 | CLANG_ANALYZER_NONNULL = YES;
362 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
363 | CLANG_CXX_LIBRARY = "libc++";
364 | CLANG_ENABLE_MODULES = YES;
365 | CLANG_ENABLE_OBJC_ARC = YES;
366 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
367 | CLANG_WARN_BOOL_CONVERSION = YES;
368 | CLANG_WARN_COMMA = YES;
369 | CLANG_WARN_CONSTANT_CONVERSION = YES;
370 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
371 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
372 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
373 | CLANG_WARN_EMPTY_BODY = YES;
374 | CLANG_WARN_ENUM_CONVERSION = YES;
375 | CLANG_WARN_INFINITE_RECURSION = YES;
376 | CLANG_WARN_INT_CONVERSION = YES;
377 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
378 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
379 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
380 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
381 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
382 | CLANG_WARN_STRICT_PROTOTYPES = YES;
383 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
384 | CLANG_WARN_UNREACHABLE_CODE = YES;
385 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
386 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
387 | COPY_PHASE_STRIP = NO;
388 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
389 | ENABLE_NS_ASSERTIONS = NO;
390 | ENABLE_STRICT_OBJC_MSGSEND = YES;
391 | GCC_C_LANGUAGE_STANDARD = gnu99;
392 | GCC_NO_COMMON_BLOCKS = YES;
393 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
394 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
395 | GCC_WARN_UNDECLARED_SELECTOR = YES;
396 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
397 | GCC_WARN_UNUSED_FUNCTION = YES;
398 | GCC_WARN_UNUSED_VARIABLE = YES;
399 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
400 | MTL_ENABLE_DEBUG_INFO = NO;
401 | SDKROOT = iphoneos;
402 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
403 | SWIFT_VERSION = 5.0;
404 | VALIDATE_PRODUCT = YES;
405 | };
406 | name = Release;
407 | };
408 | 03E8A7311E35A95B00F7A3EC /* Debug */ = {
409 | isa = XCBuildConfiguration;
410 | baseConfigurationReference = FF066ACB76646CE5837B517B /* Pods-RxKeyboardExample.debug.xcconfig */;
411 | buildSettings = {
412 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
413 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/Info.plist";
414 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
415 | PRODUCT_BUNDLE_IDENTIFIER = kr.xoul.RxKeyboardExample;
416 | PRODUCT_NAME = RxKeyboardRxKeyboardExample;
417 | };
418 | name = Debug;
419 | };
420 | 03E8A7321E35A95B00F7A3EC /* Release */ = {
421 | isa = XCBuildConfiguration;
422 | baseConfigurationReference = C2DC646E6858079F25D2BFF4 /* Pods-RxKeyboardExample.release.xcconfig */;
423 | buildSettings = {
424 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
425 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/Info.plist";
426 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
427 | PRODUCT_BUNDLE_IDENTIFIER = kr.xoul.RxKeyboardExample;
428 | PRODUCT_NAME = RxKeyboardRxKeyboardExample;
429 | };
430 | name = Release;
431 | };
432 | /* End XCBuildConfiguration section */
433 |
434 | /* Begin XCConfigurationList section */
435 | 03E8A7191E35A95B00F7A3EC /* Build configuration list for PBXProject "RxKeyboardExample" */ = {
436 | isa = XCConfigurationList;
437 | buildConfigurations = (
438 | 03E8A72E1E35A95B00F7A3EC /* Debug */,
439 | 03E8A72F1E35A95B00F7A3EC /* Release */,
440 | );
441 | defaultConfigurationIsVisible = 0;
442 | defaultConfigurationName = Release;
443 | };
444 | 03E8A7301E35A95B00F7A3EC /* Build configuration list for PBXNativeTarget "RxKeyboardExample" */ = {
445 | isa = XCConfigurationList;
446 | buildConfigurations = (
447 | 03E8A7311E35A95B00F7A3EC /* Debug */,
448 | 03E8A7321E35A95B00F7A3EC /* Release */,
449 | );
450 | defaultConfigurationIsVisible = 0;
451 | defaultConfigurationName = Release;
452 | };
453 | /* End XCConfigurationList section */
454 | };
455 | rootObject = 03E8A7161E35A95B00F7A3EC /* Project object */;
456 | }
457 |
--------------------------------------------------------------------------------