62 Commits

Author SHA1 Message Date
启星
081aa5622c 提交 2026-01-15 00:14:20 +08:00
启星
5548f5d6a5 提交 2026-01-14 20:36:58 +08:00
启星
705d891c51 预上线 2026-01-13 09:45:03 +08:00
启星
d41d74dd8e 提交 2026-01-09 20:35:23 +08:00
启星
796f64f5e3 提交 2026-01-08 18:31:27 +08:00
启星
5ab030701c 提交 2026-01-04 14:01:30 +08:00
bfcac8ab2c 提交 2025-12-30 23:13:38 +08:00
c79d438681 提交 2025-12-30 22:50:42 +08:00
启星
5ab60823e8 前端删除练歌房 2025-12-30 22:25:08 +08:00
启星
15f8b54912 前端删除练歌房 2025-12-30 22:22:13 +08:00
启星
2777ff2704 修改 2025-12-30 21:33:06 +08:00
启星
b724fe2a67 1.1.15 2025-12-30 19:08:34 +08:00
启星
1a9d6650f5 修改连送偶现不显示问题 2025-12-29 23:28:26 +08:00
启星
890c5f59ed 提交 2025-12-26 19:04:08 +08:00
启星
7b7d18e665 提交 2025-12-24 15:39:46 +08:00
启星
0fcff85625 提交 2025-12-22 10:16:05 +08:00
启星
ba4fd658a9 提交 2025-12-17 11:44:25 +08:00
启星
999bf3d804 提交 2025-12-13 00:43:57 +08:00
启星
ad6cd82a05 提交 2025-12-10 10:51:46 +08:00
启星
ff11e9715e 提交 2025-12-10 10:48:52 +08:00
启星
aedc48c098 解决冲突 2025-12-10 10:16:33 +08:00
启星
011536e8f4 合并 2025-12-10 10:07:53 +08:00
启星
1a0dac1d91 修改在线列表 2025-12-10 10:01:27 +08:00
启星
3b0e2386c5 上架前提交 2025-12-07 14:24:45 +08:00
启星
16c8869961 修改聊天闪退 2025-12-06 16:55:26 +08:00
启星
9f8a36544b 歌手认证修复 2025-12-05 21:27:36 +08:00
启星
afcf3cc9aa 提交 2025-12-05 21:03:04 +08:00
启星
57975eafc1 修改提交 2025-12-04 23:35:49 +08:00
启星
1c5a908dda 提交 2025-12-04 14:11:00 +08:00
启星
2d37ab6844 房间完成 2025-12-01 18:42:49 +08:00
启星
22025ad033 切换域名 2025-12-01 08:50:55 +08:00
启星
7eb4f8d3b8 提交 2025-11-28 22:43:06 +08:00
启星
8913ea6f48 提交 2025-11-21 16:17:05 +08:00
启星
7ac937ddbf 爵位完成 2025-11-11 17:19:21 +08:00
启星
6b01c3bb49 提交 2025-11-07 10:11:09 +08:00
启星
406c481ba4 我的 2025-11-06 13:04:04 +08:00
启星
4aa2fac912 练歌房 2025-11-04 09:13:09 +08:00
启星
f0c82c3ac7 1.1.2 2025-11-03 09:55:25 +08:00
启星
89c9dd0898 提交 2025-10-31 09:56:19 +08:00
启星
c92b8eb910 1.1.2 2025-10-30 18:30:45 +08:00
启星
aa356202e2 提交 2025-10-30 00:45:15 +08:00
启星
acc4cffad2 修改每日任务跳转房间出错 2025-10-28 09:18:28 +08:00
启星
3f76f23cb0 最后提交 2025-10-27 17:05:46 +08:00
启星
92de736c3f 提交 2025-10-25 20:31:02 +08:00
启星
90709a94c4 提交 2025-10-25 19:38:29 +08:00
启星
3a5cf56099 更换腾讯cos上传 2025-10-24 10:52:40 +08:00
启星
22ba9e1070 修改飘屏 2025-10-21 14:28:18 +08:00
启星
ca98dff004 礼物飘屏完成 2025-10-21 11:36:48 +08:00
启星
c0502d1f7b 首页完成 2025-10-21 10:29:06 +08:00
启星
412e8be86b 提交 2025-10-20 20:07:44 +08:00
启星
0e20842c69 提交 2025-10-20 20:05:55 +08:00
启星
fdeed970d4 结构调整 2025-10-20 10:29:42 +08:00
启星
2c66709436 提交移除多余文件 2025-10-20 09:52:39 +08:00
启星
48891317ea 提交ignore 2025-10-20 09:50:52 +08:00
启星
012e8d05cc 提交ignore 2025-10-20 09:45:18 +08:00
启星
0d82f9e0ef 覆盖羽声 2025-10-20 09:43:10 +08:00
启星
affed1af58 个人信息增加所属公会,更换跟随和私信图标 2025-08-18 11:22:10 +08:00
启星
9a6659045d 增加换肤功能 2025-08-14 09:40:25 +08:00
启星
8065693d4c 增加防抓包hook,登录mqtt,退出登录时不传token 2025-08-12 13:56:09 +08:00
启星
cdbbbd59fc 提交 2025-08-11 18:27:12 +08:00
启星
2156d56dae 提交 2025-08-11 17:33:47 +08:00
启星
c48a8365ef 提交 2025-08-11 17:02:47 +08:00
3285 changed files with 138030 additions and 31463 deletions

52
.gitignore vendored Normal file
View File

@@ -0,0 +1,52 @@
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
xcuserdata/
## Obj-C/Swift specific
*.hmap
## App packaging
*.ipa
*.dSYM.zip
*.dSYM
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build/
# fastlane
#
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
*.xcuserstate
project.xcworkspace
xcuserdata
UserInterfaceState.xcuserstate
project.xcworkspace/
xcuserdata/
UserInterface.xcuserstate
*.xcscheme

11
Podfile
View File

@@ -8,7 +8,7 @@ def taget_pods
pod 'LLDebugTool',:configurations => ['Debug'] #调试工具
# pod 'MLeaksFinder',:configurations => ['Debug'] #调试工具
# pod 'IQKeyboardManager'
pod 'IQKeyboardManager'
#网络请求
pod 'AFNetworking'
#图片加载
@@ -20,10 +20,13 @@ def taget_pods
pod 'MQTTClient'
#菊花
pod 'MBProgressHUD'
pod 'SVProgressHUD'
#图片选择
pod 'TZImagePickerController'
# 布局
pod 'Masonry'
# 数据存储
pod 'FMDB'
#轮播
pod 'SDCycleScrollView'
#日期选择
@@ -54,6 +57,9 @@ def taget_pods
pod 'JXPagingView/Pager'
#阿里云上传文件
pod 'AliyunOSSiOS'
#腾讯cos
pod 'QCloudCOSXML/Transfer'
#声网
pod 'AgoraRtcEngine_iOS','~> 4.5.2'
# pod 'AgoraRtcEngine_Special_iOS','~> 4.4.1.9'
# pod 'AgoraRtm_iOS'
@@ -73,6 +79,9 @@ def taget_pods
pod 'TUIConversation', :path => "./TUIKit/TUIConversation"
# pod 'RTCRoomEngine'
pod 'TIMPush'
#占位
pod 'DZNEmptyDataSet'
pod 'XHLaunchAd' #广告页
end
target 'QXLive' do

View File

@@ -89,11 +89,14 @@ PODS:
- BRPickerView/TextPicker (2.9.1):
- BRPickerView/Core
- Bugly (2.6.1)
- DZNEmptyDataSet (1.8.1)
- FLAnimatedImage (1.0.17)
- FMDB (2.7.12):
- FMDB/standard (= 2.7.12)
- FMDB/Core (2.7.12)
- FMDB/standard (2.7.12):
- FMDB/Core
- IQKeyboardManager (6.5.19)
- JXCategoryView (1.6.8)
- JXPagingView/Pager (2.1.3)
- libwebp (1.5.0):
@@ -166,6 +169,9 @@ PODS:
- MQTTClient/Min
- MQTTClient/Min (0.15.3)
- Protobuf (3.29.3)
- QCloudCore/WithoutMTA (6.5.0)
- QCloudCOSXML/Transfer (6.5.0):
- QCloudCore/WithoutMTA (= 6.5.0)
- QGVAPlayer (1.0.19)
- ReactiveObjC (3.1.1)
- SDCycleScrollView (1.82):
@@ -185,6 +191,9 @@ PODS:
- SVGAPlayer/ProtoFiles
- SVGAPlayer/ProtoFiles (2.5.7):
- Protobuf (~> 3.4)
- SVProgressHUD (2.3.1):
- SVProgressHUD/Core (= 2.3.1)
- SVProgressHUD/Core (2.3.1)
- TencentCloudHuiyanSDKFace_framework (8.7.0)
- TIMCommon (8.5.6864):
- Masonry
@@ -218,6 +227,8 @@ PODS:
- TZImagePickerController/Basic (3.8.8)
- TZImagePickerController/Location (3.8.8)
- WechatOpenSDK-XCFramework (2.0.4)
- XHLaunchAd (3.9.12):
- FLAnimatedImage (~> 1.0.12)
- YBImageBrowser (3.0.9):
- YBImageBrowser/Core (= 3.0.9)
- YBImageBrowser/Core (3.0.9):
@@ -250,6 +261,9 @@ DEPENDENCIES:
- AvoidCrash
- BRPickerView
- Bugly
- DZNEmptyDataSet
- FMDB
- IQKeyboardManager
- JXCategoryView
- JXPagingView/Pager
- LLDebugTool
@@ -257,11 +271,13 @@ DEPENDENCIES:
- MBProgressHUD
- MJRefresh
- MQTTClient
- QCloudCOSXML/Transfer
- QGVAPlayer
- SDCycleScrollView
- SDWebImage
- SDWebImageWebPCoder
- SVGAPlayer
- SVProgressHUD
- TencentCloudHuiyanSDKFace_framework (from `./TencentCloudHuiyanSDKFace_framework`)
- TIMCommon (from `./TUIKit/TIMCommon`)
- TIMPush
@@ -270,6 +286,7 @@ DEPENDENCIES:
- TUICore (from `./TUIKit/TUICore`)
- TZImagePickerController
- WechatOpenSDK-XCFramework (~> 2.0.2)
- XHLaunchAd
- YBImageBrowser (~> 3.0.9)
- YBImageBrowser/Video
- YYCache
@@ -291,7 +308,10 @@ SPEC REPOS:
- AvoidCrash
- BRPickerView
- Bugly
- DZNEmptyDataSet
- FLAnimatedImage
- FMDB
- IQKeyboardManager
- JXCategoryView
- JXPagingView
- libwebp
@@ -301,6 +321,8 @@ SPEC REPOS:
- MJRefresh
- MQTTClient
- Protobuf
- QCloudCore
- QCloudCOSXML
- QGVAPlayer
- ReactiveObjC
- SDCycleScrollView
@@ -308,10 +330,12 @@ SPEC REPOS:
- SDWebImageWebPCoder
- SSZipArchive
- SVGAPlayer
- SVProgressHUD
- TIMPush
- TXIMSDK_Plus_iOS_XCFramework
- TZImagePickerController
- WechatOpenSDK-XCFramework
- XHLaunchAd
- YBImageBrowser
- YYCache
- YYCategories
@@ -344,7 +368,10 @@ SPEC CHECKSUMS:
AvoidCrash: 28c6916fe19bd2b84126b8dec7cbe61c9a12741d
BRPickerView: 6dd69ea2c48e0a0abf1d197a705050e13143ee63
Bugly: 217ac2ce5f0f2626d43dbaa4f70764c953a26a31
DZNEmptyDataSet: 9525833b9e68ac21c30253e1d3d7076cc828eaa7
FLAnimatedImage: bbf914596368867157cc71b38a8ec834b3eeb32b
FMDB: 728731dd336af3936ce00f91d9d8495f5718a0e6
IQKeyboardManager: c8665b3396bd0b79402b4c573eac345a31c7d485
JXCategoryView: 262d503acea0b1278c79a1c25b7332ffaef4d518
JXPagingView: afdd2e9af09c90160dd232b970d603cc6e7ddd0e
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
@@ -354,6 +381,8 @@ SPEC CHECKSUMS:
MJRefresh: ff9e531227924c84ce459338414550a05d2aea78
MQTTClient: 0dfa04e789479c2e461786464969895c1fcb0875
Protobuf: e2de96d939c642ce8da186343ff8929b35223212
QCloudCore: 8681c29dc68d783d07209e7a6ebd8b9058ec73e8
QCloudCOSXML: 26ba1918f7126e650e70f091fee4760cca38038a
QGVAPlayer: a0bca68c9bd6f1c8de5ac2d10ddf98be6038cce9
ReactiveObjC: 011caa393aa0383245f2dcf9bf02e86b80b36040
SDCycleScrollView: a0d74c3384caa72bdfc81470bdbc8c14b3e1fbcf
@@ -361,6 +390,7 @@ SPEC CHECKSUMS:
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
SVGAPlayer: 318b85a78b61292d6ae9dfcd651f3f0d1cdadd86
SVProgressHUD: 4837c74bdfe2e51e8821c397825996a8d7de6e22
TencentCloudHuiyanSDKFace_framework: 1a9398776aac617c2ea7e580fbaf797be96256e1
TIMCommon: 23ed3f287fe194ce5139ce421f96cfb81232562f
TIMPush: e1d85ff0ee0c22cc3f51010ddd8de5316d60102c
@@ -370,6 +400,7 @@ SPEC CHECKSUMS:
TXIMSDK_Plus_iOS_XCFramework: ae200b67e618fef504947de6551b127d35b18d67
TZImagePickerController: d084a7b97c82d387e7669dd86dc9a9057500aacf
WechatOpenSDK-XCFramework: 36fb2bea0754266c17184adf4963d7e6ff98b69f
XHLaunchAd: 6320f9c68732667323072fc3a5f104e40fbe9cfe
YBImageBrowser: 7ecc8bf33ffa5f3b94c397c29b4f3638dd37f527
YYCache: 8105b6638f5e849296c71f331ff83891a4942952
YYCategories: 6bcd4314c6661a561410dce4a793379ebd306abd
@@ -379,6 +410,6 @@ SPEC CHECKSUMS:
YYWebImage: 5f7f36aee2ae293f016d418c7d6ba05c4863e928
Zip: b3fef584b147b6e582b2256a9815c897d60ddc67
PODFILE CHECKSUM: 9f0c224f530b4e6d6636265a8440d96ff79f0a83
PODFILE CHECKSUM: 068f3e03c3d864b09a8c3ffbb0275bbd06bd6ed7
COCOAPODS: 1.16.2

9
Pods/DZNEmptyDataSet/LICENSE generated Normal file
View File

@@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright (c) 2016 Ignacio Romero Zurbuchen iromero@dzen.cl
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

296
Pods/DZNEmptyDataSet/README.md generated Normal file
View File

@@ -0,0 +1,296 @@
DZNEmptyDataSet
=================
[![Pod Version](http://img.shields.io/cocoapods/v/DZNEmptyDataSet.svg)](http://cocoadocs.org/docsets/DZNEmptyDataSet/)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![License](http://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT)
### Projects using this library
[Add your project to the list here](https://github.com/dzenbot/DZNEmptyDataSet/wiki/Projects-using-DZNEmptyDataSet) and provide a (320px wide) render of the result.
### The Empty Data Set Pattern
Also known as *[Empty State](http://emptystat.es/)* or *[Blank Slate](http://patternry.com/p=blank-slate/)*.
Most applications show lists of content (data sets), which many turn out to be empty at one point, specially for new users with blank accounts. Empty screens create confusion by not being clear about what's going on, if there is an error/bug or if the user is supposed to do something within your app to be able to consume the content.
Please read this very interesting article about [*Designing For The Empty States*](http://tympanus.net/codrops/2013/01/09/designing-for-the-empty-states/).
![Screenshots_Row1](https://raw.githubusercontent.com/dzenbot/UITableView-DataSet/master/Examples/Applications/Screenshots/Screenshots_row1.png)
![Screenshots_Row2](https://raw.githubusercontent.com/dzenbot/UITableView-DataSet/master/Examples/Applications/Screenshots/Screenshots_row2.png)
(*These are real life examples, available in the 'Applications' sample project*)
**[Empty Data Sets](http://pttrns.com/?did=1&scid=30)** are helpful for:
* Avoiding white-screens and communicating to your users why the screen is empty.
* Calling to action (particularly as an onboarding process).
* Avoiding other interruptive mechanisms like showing error alerts.
* Being consistent and improving the user experience.
* Delivering a brand presence.
### Features
* Compatible with UITableView and UICollectionView. Also compatible with UISearchDisplayController and UIScrollView.
* Gives multiple possibilities of layout and appearance, by showing an image and/or title label and/or description label and/or button.
* Uses NSAttributedString for easier appearance customisation.
* Uses auto-layout to automagically center the content to the tableview, with auto-rotation support. Also accepts custom vertical and horizontal alignment.
* Background color customisation.
* Allows tap gesture on the whole tableview rectangle (useful for resigning first responder or similar actions).
* For more advanced customisation, it allows a custom view.
* Compatible with Storyboard.
* Compatible with iOS 6 or later.
* Compatible with iPhone and iPad.
* **App Store ready**
This library has been designed in a way where you won't need to extend UITableView or UICollectionView class. It will still work when using UITableViewController or UICollectionViewController.
By just conforming to DZNEmptyDataSetSource & DZNEmptyDataSetDelegate, you will be able to fully customize the content and appearance of the empty states for your application.
## Installation
Available in [CocoaPods](http://cocoapods.org/?q=DZNEmptyDataSet)
```ruby
pod 'DZNEmptyDataSet'
```
To integrate DZNEmptyDataSet into your Xcode project using Carthage, specify it in your `Cartfile`:
```ruby
github "dzenbot/DZNEmptyDataSet"
```
## How to use
For complete documentation, [visit CocoaPods' auto-generated doc](http://cocoadocs.org/docsets/DZNEmptyDataSet/)
### Import
```objc
#import "UIScrollView+EmptyDataSet.h"
```
Unless you are importing as a framework, then do:
```objc
#import "<DZNEmptyDataSet/UIScrollView+EmptyDataSet.h>"
```
### Protocol Conformance
Conform to datasource and/or delegate.
```objc
@interface MainViewController : UITableViewController <DZNEmptyDataSetSource, DZNEmptyDataSetDelegate>
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.emptyDataSetSource = self;
self.tableView.emptyDataSetDelegate = self;
// A little trick for removing the cell separators
self.tableView.tableFooterView = [UIView new];
}
```
### Data Source Implementation
Return the content you want to show on the empty state, and take advantage of NSAttributedString features to customise the text appearance.
The image for the empty state:
```objc
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView
{
return [UIImage imageNamed:@"empty_placeholder"];
}
```
The image view animation
```objc
- (CAAnimation *)imageAnimationForEmptyDataSet:(UIScrollView *)scrollView
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath: @"transform"];
animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI_2, 0.0, 0.0, 1.0)];
animation.duration = 0.25;
animation.cumulative = YES;
animation.repeatCount = MAXFLOAT;
return animation;
}
```
The attributed string for the title of the empty state:
```objc
- (NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView
{
NSString *text = @"Please Allow Photo Access";
NSDictionary *attributes = @{NSFontAttributeName: [UIFont boldSystemFontOfSize:18.0f],
NSForegroundColorAttributeName: [UIColor darkGrayColor]};
return [[NSAttributedString alloc] initWithString:text attributes:attributes];
}
```
The attributed string for the description of the empty state:
```objc
- (NSAttributedString *)descriptionForEmptyDataSet:(UIScrollView *)scrollView
{
NSString *text = @"This allows you to share photos from your library and save photos to your camera roll.";
NSMutableParagraphStyle *paragraph = [NSMutableParagraphStyle new];
paragraph.lineBreakMode = NSLineBreakByWordWrapping;
paragraph.alignment = NSTextAlignmentCenter;
NSDictionary *attributes = @{NSFontAttributeName: [UIFont systemFontOfSize:14.0f],
NSForegroundColorAttributeName: [UIColor lightGrayColor],
NSParagraphStyleAttributeName: paragraph};
return [[NSAttributedString alloc] initWithString:text attributes:attributes];
}
```
The attributed string to be used for the specified button state:
```objc
- (NSAttributedString *)buttonTitleForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state
{
NSDictionary *attributes = @{NSFontAttributeName: [UIFont boldSystemFontOfSize:17.0f]};
return [[NSAttributedString alloc] initWithString:@"Continue" attributes:attributes];
}
```
or the image to be used for the specified button state:
```objc
- (UIImage *)buttonImageForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state
{
return [UIImage imageNamed:@"button_image"];
}
```
The background color for the empty state:
```objc
- (UIColor *)backgroundColorForEmptyDataSet:(UIScrollView *)scrollView
{
return [UIColor whiteColor];
}
```
If you need a more complex layout, you can return a custom view instead:
```objc
- (UIView *)customViewForEmptyDataSet:(UIScrollView *)scrollView
{
UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[activityView startAnimating];
return activityView;
}
```
Additionally, you can also adjust the vertical alignment of the content view (ie: useful when there is tableHeaderView visible):
```objc
- (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView *)scrollView
{
return -self.tableView.tableHeaderView.frame.size.height/2.0f;
}
```
Finally, you can separate components from each other (default separation is 11 pts):
```objc
- (CGFloat)spaceHeightForEmptyDataSet:(UIScrollView *)scrollView
{
return 20.0f;
}
```
### Delegate Implementation
Return the behaviours you would expect from the empty states, and receive the user events.
Asks to know if the empty state should be rendered and displayed (Default is YES) :
```objc
- (BOOL)emptyDataSetShouldDisplay:(UIScrollView *)scrollView
{
return YES;
}
```
Asks for interaction permission (Default is YES) :
```objc
- (BOOL)emptyDataSetShouldAllowTouch:(UIScrollView *)scrollView
{
return YES;
}
```
Asks for scrolling permission (Default is NO) :
```objc
- (BOOL)emptyDataSetShouldAllowScroll:(UIScrollView *)scrollView
{
return YES;
}
```
Asks for image view animation permission (Default is NO) :
```objc
- (BOOL) emptyDataSetShouldAllowImageViewAnimate:(UIScrollView *)scrollView
{
return YES;
}
```
Notifies when the dataset view was tapped:
```objc
- (void)emptyDataSet:(UIScrollView *)scrollView didTapView:(UIView *)view
{
// Do something
}
```
Notifies when the data set call to action button was tapped:
```objc
- (void)emptyDataSet:(UIScrollView *)scrollView didTapButton:(UIButton *)button
{
// Do something
}
```
### Refresh layout
If you need to refresh the empty state layout, simply call:
```objc
[self.tableView reloadData];
```
or
```objc
[self.collectionView reloadData];
```
depending of which you are using.
### Force layout update
You can also call `[self.tableView reloadEmptyDataSet]` to invalidate the current empty state layout and trigger a layout update, bypassing `-reloadData`. This might be useful if you have a lot of logic on your data source that you want to avoid calling, when not needed. `[self.scrollView reloadEmptyDataSet]` is the only way to refresh content when using with UIScrollView.
## Sample projects
#### Applications
This project replicates several popular application's empty states (~20) with their exact content and appearance, such as Airbnb, Dropbox, Facebook, Foursquare, and many others. See how easy and flexible it is to customize the appearance of your empty states. You can also use this project as a playground to test things.
#### Countries
This project shows a list of the world countries loaded from CoreData. It uses NSFecthedResultController for filtering search. When searching and no content is matched, a simple empty state is shown. See how to interact between the UITableViewDataSource and the DZNEmptyDataSetSource protocols, while using a typical CoreData stack.
#### Colors
This project is a simple example of how this library also works with UICollectionView and UISearchDisplayController, while using Storyboards.
## Collaboration
Feel free to collaborate with ideas, issues and/or pull requests.
## License
(The MIT License)
Copyright (c) 2016 Ignacio Romero Zurbuchen iromero@dzen.cl
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,280 @@
//
// UIScrollView+EmptyDataSet.h
// DZNEmptyDataSet
// https://github.com/dzenbot/DZNEmptyDataSet
//
// Created by Ignacio Romero Zurbuchen on 6/20/14.
// Copyright (c) 2016 DZN Labs. All rights reserved.
// Licence: MIT-Licence
//
#import <UIKit/UIKit.h>
@protocol DZNEmptyDataSetSource;
@protocol DZNEmptyDataSetDelegate;
#define DZNEmptyDataSetDeprecated(instead) DEPRECATED_MSG_ATTRIBUTE(" Use " # instead " instead")
/**
A drop-in UITableView/UICollectionView superclass category for showing empty datasets whenever the view has no content to display.
@discussion It will work automatically, by just conforming to DZNEmptyDataSetSource, and returning the data you want to show.
*/
@interface UIScrollView (EmptyDataSet)
/** The empty datasets data source. */
@property (nonatomic, weak) IBOutlet id <DZNEmptyDataSetSource> emptyDataSetSource;
/** The empty datasets delegate. */
@property (nonatomic, weak) IBOutlet id <DZNEmptyDataSetDelegate> emptyDataSetDelegate;
/** YES if any empty dataset is visible. */
@property (nonatomic, readonly, getter = isEmptyDataSetVisible) BOOL emptyDataSetVisible;
/**
Reloads the empty dataset content receiver.
@discussion Call this method to force all the data to refresh. Calling -reloadData is similar, but this forces only the empty dataset to reload, not the entire table view or collection view.
*/
- (void)reloadEmptyDataSet;
@end
/**
The object that acts as the data source of the empty datasets.
@discussion The data source must adopt the DZNEmptyDataSetSource protocol. The data source is not retained. All data source methods are optional.
*/
@protocol DZNEmptyDataSetSource <NSObject>
@optional
/**
Asks the data source for the title of the dataset.
The dataset uses a fixed font style by default, if no attributes are set. If you want a different font style, return a attributed string.
@param scrollView A scrollView subclass informing the data source.
@return An attributed string for the dataset title, combining font, text color, text pararaph style, etc.
*/
- (NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView;
/**
Asks the data source for the description of the dataset.
The dataset uses a fixed font style by default, if no attributes are set. If you want a different font style, return a attributed string.
@param scrollView A scrollView subclass informing the data source.
@return An attributed string for the dataset description text, combining font, text color, text pararaph style, etc.
*/
- (NSAttributedString *)descriptionForEmptyDataSet:(UIScrollView *)scrollView;
/**
Asks the data source for the image of the dataset.
@param scrollView A scrollView subclass informing the data source.
@return An image for the dataset.
*/
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView;
/**
Asks the data source for a tint color of the image dataset. Default is nil.
@param scrollView A scrollView subclass object informing the data source.
@return A color to tint the image of the dataset.
*/
- (UIColor *)imageTintColorForEmptyDataSet:(UIScrollView *)scrollView;
/**
* Asks the data source for the image animation of the dataset.
*
* @param scrollView A scrollView subclass object informing the delegate.
*
* @return image animation
*/
- (CAAnimation *) imageAnimationForEmptyDataSet:(UIScrollView *) scrollView;
/**
Asks the data source for the title to be used for the specified button state.
The dataset uses a fixed font style by default, if no attributes are set. If you want a different font style, return a attributed string.
@param scrollView A scrollView subclass object informing the data source.
@param state The state that uses the specified title. The possible values are described in UIControlState.
@return An attributed string for the dataset button title, combining font, text color, text pararaph style, etc.
*/
- (NSAttributedString *)buttonTitleForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state;
/**
Asks the data source for the image to be used for the specified button state.
This method will override buttonTitleForEmptyDataSet:forState: and present the image only without any text.
@param scrollView A scrollView subclass object informing the data source.
@param state The state that uses the specified title. The possible values are described in UIControlState.
@return An image for the dataset button imageview.
*/
- (UIImage *)buttonImageForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state;
/**
Asks the data source for a background image to be used for the specified button state.
There is no default style for this call.
@param scrollView A scrollView subclass informing the data source.
@param state The state that uses the specified image. The values are described in UIControlState.
@return An attributed string for the dataset button title, combining font, text color, text pararaph style, etc.
*/
- (UIImage *)buttonBackgroundImageForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state;
/**
Asks the data source for the background color of the dataset. Default is clear color.
@param scrollView A scrollView subclass object informing the data source.
@return A color to be applied to the dataset background view.
*/
- (UIColor *)backgroundColorForEmptyDataSet:(UIScrollView *)scrollView;
/**
Asks the data source for a custom view to be displayed instead of the default views such as labels, imageview and button. Default is nil.
Use this method to show an activity view indicator for loading feedback, or for complete custom empty data set.
Returning a custom view will ignore -offsetForEmptyDataSet and -spaceHeightForEmptyDataSet configurations.
@param scrollView A scrollView subclass object informing the delegate.
@return The custom view.
*/
- (UIView *)customViewForEmptyDataSet:(UIScrollView *)scrollView;
/**
Asks the data source for a offset for vertical and horizontal alignment of the content. Default is CGPointZero.
@param scrollView A scrollView subclass object informing the delegate.
@return The offset for vertical and horizontal alignment.
*/
- (CGPoint)offsetForEmptyDataSet:(UIScrollView *)scrollView DZNEmptyDataSetDeprecated(-verticalOffsetForEmptyDataSet:);
- (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView *)scrollView;
/**
Asks the data source for a vertical space between elements. Default is 11 pts.
@param scrollView A scrollView subclass object informing the delegate.
@return The space height between elements.
*/
- (CGFloat)spaceHeightForEmptyDataSet:(UIScrollView *)scrollView;
@end
/**
The object that acts as the delegate of the empty datasets.
@discussion The delegate can adopt the DZNEmptyDataSetDelegate protocol. The delegate is not retained. All delegate methods are optional.
@discussion All delegate methods are optional. Use this delegate for receiving action callbacks.
*/
@protocol DZNEmptyDataSetDelegate <NSObject>
@optional
/**
Asks the delegate to know if the empty dataset should fade in when displayed. Default is YES.
@param scrollView A scrollView subclass object informing the delegate.
@return YES if the empty dataset should fade in.
*/
- (BOOL)emptyDataSetShouldFadeIn:(UIScrollView *)scrollView;
/**
Asks the delegate to know if the empty dataset should still be displayed when the amount of items is more than 0. Default is NO
@param scrollView A scrollView subclass object informing the delegate.
@return YES if empty dataset should be forced to display
*/
- (BOOL)emptyDataSetShouldBeForcedToDisplay:(UIScrollView *)scrollView;
/**
Asks the delegate to know if the empty dataset should be rendered and displayed. Default is YES.
@param scrollView A scrollView subclass object informing the delegate.
@return YES if the empty dataset should show.
*/
- (BOOL)emptyDataSetShouldDisplay:(UIScrollView *)scrollView;
/**
Asks the delegate for touch permission. Default is YES.
@param scrollView A scrollView subclass object informing the delegate.
@return YES if the empty dataset receives touch gestures.
*/
- (BOOL)emptyDataSetShouldAllowTouch:(UIScrollView *)scrollView;
/**
Asks the delegate for scroll permission. Default is NO.
@param scrollView A scrollView subclass object informing the delegate.
@return YES if the empty dataset is allowed to be scrollable.
*/
- (BOOL)emptyDataSetShouldAllowScroll:(UIScrollView *)scrollView;
/**
Asks the delegate for image view animation permission. Default is NO.
Make sure to return a valid CAAnimation object from imageAnimationForEmptyDataSet:
@param scrollView A scrollView subclass object informing the delegate.
@return YES if the empty dataset is allowed to animate
*/
- (BOOL)emptyDataSetShouldAnimateImageView:(UIScrollView *)scrollView;
/**
Tells the delegate that the empty dataset view was tapped.
Use this method either to resignFirstResponder of a textfield or searchBar.
@param scrollView A scrollView subclass informing the delegate.
*/
- (void)emptyDataSetDidTapView:(UIScrollView *)scrollView DZNEmptyDataSetDeprecated(-emptyDataSet:didTapView:);
/**
Tells the delegate that the action button was tapped.
@param scrollView A scrollView subclass informing the delegate.
*/
- (void)emptyDataSetDidTapButton:(UIScrollView *)scrollView DZNEmptyDataSetDeprecated(-emptyDataSet:didTapButton:);
/**
Tells the delegate that the empty dataset view was tapped.
Use this method either to resignFirstResponder of a textfield or searchBar.
@param scrollView A scrollView subclass informing the delegate.
@param view the view tapped by the user
*/
- (void)emptyDataSet:(UIScrollView *)scrollView didTapView:(UIView *)view;
/**
Tells the delegate that the action button was tapped.
@param scrollView A scrollView subclass informing the delegate.
@param button the button tapped by the user
*/
- (void)emptyDataSet:(UIScrollView *)scrollView didTapButton:(UIButton *)button;
/**
Tells the delegate that the empty data set will appear.
@param scrollView A scrollView subclass informing the delegate.
*/
- (void)emptyDataSetWillAppear:(UIScrollView *)scrollView;
/**
Tells the delegate that the empty data set did appear.
@param scrollView A scrollView subclass informing the delegate.
*/
- (void)emptyDataSetDidAppear:(UIScrollView *)scrollView;
/**
Tells the delegate that the empty data set will disappear.
@param scrollView A scrollView subclass informing the delegate.
*/
- (void)emptyDataSetWillDisappear:(UIScrollView *)scrollView;
/**
Tells the delegate that the empty data set did disappear.
@param scrollView A scrollView subclass informing the delegate.
*/
- (void)emptyDataSetDidDisappear:(UIScrollView *)scrollView;
@end
#undef DZNEmptyDataSetDeprecated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,820 @@
//
// FLAnimatedImage.m
// Flipboard
//
// Created by Raphael Schaad on 7/8/13.
// Copyright (c) Flipboard. All rights reserved.
//
#import "FLAnimatedImage.h"
#import <ImageIO/ImageIO.h>
#if __has_include(<MobileCoreServices/MobileCoreServices.h>)
#import <MobileCoreServices/MobileCoreServices.h>
#else
#import <CoreServices/CoreServices.h>
#endif
// From vm_param.h, define for iOS 8.0 or higher to build on device.
#ifndef BYTE_SIZE
#define BYTE_SIZE 8 // byte size in bits
#endif
#define MEGABYTE (1024 * 1024)
// This is how the fastest browsers do it as per 2012: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser-compatibility
const NSTimeInterval kFLAnimatedImageDelayTimeIntervalMinimum = 0.02;
// An animated image's data size (dimensions * frameCount) category; its value is the max allowed memory (in MB).
// E.g.: A 100x200px GIF with 30 frames is ~2.3MB in our pixel format and would fall into the `FLAnimatedImageDataSizeCategoryAll` category.
typedef NS_ENUM(NSUInteger, FLAnimatedImageDataSizeCategory) {
FLAnimatedImageDataSizeCategoryAll = 10, // All frames permanently in memory (be nice to the CPU)
FLAnimatedImageDataSizeCategoryDefault = 75, // A frame cache of default size in memory (usually real-time performance and keeping low memory profile)
FLAnimatedImageDataSizeCategoryOnDemand = 250, // Only keep one frame at the time in memory (easier on memory, slowest performance)
FLAnimatedImageDataSizeCategoryUnsupported // Even for one frame too large, computer says no.
};
typedef NS_ENUM(NSUInteger, FLAnimatedImageFrameCacheSize) {
FLAnimatedImageFrameCacheSizeNoLimit = 0, // 0 means no specific limit
FLAnimatedImageFrameCacheSizeLowMemory = 1, // The minimum frame cache size; this will produce frames on-demand.
FLAnimatedImageFrameCacheSizeGrowAfterMemoryWarning = 2, // If we can produce the frames faster than we consume, one frame ahead will already result in a stutter-free playback.
FLAnimatedImageFrameCacheSizeDefault = 5 // Build up a comfy buffer window to cope with CPU hiccups etc.
};
#if defined(DEBUG) && DEBUG
@protocol FLAnimatedImageDebugDelegate <NSObject>
@optional
- (void)debug_animatedImage:(FLAnimatedImage *)animatedImage didUpdateCachedFrames:(NSIndexSet *)indexesOfFramesInCache;
- (void)debug_animatedImage:(FLAnimatedImage *)animatedImage didRequestCachedFrame:(NSUInteger)index;
- (CGFloat)debug_animatedImagePredrawingSlowdownFactor:(FLAnimatedImage *)animatedImage;
@end
#endif
@interface FLAnimatedImage ()
@property (nonatomic, assign, readonly) NSUInteger frameCacheSizeOptimal; // The optimal number of frames to cache based on image size & number of frames; never changes
@property (nonatomic, assign, readonly, getter=isPredrawingEnabled) BOOL predrawingEnabled; // Enables predrawing of images to improve performance.
@property (nonatomic, assign) NSUInteger frameCacheSizeMaxInternal; // Allow to cap the cache size e.g. when memory warnings occur; 0 means no specific limit (default)
@property (nonatomic, assign) NSUInteger requestedFrameIndex; // Most recently requested frame index
@property (nonatomic, assign, readonly) NSUInteger posterImageFrameIndex; // Index of non-purgable poster image; never changes
@property (nonatomic, strong, readonly) NSMutableDictionary *cachedFramesForIndexes;
@property (nonatomic, strong, readonly) NSMutableIndexSet *cachedFrameIndexes; // Indexes of cached frames
@property (nonatomic, strong, readonly) NSMutableIndexSet *requestedFrameIndexes; // Indexes of frames that are currently produced in the background
@property (nonatomic, strong, readonly) NSIndexSet *allFramesIndexSet; // Default index set with the full range of indexes; never changes
@property (nonatomic, assign) NSUInteger memoryWarningCount;
@property (nonatomic, strong, readonly) dispatch_queue_t serialQueue;
@property (nonatomic, strong, readonly) __attribute__((NSObject)) CGImageSourceRef imageSource;
// The weak proxy is used to break retain cycles with delayed actions from memory warnings.
// We are lying about the actual type here to gain static type checking and eliminate casts.
// The actual type of the object is `FLWeakProxy`.
@property (nonatomic, strong, readonly) FLAnimatedImage *weakProxy;
#if defined(DEBUG) && DEBUG
@property (nonatomic, weak) id<FLAnimatedImageDebugDelegate> debug_delegate;
#endif
@end
// For custom dispatching of memory warnings to avoid deallocation races since NSNotificationCenter doesn't retain objects it is notifying.
static NSHashTable *allAnimatedImagesWeak;
@implementation FLAnimatedImage
#pragma mark - Accessors
#pragma mark Public
// This is the definite value the frame cache needs to size itself to.
- (NSUInteger)frameCacheSizeCurrent
{
NSUInteger frameCacheSizeCurrent = self.frameCacheSizeOptimal;
// If set, respect the caps.
if (self.frameCacheSizeMax > FLAnimatedImageFrameCacheSizeNoLimit) {
frameCacheSizeCurrent = MIN(frameCacheSizeCurrent, self.frameCacheSizeMax);
}
if (self.frameCacheSizeMaxInternal > FLAnimatedImageFrameCacheSizeNoLimit) {
frameCacheSizeCurrent = MIN(frameCacheSizeCurrent, self.frameCacheSizeMaxInternal);
}
return frameCacheSizeCurrent;
}
- (void)setFrameCacheSizeMax:(NSUInteger)frameCacheSizeMax
{
if (_frameCacheSizeMax != frameCacheSizeMax) {
// Remember whether the new cap will cause the current cache size to shrink; then we'll make sure to purge from the cache if needed.
const BOOL willFrameCacheSizeShrink = (frameCacheSizeMax < self.frameCacheSizeCurrent);
// Update the value
_frameCacheSizeMax = frameCacheSizeMax;
if (willFrameCacheSizeShrink) {
[self purgeFrameCacheIfNeeded];
}
}
}
#pragma mark Private
- (void)setFrameCacheSizeMaxInternal:(NSUInteger)frameCacheSizeMaxInternal
{
if (_frameCacheSizeMaxInternal != frameCacheSizeMaxInternal) {
// Remember whether the new cap will cause the current cache size to shrink; then we'll make sure to purge from the cache if needed.
BOOL willFrameCacheSizeShrink = (frameCacheSizeMaxInternal < self.frameCacheSizeCurrent);
// Update the value
_frameCacheSizeMaxInternal = frameCacheSizeMaxInternal;
if (willFrameCacheSizeShrink) {
[self purgeFrameCacheIfNeeded];
}
}
}
#pragma mark - Life Cycle
+ (void)initialize
{
if (self == [FLAnimatedImage class]) {
// UIKit memory warning notification handler shared by all of the instances
allAnimatedImagesWeak = [NSHashTable weakObjectsHashTable];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
// UIKit notifications are posted on the main thread. didReceiveMemoryWarning: is expecting the main run loop, and we don't lock on allAnimatedImagesWeak
NSAssert([NSThread isMainThread], @"Received memory warning on non-main thread");
// Get a strong reference to all of the images. If an instance is returned in this array, it is still live and has not entered dealloc.
// Note that FLAnimatedImages can be created on any thread, so the hash table must be locked.
NSArray *images = nil;
@synchronized(allAnimatedImagesWeak) {
images = [[allAnimatedImagesWeak allObjects] copy];
}
// Now issue notifications to all of the images while holding a strong reference to them
[images makeObjectsPerformSelector:@selector(didReceiveMemoryWarning:) withObject:note];
}];
}
}
- (instancetype)init
{
FLAnimatedImage *_Nullable const animatedImage = [self initWithAnimatedGIFData:nil];
if (!animatedImage) {
FLLog(FLLogLevelError, @"Use `-initWithAnimatedGIFData:` and supply the animated GIF data as an argument to initialize an object of type `FLAnimatedImage`.");
}
return animatedImage;
}
- (instancetype)initWithAnimatedGIFData:(NSData *)data
{
return [self initWithAnimatedGIFData:data optimalFrameCacheSize:0 predrawingEnabled:YES];
}
- (instancetype)initWithAnimatedGIFData:(NSData *)data optimalFrameCacheSize:(NSUInteger)optimalFrameCacheSize predrawingEnabled:(BOOL)isPredrawingEnabled
{
// Early return if no data supplied!
const BOOL hasData = (data.length > 0);
if (!hasData) {
FLLog(FLLogLevelError, @"No animated GIF data supplied.");
return nil;
}
self = [super init];
if (self) {
// Do one-time initializations of `readonly` properties directly to ivar to prevent implicit actions and avoid need for private `readwrite` property overrides.
// Keep a strong reference to `data` and expose it read-only publicly.
// However, we will use the `_imageSource` as handler to the image data throughout our life cycle.
_data = data;
_predrawingEnabled = isPredrawingEnabled;
// Initialize internal data structures
_cachedFramesForIndexes = [[NSMutableDictionary alloc] init];
_cachedFrameIndexes = [[NSMutableIndexSet alloc] init];
_requestedFrameIndexes = [[NSMutableIndexSet alloc] init];
// Note: We could leverage `CGImageSourceCreateWithURL` too to add a second initializer `-initWithAnimatedGIFContentsOfURL:`.
_imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)data,
(__bridge CFDictionaryRef)@{(NSString *)kCGImageSourceShouldCache: @NO});
// Early return on failure!
if (!_imageSource) {
FLLog(FLLogLevelError, @"Failed to `CGImageSourceCreateWithData` for animated GIF data %@", data);
return nil;
}
// Early return if not GIF!
const CFStringRef _Nullable imageSourceContainerType = CGImageSourceGetType(_imageSource);
const BOOL isGIFData = imageSourceContainerType ? UTTypeConformsTo(imageSourceContainerType, kUTTypeGIF) : NO;
if (!isGIFData) {
FLLog(FLLogLevelError, @"Supplied data is of type %@ and doesn't seem to be GIF data %@", imageSourceContainerType, data);
return nil;
}
// Get `LoopCount`
// Note: 0 means repeating the animation indefinitely.
// Image properties example:
// {
// FileSize = 314446;
// "{GIF}" = {
// HasGlobalColorMap = 1;
// LoopCount = 0;
// };
// }
NSDictionary *_Nullable const imageProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(_imageSource, NULL);
_loopCount = [[[imageProperties objectForKey:(id)kCGImagePropertyGIFDictionary] objectForKey:(id)kCGImagePropertyGIFLoopCount] unsignedIntegerValue];
// Iterate through frame images
const size_t imageCount = CGImageSourceGetCount(_imageSource);
NSUInteger skippedFrameCount = 0;
NSMutableDictionary *const delayTimesForIndexesMutable = [NSMutableDictionary dictionaryWithCapacity:imageCount];
for (size_t i = 0; i < imageCount; i++) {
@autoreleasepool {
const CGImageRef _Nullable frameImageRef = CGImageSourceCreateImageAtIndex(_imageSource, i, NULL);
if (frameImageRef) {
UIImage *frameImage = [UIImage imageWithCGImage:frameImageRef];
// Check for valid `frameImage` before parsing its properties as frames can be corrupted (and `frameImage` even `nil` when `frameImageRef` was valid).
if (frameImage) {
// Set poster image
if (!self.posterImage) {
_posterImage = frameImage;
// Set its size to proxy our size.
_size = _posterImage.size;
// Remember index of poster image so we never purge it; also add it to the cache.
_posterImageFrameIndex = i;
[self.cachedFramesForIndexes setObject:self.posterImage forKey:@(self.posterImageFrameIndex)];
[self.cachedFrameIndexes addIndex:self.posterImageFrameIndex];
}
// Get `DelayTime`
// Note: It's not in (1/100) of a second like still falsely described in the documentation as per iOS 8 (rdar://19507384) but in seconds stored as `kCFNumberFloat32Type`.
// Frame properties example:
// {
// ColorModel = RGB;
// Depth = 8;
// PixelHeight = 960;
// PixelWidth = 640;
// "{GIF}" = {
// DelayTime = "0.4";
// UnclampedDelayTime = "0.4";
// };
// }
NSDictionary *_Nullable const frameProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(_imageSource, i, NULL);
NSDictionary *_Nullable const framePropertiesGIF = [frameProperties objectForKey:(id)kCGImagePropertyGIFDictionary];
// Try to use the unclamped delay time; fall back to the normal delay time.
NSNumber *_Nullable delayTime = [framePropertiesGIF objectForKey:(id)kCGImagePropertyGIFUnclampedDelayTime];
if (delayTime == nil) {
delayTime = [framePropertiesGIF objectForKey:(id)kCGImagePropertyGIFDelayTime];
}
// If we don't get a delay time from the properties, fall back to `kDelayTimeIntervalDefault` or carry over the preceding frame's value.
const NSTimeInterval kDelayTimeIntervalDefault = 0.1;
if (delayTime == nil) {
if (i == 0) {
FLLog(FLLogLevelInfo, @"Falling back to default delay time for first frame %@ because none found in GIF properties %@", frameImage, frameProperties);
delayTime = @(kDelayTimeIntervalDefault);
} else {
FLLog(FLLogLevelInfo, @"Falling back to preceding delay time for frame %zu %@ because none found in GIF properties %@", i, frameImage, frameProperties);
delayTime = delayTimesForIndexesMutable[@(i - 1)];
}
}
// Support frame delays as low as `kFLAnimatedImageDelayTimeIntervalMinimum`, with anything below being rounded up to `kDelayTimeIntervalDefault` for legacy compatibility.
// To support the minimum even when rounding errors occur, use an epsilon when comparing. We downcast to float because that's what we get for delayTime from ImageIO.
if ([delayTime floatValue] < ((float)kFLAnimatedImageDelayTimeIntervalMinimum - FLT_EPSILON)) {
FLLog(FLLogLevelInfo, @"Rounding frame %zu's `delayTime` from %f up to default %f (minimum supported: %f).", i, [delayTime floatValue], kDelayTimeIntervalDefault, kFLAnimatedImageDelayTimeIntervalMinimum);
delayTime = @(kDelayTimeIntervalDefault);
}
delayTimesForIndexesMutable[@(i)] = delayTime;
} else {
skippedFrameCount++;
FLLog(FLLogLevelInfo, @"Dropping frame %zu because valid `CGImageRef` %@ did result in `nil`-`UIImage`.", i, frameImageRef);
}
CFRelease(frameImageRef);
} else {
skippedFrameCount++;
FLLog(FLLogLevelInfo, @"Dropping frame %zu because failed to `CGImageSourceCreateImageAtIndex` with image source %@", i, self->_imageSource);
}
}
}
_delayTimesForIndexes = [delayTimesForIndexesMutable copy];
_frameCount = imageCount;
if (self.frameCount == 0) {
FLLog(FLLogLevelInfo, @"Failed to create any valid frames for GIF with properties %@", imageProperties);
return nil;
} else if (self.frameCount == 1) {
// Warn when we only have a single frame but return a valid GIF.
FLLog(FLLogLevelInfo, @"Created valid GIF but with only a single frame. Image properties: %@", imageProperties);
} else {
// We have multiple frames, rock on!
}
// If no value is provided, select a default based on the GIF.
if (optimalFrameCacheSize == 0) {
// Calculate the optimal frame cache size: try choosing a larger buffer window depending on the predicted image size.
// It's only dependent on the image size & number of frames and never changes.
const CGFloat animatedImageDataSize = (CGFloat)CGImageGetBytesPerRow(self.posterImage.CGImage) * self.size.height * (CGFloat)(self.frameCount - skippedFrameCount) / (CGFloat)MEGABYTE;
if (animatedImageDataSize <= FLAnimatedImageDataSizeCategoryAll) {
_frameCacheSizeOptimal = self.frameCount;
} else if (animatedImageDataSize <= FLAnimatedImageDataSizeCategoryDefault) {
// This value doesn't depend on device memory much because if we're not keeping all frames in memory we will always be decoding 1 frame up ahead per 1 frame that gets played and at this point we might as well just keep a small buffer just large enough to keep from running out of frames.
_frameCacheSizeOptimal = FLAnimatedImageFrameCacheSizeDefault;
} else {
// The predicted size exceeds the limits to build up a cache and we go into low memory mode from the beginning.
_frameCacheSizeOptimal = FLAnimatedImageFrameCacheSizeLowMemory;
}
} else {
// Use the provided value.
_frameCacheSizeOptimal = optimalFrameCacheSize;
}
// In any case, cap the optimal cache size at the frame count.
_frameCacheSizeOptimal = MIN(_frameCacheSizeOptimal, self.frameCount);
// Convenience/minor performance optimization; keep an index set handy with the full range to return in `-frameIndexesToCache`.
_allFramesIndexSet = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, self.frameCount)];
// See the property declarations for descriptions.
_weakProxy = (id)[FLWeakProxy weakProxyForObject:self];
// Register this instance in the weak table for memory notifications. The NSHashTable will clean up after itself when we're gone.
// Note that FLAnimatedImages can be created on any thread, so the hash table must be locked.
@synchronized(allAnimatedImagesWeak) {
[allAnimatedImagesWeak addObject:self];
}
}
return self;
}
+ (instancetype)animatedImageWithGIFData:(NSData *)data
{
FLAnimatedImage *const animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:data];
return animatedImage;
}
- (void)dealloc
{
if (_weakProxy) {
[NSObject cancelPreviousPerformRequestsWithTarget:_weakProxy];
}
if (_imageSource) {
CFRelease(_imageSource);
}
}
#pragma mark - Public Methods
// See header for more details.
// Note: both consumer and producer are throttled: consumer by frame timings and producer by the available memory (max buffer window size).
- (UIImage *)imageLazilyCachedAtIndex:(NSUInteger)index
{
// Early return if the requested index is beyond bounds.
// Note: We're comparing an index with a count and need to bail on greater than or equal to.
if (index >= self.frameCount) {
FLLog(FLLogLevelWarn, @"Skipping requested frame %lu beyond bounds (total frame count: %lu) for animated image: %@", (unsigned long)index, (unsigned long)self.frameCount, self);
return nil;
}
// Remember requested frame index, this influences what we should cache next.
self.requestedFrameIndex = index;
#if defined(DEBUG) && DEBUG
if ([self.debug_delegate respondsToSelector:@selector(debug_animatedImage:didRequestCachedFrame:)]) {
[self.debug_delegate debug_animatedImage:self didRequestCachedFrame:index];
}
#endif
// Quick check to avoid doing any work if we already have all possible frames cached, a common case.
if ([self.cachedFrameIndexes count] < self.frameCount) {
// If we have frames that should be cached but aren't and aren't requested yet, request them.
// Exclude existing cached frames, frames already requested, and specially cached poster image.
NSMutableIndexSet *frameIndexesToAddToCacheMutable = [self frameIndexesToCache];
[frameIndexesToAddToCacheMutable removeIndexes:self.cachedFrameIndexes];
[frameIndexesToAddToCacheMutable removeIndexes:self.requestedFrameIndexes];
[frameIndexesToAddToCacheMutable removeIndex:self.posterImageFrameIndex];
NSIndexSet *frameIndexesToAddToCache = [frameIndexesToAddToCacheMutable copy];
// Asynchronously add frames to our cache.
if ([frameIndexesToAddToCache count] > 0) {
[self addFrameIndexesToCache:frameIndexesToAddToCache];
}
}
// Get the specified image.
UIImage *const image = self.cachedFramesForIndexes[@(index)];
// Purge if needed based on the current playhead position.
[self purgeFrameCacheIfNeeded];
return image;
}
// Only called once from `-imageLazilyCachedAtIndex` but factored into its own method for logical grouping.
- (void)addFrameIndexesToCache:(NSIndexSet *)frameIndexesToAddToCache
{
// Order matters. First, iterate over the indexes starting from the requested frame index.
// Then, if there are any indexes before the requested frame index, do those.
const NSRange firstRange = NSMakeRange(self.requestedFrameIndex, self.frameCount - self.requestedFrameIndex);
const NSRange secondRange = NSMakeRange(0, self.requestedFrameIndex);
if (firstRange.length + secondRange.length != self.frameCount) {
FLLog(FLLogLevelWarn, @"Two-part frame cache range doesn't equal full range.");
}
// Add to the requested list before we actually kick them off, so they don't get into the queue twice.
[self.requestedFrameIndexes addIndexes:frameIndexesToAddToCache];
// Lazily create dedicated isolation queue.
if (!self.serialQueue) {
_serialQueue = dispatch_queue_create("com.flipboard.framecachingqueue", DISPATCH_QUEUE_SERIAL);
}
// Start streaming requested frames in the background into the cache.
// Avoid capturing self in the block as there's no reason to keep doing work if the animated image went away.
__weak __typeof(self) weakSelf = self;
dispatch_async(self.serialQueue, ^{
// Produce and cache next needed frame.
void (^frameRangeBlock)(NSRange, BOOL *) = ^(NSRange range, BOOL *stop) {
// Iterate through contiguous indexes; can be faster than `enumerateIndexesInRange:options:usingBlock:`.
for (NSUInteger i = range.location; i < NSMaxRange(range); i++) {
#if defined(DEBUG) && DEBUG
const CFTimeInterval predrawBeginTime = CACurrentMediaTime();
#endif
UIImage *const image = [weakSelf imageAtIndex:i];
#if defined(DEBUG) && DEBUG
const CFTimeInterval predrawDuration = CACurrentMediaTime() - predrawBeginTime;
CFTimeInterval slowdownDuration = 0.0;
if ([self.debug_delegate respondsToSelector:@selector(debug_animatedImagePredrawingSlowdownFactor:)]) {
CGFloat predrawingSlowdownFactor = [self.debug_delegate debug_animatedImagePredrawingSlowdownFactor:self];
slowdownDuration = predrawDuration * predrawingSlowdownFactor - predrawDuration;
[NSThread sleepForTimeInterval:slowdownDuration];
}
FLLog(FLLogLevelVerbose, @"Predrew frame %lu in %f ms for animated image: %@", (unsigned long)i, (predrawDuration + slowdownDuration) * 1000, self);
#endif
// The results get returned one by one as soon as they're ready (and not in batch).
// The benefits of having the first frames as quick as possible outweigh building up a buffer to cope with potential hiccups when the CPU suddenly gets busy.
if (image && weakSelf) {
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.cachedFramesForIndexes[@(i)] = image;
[weakSelf.cachedFrameIndexes addIndex:i];
[weakSelf.requestedFrameIndexes removeIndex:i];
#if defined(DEBUG) && DEBUG
if ([weakSelf.debug_delegate respondsToSelector:@selector(debug_animatedImage:didUpdateCachedFrames:)]) {
[weakSelf.debug_delegate debug_animatedImage:weakSelf didUpdateCachedFrames:weakSelf.cachedFrameIndexes];
}
#endif
});
}
}
};
[frameIndexesToAddToCache enumerateRangesInRange:firstRange options:0 usingBlock:frameRangeBlock];
[frameIndexesToAddToCache enumerateRangesInRange:secondRange options:0 usingBlock:frameRangeBlock];
});
}
+ (CGSize)sizeForImage:(id)image
{
CGSize imageSize = CGSizeZero;
// Early return for nil
if (!image) {
return imageSize;
}
if ([image isKindOfClass:[UIImage class]]) {
UIImage *const uiImage = (UIImage *)image;
imageSize = uiImage.size;
} else if ([image isKindOfClass:[FLAnimatedImage class]]) {
FLAnimatedImage *const animatedImage = (FLAnimatedImage *)image;
imageSize = animatedImage.size;
} else {
// Bear trap to capture bad images; we have seen crashers cropping up on iOS 7.
FLLog(FLLogLevelError, @"`image` isn't of expected types `UIImage` or `FLAnimatedImage`: %@", image);
}
return imageSize;
}
#pragma mark - Private Methods
#pragma mark Frame Loading
- (UIImage *)imageAtIndex:(NSUInteger)index
{
// It's very important to use the cached `_imageSource` since the random access to a frame with `CGImageSourceCreateImageAtIndex` turns from an O(1) into an O(n) operation when re-initializing the image source every time.
const CGImageRef _Nullable imageRef = CGImageSourceCreateImageAtIndex(_imageSource, index, NULL);
// Early return for nil
if (!imageRef) {
return nil;
}
UIImage *image = [UIImage imageWithCGImage:imageRef];
CFRelease(imageRef);
// Loading in the image object is only half the work, the displaying image view would still have to synchronosly wait and decode the image, so we go ahead and do that here on the background thread.
if (self.isPredrawingEnabled) {
image = [[self class] predrawnImageFromImage:image];
}
return image;
}
#pragma mark Frame Caching
- (NSMutableIndexSet *)frameIndexesToCache
{
NSMutableIndexSet *indexesToCache = nil;
// Quick check to avoid building the index set if the number of frames to cache equals the total frame count.
if (self.frameCacheSizeCurrent == self.frameCount) {
indexesToCache = [self.allFramesIndexSet mutableCopy];
} else {
indexesToCache = [[NSMutableIndexSet alloc] init];
// Add indexes to the set in two separate blocks- the first starting from the requested frame index, up to the limit or the end.
// The second, if needed, the remaining number of frames beginning at index zero.
const NSUInteger firstLength = MIN(self.frameCacheSizeCurrent, self.frameCount - self.requestedFrameIndex);
const NSRange firstRange = NSMakeRange(self.requestedFrameIndex, firstLength);
[indexesToCache addIndexesInRange:firstRange];
const NSUInteger secondLength = self.frameCacheSizeCurrent - firstLength;
if (secondLength > 0) {
NSRange secondRange = NSMakeRange(0, secondLength);
[indexesToCache addIndexesInRange:secondRange];
}
// Double check our math, before we add the poster image index which may increase it by one.
if ([indexesToCache count] != self.frameCacheSizeCurrent) {
FLLog(FLLogLevelWarn, @"Number of frames to cache doesn't equal expected cache size.");
}
[indexesToCache addIndex:self.posterImageFrameIndex];
}
return indexesToCache;
}
- (void)purgeFrameCacheIfNeeded
{
// Purge frames that are currently cached but don't need to be.
// But not if we're still under the number of frames to cache.
// This way, if all frames are allowed to be cached (the common case), we can skip all the `NSIndexSet` math below.
if ([self.cachedFrameIndexes count] > self.frameCacheSizeCurrent) {
NSMutableIndexSet *indexesToPurge = [self.cachedFrameIndexes mutableCopy];
[indexesToPurge removeIndexes:[self frameIndexesToCache]];
[indexesToPurge enumerateRangesUsingBlock:^(NSRange range, BOOL *stop) {
// Iterate through contiguous indexes; can be faster than `enumerateIndexesInRange:options:usingBlock:`.
for (NSUInteger i = range.location; i < NSMaxRange(range); i++) {
[self.cachedFrameIndexes removeIndex:i];
[self.cachedFramesForIndexes removeObjectForKey:@(i)];
// Note: Don't `CGImageSourceRemoveCacheAtIndex` on the image source for frames that we don't want cached any longer to maintain O(1) time access.
#if defined(DEBUG) && DEBUG
if ([self.debug_delegate respondsToSelector:@selector(debug_animatedImage:didUpdateCachedFrames:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.debug_delegate debug_animatedImage:self didUpdateCachedFrames:self.cachedFrameIndexes];
});
}
#endif
}
}];
}
}
- (void)growFrameCacheSizeAfterMemoryWarning:(NSNumber *)frameCacheSize
{
self.frameCacheSizeMaxInternal = [frameCacheSize unsignedIntegerValue];
FLLog(FLLogLevelDebug, @"Grew frame cache size max to %lu after memory warning for animated image: %@", (unsigned long)self.frameCacheSizeMaxInternal, self);
// Schedule resetting the frame cache size max completely after a while.
const NSTimeInterval kResetDelay = 3.0;
[self.weakProxy performSelector:@selector(resetFrameCacheSizeMaxInternal) withObject:nil afterDelay:kResetDelay];
}
- (void)resetFrameCacheSizeMaxInternal
{
self.frameCacheSizeMaxInternal = FLAnimatedImageFrameCacheSizeNoLimit;
FLLog(FLLogLevelDebug, @"Reset frame cache size max (current frame cache size: %lu) for animated image: %@", (unsigned long)self.frameCacheSizeCurrent, self);
}
#pragma mark System Memory Warnings Notification Handler
- (void)didReceiveMemoryWarning:(NSNotification *)notification
{
self.memoryWarningCount++;
// If we were about to grow larger, but got rapped on our knuckles by the system again, cancel.
[NSObject cancelPreviousPerformRequestsWithTarget:self.weakProxy selector:@selector(growFrameCacheSizeAfterMemoryWarning:) object:@(FLAnimatedImageFrameCacheSizeGrowAfterMemoryWarning)];
[NSObject cancelPreviousPerformRequestsWithTarget:self.weakProxy selector:@selector(resetFrameCacheSizeMaxInternal) object:nil];
// Go down to the minimum and by that implicitly immediately purge from the cache if needed to not get jettisoned by the system and start producing frames on-demand.
FLLog(FLLogLevelDebug, @"Attempt setting frame cache size max to %lu (previous was %lu) after memory warning #%lu for animated image: %@", (unsigned long)FLAnimatedImageFrameCacheSizeLowMemory, (unsigned long)self.frameCacheSizeMaxInternal, (unsigned long)self.memoryWarningCount, self);
self.frameCacheSizeMaxInternal = FLAnimatedImageFrameCacheSizeLowMemory;
// Schedule growing larger again after a while, but cap our attempts to prevent a periodic sawtooth wave (ramps upward and then sharply drops) of memory usage.
//
// [mem]^ (2) (5) (6) 1) Loading frames for the first time
// (*)| , , , 2) Mem warning #1; purge cache
// | /| (4)/| /| 3) Grow cache size a bit after a while, if no mem warning occurs
// | / | _/ | _/ | 4) Try to grow cache size back to optimum after a while, if no mem warning occurs
// |(1)/ |_/ |/ |__(7) 5) Mem warning #2; purge cache
// |__/ (3) 6) After repetition of (3) and (4), mem warning #3; purge cache
// +----------------------> 7) After 3 mem warnings, stay at minimum cache size
// [t]
// *) The mem high water mark before we get warned might change for every cycle.
//
const NSUInteger kGrowAttemptsMax = 2;
const NSTimeInterval kGrowDelay = 2.0;
if ((self.memoryWarningCount - 1) <= kGrowAttemptsMax) {
[self.weakProxy performSelector:@selector(growFrameCacheSizeAfterMemoryWarning:) withObject:@(FLAnimatedImageFrameCacheSizeGrowAfterMemoryWarning) afterDelay:kGrowDelay];
}
// Note: It's not possible to get the level of a memory warning with a public API: http://stackoverflow.com/questions/2915247/iphone-os-memory-warnings-what-do-the-different-levels-mean/2915477#2915477
}
#pragma mark Image Decoding
// Decodes the image's data and draws it off-screen fully in memory; it's thread-safe and hence can be called on a background thread.
// On success, the returned object is a new `UIImage` instance with the same content as the one passed in.
// On failure, the returned object is the unchanged passed in one; the data will not be predrawn in memory though and an error will be logged.
// First inspired by & good Karma to: https://gist.github.com/steipete/1144242
+ (UIImage *)predrawnImageFromImage:(UIImage *)imageToPredraw
{
// Always use a device RGB color space for simplicity and predictability what will be going on.
const CGColorSpaceRef _Nullable colorSpaceDeviceRGBRef = CGColorSpaceCreateDeviceRGB();
// Early return on failure!
if (!colorSpaceDeviceRGBRef) {
FLLog(FLLogLevelError, @"Failed to `CGColorSpaceCreateDeviceRGB` for image %@", imageToPredraw);
return imageToPredraw;
}
// Even when the image doesn't have transparency, we have to add the extra channel because Quartz doesn't support other pixel formats than 32 bpp/8 bpc for RGB:
// kCGImageAlphaNoneSkipFirst, kCGImageAlphaNoneSkipLast, kCGImageAlphaPremultipliedFirst, kCGImageAlphaPremultipliedLast
// (source: docs "Quartz 2D Programming Guide > Graphics Contexts > Table 2-1 Pixel formats supported for bitmap graphics contexts")
const size_t numberOfComponents = CGColorSpaceGetNumberOfComponents(colorSpaceDeviceRGBRef) + 1; // 4: RGB + A
// "In iOS 4.0 and later, and OS X v10.6 and later, you can pass NULL if you want Quartz to allocate memory for the bitmap." (source: docs)
void *_Nullable data = NULL;
const size_t width = imageToPredraw.size.width;
const size_t height = imageToPredraw.size.height;
const size_t bitsPerComponent = CHAR_BIT;
const size_t bitsPerPixel = (bitsPerComponent * numberOfComponents);
const size_t bytesPerPixel = (bitsPerPixel / BYTE_SIZE);
const size_t bytesPerRow = (bytesPerPixel * width);
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageToPredraw.CGImage);
// If the alpha info doesn't match to one of the supported formats (see above), pick a reasonable supported one.
// "For bitmaps created in iOS 3.2 and later, the drawing environment uses the premultiplied ARGB format to store the bitmap data." (source: docs)
if (alphaInfo == kCGImageAlphaNone || alphaInfo == kCGImageAlphaOnly) {
alphaInfo = kCGImageAlphaNoneSkipFirst;
} else if (alphaInfo == kCGImageAlphaFirst) {
alphaInfo = kCGImageAlphaPremultipliedFirst;
} else if (alphaInfo == kCGImageAlphaLast) {
alphaInfo = kCGImageAlphaPremultipliedLast;
}
// "The constants for specifying the alpha channel information are declared with the `CGImageAlphaInfo` type but can be passed to this parameter safely." (source: docs)
bitmapInfo |= alphaInfo;
// Create our own graphics context to draw to; `UIGraphicsGetCurrentContext`/`UIGraphicsBeginImageContextWithOptions` doesn't create a new context but returns the current one which isn't thread-safe (e.g. main thread could use it at the same time).
// Note: It's not worth caching the bitmap context for multiple frames ("unique key" would be `width`, `height` and `hasAlpha`), it's ~50% slower. Time spent in libRIP's `CGSBlendBGRA8888toARGB8888` suddenly shoots up -- not sure why.
const CGContextRef _Nullable bitmapContextRef = CGBitmapContextCreate(data, width, height, bitsPerComponent, bytesPerRow, colorSpaceDeviceRGBRef, bitmapInfo);
CGColorSpaceRelease(colorSpaceDeviceRGBRef);
// Early return on failure!
if (!bitmapContextRef) {
FLLog(FLLogLevelError, @"Failed to `CGBitmapContextCreate` with color space %@ and parameters (width: %zu height: %zu bitsPerComponent: %zu bytesPerRow: %zu) for image %@", colorSpaceDeviceRGBRef, width, height, bitsPerComponent, bytesPerRow, imageToPredraw);
return imageToPredraw;
}
// Draw image in bitmap context and create image by preserving receiver's properties.
CGContextDrawImage(bitmapContextRef, CGRectMake(0.0, 0.0, imageToPredraw.size.width, imageToPredraw.size.height), imageToPredraw.CGImage);
const CGImageRef _Nullable predrawnImageRef = CGBitmapContextCreateImage(bitmapContextRef);
UIImage *_Nullable predrawnImage = predrawnImageRef ? [UIImage imageWithCGImage:predrawnImageRef scale:imageToPredraw.scale orientation:imageToPredraw.imageOrientation] : nil;
CGImageRelease(predrawnImageRef);
CGContextRelease(bitmapContextRef);
// Early return on failure!
if (!predrawnImage) {
FLLog(FLLogLevelError, @"Failed to `imageWithCGImage:scale:orientation:` with image ref %@ created with color space %@ and bitmap context %@ and properties and properties (scale: %f orientation: %ld) for image %@", predrawnImageRef, colorSpaceDeviceRGBRef, bitmapContextRef, imageToPredraw.scale, (long)imageToPredraw.imageOrientation, imageToPredraw);
return imageToPredraw;
}
return predrawnImage;
}
#pragma mark - Description
- (NSString *)description
{
NSString *description = [super description];
description = [description stringByAppendingFormat:@" size=%@", NSStringFromCGSize(self.size)];
description = [description stringByAppendingFormat:@" frameCount=%lu", (unsigned long)self.frameCount];
return description;
}
@end
#pragma mark - Logging
@implementation FLAnimatedImage (Logging)
static void (^_logBlock)(NSString *logString, FLLogLevel logLevel) = nil;
static FLLogLevel _logLevel;
+ (void)setLogBlock:(void (^_Nullable)(NSString *logString, FLLogLevel logLevel))logBlock logLevel:(FLLogLevel)logLevel
{
_logBlock = [logBlock copy];
_logLevel = logLevel;
}
+ (void)logStringFromBlock:(NSString *(^_Nullable)(void))stringBlock withLevel:(FLLogLevel)level
{
if (level <= _logLevel && _logBlock && stringBlock) {
_logBlock(stringBlock(), level);
}
}
@end
#pragma mark - FLWeakProxy
@interface FLWeakProxy ()
@property (nonatomic, weak) id target;
@end
@implementation FLWeakProxy
#pragma mark Life Cycle
// This is the designated creation method of an `FLWeakProxy` and
// as a subclass of `NSProxy` it doesn't respond to or need `-init`.
+ (instancetype)weakProxyForObject:(id)targetObject
{
FLWeakProxy *weakProxy = [FLWeakProxy alloc];
weakProxy.target = targetObject;
return weakProxy;
}
#pragma mark Forwarding Messages
- (id)forwardingTargetForSelector:(SEL)selector
{
// Keep it lightweight: access the ivar directly
return _target;
}
#pragma mark - NSWeakProxy Method Overrides
#pragma mark Handling Unimplemented Methods
- (void)forwardInvocation:(NSInvocation *)invocation
{
// Fallback for when target is nil. Don't do anything, just return 0/NULL/nil.
// The method signature we've received to get here is just a dummy to keep `doesNotRecognizeSelector:` from firing.
// We can't really handle struct return types here because we don't know the length.
void *_Nullable nullPointer = NULL;
[invocation setReturnValue:&nullPointer];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
// We only get here if `forwardingTargetForSelector:` returns nil.
// In that case, our weak target has been reclaimed. Return a dummy method signature to keep `doesNotRecognizeSelector:` from firing.
// We'll emulate the Obj-c messaging nil behavior by setting the return value to nil in `forwardInvocation:`, but we'll assume that the return value is `sizeof(void *)`.
// Other libraries handle this situation by making use of a global method signature cache, but that seems heavier than necessary and has issues as well.
// See https://www.mikeash.com/pyblog/friday-qa-2010-02-26-futures.html and https://github.com/steipete/PSTDelegateProxy/issues/1 for examples of using a method signature cache.
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
@end

View File

@@ -0,0 +1,460 @@
//
// FLAnimatedImageView.h
// Flipboard
//
// Created by Raphael Schaad on 7/8/13.
// Copyright (c) Flipboard. All rights reserved.
//
#import "FLAnimatedImageView.h"
#import "FLAnimatedImage.h"
#import <QuartzCore/QuartzCore.h>
#if defined(DEBUG) && DEBUG
@protocol FLAnimatedImageViewDebugDelegate <NSObject>
@optional
- (void)debug_animatedImageView:(FLAnimatedImageView *)animatedImageView waitingForFrame:(NSUInteger)index duration:(NSTimeInterval)duration;
@end
#endif
@interface FLAnimatedImageView ()
// Override of public `readonly` properties as private `readwrite`
@property (nonatomic, strong, readwrite) UIImage *currentFrame;
@property (nonatomic, assign, readwrite) NSUInteger currentFrameIndex;
@property (nonatomic, assign) NSUInteger loopCountdown;
@property (nonatomic, assign) NSTimeInterval accumulator;
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, assign) BOOL shouldAnimate; // Before checking this value, call `-updateShouldAnimate` whenever the animated image or visibility (window, superview, hidden, alpha) has changed.
@property (nonatomic, assign) BOOL needsDisplayWhenImageBecomesAvailable;
#if defined(DEBUG) && DEBUG
@property (nonatomic, weak) id<FLAnimatedImageViewDebugDelegate> debug_delegate;
#endif
@end
@implementation FLAnimatedImageView
@synthesize runLoopMode = _runLoopMode;
#pragma mark - Initializers
// -initWithImage: isn't documented as a designated initializer of UIImageView, but it actually seems to be.
// Using -initWithImage: doesn't call any of the other designated initializers.
- (instancetype)initWithImage:(UIImage *)image
{
self = [super initWithImage:image];
if (self) {
[self commonInit];
}
return self;
}
// -initWithImage:highlightedImage: also isn't documented as a designated initializer of UIImageView, but it doesn't call any other designated initializers.
- (instancetype)initWithImage:(UIImage *)image highlightedImage:(UIImage *)highlightedImage
{
self = [super initWithImage:image highlightedImage:highlightedImage];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit
{
self.runLoopMode = [[self class] defaultRunLoopMode];
if (@available(iOS 11.0, *)) {
self.accessibilityIgnoresInvertColors = YES;
}
}
#pragma mark - Accessors
#pragma mark Public
- (void)setAnimatedImage:(FLAnimatedImage *)animatedImage
{
if (![_animatedImage isEqual:animatedImage]) {
if (animatedImage) {
if (super.image) {
// UIImageView's `setImage:` will internally call its layer's `setContentsTransform:` based on the `image.imageOrientation`.
// The `contentsTransform` will affect layer rendering rotation because the CGImage's bitmap buffer does not actually take rotation.
// However, when calling `setImage:nil`, this `contentsTransform` will not be reset to identity.
// Further animation frame will be rendered as rotated. So we must set it to the poster image to clear the previous state.
// See more here: https://github.com/Flipboard/FLAnimatedImage/issues/100
super.image = animatedImage.posterImage;
// Clear out the image.
super.image = nil;
}
// Ensure disabled highlighting; it's not supported (see `-setHighlighted:`).
super.highlighted = NO;
// UIImageView seems to bypass some accessors when calculating its intrinsic content size, so this ensures its intrinsic content size comes from the animated image.
[self invalidateIntrinsicContentSize];
} else {
// Stop animating before the animated image gets cleared out.
[self stopAnimating];
}
_animatedImage = animatedImage;
self.currentFrame = animatedImage.posterImage;
self.currentFrameIndex = 0;
if (animatedImage.loopCount > 0) {
self.loopCountdown = animatedImage.loopCount;
} else {
self.loopCountdown = NSUIntegerMax;
}
self.accumulator = 0.0;
// Start animating after the new animated image has been set.
[self updateShouldAnimate];
if (self.shouldAnimate) {
[self startAnimating];
}
[self.layer setNeedsDisplay];
}
}
#pragma mark - Life Cycle
- (void)dealloc
{
// Removes the display link from all run loop modes.
[_displayLink invalidate];
}
#pragma mark - UIView Method Overrides
#pragma mark Observing View-Related Changes
- (void)didMoveToSuperview
{
[super didMoveToSuperview];
[self updateShouldAnimate];
if (self.shouldAnimate) {
[self startAnimating];
} else {
[self stopAnimating];
}
}
- (void)didMoveToWindow
{
[super didMoveToWindow];
[self updateShouldAnimate];
if (self.shouldAnimate) {
[self startAnimating];
} else {
[self stopAnimating];
}
}
- (void)setAlpha:(CGFloat)alpha
{
[super setAlpha:alpha];
[self updateShouldAnimate];
if (self.shouldAnimate) {
[self startAnimating];
} else {
[self stopAnimating];
}
}
- (void)setHidden:(BOOL)hidden
{
[super setHidden:hidden];
[self updateShouldAnimate];
if (self.shouldAnimate) {
[self startAnimating];
} else {
[self stopAnimating];
}
}
#pragma mark Auto Layout
- (CGSize)intrinsicContentSize
{
// Default to let UIImageView handle the sizing of its image, and anything else it might consider.
CGSize intrinsicContentSize = [super intrinsicContentSize];
// If we have have an animated image, use its image size.
// UIImageView's intrinsic content size seems to be the size of its image. The obvious approach, simply calling `-invalidateIntrinsicContentSize` when setting an animated image, results in UIImageView steadfastly returning `{UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric}` for its intrinsicContentSize.
// (Perhaps UIImageView bypasses its `-image` getter in its implementation of `-intrinsicContentSize`, as `-image` is not called after calling `-invalidateIntrinsicContentSize`.)
if (self.animatedImage) {
intrinsicContentSize = self.image.size;
}
return intrinsicContentSize;
}
#pragma mark - UIImageView Method Overrides
#pragma mark Image Data
- (UIImage *)image
{
UIImage *image = nil;
if (self.animatedImage) {
// Initially set to the poster image.
image = self.currentFrame;
} else {
image = super.image;
}
return image;
}
- (void)setImage:(UIImage *)image
{
if (image) {
// Clear out the animated image and implicitly pause animation playback.
self.animatedImage = nil;
}
super.image = image;
}
#pragma mark Animating Images
- (NSTimeInterval)frameDelayGreatestCommonDivisor
{
// Presision is set to half of the `kFLAnimatedImageDelayTimeIntervalMinimum` in order to minimize frame dropping.
const NSTimeInterval kGreatestCommonDivisorPrecision = 2.0 / kFLAnimatedImageDelayTimeIntervalMinimum;
NSArray *const delays = self.animatedImage.delayTimesForIndexes.allValues;
// Scales the frame delays by `kGreatestCommonDivisorPrecision`
// then converts it to an UInteger for in order to calculate the GCD.
NSUInteger scaledGCD = lrint([delays.firstObject floatValue] * kGreatestCommonDivisorPrecision);
for (NSNumber *value in delays) {
scaledGCD = gcd(lrint([value floatValue] * kGreatestCommonDivisorPrecision), scaledGCD);
}
// Reverse to scale to get the value back into seconds.
return (double)scaledGCD / kGreatestCommonDivisorPrecision;
}
static NSUInteger gcd(NSUInteger a, NSUInteger b)
{
// http://en.wikipedia.org/wiki/Greatest_common_divisor
if (a < b) {
return gcd(b, a);
} else if (a == b) {
return b;
}
while (true) {
const NSUInteger remainder = a % b;
if (remainder == 0) {
return b;
}
a = b;
b = remainder;
}
}
- (void)startAnimating
{
if (self.animatedImage) {
// Lazily create the display link.
if (!self.displayLink) {
// It is important to note the use of a weak proxy here to avoid a retain cycle. `-displayLinkWithTarget:selector:`
// will retain its target until it is invalidated. We use a weak proxy so that the image view will get deallocated
// independent of the display link's lifetime. Upon image view deallocation, we invalidate the display
// link which will lead to the deallocation of both the display link and the weak proxy.
FLWeakProxy *weakProxy = [FLWeakProxy weakProxyForObject:self];
self.displayLink = [CADisplayLink displayLinkWithTarget:weakProxy selector:@selector(displayDidRefresh:)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:self.runLoopMode];
}
if (@available(iOS 10, *)) {
// Adjusting preferredFramesPerSecond allows us to skip unnecessary calls to displayDidRefresh: when showing GIFs
// that don't animate quickly. Use ceil to err on the side of too many FPS so we don't miss a frame transition moment.
self.displayLink.preferredFramesPerSecond = ceil(1.0 / [self frameDelayGreatestCommonDivisor]);
} else {
const NSTimeInterval kDisplayRefreshRate = 60.0; // 60Hz
self.displayLink.frameInterval = MAX([self frameDelayGreatestCommonDivisor] * kDisplayRefreshRate, 1);
}
self.displayLink.paused = NO;
} else {
[super startAnimating];
}
}
- (void)setRunLoopMode:(NSRunLoopMode)runLoopMode
{
if (![@[NSDefaultRunLoopMode, NSRunLoopCommonModes] containsObject:runLoopMode]) {
NSAssert(NO, @"Invalid run loop mode: %@", runLoopMode);
_runLoopMode = [[self class] defaultRunLoopMode];
} else {
_runLoopMode = runLoopMode;
}
}
- (void)stopAnimating
{
if (self.animatedImage) {
self.displayLink.paused = YES;
} else {
[super stopAnimating];
}
}
- (BOOL)isAnimating
{
BOOL isAnimating = NO;
if (self.animatedImage) {
isAnimating = self.displayLink && !self.displayLink.isPaused;
} else {
isAnimating = [super isAnimating];
}
return isAnimating;
}
#pragma mark Highlighted Image Unsupport
- (void)setHighlighted:(BOOL)highlighted
{
// Highlighted image is unsupported for animated images, but implementing it breaks the image view when embedded in a UICollectionViewCell.
if (!self.animatedImage) {
[super setHighlighted:highlighted];
}
}
#pragma mark - Private Methods
#pragma mark Animation
// Don't repeatedly check our window & superview in `-displayDidRefresh:` for performance reasons.
// Just update our cached value whenever the animated image or visibility (window, superview, hidden, alpha) is changed.
- (void)updateShouldAnimate
{
const BOOL isVisible = self.window && self.superview && ![self isHidden] && self.alpha > 0.0;
self.shouldAnimate = self.animatedImage && isVisible;
}
- (void)displayDidRefresh:(CADisplayLink *)displayLink
{
// If for some reason a wild call makes it through when we shouldn't be animating, bail.
// Early return!
if (!self.shouldAnimate) {
FLLog(FLLogLevelWarn, @"Trying to animate image when we shouldn't: %@", self);
return;
}
NSNumber *_Nullable const delayTimeNumber = [self.animatedImage.delayTimesForIndexes objectForKey:@(self.currentFrameIndex)];
// If we don't have a frame delay (e.g. corrupt frame), don't update the view but skip the playhead to the next frame (in else-block).
if (delayTimeNumber != nil) {
const NSTimeInterval delayTime = [delayTimeNumber floatValue];
// If we have a nil image (e.g. waiting for frame), don't update the view nor playhead.
UIImage *_Nullable const image = [self.animatedImage imageLazilyCachedAtIndex:self.currentFrameIndex];
if (image) {
FLLog(FLLogLevelVerbose, @"Showing frame %lu for animated image: %@", (unsigned long)self.currentFrameIndex, self.animatedImage);
self.currentFrame = image;
if (self.needsDisplayWhenImageBecomesAvailable) {
[self.layer setNeedsDisplay];
self.needsDisplayWhenImageBecomesAvailable = NO;
}
if (@available(iOS 10, *)) {
self.accumulator += displayLink.targetTimestamp - CACurrentMediaTime();
} else {
self.accumulator += displayLink.duration * (NSTimeInterval)displayLink.frameInterval;
}
// While-loop first inspired by & good Karma to: https://github.com/ondalabs/OLImageView/blob/master/OLImageView.m
while (self.accumulator >= delayTime) {
self.accumulator -= delayTime;
self.currentFrameIndex++;
if (self.currentFrameIndex >= self.animatedImage.frameCount) {
// If we've looped the number of times that this animated image describes, stop looping.
self.loopCountdown--;
if (self.loopCompletionBlock) {
self.loopCompletionBlock(self.loopCountdown);
}
if (self.loopCountdown == 0) {
[self stopAnimating];
return;
}
self.currentFrameIndex = 0;
}
// Calling `-setNeedsDisplay` will just paint the current frame, not the new frame that we may have moved to.
// Instead, set `needsDisplayWhenImageBecomesAvailable` to `YES` -- this will paint the new image once loaded.
self.needsDisplayWhenImageBecomesAvailable = YES;
}
} else {
FLLog(FLLogLevelDebug, @"Waiting for frame %lu for animated image: %@", (unsigned long)self.currentFrameIndex, self.animatedImage);
#if defined(DEBUG) && DEBUG
if ([self.debug_delegate respondsToSelector:@selector(debug_animatedImageView:waitingForFrame:duration:)]) {
if (@available(iOS 10, *)) {
[self.debug_delegate debug_animatedImageView:self waitingForFrame:self.currentFrameIndex duration:displayLink.targetTimestamp - CACurrentMediaTime()];
} else {
[self.debug_delegate debug_animatedImageView:self waitingForFrame:self.currentFrameIndex duration:displayLink.duration * (NSTimeInterval)displayLink.frameInterval];
}
}
#endif
}
} else {
self.currentFrameIndex++;
}
}
+ (NSRunLoopMode)defaultRunLoopMode
{
// Key off `activeProcessorCount` (as opposed to `processorCount`) since the system could shut down cores in certain situations.
return [NSProcessInfo processInfo].activeProcessorCount > 1 ? NSRunLoopCommonModes : NSDefaultRunLoopMode;
}
#pragma mark - CALayerDelegate (Informal)
#pragma mark Providing the Layer's Content
- (void)displayLayer:(CALayer *)layer
{
layer.contents = (__bridge id)self.image.CGImage;
}
@end

View File

@@ -0,0 +1,83 @@
//
// FLAnimatedImage.h
// Flipboard
//
// Created by Raphael Schaad on 7/8/13.
// Copyright (c) Flipboard. All rights reserved.
//
#import <UIKit/UIKit.h>
// Allow user classes conveniently just importing one header.
#import "FLAnimatedImageView.h"
#ifndef NS_DESIGNATED_INITIALIZER
#if __has_attribute(objc_designated_initializer)
#define NS_DESIGNATED_INITIALIZER __attribute((objc_designated_initializer))
#else
#define NS_DESIGNATED_INITIALIZER
#endif
#endif
extern const NSTimeInterval kFLAnimatedImageDelayTimeIntervalMinimum;
//
// An `FLAnimatedImage`'s job is to deliver frames in a highly performant way and works in conjunction with `FLAnimatedImageView`.
// It subclasses `NSObject` and not `UIImage` because it's only an "image" in the sense that a sea lion is a lion.
// It tries to intelligently choose the frame cache size depending on the image and memory situation with the goal to lower CPU usage for smaller ones, lower memory usage for larger ones and always deliver frames for high performant play-back.
// Note: `posterImage`, `size`, `loopCount`, `delayTimes` and `frameCount` don't change after successful initialization.
//
@interface FLAnimatedImage : NSObject
@property (nonatomic, strong, readonly) UIImage *posterImage; // Guaranteed to be loaded; usually equivalent to `-imageLazilyCachedAtIndex:0`
@property (nonatomic, assign, readonly) CGSize size; // The `.posterImage`'s `.size`
@property (nonatomic, assign, readonly) NSUInteger loopCount; // "The number of times to repeat an animated sequence." according to ImageIO (note the slightly different definition to Netscape 2.0 Loop Extension); 0 means repeating the animation forever
@property (nonatomic, strong, readonly) NSDictionary *delayTimesForIndexes; // Of type `NSTimeInterval` boxed in `NSNumber`s
@property (nonatomic, assign, readonly) NSUInteger frameCount; // Number of valid frames; equal to `[.delayTimes count]`
@property (nonatomic, assign, readonly) NSUInteger frameCacheSizeCurrent; // Current size of intelligently chosen buffer window; can range in the interval [1..frameCount]
@property (nonatomic, assign) NSUInteger frameCacheSizeMax; // Allow to cap the cache size; 0 means no specific limit (default)
// Intended to be called from main thread synchronously; will return immediately.
// If the result isn't cached, will return `nil`; the caller should then pause playback, not increment frame counter and keep polling.
// After an initial loading time, depending on `frameCacheSize`, frames should be available immediately from the cache.
- (UIImage *)imageLazilyCachedAtIndex:(NSUInteger)index;
// Pass either a `UIImage` or an `FLAnimatedImage` and get back its size
+ (CGSize)sizeForImage:(id)image;
// On success, the initializers return an `FLAnimatedImage` with all fields initialized, on failure they return `nil` and an error will be logged.
- (instancetype)initWithAnimatedGIFData:(NSData *)data;
// Pass 0 for optimalFrameCacheSize to get the default, predrawing is enabled by default.
- (instancetype)initWithAnimatedGIFData:(NSData *)data optimalFrameCacheSize:(NSUInteger)optimalFrameCacheSize predrawingEnabled:(BOOL)isPredrawingEnabled NS_DESIGNATED_INITIALIZER;
+ (instancetype)animatedImageWithGIFData:(NSData *)data;
@property (nonatomic, strong, readonly) NSData *data; // The data the receiver was initialized with; read-only
@end
typedef NS_ENUM(NSUInteger, FLLogLevel) {
FLLogLevelNone = 0,
FLLogLevelError,
FLLogLevelWarn,
FLLogLevelInfo,
FLLogLevelDebug,
FLLogLevelVerbose
};
@interface FLAnimatedImage (Logging)
+ (void)setLogBlock:(void (^)(NSString *logString, FLLogLevel logLevel))logBlock logLevel:(FLLogLevel)logLevel;
+ (void)logStringFromBlock:(NSString *(^)(void))stringBlock withLevel:(FLLogLevel)level;
@end
#define FLLog(logLevel, format, ...) [FLAnimatedImage logStringFromBlock:^NSString *{ return [NSString stringWithFormat:(format), ## __VA_ARGS__]; } withLevel:(logLevel)]
@interface FLWeakProxy : NSProxy
+ (instancetype)weakProxyForObject:(id)targetObject;
@end

View File

@@ -0,0 +1,36 @@
//
// FLAnimatedImageView.h
// Flipboard
//
// Created by Raphael Schaad on 7/8/13.
// Copyright (c) Flipboard. All rights reserved.
//
#import <UIKit/UIKit.h>
@class FLAnimatedImage;
@protocol FLAnimatedImageViewDebugDelegate;
//
// An `FLAnimatedImageView` can take an `FLAnimatedImage` and plays it automatically when in view hierarchy and stops when removed.
// The animation can also be controlled with the `UIImageView` methods `-start/stop/isAnimating`.
// It is a fully compatible `UIImageView` subclass and can be used as a drop-in component to work with existing code paths expecting to display a `UIImage`.
// Under the hood it uses a `CADisplayLink` for playback, which can be inspected with `currentFrame` & `currentFrameIndex`.
//
@interface FLAnimatedImageView : UIImageView
// Setting `[UIImageView.image]` to a non-`nil` value clears out existing `animatedImage`.
// And vice versa, setting `animatedImage` will initially populate the `[UIImageView.image]` to its `posterImage` and then start animating and hold `currentFrame`.
@property (nonatomic, strong) FLAnimatedImage *animatedImage;
@property (nonatomic, copy) void(^loopCompletionBlock)(NSUInteger loopCountRemaining);
@property (nonatomic, strong, readonly) UIImage *currentFrame;
@property (nonatomic, assign, readonly) NSUInteger currentFrameIndex;
// The animation runloop mode. Enables playback during scrolling by allowing timer events (i.e. animation) with NSRunLoopCommonModes.
// To keep scrolling smooth on single-core devices such as iPhone 3GS/4 and iPod Touch 4th gen, the default run loop mode is NSDefaultRunLoopMode. Otherwise, the default is NSDefaultRunLoopMode.
@property (nonatomic, copy) NSRunLoopMode runLoopMode;
@end

21
Pods/FLAnimatedImage/LICENSE generated Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2016 Flipboard
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

122
Pods/FLAnimatedImage/README.md generated Normal file
View File

@@ -0,0 +1,122 @@
# [FLAnimatedImage](https://github.com/Flipboard/FLAnimatedImage) &middot; [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Flipboard/FLAnimatedImage/blob/master/LICENSE) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/Flipboard/FLAnimatedImage/pulls)
FLAnimatedImage is a performant animated GIF engine for iOS:
- Plays multiple GIFs simultaneously with a playback speed comparable to desktop browsers
- Honors variable frame delays
- Behaves gracefully under memory pressure
- Eliminates delays or blocking during the first playback loop
- Interprets the frame delays of fast GIFs the same way modern browsers do
It's a well-tested [component that powers all GIFs in Flipboard](http://engineering.flipboard.com/2014/05/animated-gif). To understand its behavior it comes with an interactive demo:
![Flipboard playing multiple GIFs](https://github.com/Flipboard/FLAnimatedImage/raw/master/images/flanimatedimage-demo-player.gif)
## Who is this for?
- Apps that don't support animated GIFs yet
- Apps that already support animated GIFs but want a higher performance solution
- People who want to tinker with the code ([the corresponding blog post](http://engineering.flipboard.com/2014/05/animated-gif/) is a great place to start; also see the *To Do* section below)
## Installation & Usage
FLAnimatedImage is a well-encapsulated drop-in component. Simply replace your `UIImageView` instances with instances of `FLAnimatedImageView` to get animated GIF support. There is no central cache or state to manage.
If using CocoaPods, the quickest way to try it out is to type this on the command line:
```shell
$ pod try FLAnimatedImage
```
To add it to your app, copy the two classes `FLAnimatedImage.h/.m` and `FLAnimatedImageView.h/.m` into your Xcode project or add via [CocoaPods](http://cocoapods.org) by adding this to your Podfile:
```ruby
pod 'FLAnimatedImage', '~> 1.0'
```
If using [Carthage](https://github.com/Carthage/Carthage), add the following line into your `Cartfile`
```
github "Flipboard/FLAnimatedImage"
```
If using [Swift Package Manager](https://github.com/apple/swift-package-manager), add the following to your `Package.swift` or add via XCode:
```swift
dependencies: [
.package(url: "https://github.com/Flipboard/FLAnimatedImage.git", .upToNextMajor(from: "1.0.16"))
],
targets: [
.target(name: "TestProject", dependencies: ["FLAnimatedImage""])
]
```
In your code, `#import "FLAnimatedImage.h"`, create an image from an animated GIF, and setup the image view to display it:
```objective-c
FLAnimatedImage *image = [FLAnimatedImage animatedImageWithGIFData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://upload.wikimedia.org/wikipedia/commons/2/2c/Rotating_earth_%28large%29.gif"]]];
FLAnimatedImageView *imageView = [[FLAnimatedImageView alloc] init];
imageView.animatedImage = image;
imageView.frame = CGRectMake(0.0, 0.0, 100.0, 100.0);
[self.view addSubview:imageView];
```
It's flexible to integrate in your custom image loading stack and backwards compatible to iOS 9.
It uses ARC and the Apple frameworks `QuartzCore`, `ImageIO`, `MobileCoreServices`, and `CoreGraphics`.
It is capable of fine-grained logging. A block can be set on `FLAnimatedImage` that's invoked when logging occurs with various log levels via the `+setLogBlock:logLevel:` method. For example:
```objective-c
// Set up FLAnimatedImage logging.
[FLAnimatedImage setLogBlock:^(NSString *logString, FLLogLevel logLevel) {
// Using NSLog
NSLog(@"%@", logString);
// ...or CocoaLumberjackLogger only logging warnings and errors
if (logLevel == FLLogLevelError) {
DDLogError(@"%@", logString);
} else if (logLevel == FLLogLevelWarn) {
DDLogWarn(@"%@", logString);
}
} logLevel:FLLogLevelWarn];
```
Since FLAnimatedImage is licensed under MIT, it's compatible with the terms of using it for any app on the App Store.
## Release process
1. Bump version in `FLAnimatedImage.podspec`, update CHANGES, and commit.
2. Tag commit with `> git tag -a <VERSION> -m "<VERSION>"` and `> git push --tags`.
3. [Submit Podspec to Trunk with](https://guides.cocoapods.org/making/specs-and-specs-repo.html#how-do-i-update-an-existing-pod) `> pod trunk push FLAnimatedImage.podspec` ([ensure you're auth'ed](https://guides.cocoapods.org/making/getting-setup-with-trunk.html#getting-started)).
## To Do
- Support other animated image formats such as APNG or WebP (WebP support implemented [here](https://github.com/Flipboard/FLAnimatedImage/pull/86))
- Integration into network libraries and image caches
- Investigate whether `FLAnimatedImage` should become a `UIImage` subclass
- Smarter buffering
- Bring demo app to iPhone
This code has successfully shipped to many people as is, but please do come with your questions, issues and pull requests!
## Select apps using FLAnimatedImage
(alphabetically)
- [Close-up](http://closeu.pe)
- [Design Shots](https://itunes.apple.com/app/id792517951)
- [Dropbox](https://www.dropbox.com)
- [Dumpert](http://dumpert.nl)
- [Ello](https://ello.co/)
- [Facebook](https://facebook.com)
- [Flipboard](https://flipboard.com)
- [getGIF](https://itunes.apple.com/app/id964784701)
- [Gifalicious](https://itunes.apple.com/us/app/gifalicious-see-your-gifs/id965346708?mt=8)
- [HashPhotos](https://itunes.apple.com/app/id685784609)
- [Instagram](https://www.instagram.com/)
- [LiveBooth](http://www.liveboothapp.com)
- [lWlVl Festival](http://lwlvl.com)
- [Medium](https://medium.com)
- [Pinterest](https://pinterest.com)
- [Slack](https://slack.com/)
- [Telegram](https://telegram.org/)
- [Zip Code Finder](https://itunes.apple.com/app/id893031254)
If you're using FLAnimatedImage in your app, please open a PR to add it to this list!

View File

@@ -0,0 +1,48 @@
//
// IQNSArray+Sort.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
@class UIView;
/**
UIView.subviews sorting category.
*/
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface NSArray (IQ_NSArray_Sort)
///--------------
/// @name Sorting
///--------------
/**
Returns the array by sorting the UIView's by their tag property.
*/
@property (nonnull, nonatomic, readonly, copy) NSArray<__kindof UIView*> * sortedArrayByTag;
/**
Returns the array by sorting the UIView's by their tag property.
*/
@property (nonnull, nonatomic, readonly, copy) NSArray<__kindof UIView*> * sortedArrayByPosition;
@end

View File

@@ -0,0 +1,72 @@
//
// IQNSArray+Sort.m
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import "IQNSArray+Sort.h"
#import "IQUIView+Hierarchy.h"
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation NSArray (IQ_NSArray_Sort)
- (NSArray<UIView*>*)sortedArrayByTag
{
return [self sortedArrayUsingComparator:^NSComparisonResult(UIView *view1, UIView *view2) {
if ([view1 respondsToSelector:@selector(tag)] && [view2 respondsToSelector:@selector(tag)])
{
if ([view1 tag] < [view2 tag]) return NSOrderedAscending;
else if ([view1 tag] > [view2 tag]) return NSOrderedDescending;
else return NSOrderedSame;
}
else
return NSOrderedSame;
}];
}
- (NSArray<UIView*>*)sortedArrayByPosition
{
return [self sortedArrayUsingComparator:^NSComparisonResult(UIView *view1, UIView *view2) {
CGFloat x1 = CGRectGetMinX(view1.frame);
CGFloat y1 = CGRectGetMinY(view1.frame);
CGFloat x2 = CGRectGetMinX(view2.frame);
CGFloat y2 = CGRectGetMinY(view2.frame);
if (y1 < y2) return NSOrderedAscending;
else if (y1 > y2) return NSOrderedDescending;
//Else both y are same so checking for x positions
else if (x1 < x2) return NSOrderedAscending;
else if (x1 > x2) return NSOrderedDescending;
else return NSOrderedSame;
}];
}
@end

View File

@@ -0,0 +1,62 @@
//
// IQUIScrollView+Additions.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface UIScrollView (Additions)
/**
If YES, then scrollview will ignore scrolling (simply not scroll it) for adjusting textfield position. Default is NO.
*/
@property(nonatomic, assign) BOOL shouldIgnoreScrollingAdjustment;
/**
If YES, then scrollview will ignore content inset adjustment (simply not updating it) when keyboard is shown. Default is NO.
*/
@property(nonatomic, assign) BOOL shouldIgnoreContentInsetAdjustment;
/**
Restore scrollViewContentOffset when resigning from scrollView. Default is NO.
*/
@property(nonatomic, assign) BOOL shouldRestoreScrollViewContentOffset;
@end
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface UITableView (PreviousNextIndexPath)
-(nullable NSIndexPath*)previousIndexPathOfIndexPath:(nonnull NSIndexPath*)indexPath;
//-(nullable NSIndexPath*)nextIndexPathOfIndexPath:(nonnull NSIndexPath*)indexPath;
@end
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface UICollectionView (PreviousNextIndexPath)
-(nullable NSIndexPath*)previousIndexPathOfIndexPath:(nonnull NSIndexPath*)indexPath;
//-(nullable NSIndexPath*)nextIndexPathOfIndexPath:(nonnull NSIndexPath*)indexPath;
@end

View File

@@ -0,0 +1,165 @@
//
// IQUIScrollView+Additions.m
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <objc/runtime.h>
#import "IQUIScrollView+Additions.h"
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation UIScrollView (Additions)
-(void)setShouldIgnoreScrollingAdjustment:(BOOL)shouldIgnoreScrollingAdjustment
{
objc_setAssociatedObject(self, @selector(shouldIgnoreScrollingAdjustment), @(shouldIgnoreScrollingAdjustment), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(BOOL)shouldIgnoreScrollingAdjustment
{
NSNumber *shouldIgnoreScrollingAdjustment = objc_getAssociatedObject(self, @selector(shouldIgnoreScrollingAdjustment));
return [shouldIgnoreScrollingAdjustment boolValue];
}
-(void)setShouldIgnoreContentInsetAdjustment:(BOOL)shouldIgnoreContentInsetAdjustment
{
objc_setAssociatedObject(self, @selector(shouldIgnoreContentInsetAdjustment), @(shouldIgnoreContentInsetAdjustment), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(BOOL)shouldIgnoreContentInsetAdjustment
{
NSNumber *shouldIgnoreContentInsetAdjustment = objc_getAssociatedObject(self, @selector(shouldIgnoreContentInsetAdjustment));
return [shouldIgnoreContentInsetAdjustment boolValue];
}
-(void)setShouldRestoreScrollViewContentOffset:(BOOL)shouldRestoreScrollViewContentOffset
{
objc_setAssociatedObject(self, @selector(shouldRestoreScrollViewContentOffset), @(shouldRestoreScrollViewContentOffset), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(BOOL)shouldRestoreScrollViewContentOffset
{
NSNumber *shouldRestoreScrollViewContentOffset = objc_getAssociatedObject(self, @selector(shouldRestoreScrollViewContentOffset));
return [shouldRestoreScrollViewContentOffset boolValue];
}
@end
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation UITableView (PreviousNextIndexPath)
-(nullable NSIndexPath*)previousIndexPathOfIndexPath:(nonnull NSIndexPath*)indexPath
{
NSInteger previousRow = indexPath.row - 1;
NSInteger previousSection = indexPath.section;
//Fixing indexPath
if (previousRow < 0)
{
previousSection -= 1;
if (previousSection >= 0)
{
previousRow = [self numberOfRowsInSection:previousSection]-1;
}
}
if (previousRow >= 0 && previousSection >= 0)
{
return [NSIndexPath indexPathForRow:previousRow inSection:previousSection];
}
return nil;
}
//-(nullable NSIndexPath*)nextIndexPathOfIndexPath:(nonnull NSIndexPath*)indexPath
//{
// NSInteger nextRow = indexPath.row + 1;
// NSInteger nextSection = indexPath.section;
//
// //Fixing indexPath
// if (nextRow >= [self numberOfRowsInSection:nextSection])
// {
// nextRow = 0;
// nextSection += 1;
// }
//
// if (self.numberOfSections > nextSection && [self numberOfRowsInSection:nextSection] > nextRow)
// {
// return [NSIndexPath indexPathForItem:nextRow inSection:nextSection];
// }
//
// return nil;
//}
//
@end
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation UICollectionView (PreviousNextIndexPath)
-(nullable NSIndexPath*)previousIndexPathOfIndexPath:(nonnull NSIndexPath*)indexPath
{
NSInteger previousRow = indexPath.row - 1;
NSInteger previousSection = indexPath.section;
//Fixing indexPath
if (previousRow < 0)
{
previousSection -= 1;
if (previousSection >= 0)
{
previousRow = [self numberOfItemsInSection:previousSection]-1;
}
}
if (previousRow >= 0 && previousSection >= 0)
{
return [NSIndexPath indexPathForItem:previousRow inSection:previousSection];
}
return nil;
}
//-(nullable NSIndexPath*)nextIndexPathOfIndexPath:(nonnull NSIndexPath*)indexPath
//{
// NSInteger nextRow = indexPath.row + 1;
// NSInteger nextSection = indexPath.section;
//
// //Fixing indexPath
// if (nextRow >= [self numberOfItemsInSection:nextSection])
// {
// nextRow = 0;
// nextSection += 1;
// }
//
// if (self.numberOfSections > nextSection && [self numberOfItemsInSection:nextSection] > nextRow)
// {
// return [NSIndexPath indexPathForItem:nextRow inSection:nextSection];
// }
//
// return nil;
//}
@end

View File

@@ -0,0 +1,63 @@
//
// IQUITextFieldView+Additions.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import <IQKeyboardManager/IQKeyboardManagerConstants.h>
/**
UIView category for managing UITextField/UITextView
*/
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface UIView (Additions)
/**
To set customized distance from keyboard for textField/textView. Can't be less than zero
*/
@property(nonatomic, assign) CGFloat keyboardDistanceFromTextField;
/**
If shouldIgnoreSwitchingByNextPrevious is YES then library will ignore this textField/textView while moving to other textField/textView using keyboard toolbar next previous buttons. Default is NO
*/
@property(nonatomic, assign) BOOL ignoreSwitchingByNextPrevious;
///**
// Override Enable/disable managing distance between keyboard and textField behavior for this particular textField.
// */
@property(nonatomic, assign) IQEnableMode enableMode;
/**
Override resigns Keyboard on touching outside of UITextField/View behavior for this particular textField.
*/
@property(nonatomic, assign) IQEnableMode shouldResignOnTouchOutsideMode;
@end
///-------------------------------------------
/// @name Custom KeyboardDistanceFromTextField
///-------------------------------------------
/**
Uses default keyboard distance for textField.
*/
extern CGFloat const kIQUseDefaultKeyboardDistance;

View File

@@ -0,0 +1,92 @@
//
// IQUITextFieldView+Additions.m
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <objc/runtime.h>
#import "IQUITextFieldView+Additions.h"
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation UIView (Additions)
-(void)setKeyboardDistanceFromTextField:(CGFloat)keyboardDistanceFromTextField
{
//Can't be less than zero. Minimum is zero.
keyboardDistanceFromTextField = MAX(keyboardDistanceFromTextField, 0);
objc_setAssociatedObject(self, @selector(keyboardDistanceFromTextField), @(keyboardDistanceFromTextField), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(CGFloat)keyboardDistanceFromTextField
{
NSNumber *keyboardDistanceFromTextField = objc_getAssociatedObject(self, @selector(keyboardDistanceFromTextField));
return (keyboardDistanceFromTextField != nil)?[keyboardDistanceFromTextField floatValue]:kIQUseDefaultKeyboardDistance;
}
-(void)setIgnoreSwitchingByNextPrevious:(BOOL)ignoreSwitchingByNextPrevious
{
objc_setAssociatedObject(self, @selector(ignoreSwitchingByNextPrevious), @(ignoreSwitchingByNextPrevious), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(BOOL)ignoreSwitchingByNextPrevious
{
NSNumber *ignoreSwitchingByNextPrevious = objc_getAssociatedObject(self, @selector(ignoreSwitchingByNextPrevious));
return [ignoreSwitchingByNextPrevious boolValue];
}
-(void)setEnableMode:(IQEnableMode)enableMode
{
objc_setAssociatedObject(self, @selector(enableMode), @(enableMode), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(IQEnableMode)enableMode
{
NSNumber *enableMode = objc_getAssociatedObject(self, @selector(enableMode));
return [enableMode unsignedIntegerValue];
}
-(void)setShouldResignOnTouchOutsideMode:(IQEnableMode)shouldResignOnTouchOutsideMode
{
objc_setAssociatedObject(self, @selector(shouldResignOnTouchOutsideMode), @(shouldResignOnTouchOutsideMode), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(IQEnableMode)shouldResignOnTouchOutsideMode
{
NSNumber *shouldResignOnTouchOutsideMode = objc_getAssociatedObject(self, @selector(shouldResignOnTouchOutsideMode));
return [shouldResignOnTouchOutsideMode unsignedIntegerValue];
}
@end
///------------------------------------
/// @name keyboardDistanceFromTextField
///------------------------------------
/**
Uses default keyboard distance for textField.
*/
CGFloat const kIQUseDefaultKeyboardDistance = CGFLOAT_MAX;

View File

@@ -0,0 +1,135 @@
//
// IQUIView+Hierarchy.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import <IQKeyboardManager/IQKeyboardManagerConstants.h>
@class UICollectionView, UIScrollView, UITableView, UISearchBar, NSArray;
/**
UIView hierarchy category.
*/
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface UIView (IQ_UIView_Hierarchy)
///----------------------
/// @name viewControllers
///----------------------
/**
Returns the UIViewController object that manages the receiver.
*/
@property (nullable, nonatomic, readonly, strong) UIViewController *viewContainingController;
/**
Returns the topMost UIViewController object in hierarchy.
*/
@property (nullable, nonatomic, readonly, strong) UIViewController *topMostController;
/**
Returns the UIViewController object that is actually the parent of this object. Most of the time it's the viewController object which actually contains it, but result may be different if it's viewController is added as childViewController of another viewController.
*/
@property (nullable, nonatomic, readonly, strong) UIViewController *parentContainerViewController;
///-----------------------------------
/// @name Superviews/Subviews/Siblings
///-----------------------------------
/**
Returns the superView of provided class type.
@param classType class type of the object which is to be search in above hierarchy and return
@param belowView view object in upper hierarchy where method should stop searching and return nil
*/
-(nullable __kindof UIView*)superviewOfClassType:(nonnull Class)classType belowView:(nullable UIView*)belowView;
-(nullable __kindof UIView*)superviewOfClassType:(nonnull Class)classType;
/**
Returns all siblings of the receiver which canBecomeFirstResponder.
*/
@property (nonnull, nonatomic, readonly, copy) NSArray<__kindof UIView*> *responderSiblings;
/**
Returns all deep subViews of the receiver which canBecomeFirstResponder.
*/
@property (nonnull, nonatomic, readonly, copy) NSArray<__kindof UIView*> *deepResponderViews;
///-------------------------
/// @name Special TextFields
///-------------------------
/**
Returns searchBar if receiver object is UISearchBarTextField, otherwise return nil.
*/
@property (nullable, nonatomic, readonly) UISearchBar *textFieldSearchBar;
/**
Returns YES if the receiver object is UIAlertSheetTextField, otherwise return NO.
*/
@property (nonatomic, getter=isAlertViewTextField, readonly) BOOL alertViewTextField;
///----------------
/// @name Transform
///----------------
/**
Returns current view transform with respect to the 'toView'.
*/
-(CGAffineTransform)convertTransformToView:(nullable UIView*)toView;
///-----------------
/// @name Hierarchy
///-----------------
/**
Returns a string that represent the information about it's subview's hierarchy. You can use this method to debug the subview's positions.
*/
@property (nonnull, nonatomic, readonly, copy) NSString *subHierarchy;
/**
Returns an string that represent the information about it's upper hierarchy. You can use this method to debug the superview's positions.
*/
@property (nonnull, nonatomic, readonly, copy) NSString *superHierarchy;
/**
Returns an string that represent the information about it's frame positions. You can use this method to debug self positions.
*/
@property (nonnull, nonatomic, readonly, copy) NSString *debugHierarchy;
@end
/**
NSObject category to used for logging purposes
*/
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface NSObject (IQ_Logging)
/**
Short description for logging purpose.
*/
@property (nonnull, nonatomic, readonly, copy) NSString *_IQDescription;
@end

View File

@@ -0,0 +1,435 @@
//
// IQUIView+Hierarchy.m
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#import "IQUIView+Hierarchy.h"
#import "IQUITextFieldView+Additions.h"
#import "IQUIViewController+Additions.h"
#import "IQNSArray+Sort.h"
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation UIView (IQ_UIView_Hierarchy)
-(UIViewController*)viewContainingController
{
UIResponder *nextResponder = self;
do
{
nextResponder = [nextResponder nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]])
return (UIViewController*)nextResponder;
}
while (nextResponder);
return nil;
}
-(UIViewController *)topMostController
{
NSMutableArray<UIViewController*> *controllersHierarchy = [[NSMutableArray alloc] init];
UIViewController *topController = self.window.rootViewController;
if (topController)
{
[controllersHierarchy addObject:topController];
}
while ([topController presentedViewController])
{
topController = [topController presentedViewController];
[controllersHierarchy addObject:topController];
}
UIViewController *matchController = [self viewContainingController];
while (matchController && [controllersHierarchy containsObject:matchController] == NO)
{
do
{
matchController = (UIViewController*)[matchController nextResponder];
}
while (matchController && [matchController isKindOfClass:[UIViewController class]] == NO);
}
return matchController;
}
-(UIViewController *)parentContainerViewController
{
UIViewController *matchController = [self viewContainingController];
UIViewController *parentContainerViewController = nil;
if (matchController.navigationController)
{
UINavigationController *navController = matchController.navigationController;
while (navController.navigationController)
{
navController = navController.navigationController;
}
UIViewController *parentController = navController;
UIViewController *parentParentController = parentController.parentViewController;
while (parentParentController &&
([parentParentController isKindOfClass:[UINavigationController class]] == NO &&
[parentParentController isKindOfClass:[UITabBarController class]] == NO &&
[parentParentController isKindOfClass:[UISplitViewController class]] == NO))
{
parentController = parentParentController;
parentParentController = parentController.parentViewController;
}
if (navController == parentController)
{
parentContainerViewController = navController.topViewController;
}
else
{
parentContainerViewController = parentController;
}
}
else if (matchController.tabBarController)
{
if ([matchController.tabBarController.selectedViewController isKindOfClass:[UINavigationController class]])
{
parentContainerViewController = [(UINavigationController*)matchController.tabBarController.selectedViewController topViewController];
}
else
{
parentContainerViewController = matchController.tabBarController.selectedViewController;
}
}
else
{
UIViewController *matchParentController = matchController.parentViewController;
while (matchParentController &&
([matchParentController isKindOfClass:[UINavigationController class]] == NO &&
[matchParentController isKindOfClass:[UITabBarController class]] == NO &&
[matchParentController isKindOfClass:[UISplitViewController class]] == NO))
{
matchController = matchParentController;
matchParentController = matchController.parentViewController;
}
parentContainerViewController = matchController;
}
UIViewController *finalController = [parentContainerViewController parentIQContainerViewController] ?: parentContainerViewController;
return finalController;
}
-(UIView*)superviewOfClassType:(nonnull Class)classType
{
return [self superviewOfClassType:classType belowView:nil];
}
-(nullable __kindof UIView*)superviewOfClassType:(nonnull Class)classType belowView:(nullable UIView*)belowView
{
UIView *superview = self.superview;
while (superview)
{
if ([superview isKindOfClass:classType])
{
//If it's UIScrollView, then validating for special cases
if ([superview isKindOfClass:[UIScrollView class]])
{
NSString *classNameString = NSStringFromClass([superview class]);
// If it's not UITableViewWrapperView class, this is internal class which is actually manage in UITableview. The speciality of this class is that it's superview is UITableView.
// If it's not UITableViewCellScrollView class, this is internal class which is actually manage in UITableviewCell. The speciality of this class is that it's superview is UITableViewCell.
//If it's not _UIQueuingScrollView class, actually we validate for _ prefix which usually used by Apple internal classes
if ([superview.superview isKindOfClass:[UITableView class]] == NO &&
[superview.superview isKindOfClass:[UITableViewCell class]] == NO &&
[classNameString hasPrefix:@"_"] == NO)
{
return superview;
}
}
else
{
return superview;
}
}
else if (belowView == superview)
{
return nil;
}
superview = superview.superview;
}
return nil;
}
-(BOOL)_IQCanBecomeFirstResponder
{
BOOL _IQCanBecomeFirstResponder = NO;
if ([self conformsToProtocol:@protocol(UITextInput)])
{
if ([self respondsToSelector:@selector(isEditable)] && [self isKindOfClass:[UIScrollView class]])
{
_IQCanBecomeFirstResponder = [(UITextView*)self isEditable];
}
else if ([self respondsToSelector:@selector(isEnabled)])
{
_IQCanBecomeFirstResponder = [(UITextField*)self isEnabled];
}
}
if (_IQCanBecomeFirstResponder == YES)
{
_IQCanBecomeFirstResponder = ([self isUserInteractionEnabled] && ![self isHidden] && [self alpha]!=0.0 && ![self isAlertViewTextField] && !self.textFieldSearchBar);
}
return _IQCanBecomeFirstResponder;
}
- (NSArray<UIView*>*)responderSiblings
{
// Getting all siblings
NSArray<UIView*> *siblings = self.superview.subviews;
//Array of (UITextField/UITextView's).
NSMutableArray<UIView*> *tempTextFields = [[NSMutableArray alloc] init];
for (UIView *textField in siblings)
if ((textField == self || textField.ignoreSwitchingByNextPrevious == NO) && [textField _IQCanBecomeFirstResponder])
[tempTextFields addObject:textField];
return tempTextFields;
}
- (NSArray<UIView*>*)deepResponderViews
{
NSMutableArray<UIView*> *textFields = [[NSMutableArray alloc] init];
for (UIView *textField in self.subviews)
{
if ((textField == self || textField.ignoreSwitchingByNextPrevious == NO) && [textField _IQCanBecomeFirstResponder])
{
[textFields addObject:textField];
}
//Sometimes there are hidden or disabled views and textField inside them still recorded, so we added some more validations here (Bug ID: #458)
//Uncommented else (Bug ID: #625)
else if (textField.subviews.count && [textField isUserInteractionEnabled] && ![textField isHidden] && [textField alpha]!=0.0)
{
[textFields addObjectsFromArray:[textField deepResponderViews]];
}
}
//subviews are returning in incorrect order. Sorting according the frames 'y'.
return [textFields sortedArrayUsingComparator:^NSComparisonResult(UIView *view1, UIView *view2) {
CGRect frame1 = [view1 convertRect:view1.bounds toView:self];
CGRect frame2 = [view2 convertRect:view2.bounds toView:self];
CGFloat x1 = CGRectGetMinX(frame1);
CGFloat y1 = CGRectGetMinY(frame1);
CGFloat x2 = CGRectGetMinX(frame2);
CGFloat y2 = CGRectGetMinY(frame2);
if (y1 < y2) return NSOrderedAscending;
else if (y1 > y2) return NSOrderedDescending;
//Else both y are same so checking for x positions
else if (x1 < x2) return NSOrderedAscending;
else if (x1 > x2) return NSOrderedDescending;
else return NSOrderedSame;
}];
return textFields;
}
-(CGAffineTransform)convertTransformToView:(UIView*)toView
{
if (toView == nil)
{
toView = self.window;
}
CGAffineTransform myTransform = CGAffineTransformIdentity;
//My Transform
{
UIView *superView = [self superview];
if (superView) myTransform = CGAffineTransformConcat(self.transform, [superView convertTransformToView:nil]);
else myTransform = self.transform;
}
CGAffineTransform viewTransform = CGAffineTransformIdentity;
//view Transform
{
UIView *superView = [toView superview];
if (superView) viewTransform = CGAffineTransformConcat(toView.transform, [superView convertTransformToView:nil]);
else if (toView) viewTransform = toView.transform;
}
return CGAffineTransformConcat(myTransform, CGAffineTransformInvert(viewTransform));
}
- (NSInteger)depth
{
NSInteger depth = 0;
if ([self superview])
{
depth = [[self superview] depth] + 1;
}
return depth;
}
- (NSString *)subHierarchy
{
NSMutableString *debugInfo = [[NSMutableString alloc] initWithString:@"\n"];
NSInteger depth = [self depth];
for (int counter = 0; counter < depth; counter ++) [debugInfo appendString:@"| "];
[debugInfo appendString:[self debugHierarchy]];
for (UIView *subview in self.subviews)
{
[debugInfo appendString:[subview subHierarchy]];
}
return debugInfo;
}
- (NSString *)superHierarchy
{
NSMutableString *debugInfo = [[NSMutableString alloc] init];
if (self.superview)
{
[debugInfo appendString:[self.superview superHierarchy]];
}
else
{
[debugInfo appendString:@"\n"];
}
NSInteger depth = [self depth];
for (int counter = 0; counter < depth; counter ++) [debugInfo appendString:@"| "];
[debugInfo appendString:[self debugHierarchy]];
[debugInfo appendString:@"\n"];
return debugInfo;
}
-(NSString *)debugHierarchy
{
NSMutableString *debugInfo = [[NSMutableString alloc] init];
[debugInfo appendFormat:@"%@: ( %.0f, %.0f, %.0f, %.0f )",NSStringFromClass([self class]), CGRectGetMinX(self.frame), CGRectGetMinY(self.frame), CGRectGetWidth(self.frame), CGRectGetHeight(self.frame)];
if ([self isKindOfClass:[UIScrollView class]])
{
UIScrollView *scrollView = (UIScrollView*)self;
[debugInfo appendFormat:@"%@: ( %.0f, %.0f )",NSStringFromSelector(@selector(contentSize)),scrollView.contentSize.width,scrollView.contentSize.height];
}
if (CGAffineTransformEqualToTransform(self.transform, CGAffineTransformIdentity) == false)
{
[debugInfo appendFormat:@"%@: %@",NSStringFromSelector(@selector(transform)),NSStringFromCGAffineTransform(self.transform)];
}
return debugInfo;
}
-(UISearchBar *)textFieldSearchBar
{
UIResponder *searchBar = [self nextResponder];
while (searchBar)
{
if ([searchBar isKindOfClass:[UISearchBar class]])
{
return (UISearchBar*)searchBar;
}
else if ([searchBar isKindOfClass:[UIViewController class]]) //If found viewController but still not found UISearchBar then it's not the search bar textfield
{
break;
}
searchBar = [searchBar nextResponder];
}
return nil;
}
-(BOOL)isAlertViewTextField
{
UIResponder *alertViewController = [self viewContainingController];
BOOL isAlertViewTextField = NO;
while (alertViewController && isAlertViewTextField == NO)
{
if ([alertViewController isKindOfClass:[UIAlertController class]])
{
isAlertViewTextField = YES;
break;
}
alertViewController = [alertViewController nextResponder];
}
return isAlertViewTextField;
}
@end
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation NSObject (IQ_Logging)
-(NSString *)_IQDescription
{
return [NSString stringWithFormat:@"<%@ %p>",NSStringFromClass([self class]),self];
}
@end

View File

@@ -0,0 +1,36 @@
//
// IQUIViewController+Additions.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
@class NSLayoutConstraint;
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface UIViewController (Additions)
/**
This method is provided to override by viewController's if the library lifts a viewController which you doesn't want to lift . This may happen if you have implemented side menu feature in your app and the library try to lift the side menu controller. Overriding this method in side menu class to return correct controller should fix the problem.
*/
-(nullable UIViewController*)parentIQContainerViewController;
@end

View File

@@ -0,0 +1,38 @@
//
// IQUIViewController+Additions.m
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#import "IQUIViewController+Additions.h"
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation UIViewController (Additions)
-(nullable UIViewController*)parentIQContainerViewController
{
return self;
}
@end

View File

@@ -0,0 +1,156 @@
//
// IQKeyboardManagerConstants.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef IQKeyboardManagerConstants_h
#define IQKeyboardManagerConstants_h
#import <Foundation/Foundation.h>
///-----------------------------------
/// @name IQAutoToolbarManageBehavior
///-----------------------------------
/**
`IQAutoToolbarBySubviews`
Creates Toolbar according to subview's hierarchy of Textfield's in view.
`IQAutoToolbarByTag`
Creates Toolbar according to tag property of TextField's.
`IQAutoToolbarByPosition`
Creates Toolbar according to the y,x position of textField in it's superview coordinate.
*/
typedef NS_ENUM(NSInteger, IQAutoToolbarManageBehavior) {
IQAutoToolbarBySubviews,
IQAutoToolbarByTag,
IQAutoToolbarByPosition,
};
/**
`IQPreviousNextDisplayModeDefault`
Show NextPrevious when there are more than 1 textField otherwise hide.
`IQPreviousNextDisplayModeAlwaysHide`
Do not show NextPrevious buttons in any case.
`IQPreviousNextDisplayModeAlwaysShow`
Always show nextPrevious buttons, if there are more than 1 textField then both buttons will be visible but will be shown as disabled.
*/
typedef NS_ENUM(NSUInteger, IQPreviousNextDisplayMode) {
IQPreviousNextDisplayModeDefault,
IQPreviousNextDisplayModeAlwaysHide,
IQPreviousNextDisplayModeAlwaysShow,
};
/**
`IQEnableModeDefault`
Pick default settings.
`IQEnableModeEnabled`
setting is enabled.
`IQEnableModeDisabled`
setting is disabled.
*/
typedef NS_ENUM(NSUInteger, IQEnableMode) {
IQEnableModeDefault,
IQEnableModeEnabled,
IQEnableModeDisabled,
};
#endif
/*
/---------------------------------------------------------------------------------------------------\
\---------------------------------------------------------------------------------------------------/
| iOS NSNotification Mechanism |
/---------------------------------------------------------------------------------------------------\
\---------------------------------------------------------------------------------------------------/
------------------------------------------------------------
When UITextField become first responder
------------------------------------------------------------
- UITextFieldTextDidBeginEditingNotification (UITextField)
- UIKeyboardWillShowNotification
- UIKeyboardDidShowNotification
------------------------------------------------------------
When UITextView become first responder
------------------------------------------------------------
- UIKeyboardWillShowNotification
- UITextViewTextDidBeginEditingNotification (UITextView)
- UIKeyboardDidShowNotification
------------------------------------------------------------
When switching focus from UITextField to another UITextField
------------------------------------------------------------
- UITextFieldTextDidEndEditingNotification (UITextField1)
- UITextFieldTextDidBeginEditingNotification (UITextField2)
- UIKeyboardWillShowNotification
- UIKeyboardDidShowNotification
------------------------------------------------------------
When switching focus from UITextView to another UITextView
------------------------------------------------------------
- UITextViewTextDidEndEditingNotification : (UITextView1)
- UIKeyboardWillShowNotification
- UITextViewTextDidBeginEditingNotification : (UITextView2)
- UIKeyboardDidShowNotification
------------------------------------------------------------
When switching focus from UITextField to UITextView
------------------------------------------------------------
- UITextFieldTextDidEndEditingNotification (UITextField)
- UIKeyboardWillShowNotification
- UITextViewTextDidBeginEditingNotification (UITextView)
- UIKeyboardDidShowNotification
------------------------------------------------------------
When switching focus from UITextView to UITextField
------------------------------------------------------------
- UITextViewTextDidEndEditingNotification (UITextView)
- UITextFieldTextDidBeginEditingNotification (UITextField)
- UIKeyboardWillShowNotification
- UIKeyboardDidShowNotification
------------------------------------------------------------
When opening/closing UIKeyboard Predictive bar
------------------------------------------------------------
- UIKeyboardWillShowNotification
- UIKeyboardDidShowNotification
------------------------------------------------------------
On orientation change
------------------------------------------------------------
- UIApplicationWillChangeStatusBarOrientationNotification
- UIKeyboardWillHideNotification
- UIKeyboardDidHideNotification
- UIApplicationDidChangeStatusBarOrientationNotification
- UIKeyboardWillShowNotification
- UIKeyboardDidShowNotification
- UIKeyboardWillShowNotification
- UIKeyboardDidShowNotification
*/

View File

@@ -0,0 +1,28 @@
//
// IQKeyboardManagerConstantsInternal.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef IQKeyboardManagerConstantsInternal_h
#define IQKeyboardManagerConstantsInternal_h
#endif

View File

@@ -0,0 +1,359 @@
//
// IQKeyboardManager.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <CoreGraphics/CoreGraphics.h>
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <IQKeyboardManager/IQKeyboardManagerConstants.h>
#import <IQKeyboardManager/IQUIView+IQKeyboardToolbar.h>
#import <IQKeyboardManager/IQPreviousNextView.h>
#import <IQKeyboardManager/IQUIViewController+Additions.h>
#import <IQKeyboardManager/IQKeyboardReturnKeyHandler.h>
#import <IQKeyboardManager/IQTextView.h>
#import <IQKeyboardManager/IQToolbar.h>
#import <IQKeyboardManager/IQUIScrollView+Additions.h>
#import <IQKeyboardManager/IQUITextFieldView+Additions.h>
#import <IQKeyboardManager/IQBarButtonItem.h>
#import <IQKeyboardManager/IQTitleBarButtonItem.h>
#import <IQKeyboardManager/IQUIView+Hierarchy.h>
@class UIFont, UIColor, UITapGestureRecognizer, UIView, UIImage;
@class NSString;
///---------------------
/// @name IQToolbar tags
///---------------------
/**
Default tag for toolbar with Done button -1002.
*/
extern NSInteger const kIQDoneButtonToolbarTag;
/**
Default tag for toolbar with Previous/Next buttons -1005.
*/
extern NSInteger const kIQPreviousNextButtonToolbarTag;
/**
Code-less drop-in universal library allows to prevent issues of keyboard sliding up and cover UITextField/UITextView. Neither need to write any code nor any setup required and much more. A generic version of KeyboardManagement. https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
*/
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface IQKeyboardManager : NSObject
///--------------------------
/// @name UIKeyboard handling
///--------------------------
/**
Returns the default singleton instance. You are not allowed to create your own instances of this class.
*/
+ (nonnull instancetype)sharedManager;
/**
Enable/disable managing distance between keyboard and textField. Default is YES(Enabled when class loads in `+(void)load` method).
*/
@property(nonatomic, assign, getter = isEnabled) BOOL enable;
/**
To set keyboard distance from textField. can't be less than zero. Default is 10.0.
*/
@property(nonatomic, assign) CGFloat keyboardDistanceFromTextField;
/**
Refreshes textField/textView position if any external changes is explicitly made by user.
*/
- (void)reloadLayoutIfNeeded;
/**
Boolean to know if keyboard is showing.
*/
@property(nonatomic, assign, readonly, getter = isKeyboardShowing) BOOL keyboardShowing;
/**
moved distance to the top used to maintain distance between keyboard and textField. Most of the time this will be a positive value.
*/
@property(nonatomic, assign, readonly) CGFloat movedDistance;
/**
Will be called then movedDistance will be changed.
*/
@property(nullable, nonatomic, copy) void (^movedDistanceChanged)(CGFloat movedDistance);
///-------------------------
/// @name IQToolbar handling
///-------------------------
/**
Automatic add IQToolbar functionality. Default is YES.
*/
@property(nonatomic, assign, getter = isEnableAutoToolbar) BOOL enableAutoToolbar;
/**
IQAutoToolbarBySubviews: Creates Toolbar according to subview's hierarchy of Textfield's in view.
IQAutoToolbarByTag: Creates Toolbar according to tag property of TextField's.
IQAutoToolbarByPosition: Creates Toolbar according to the y,x position of textField in it's superview coordinate.
Default is IQAutoToolbarBySubviews.
*/
@property(nonatomic, assign) IQAutoToolbarManageBehavior toolbarManageBehavior;
/**
If YES, then uses textField's tintColor property for IQToolbar, otherwise tint color is nil. Default is NO.
*/
@property(nonatomic, assign) BOOL shouldToolbarUsesTextFieldTintColor;
/**
This is used for toolbar.tintColor when textfield.keyboardAppearance is UIKeyboardAppearanceDefault. If shouldToolbarUsesTextFieldTintColor is YES then this property is ignored. Default is nil.
*/
@property(nullable, nonatomic, strong) UIColor *toolbarTintColor;
/**
This is used for toolbar.barTintColor. Default is nil.
*/
@property(nullable, nonatomic, strong) UIColor *toolbarBarTintColor;
/**
IQPreviousNextDisplayModeDefault: Show NextPrevious when there are more than 1 textField otherwise hide.
IQPreviousNextDisplayModeAlwaysHide: Do not show NextPrevious buttons in any case.
IQPreviousNextDisplayModeAlwaysShow: Always show nextPrevious buttons, if there are more than 1 textField then both buttons will be visible but will be shown as disabled.
*/
@property(nonatomic, assign) IQPreviousNextDisplayMode previousNextDisplayMode;
/**
Toolbar previous/next/done button icon, If nothing is provided then check toolbarDoneBarButtonItemText to draw done button.
*/
@property(nullable, nonatomic, strong) UIImage *toolbarPreviousBarButtonItemImage;
@property(nullable, nonatomic, strong) UIImage *toolbarNextBarButtonItemImage;
@property(nullable, nonatomic, strong) UIImage *toolbarDoneBarButtonItemImage;
/**
Toolbar previous/next/done button text, If nothing is provided then system default 'UIBarButtonSystemItemDone' will be used.
*/
@property(nullable, nonatomic, strong) NSString *toolbarPreviousBarButtonItemText;
@property(nullable, nonatomic, strong) NSString *toolbarPreviousBarButtonItemAccessibilityLabel;
@property(nullable, nonatomic, strong) NSString *toolbarNextBarButtonItemText;
@property(nullable, nonatomic, strong) NSString *toolbarNextBarButtonItemAccessibilityLabel;
@property(nullable, nonatomic, strong) NSString *toolbarDoneBarButtonItemText;
@property(nullable, nonatomic, strong) NSString *toolbarDoneBarButtonItemAccessibilityLabel;
/**
If YES, then it add the textField's placeholder text on IQToolbar. Default is YES.
*/
@property(nonatomic, assign) BOOL shouldShowToolbarPlaceholder;
/**
Placeholder Font. Default is nil.
*/
@property(nullable, nonatomic, strong) UIFont *placeholderFont;
/**
Placeholder Color. Default is nil. Which means lightGray
*/
@property(nullable, nonatomic, strong) UIColor *placeholderColor;
/**
Placeholder Button Color when it's treated as button. Default is nil
*/
@property(nullable, nonatomic, strong) UIColor *placeholderButtonColor;
/**
Reload all toolbar buttons on the fly.
*/
- (void)reloadInputViews;
///---------------------------------------
/// @name UIKeyboard appearance overriding
///---------------------------------------
/**
Override the keyboardAppearance for all textField/textView. Default is NO.
*/
@property(nonatomic, assign) BOOL overrideKeyboardAppearance;
/**
If overrideKeyboardAppearance is YES, then all the textField keyboardAppearance is set using this property.
*/
@property(nonatomic, assign) UIKeyboardAppearance keyboardAppearance;
///-----------------------------------------------------------
/// @name UITextField/UITextView Next/Previous/Resign handling
///-----------------------------------------------------------
/**
Resigns Keyboard on touching outside of UITextField/View. Default is NO.
*/
@property(nonatomic, assign) BOOL shouldResignOnTouchOutside;
/** TapGesture to resign keyboard on view's touch. It's a readonly property and exposed only for adding/removing dependencies if your added gesture does have collision with this one */
@property(nonnull, nonatomic, strong, readonly) UITapGestureRecognizer *resignFirstResponderGesture;
/**
Resigns currently first responder field.
*/
- (BOOL)resignFirstResponder;
/**
Returns YES if can navigate to previous responder textField/textView, otherwise NO.
*/
@property (nonatomic, readonly) BOOL canGoPrevious;
/**
Returns YES if can navigate to next responder textField/textView, otherwise NO.
*/
@property (nonatomic, readonly) BOOL canGoNext;
/**
Navigate to previous responder textField/textView.
*/
- (BOOL)goPrevious;
/**
Navigate to next responder textField/textView.
*/
- (BOOL)goNext;
///-----------------------
/// @name UISound handling
///-----------------------
/**
If YES, then it plays inputClick sound on next/previous/done click. Default is YES.
*/
@property(nonatomic, assign) BOOL shouldPlayInputClicks;
///---------------------------
/// @name UIAnimation handling
///---------------------------
/**
If YES, then calls 'setNeedsLayout' and 'layoutIfNeeded' on any frame update of to viewController's view.
*/
@property(nonatomic, assign) BOOL layoutIfNeededOnUpdate;
///---------------------------------------------
/// @name Class Level enabling/disabling methods
///---------------------------------------------
/**
Disable distance handling within the scope of disabled distance handling viewControllers classes. Within this scope, 'enabled' property is ignored. Class should be kind of UIViewController. Default is [UITableViewController, UIAlertController, _UIAlertControllerTextFieldViewController].
*/
@property(nonatomic, strong, nonnull, readonly) NSMutableSet<Class> *disabledDistanceHandlingClasses;
/**
Enable distance handling within the scope of enabled distance handling viewControllers classes. Within this scope, 'enabled' property is ignored. Class should be kind of UIViewController. Default is [].
*/
@property(nonatomic, strong, nonnull, readonly) NSMutableSet<Class> *enabledDistanceHandlingClasses;
/**
Disable automatic toolbar creation within the scope of disabled toolbar viewControllers classes. Within this scope, 'enableAutoToolbar' property is ignored. Class should be kind of UIViewController. Default is [UIAlertController, _UIAlertControllerTextFieldViewController].
*/
@property(nonatomic, strong, nonnull, readonly) NSMutableSet<Class> *disabledToolbarClasses;
/**
Enable automatic toolbar creation within the scope of enabled toolbar viewControllers classes. Within this scope, 'enableAutoToolbar' property is ignored. Class should be kind of UIViewController. Default is [].
*/
@property(nonatomic, strong, nonnull, readonly) NSMutableSet<Class> *enabledToolbarClasses;
/**
Allowed subclasses of UIView to add all inner textField, this will allow to navigate between textField contains in different superview. Class should be kind of UIView. Default is [UITableView, UICollectionView, IQPreviousNextView].
*/
@property(nonatomic, strong, nonnull, readonly) NSMutableSet<Class> *toolbarPreviousNextAllowedClasses;
/**
Disabled classes to ignore 'shouldResignOnTouchOutside' property, Class should be kind of UIViewController. Default is [UIAlertController, UIAlertControllerTextFieldViewController]
*/
@property(nonatomic, strong, nonnull, readonly) NSMutableSet<Class> *disabledTouchResignedClasses;
/**
Enabled classes to forcefully enable 'shouldResignOnTouchOutside' property. Class should be kind of UIViewController. Default is [].
*/
@property(nonatomic, strong, nonnull, readonly) NSMutableSet<Class> *enabledTouchResignedClasses;
/**
if shouldResignOnTouchOutside is enabled then you can customize the behavior to not recognize gesture touches on some specific view subclasses. Class should be kind of UIView. Default is [UIControl, UINavigationBar]
*/
@property(nonatomic, strong, nonnull, readonly) NSMutableSet<Class> *touchResignedGestureIgnoreClasses;
///---------------------------------------------
/// @name Register for keyboard size events
///---------------------------------------------
/**
register an object to get keyboard size change events
*/
-(void)registerKeyboardSizeChangeWithIdentifier:(nonnull id<NSCopying>)identifier sizeHandler:(void (^_Nonnull)(CGSize size))sizeHandler;
/**
unregister the object which was registered before
*/
-(void)unregisterKeyboardSizeChangeWithIdentifier:(nonnull id<NSCopying>)identifier;
///-------------------------------------------
/// @name Third Party Library support
/// Add TextField/TextView Notifications customized NSNotifications. For example while using YYTextView https://github.com/ibireme/YYText
///-------------------------------------------
/**
Add/Remove customized Notification for third party customized TextField/TextView. Please be aware that the NSNotification object must be identical to UITextField/UITextView NSNotification objects and customized TextField/TextView support must be identical to UITextField/UITextView.
@param didBeginEditingNotificationName This should be identical to UITextViewTextDidBeginEditingNotification
@param didEndEditingNotificationName This should be identical to UITextViewTextDidEndEditingNotification
*/
-(void)registerTextFieldViewClass:(nonnull Class)aClass
didBeginEditingNotificationName:(nonnull NSString *)didBeginEditingNotificationName
didEndEditingNotificationName:(nonnull NSString *)didEndEditingNotificationName;
-(void)unregisterTextFieldViewClass:(nonnull Class)aClass
didBeginEditingNotificationName:(nonnull NSString *)didBeginEditingNotificationName
didEndEditingNotificationName:(nonnull NSString *)didEndEditingNotificationName;
///----------------------------------------
/// @name Debugging & Developer options
///----------------------------------------
@property(nonatomic, assign) BOOL enableDebugging;
/**
@warning Use these methods to completely enable/disable notifications registered by library internally. Please keep in mind that library is totally dependent on NSNotification of UITextField, UITextField, Keyboard etc. If you do unregisterAllNotifications then library will not work at all. You should only use below methods if you want to completely disable all library functions. You should use below methods at your own risk.
*/
-(void)registerAllNotifications;
-(void)unregisterAllNotifications;
///----------------------------------------
/// @name Must not be used for subclassing.
///----------------------------------------
/**
Unavailable. Please use sharedManager method
*/
-(nonnull instancetype)init NS_UNAVAILABLE;
/**
Unavailable. Please use sharedManager method
*/
+ (nonnull instancetype)new NS_UNAVAILABLE;
@end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,98 @@
//
// IQKeyboardReturnKeyHandler.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <IQKeyboardManager/IQKeyboardManagerConstants.h>
@class UITextField, UIView, UIViewController;
@protocol UITextFieldDelegate, UITextViewDelegate;
/**
Manages the return key to work like next/done in a view hierarchy.
*/
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface IQKeyboardReturnKeyHandler : NSObject
///----------------------
/// @name Initializations
///----------------------
/**
Add all the textFields available in UIViewController's view.
*/
-(nonnull instancetype)initWithViewController:(nullable UIViewController*)controller NS_DESIGNATED_INITIALIZER;
/**
Unavailable. Please use initWithViewController: or init method
*/
-(nonnull instancetype)initWithCoder:(nullable NSCoder *)aDecoder NS_UNAVAILABLE;
///---------------
/// @name Settings
///---------------
/**
Delegate of textField/textView.
*/
@property(nullable, nonatomic, weak) id<UITextFieldDelegate,UITextViewDelegate> delegate;
/**
Set the last textfield return key type. Default is UIReturnKeyDefault.
*/
@property(nonatomic, assign) UIReturnKeyType lastTextFieldReturnKeyType;
///----------------------------------------------
/// @name Registering/Unregistering textFieldView
///----------------------------------------------
/**
Should pass UITextField/UITextView instance. Assign textFieldView delegate to self, change it's returnKeyType.
@param textFieldView UITextField/UITextView object to register.
*/
-(void)addTextFieldView:(nonnull UIView*)textFieldView;
/**
Should pass UITextField/UITextView instance. Restore it's textFieldView delegate and it's returnKeyType.
@param textFieldView UITextField/UITextView object to unregister.
*/
-(void)removeTextFieldView:(nonnull UIView*)textFieldView;
/**
Add all the UITextField/UITextView responderView's.
@param view object to register all it's responder subviews.
*/
-(void)addResponderFromView:(nonnull UIView*)view;
/**
Remove all the UITextField/UITextView responderView's.
@param view object to unregister all it's responder subviews.
*/
-(void)removeResponderFromView:(nonnull UIView*)view;
@end

View File

@@ -0,0 +1,733 @@
//
// IQKeyboardReturnKeyHandler.m
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import "IQKeyboardReturnKeyHandler.h"
#import "IQKeyboardManager.h"
#import "IQUIView+Hierarchy.h"
#import "IQNSArray+Sort.h"
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface IQTextFieldViewInfoModel : NSObject
@property(nullable, nonatomic, weak) UIView *textFieldView;
@property(nullable, nonatomic, weak) id<UITextFieldDelegate> textFieldDelegate;
@property(nullable, nonatomic, weak) id<UITextViewDelegate> textViewDelegate;
@property(nonatomic) UIReturnKeyType originalReturnKeyType;
@end
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation IQTextFieldViewInfoModel
-(instancetype)initWithTextFieldView:(UIView*)textFieldView textFieldDelegate:(id<UITextFieldDelegate>)textFieldDelegate textViewDelegate:(id<UITextViewDelegate>)textViewDelegate originalReturnKey:(UIReturnKeyType)returnKeyType
{
self = [super init];
if (self)
{
_textFieldView = textFieldView;
_textFieldDelegate = textFieldDelegate;
_textViewDelegate = textViewDelegate;
_originalReturnKeyType = returnKeyType;
}
return self;
}
@end
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface IQKeyboardReturnKeyHandler ()<UITextFieldDelegate,UITextViewDelegate>
-(void)updateReturnKeyTypeOnTextField:(UIView*)textField;
@end
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation IQKeyboardReturnKeyHandler
{
NSMutableSet<IQTextFieldViewInfoModel*> *textFieldInfoCache;
}
@synthesize lastTextFieldReturnKeyType = _lastTextFieldReturnKeyType;
@synthesize delegate = _delegate;
- (instancetype)init
{
self = [self initWithViewController:nil];
return self;
}
-(instancetype)initWithViewController:(nullable UIViewController*)controller
{
self = [super init];
if (self)
{
textFieldInfoCache = [[NSMutableSet alloc] init];
if (controller.view)
{
[self addResponderFromView:controller.view];
}
}
return self;
}
-(IQTextFieldViewInfoModel*)textFieldViewCachedInfo:(UIView*)textField
{
for (IQTextFieldViewInfoModel *model in textFieldInfoCache)
if (model.textFieldView == textField) return model;
return nil;
}
#pragma mark - Add/Remove TextFields
-(void)addResponderFromView:(UIView*)view
{
NSArray<UIView*> *textFields = [view deepResponderViews];
for (UIView *textField in textFields) [self addTextFieldView:textField];
}
-(void)removeResponderFromView:(UIView*)view
{
NSArray<UIView*> *textFields = [view deepResponderViews];
for (UIView *textField in textFields) [self removeTextFieldView:textField];
}
-(void)removeTextFieldView:(UIView*)view
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:view];
if (model)
{
UITextField *textField = (UITextField*)view;
if ([view respondsToSelector:@selector(setReturnKeyType:)])
{
textField.returnKeyType = model.originalReturnKeyType;
}
if ([view respondsToSelector:@selector(setDelegate:)])
{
textField.delegate = model.textFieldDelegate;
}
[textFieldInfoCache removeObject:model];
}
}
-(void)addTextFieldView:(UIView*)view
{
IQTextFieldViewInfoModel *model = [[IQTextFieldViewInfoModel alloc] initWithTextFieldView:view textFieldDelegate:nil textViewDelegate:nil originalReturnKey:UIReturnKeyDefault];
UITextField *textField = (UITextField*)view;
if ([view respondsToSelector:@selector(setReturnKeyType:)])
{
model.originalReturnKeyType = textField.returnKeyType;
}
if ([view respondsToSelector:@selector(setDelegate:)])
{
model.textFieldDelegate = textField.delegate;
[textField setDelegate:self];
}
[textFieldInfoCache addObject:model];
}
-(void)updateReturnKeyTypeOnTextField:(UIView*)textField
{
UIView *superConsideredView;
//If find any consider responderView in it's upper hierarchy then will get deepResponderView. (Bug ID: #347)
for (Class consideredClass in [[IQKeyboardManager sharedManager] toolbarPreviousNextAllowedClasses])
{
superConsideredView = [textField superviewOfClassType:consideredClass];
if (superConsideredView)
break;
}
NSArray<UIView*> *textFields = nil;
//If there is a tableView in view's hierarchy, then fetching all it's subview that responds. No sorting for tableView, it's by subView position.
if (superConsideredView) // // (Enhancement ID: #22)
{
textFields = [superConsideredView deepResponderViews];
}
//Otherwise fetching all the siblings
else
{
textFields = [textField responderSiblings];
//Sorting textFields according to behavior
switch ([[IQKeyboardManager sharedManager] toolbarManageBehavior])
{
//If needs to sort it by tag
case IQAutoToolbarByTag:
textFields = [textFields sortedArrayByTag];
break;
//If needs to sort it by Position
case IQAutoToolbarByPosition:
textFields = [textFields sortedArrayByPosition];
break;
default:
break;
}
}
//If it's the last textField in responder view, else next
[(UITextField*)textField setReturnKeyType:(([textFields lastObject] == textField) ? self.lastTextFieldReturnKeyType : UIReturnKeyNext)];
}
#pragma mark - Goto next or Resign.
-(BOOL)goToNextResponderOrResign:(UIView*)textField
{
UIView *superConsideredView;
//If find any consider responderView in it's upper hierarchy then will get deepResponderView. (Bug ID: #347)
for (Class consideredClass in [[IQKeyboardManager sharedManager] toolbarPreviousNextAllowedClasses])
{
superConsideredView = [textField superviewOfClassType:consideredClass];
if (superConsideredView)
break;
}
NSArray<UIView*> *textFields = nil;
//If there is a tableView in view's hierarchy, then fetching all it's subview that responds. No sorting for tableView, it's by subView position.
if (superConsideredView) // // (Enhancement ID: #22)
{
textFields = [superConsideredView deepResponderViews];
}
//Otherwise fetching all the siblings
else
{
textFields = [textField responderSiblings];
//Sorting textFields according to behavior
switch ([[IQKeyboardManager sharedManager] toolbarManageBehavior])
{
//If needs to sort it by tag
case IQAutoToolbarByTag:
textFields = [textFields sortedArrayByTag];
break;
//If needs to sort it by Position
case IQAutoToolbarByPosition:
textFields = [textFields sortedArrayByPosition];
break;
default:
break;
}
}
//Getting index of current textField.
NSUInteger index = [textFields indexOfObject:textField];
//If it is not last textField. then it's next object becomeFirstResponder.
if (index != NSNotFound && index < textFields.count-1)
{
[textFields[index+1] becomeFirstResponder];
return NO;
}
else
{
[textField resignFirstResponder];
return YES;
}
}
#pragma mark - TextField delegate
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
id<UITextFieldDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textField];
delegate = model.textFieldDelegate;
}
if ([delegate respondsToSelector:@selector(textFieldShouldBeginEditing:)])
return [delegate textFieldShouldBeginEditing:textField];
else
return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self updateReturnKeyTypeOnTextField:textField];
id<UITextFieldDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textField];
delegate = model.textFieldDelegate;
}
if ([delegate respondsToSelector:@selector(textFieldDidBeginEditing:)])
[delegate textFieldDidBeginEditing:textField];
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
id<UITextFieldDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textField];
delegate = model.textFieldDelegate;
}
if ([delegate respondsToSelector:@selector(textFieldShouldEndEditing:)])
return [delegate textFieldShouldEndEditing:textField];
else
return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
id<UITextFieldDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textField];
delegate = model.textFieldDelegate;
}
if ([delegate respondsToSelector:@selector(textFieldDidEndEditing:)])
[delegate textFieldDidEndEditing:textField];
}
- (void)textFieldDidEndEditing:(UITextField *)textField reason:(UITextFieldDidEndEditingReason)reason
{
id<UITextFieldDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textField];
delegate = model.textFieldDelegate;
}
if ([delegate respondsToSelector:@selector(textFieldDidEndEditing:reason:)])
[delegate textFieldDidEndEditing:textField reason:reason];
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
id<UITextFieldDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textField];
delegate = model.textFieldDelegate;
}
if ([delegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)])
return [delegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
else
return YES;
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 160000
- (UIMenu *)textField:(UITextField *)textField editMenuForCharactersInRange:(NSRange)range suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions NS_AVAILABLE_IOS(16_0);
{
id<UITextFieldDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textField];
delegate = model.textFieldDelegate;
}
if ([delegate respondsToSelector:@selector(textField:editMenuForCharactersInRange:suggestedActions:)])
return [delegate textField:textField editMenuForCharactersInRange:range suggestedActions:suggestedActions];
else
return nil;
}
- (void)textField:(UITextField *)textField willPresentEditMenuWithAnimator:(id<UIEditMenuInteractionAnimating>)animator NS_AVAILABLE_IOS(16_0);
{
id<UITextFieldDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textField];
delegate = model.textFieldDelegate;
}
if ([delegate respondsToSelector:@selector(textField:willPresentEditMenuWithAnimator:)])
[delegate textField:textField willPresentEditMenuWithAnimator:animator];
}
- (void)textField:(UITextField *)textField willDismissEditMenuWithAnimator:(id<UIEditMenuInteractionAnimating>)animator NS_AVAILABLE_IOS(16_0);
{
id<UITextFieldDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textField];
delegate = model.textFieldDelegate;
}
if ([delegate respondsToSelector:@selector(textField:willDismissEditMenuWithAnimator:)])
[delegate textField:textField willDismissEditMenuWithAnimator:animator];
}
#endif
- (BOOL)textFieldShouldClear:(UITextField *)textField
{
id<UITextFieldDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textField];
delegate = model.textFieldDelegate;
}
if ([delegate respondsToSelector:@selector(textFieldShouldClear:)])
return [delegate textFieldShouldClear:textField];
else
return YES;
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
id<UITextFieldDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textField];
delegate = model.textFieldDelegate;
}
if ([delegate respondsToSelector:@selector(textFieldShouldReturn:)])
{
BOOL shouldReturn = [delegate textFieldShouldReturn:textField];
if (shouldReturn)
{
shouldReturn = [self goToNextResponderOrResign:textField];
}
return shouldReturn;
}
else
{
return [self goToNextResponderOrResign:textField];
}
}
#pragma mark - TextView delegate
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textViewShouldBeginEditing:)])
return [delegate textViewShouldBeginEditing:textView];
else
return YES;
}
- (BOOL)textViewShouldEndEditing:(UITextView *)textView
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textViewShouldEndEditing:)])
return [delegate textViewShouldEndEditing:textView];
else
return YES;
}
- (void)textViewDidBeginEditing:(UITextView *)textView
{
[self updateReturnKeyTypeOnTextField:textView];
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textViewDidBeginEditing:)])
[delegate textViewDidBeginEditing:textView];
}
- (void)textViewDidEndEditing:(UITextView *)textView
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textViewDidEndEditing:)])
[delegate textViewDidEndEditing:textView];
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
BOOL shouldReturn = YES;
if ([delegate respondsToSelector:@selector(textView:shouldChangeTextInRange:replacementText:)])
shouldReturn = [delegate textView:textView shouldChangeTextInRange:range replacementText:text];
if (shouldReturn && [text isEqualToString:@"\n"])
{
shouldReturn = [self goToNextResponderOrResign:textView];
}
return shouldReturn;
}
- (void)textViewDidChange:(UITextView *)textView
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textViewDidChange:)])
[delegate textViewDidChange:textView];
}
- (void)textViewDidChangeSelection:(UITextView *)textView
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textViewDidChangeSelection:)])
[delegate textViewDidChangeSelection:textView];
}
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textView:shouldInteractWithURL:inRange:interaction:)])
return [delegate textView:textView shouldInteractWithURL:URL inRange:characterRange interaction:interaction];
return YES;
}
- (BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textView:shouldInteractWithTextAttachment:inRange:interaction:)])
return [delegate textView:textView shouldInteractWithTextAttachment:textAttachment inRange:characterRange interaction:interaction];
return YES;
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 160000
-(UIMenu *)textView:(UITextView *)textView editMenuForTextInRange:(NSRange)range suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions NS_AVAILABLE_IOS(16_0);
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textView:editMenuForTextInRange:suggestedActions:)])
return [delegate textView:textView editMenuForTextInRange:range suggestedActions:suggestedActions];
else
return nil;
}
- (void)textView:(UITextView *)textView willPresentEditMenuWithAnimator:(id<UIEditMenuInteractionAnimating>)animator NS_AVAILABLE_IOS(16_0);
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textView:willPresentEditMenuWithAnimator:)])
[delegate textView:textView willPresentEditMenuWithAnimator:animator];
}
- (void)textView:(UITextView *)textView willDismissEditMenuWithAnimator:(id<UIEditMenuInteractionAnimating>)animator NS_AVAILABLE_IOS(16_0);
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textView:willDismissEditMenuWithAnimator:)])
[delegate textView:textView willDismissEditMenuWithAnimator:animator];
}
#endif
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000
- (nullable UIAction *)textView:(UITextView *)textView primaryActionForTextItem:(UITextItem *)textItem defaultAction:(UIAction *)defaultAction NS_AVAILABLE_IOS(17_0);
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textView:primaryActionForTextItem:defaultAction:)])
return [delegate textView:textView primaryActionForTextItem:textItem defaultAction:defaultAction];
else
return nil;
}
- (nullable UITextItemMenuConfiguration *)textView:(UITextView *)textView menuConfigurationForTextItem:(UITextItem *)textItem defaultMenu:(UIMenu *)defaultMenu NS_AVAILABLE_IOS(17_0);
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textView:menuConfigurationForTextItem:defaultMenu:)])
return [delegate textView:textView menuConfigurationForTextItem:textItem defaultMenu:defaultMenu];
else
return nil;
}
- (void)textView:(UITextView *)textView textItemMenuWillDisplayForTextItem:(UITextItem *)textItem animator:(id<UIContextMenuInteractionAnimating>)animator NS_AVAILABLE_IOS(17_0);
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textView:textItemMenuWillDisplayForTextItem:animator:)])
[delegate textView:textView textItemMenuWillDisplayForTextItem:textItem animator:animator];
}
- (void)textView:(UITextView *)textView textItemMenuWillEndForTextItem:(UITextItem *)textItem animator:(id<UIContextMenuInteractionAnimating>)animator NS_AVAILABLE_IOS(17_0);
{
id<UITextViewDelegate> delegate = self.delegate;
if (delegate == nil)
{
IQTextFieldViewInfoModel *model = [self textFieldViewCachedInfo:textView];
delegate = model.textViewDelegate;
}
if ([delegate respondsToSelector:@selector(textView:textItemMenuWillEndForTextItem:animator:)])
[delegate textView:textView textItemMenuWillEndForTextItem:textItem animator:animator];
}
#endif
-(void)dealloc
{
for (IQTextFieldViewInfoModel *model in textFieldInfoCache)
{
UITextField *textField = (UITextField*)model.textFieldView;
if ([textField respondsToSelector:@selector(setReturnKeyType:)])
{
textField.returnKeyType = model.originalReturnKeyType;
}
if ([textField respondsToSelector:@selector(setDelegate:)])
{
textField.delegate = model.textFieldDelegate;
}
}
[textFieldInfoCache removeAllObjects];
}
@end

View File

@@ -0,0 +1,54 @@
//
// IQTextView.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import <IQKeyboardManager/IQKeyboardManagerConstants.h>
/**
UITextView with placeholder support
*/
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface IQTextView : UITextView
/**
Set textView's placeholder text. Default is nil.
*/
@property(nullable, nonatomic,copy) IBInspectable NSString *placeholder;
/**
Set textView's placeholder attributed text. Default is nil.
*/
@property(nullable, nonatomic,copy) IBInspectable NSAttributedString *attributedPlaceholder;
/**
To set textView's placeholder text color. Default is nil.
*/
@property(nullable, nonatomic,copy) IBInspectable UIColor *placeholderTextColor;
@end

View File

@@ -0,0 +1,229 @@
//
// IQTextView.m
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import "IQTextView.h"
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface IQTextView ()
@property(nullable, nonatomic, strong) UILabel *placeholderLabel;
@end
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation IQTextView
@synthesize placeholder = _placeholder;
@synthesize placeholderLabel = _placeholderLabel;
@synthesize placeholderTextColor = _placeholderTextColor;
-(void)initialize
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshPlaceholder) name:UITextViewTextDidChangeNotification object:self];
}
-(void)dealloc
{
[_placeholderLabel removeFromSuperview];
_placeholderLabel = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (instancetype)init
{
self = [super init];
if (self) {
[self initialize];
}
return self;
}
-(void)awakeFromNib
{
[super awakeFromNib];
[self initialize];
}
-(void)refreshPlaceholder
{
if([[self text] length] || [[self attributedText] length])
{
if (self.placeholderLabel.alpha != 0)
{
[self.placeholderLabel setAlpha:0];
[self setNeedsLayout];
[self layoutIfNeeded];
}
}
else if(self.placeholderLabel.alpha != 1)
{
[self.placeholderLabel setAlpha:1];
[self setNeedsLayout];
[self layoutIfNeeded];
}
}
- (void)setText:(NSString *)text
{
[super setText:text];
[self refreshPlaceholder];
}
-(void)setAttributedText:(NSAttributedString *)attributedText
{
[super setAttributedText:attributedText];
[self refreshPlaceholder];
}
-(void)setFont:(UIFont *)font
{
[super setFont:font];
self.placeholderLabel.font = self.font;
[self setNeedsLayout];
[self layoutIfNeeded];
}
-(void)setTextAlignment:(NSTextAlignment)textAlignment
{
[super setTextAlignment:textAlignment];
self.placeholderLabel.textAlignment = textAlignment;
[self setNeedsLayout];
[self layoutIfNeeded];
}
-(void)layoutSubviews
{
[super layoutSubviews];
self.placeholderLabel.frame = [self placeholderExpectedFrame];
}
-(void)setPlaceholder:(NSString *)placeholder
{
_placeholder = placeholder;
self.placeholderLabel.text = placeholder;
[self refreshPlaceholder];
}
-(void)setAttributedPlaceholder:(NSAttributedString *)attributedPlaceholder
{
_attributedPlaceholder = attributedPlaceholder;
self.placeholderLabel.attributedText = attributedPlaceholder;
[self refreshPlaceholder];
}
-(void)setPlaceholderTextColor:(UIColor*)placeholderTextColor
{
_placeholderTextColor = placeholderTextColor;
self.placeholderLabel.textColor = placeholderTextColor;
}
-(UIEdgeInsets)placeholderInsets
{
return UIEdgeInsetsMake(self.textContainerInset.top, self.textContainerInset.left + self.textContainer.lineFragmentPadding, self.textContainerInset.bottom, self.textContainerInset.right + self.textContainer.lineFragmentPadding);
}
-(CGRect)placeholderExpectedFrame
{
UIEdgeInsets placeholderInsets = [self placeholderInsets];
CGFloat maxWidth = CGRectGetWidth(self.frame)-placeholderInsets.left-placeholderInsets.right;
CGSize expectedSize = [self.placeholderLabel sizeThatFits:CGSizeMake(maxWidth, CGRectGetHeight(self.frame)-placeholderInsets.top-placeholderInsets.bottom)];
return CGRectMake(placeholderInsets.left, placeholderInsets.top, maxWidth, expectedSize.height);
}
-(UILabel*)placeholderLabel
{
if (_placeholderLabel == nil)
{
_placeholderLabel = [[UILabel alloc] init];
_placeholderLabel.autoresizingMask = (UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight);
_placeholderLabel.lineBreakMode = NSLineBreakByWordWrapping;
_placeholderLabel.numberOfLines = 0;
_placeholderLabel.font = self.font;
_placeholderLabel.textAlignment = self.textAlignment;
_placeholderLabel.backgroundColor = [UIColor clearColor];
_placeholderLabel.isAccessibilityElement = NO;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
if (@available(iOS 13.0, *))
{
_placeholderLabel.textColor = [UIColor placeholderTextColor];
}
else
#endif
{
_placeholderLabel.textColor = [UIColor lightTextColor];
}
_placeholderLabel.alpha = 0;
[self addSubview:_placeholderLabel];
}
return _placeholderLabel;
}
//When any text changes on textField, the delegate getter is called. At this time we refresh the textView's placeholder
-(id<UITextViewDelegate>)delegate
{
[self refreshPlaceholder];
return [super delegate];
}
-(CGSize)intrinsicContentSize
{
if (self.hasText)
{
return [super intrinsicContentSize];
}
UIEdgeInsets placeholderInsets = [self placeholderInsets];
CGSize newSize = [super intrinsicContentSize];
newSize.height = [self placeholderExpectedFrame].size.height + placeholderInsets.top + placeholderInsets.bottom;
return newSize;
}
- (CGRect)caretRectForPosition:(UITextPosition *)position {
CGRect originalRect = [super caretRectForPosition:position];
// When placeholder is visible and text alignment is centered
if (_placeholderLabel.alpha == 1 && self.textAlignment == NSTextAlignmentCenter) {
// Calculate the width of the placeholder text
CGSize textSize = [_placeholderLabel.text sizeWithAttributes:@{NSFontAttributeName:_placeholderLabel.font}];
// Calculate the starting x position of the centered placeholder text
CGFloat centeredTextX = (self.bounds.size.width - textSize.width) / 2;
// Update the caret position to match the starting x position of the centered text
originalRect.origin.x = centeredTextX;
}
return originalRect;
}
@end

View File

@@ -0,0 +1,52 @@
//
// IQBarButtonItem.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
@class NSInvocation;
/**
IQBarButtonItem used for IQToolbar.
*/
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface IQBarButtonItem : UIBarButtonItem
/**
Boolean to know if it's a system item or custom item
*/
@property (nonatomic, readonly) BOOL isSystemItem;
/**
Additional target & action to do get callback action. Note that setting custom target & selector doesn't affect native functionality, this is just an additional target to get a callback.
@param target Target object.
@param action Target Selector.
*/
-(void)setTarget:(nullable id)target action:(nullable SEL)action;
/**
Customized Invocation to be called when button is pressed. invocation is internally created using setTarget:action: method.
*/
@property (nullable, strong, nonatomic) NSInvocation *invocation;
@end

View File

@@ -0,0 +1,119 @@
//
// IQBarButtonItem.m
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import "IQBarButtonItem.h"
#import "IQKeyboardManagerConstantsInternal.h"
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation IQBarButtonItem
-(void)initialize
{
NSArray <NSNumber*> *states = @[@(UIControlStateNormal),@(UIControlStateHighlighted),@(UIControlStateDisabled),@(UIControlStateFocused)];
for (NSNumber *state in states)
{
UIControlState controlState = [state unsignedIntegerValue];
[self setBackgroundImage:[UIImage new] forState:controlState barMetrics:UIBarMetricsDefault];
[self setBackgroundImage:[UIImage new] forState:controlState style:UIBarButtonItemStylePlain barMetrics:UIBarMetricsDefault];
[self setBackButtonBackgroundImage:[UIImage new] forState:controlState barMetrics:UIBarMetricsDefault];
}
[self setTitlePositionAdjustment:UIOffsetZero forBarMetrics:UIBarMetricsDefault];
[self setBackgroundVerticalPositionAdjustment:0 forBarMetrics:UIBarMetricsDefault];
[self setBackButtonBackgroundVerticalPositionAdjustment:0 forBarMetrics:UIBarMetricsDefault];
}
- (instancetype)init
{
self = [super init];
if (self)
{
[self initialize];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder: coder];
if (self)
{
[self initialize];
}
return self;
}
-(void)setTintColor:(UIColor *)tintColor
{
[super setTintColor:tintColor];
//titleTextAttributes tweak is to overcome an issue comes with iOS11 where appearanceProxy set for NSForegroundColorAttributeName and bar button texts start appearing in appearance proxy color
NSMutableDictionary *textAttributes = [[self titleTextAttributesForState:UIControlStateNormal] mutableCopy]?:[NSMutableDictionary new];
textAttributes[NSForegroundColorAttributeName] = tintColor;
[self setTitleTextAttributes:textAttributes forState:UIControlStateNormal];
}
- (instancetype)initWithBarButtonSystemItem:(UIBarButtonSystemItem)systemItem target:(nullable id)target action:(nullable SEL)action
{
self = [super initWithBarButtonSystemItem:systemItem target:target action:action];
if (self)
{
_isSystemItem = YES;
}
return self;
}
-(void)setTarget:(nullable id)target action:(nullable SEL)action
{
NSInvocation *invocation = nil;
if (target && action)
{
invocation = [NSInvocation invocationWithMethodSignature:[target methodSignatureForSelector:action]];
invocation.target = target;
invocation.selector = action;
}
self.invocation = invocation;
}
-(void)dealloc
{
self.target = nil;
self.invocation = nil;
}
@end

View File

@@ -0,0 +1,31 @@
//
// IQPreviousNextView.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
/**
If you need to enable previous/next toolbar button with some complex hierarchy where your textFields are not in same view, then make the top view as IQPreviousNextView.
*/
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface IQPreviousNextView : UIView
@end

View File

@@ -0,0 +1,29 @@
//
// IQPreviousNextView.m
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "IQPreviousNextView.h"
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation IQPreviousNextView
@end

View File

@@ -0,0 +1,73 @@
//
// IQTitleBarButtonItem.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <IQKeyboardManager/IQKeyboardManagerConstants.h>
#import <IQKeyboardManager/IQBarButtonItem.h>
/**
BarButtonItem with title text.
*/
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface IQTitleBarButtonItem : IQBarButtonItem
/**
Font to be used in bar button. Default is (system font 12.0 bold).
*/
@property(nullable, nonatomic, strong) UIFont *titleFont;
/**
titleColor to be used for displaying button text when displaying title (disabled state).
*/
@property(nullable, nonatomic, strong) UIColor *titleColor;
/**
selectableTitleColor to be used for displaying button text when button is enabled.
*/
@property(nullable, nonatomic, strong) UIColor *selectableTitleColor;
/**
Initialize with frame and title.
@param title Title of barButtonItem.
*/
-(nonnull instancetype)initWithTitle:(nullable NSString *)title NS_DESIGNATED_INITIALIZER;
/**
Unavailable. Please use initWithFrame:title: method
*/
-(nonnull instancetype)init NS_UNAVAILABLE;
/**
Unavailable. Please use initWithFrame:title: method
*/
-(nonnull instancetype)initWithCoder:(nullable NSCoder *)aDecoder NS_UNAVAILABLE;
/**
Unavailable. Please use initWithFrame:title: method
*/
+ (nonnull instancetype)new NS_UNAVAILABLE;
@end

View File

@@ -0,0 +1,183 @@
//
// IQTitleBarButtonItem.m
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import "IQTitleBarButtonItem.h"
#import "IQKeyboardManagerConstants.h"
#import "IQKeyboardManagerConstantsInternal.h"
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface IQTitleBarButtonItem ()
@property(nullable, nonatomic, strong) UIView *titleView;
@property(nullable, nonatomic, strong) UIButton *titleButton;
@end
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation IQTitleBarButtonItem
-(nonnull instancetype)initWithTitle:(nullable NSString *)title
{
self = [super init];
if (self)
{
_titleView = [[UIView alloc] init];
_titleView.backgroundColor = [UIColor clearColor];
_titleButton = [UIButton buttonWithType:UIButtonTypeSystem];
_titleButton.enabled = NO;
_titleButton.titleLabel.numberOfLines = 3;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
if (@available(iOS 13.0, *))
{
[_titleButton setTitleColor:[UIColor systemBlueColor] forState:UIControlStateNormal];
}
else
#endif
{
[_titleButton setTitleColor:[UIColor colorWithRed:0.0 green:0.5 blue:1.0 alpha:1.0] forState:UIControlStateNormal];
}
[_titleButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled];
[_titleButton setBackgroundColor:[UIColor clearColor]];
[_titleButton.titleLabel setTextAlignment:NSTextAlignmentCenter];
[self setTitle:title];
[self setTitleFont:[UIFont systemFontOfSize:13.0]];
[_titleView addSubview:_titleButton];
CGFloat layoutDefaultLowPriority = UILayoutPriorityDefaultLow-1;
CGFloat layoutDefaultHighPriority = UILayoutPriorityDefaultHigh-1;
_titleView.translatesAutoresizingMaskIntoConstraints = NO;
[_titleView setContentHuggingPriority:layoutDefaultLowPriority forAxis:UILayoutConstraintAxisVertical];
[_titleView setContentHuggingPriority:layoutDefaultLowPriority forAxis:UILayoutConstraintAxisHorizontal];
[_titleView setContentCompressionResistancePriority:layoutDefaultHighPriority forAxis:UILayoutConstraintAxisVertical];
[_titleView setContentCompressionResistancePriority:layoutDefaultHighPriority forAxis:UILayoutConstraintAxisHorizontal];
_titleButton.translatesAutoresizingMaskIntoConstraints = NO;
[_titleButton setContentHuggingPriority:layoutDefaultLowPriority forAxis:UILayoutConstraintAxisVertical];
[_titleButton setContentHuggingPriority:layoutDefaultLowPriority forAxis:UILayoutConstraintAxisHorizontal];
[_titleButton setContentCompressionResistancePriority:layoutDefaultHighPriority forAxis:UILayoutConstraintAxisVertical];
[_titleButton setContentCompressionResistancePriority:layoutDefaultHighPriority forAxis:UILayoutConstraintAxisHorizontal];
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:_titleButton attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_titleView attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:_titleButton attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:_titleView attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:_titleButton attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:_titleView attribute:NSLayoutAttributeLeading multiplier:1 constant:0];
NSLayoutConstraint *trailing = [NSLayoutConstraint constraintWithItem:_titleButton attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:_titleView attribute:NSLayoutAttributeTrailing multiplier:1 constant:0];
[_titleView addConstraints:@[top,bottom, leading, trailing]];
self.customView = _titleView;
}
return self;
}
-(void)setTitleFont:(UIFont *)titleFont
{
_titleFont = titleFont;
if (titleFont)
{
_titleButton.titleLabel.font = titleFont;
}
else
{
_titleButton.titleLabel.font = [UIFont systemFontOfSize:13];
}
}
-(void)setTitle:(NSString *)title
{
[super setTitle:title];
[_titleButton setTitle:title forState:UIControlStateNormal];
[self updateAccessibility];
}
-(void)setTitleColor:(UIColor*)titleColor
{
_titleColor = titleColor;
[_titleButton setTitleColor:_titleColor?:[UIColor lightGrayColor] forState:UIControlStateDisabled];
}
-(void)setSelectableTitleColor:(UIColor*)selectableTitleColor
{
_selectableTitleColor = selectableTitleColor;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
if (@available(iOS 13.0, *))
{
[_titleButton setTitleColor:_selectableTitleColor?:[UIColor systemBlueColor] forState:UIControlStateNormal];
}
else
#endif
{
[_titleButton setTitleColor:_selectableTitleColor?:[UIColor colorWithRed:0.0 green:0.5 blue:1.0 alpha:1.0] forState:UIControlStateNormal];
}
}
-(void)setInvocation:(NSInvocation *)invocation
{
[super setInvocation:invocation];
if (invocation.target == nil || invocation.selector == NULL)
{
self.enabled = NO;
_titleButton.enabled = NO;
[_titleButton removeTarget:nil action:NULL forControlEvents:UIControlEventTouchUpInside];
}
else
{
self.enabled = YES;
_titleButton.enabled = YES;
[_titleButton addTarget:invocation.target action:invocation.selector forControlEvents:UIControlEventTouchUpInside];
}
}
-(void)updateAccessibility
{
if (self.title == nil || self.title.length == 0)
{
self.isAccessibilityElement = NO;
self.accessibilityTraits = UIAccessibilityTraitNone;
}
else if (self.titleButton.isEnabled)
{
self.isAccessibilityElement = YES;
self.accessibilityTraits = UIAccessibilityTraitButton;
}
else
{
self.isAccessibilityElement = YES;
self.accessibilityTraits = UIAccessibilityTraitStaticText;
}
}
-(void)dealloc
{
self.customView = nil;
[_titleButton removeTarget:nil action:NULL forControlEvents:UIControlEventTouchUpInside];
_titleView = nil;
_titleButton = nil;
}
@end

View File

@@ -0,0 +1,61 @@
//
// IQToolbar.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import <IQKeyboardManager/IQTitleBarButtonItem.h>
/**
IQToolbar for IQKeyboardManager.
*/
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface IQToolbar : UIToolbar <UIInputViewAudioFeedback>
/**
Previous bar button of toolbar.
*/
@property(nonnull, nonatomic, strong) IQBarButtonItem *previousBarButton;
/**
Next bar button of toolbar.
*/
@property(nonnull, nonatomic, strong) IQBarButtonItem *nextBarButton;
/**
Title bar button of toolbar.
*/
@property(nonnull, nonatomic, strong, readonly) IQTitleBarButtonItem *titleBarButton;
/**
Done bar button of toolbar.
*/
@property(nonnull, nonatomic, strong) IQBarButtonItem *doneBarButton;
/**
Fixed space bar button of toolbar.
*/
@property(nonnull, nonatomic, strong) IQBarButtonItem *fixedSpaceBarButton;
@end

View File

@@ -0,0 +1,158 @@
//
// IQToolbar.m
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import "IQToolbar.h"
#import "IQKeyboardManagerConstantsInternal.h"
#import "IQUIView+Hierarchy.h"
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation IQToolbar
@synthesize previousBarButton = _previousBarButton;
@synthesize nextBarButton = _nextBarButton;
@synthesize titleBarButton = _titleBarButton;
@synthesize doneBarButton = _doneBarButton;
@synthesize fixedSpaceBarButton = _fixedSpaceBarButton;
-(void)initialize
{
[self sizeToFit];
self.autoresizingMask = UIViewAutoresizingFlexibleWidth;// | UIViewAutoresizingFlexibleHeight;
self.translucent = YES;
self.barTintColor = nil;
NSArray <NSNumber*> *positions = @[@(UIBarPositionAny),@(UIBarPositionBottom),@(UIBarPositionTop),@(UIBarPositionTopAttached)];
for (NSNumber *position in positions)
{
UIToolbarPosition toolbarPosition = [position unsignedIntegerValue];
[self setBackgroundImage:nil forToolbarPosition:toolbarPosition barMetrics:UIBarMetricsDefault];
[self setShadowImage:nil forToolbarPosition:toolbarPosition];
}
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self initialize];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self)
{
[self initialize];
}
return self;
}
-(void)dealloc
{
self.items = nil;
}
-(IQBarButtonItem *)previousBarButton
{
if (_previousBarButton == nil)
{
_previousBarButton = [[IQBarButtonItem alloc] initWithImage:nil style:UIBarButtonItemStylePlain target:nil action:nil];
}
return _previousBarButton;
}
-(IQBarButtonItem *)nextBarButton
{
if (_nextBarButton == nil)
{
_nextBarButton = [[IQBarButtonItem alloc] initWithImage:nil style:UIBarButtonItemStylePlain target:nil action:nil];
}
return _nextBarButton;
}
-(IQTitleBarButtonItem *)titleBarButton
{
if (_titleBarButton == nil)
{
_titleBarButton = [[IQTitleBarButtonItem alloc] initWithTitle:nil];
}
return _titleBarButton;
}
-(IQBarButtonItem *)doneBarButton
{
if (_doneBarButton == nil)
{
_doneBarButton = [[IQBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:nil action:nil];
}
return _doneBarButton;
}
-(IQBarButtonItem *)fixedSpaceBarButton
{
if (_fixedSpaceBarButton == nil)
{
_fixedSpaceBarButton = [[IQBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
[_fixedSpaceBarButton setWidth:6];
}
return _fixedSpaceBarButton;
}
-(CGSize)sizeThatFits:(CGSize)size
{
CGSize sizeThatFit = [super sizeThatFits:size];
sizeThatFit.height = 44;
return sizeThatFit;
}
-(void)setTintColor:(UIColor *)tintColor
{
[super setTintColor:tintColor];
for (UIBarButtonItem *item in self.items)
{
[item setTintColor:tintColor];
}
}
#pragma mark - UIInputViewAudioFeedback delegate
- (BOOL) enableInputClicksWhenVisible
{
return YES;
}
@end

View File

@@ -0,0 +1,145 @@
//
// IQUIView+IQKeyboardToolbar.h
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import <IQKeyboardManager/IQToolbar.h>
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface IQBarButtonItemConfiguration : NSObject
-(nonnull instancetype)initWithBarButtonSystemItem:(UIBarButtonSystemItem)barButtonSystemItem action:(nullable SEL)action;
-(nonnull instancetype)initWithImage:(nonnull UIImage*)image action:(nullable SEL)action;
-(nonnull instancetype)initWithTitle:(nonnull NSString*)title action:(nullable SEL)action;
@property (readonly, nonatomic) UIBarButtonSystemItem barButtonSystemItem; //System Item to be used to instantiate bar button
@property (readonly, nonatomic, nullable) UIImage *image; //Image to show on bar button item if it's not a system item.
@property (readonly, nonatomic, nullable) NSString *title; //Title to show on bar button item if it's not a system item.
@property (readonly, nonatomic, nullable) SEL action; //action for bar button item. Usually 'doneAction:(IQBarButtonItem*)item'.
@end
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface UIImage (IQKeyboardToolbarNextPreviousImage)
+(nullable UIImage*)keyboardPreviousImage;
+(nullable UIImage*)keyboardNextImage;
@end
/**
UIView category methods to add IQToolbar on UIKeyboard.
*/
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@interface UIView (IQToolbarAddition)
///-------------------------
/// @name Toolbar Title
///-------------------------
/**
IQToolbar references for better customization control.
*/
@property (readonly, nonatomic, nonnull) IQToolbar *keyboardToolbar;
/**
If `shouldHideToolbarPlaceholder` is YES, then title will not be added to the toolbar. Default to NO.
*/
@property (assign, nonatomic) BOOL shouldHideToolbarPlaceholder;
/**
`toolbarPlaceholder` to override default `placeholder` text when drawing text on toolbar.
*/
@property (nullable, strong, nonatomic) NSString* toolbarPlaceholder;
/**
`drawingToolbarPlaceholder` will be actual text used to draw on toolbar. This would either `placeholder` or `toolbarPlaceholder`.
*/
@property (nullable, strong, nonatomic, readonly) NSString* drawingToolbarPlaceholder;
///-------------
/// MARK: Common
///-------------
- (void)addKeyboardToolbarWithTarget:(nullable id)target titleText:(nullable NSString*)titleText rightBarButtonConfiguration:(nullable IQBarButtonItemConfiguration*)rightBarButtonConfiguration previousBarButtonConfiguration:(nullable IQBarButtonItemConfiguration*)previousBarButtonConfiguration nextBarButtonConfiguration:(nullable IQBarButtonItemConfiguration*)nextBarButtonConfiguration;
///------------
/// @name Done
///------------
- (void)addDoneOnKeyboardWithTarget:(nullable id)target action:(nullable SEL)action;
- (void)addDoneOnKeyboardWithTarget:(nullable id)target action:(nullable SEL)action shouldShowPlaceholder:(BOOL)shouldShowPlaceholder;
- (void)addDoneOnKeyboardWithTarget:(nullable id)target action:(nullable SEL)action titleText:(nullable NSString*)titleText;
///------------
/// @name Right
///------------
- (void)addRightButtonOnKeyboardWithText:(nullable NSString*)text target:(nullable id)target action:(nullable SEL)action;
- (void)addRightButtonOnKeyboardWithText:(nullable NSString*)text target:(nullable id)target action:(nullable SEL)action shouldShowPlaceholder:(BOOL)shouldShowPlaceholder;
- (void)addRightButtonOnKeyboardWithText:(nullable NSString*)text target:(nullable id)target action:(nullable SEL)action titleText:(nullable NSString*)titleText;
- (void)addRightButtonOnKeyboardWithImage:(nullable UIImage*)image target:(nullable id)target action:(nullable SEL)action;
- (void)addRightButtonOnKeyboardWithImage:(nullable UIImage*)image target:(nullable id)target action:(nullable SEL)action shouldShowPlaceholder:(BOOL)shouldShowPlaceholder;
- (void)addRightButtonOnKeyboardWithImage:(nullable UIImage*)image target:(nullable id)target action:(nullable SEL)action titleText:(nullable NSString*)titleText;
///------------------
/// @name Cancel/Done
///------------------
- (void)addCancelDoneOnKeyboardWithTarget:(nullable id)target cancelAction:(nullable SEL)cancelAction doneAction:(nullable SEL)doneAction;
- (void)addCancelDoneOnKeyboardWithTarget:(nullable id)target cancelAction:(nullable SEL)cancelAction doneAction:(nullable SEL)doneAction shouldShowPlaceholder:(BOOL)shouldShowPlaceholder;
- (void)addCancelDoneOnKeyboardWithTarget:(nullable id)target cancelAction:(nullable SEL)cancelAction doneAction:(nullable SEL)doneAction titleText:(nullable NSString*)titleText;
///-----------------
/// @name Right/Left
///-----------------
- (void)addLeftRightOnKeyboardWithTarget:(nullable id)target leftButtonTitle:(nullable NSString*)leftButtonTitle rightButtonTitle:(nullable NSString*)rightButtonTitle leftButtonAction:(nullable SEL)leftButtonAction rightButtonAction:(nullable SEL)rightButtonAction;
- (void)addLeftRightOnKeyboardWithTarget:(nullable id)target leftButtonTitle:(nullable NSString*)leftButtonTitle rightButtonTitle:(nullable NSString*)rightButtonTitle leftButtonAction:(nullable SEL)leftButtonAction rightButtonAction:(nullable SEL)rightButtonAction shouldShowPlaceholder:(BOOL)shouldShowPlaceholder;
- (void)addLeftRightOnKeyboardWithTarget:(nullable id)target leftButtonTitle:(nullable NSString*)leftButtonTitle rightButtonTitle:(nullable NSString*)rightButtonTitle leftButtonAction:(nullable SEL)leftButtonAction rightButtonAction:(nullable SEL)rightButtonAction titleText:(nullable NSString*)titleText;
///-------------------------
/// @name Previous/Next/Done
///-------------------------
- (void)addPreviousNextDoneOnKeyboardWithTarget:(nullable id)target previousAction:(nullable SEL)previousAction nextAction:(nullable SEL)nextAction doneAction:(nullable SEL)doneAction;
- (void)addPreviousNextDoneOnKeyboardWithTarget:(nullable id)target previousAction:(nullable SEL)previousAction nextAction:(nullable SEL)nextAction doneAction:(nullable SEL)doneAction shouldShowPlaceholder:(BOOL)shouldShowPlaceholder;
- (void)addPreviousNextDoneOnKeyboardWithTarget:(nullable id)target previousAction:(nullable SEL)previousAction nextAction:(nullable SEL)nextAction doneAction:(nullable SEL)doneAction titleText:(nullable NSString*)titleText;
///--------------------------
/// @name Previous/Next/Right
///--------------------------
- (void)addPreviousNextRightOnKeyboardWithTarget:(nullable id)target rightButtonTitle:(nullable NSString*)rightButtonTitle previousAction:(nullable SEL)previousAction nextAction:(nullable SEL)nextAction rightButtonAction:(nullable SEL)rightButtonAction;
- (void)addPreviousNextRightOnKeyboardWithTarget:(nullable id)target rightButtonTitle:(nullable NSString*)rightButtonTitle previousAction:(nullable SEL)previousAction nextAction:(nullable SEL)nextAction rightButtonAction:(nullable SEL)rightButtonAction shouldShowPlaceholder:(BOOL)shouldShowPlaceholder;
- (void)addPreviousNextRightOnKeyboardWithTarget:(nullable id)target rightButtonTitle:(nullable NSString*)rightButtonTitle previousAction:(nullable SEL)previousAction nextAction:(nullable SEL)nextAction rightButtonAction:(nullable SEL)rightButtonAction titleText:(nullable NSString*)titleText;
- (void)addPreviousNextRightOnKeyboardWithTarget:(nullable id)target rightButtonImage:(nullable UIImage*)rightButtonImage previousAction:(nullable SEL)previousAction nextAction:(nullable SEL)nextAction rightButtonAction:(nullable SEL)rightButtonAction;
- (void)addPreviousNextRightOnKeyboardWithTarget:(nullable id)target rightButtonImage:(nullable UIImage*)rightButtonImage previousAction:(nullable SEL)previousAction nextAction:(nullable SEL)nextAction rightButtonAction:(nullable SEL)rightButtonAction shouldShowPlaceholder:(BOOL)shouldShowPlaceholder;
- (void)addPreviousNextRightOnKeyboardWithTarget:(nullable id)target rightButtonImage:(nullable UIImage*)rightButtonImage previousAction:(nullable SEL)previousAction nextAction:(nullable SEL)nextAction rightButtonAction:(nullable SEL)rightButtonAction titleText:(nullable NSString*)titleText;
@end

View File

@@ -0,0 +1,561 @@
//
// IQUIView+IQKeyboardToolbar.m
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#import "IQUIView+IQKeyboardToolbar.h"
#import "IQKeyboardManagerConstantsInternal.h"
#import "IQKeyboardManager.h"
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation IQBarButtonItemConfiguration
-(instancetype)initWithBarButtonSystemItem:(UIBarButtonSystemItem)barButtonSystemItem action:(SEL)action
{
self = [super init];
if (self) {
_barButtonSystemItem = barButtonSystemItem;
_action = action;
}
return self;
}
-(instancetype)initWithImage:(UIImage *)image action:(SEL)action
{
self = [super init];
if (self) {
_image = image;
_action = action;
}
return self;
}
-(instancetype)initWithTitle:(NSString *)title action:(SEL)action
{
self = [super init];
if (self) {
_title = title;
_action = action;
}
return self;
}
@end
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation UIImage (IQKeyboardToolbarNextPreviousImage)
+(UIImage*)keyboardPreviousImage
{
static UIImage *keyboardUpImage = nil;
if (keyboardUpImage == nil)
{
NSString *base64Data = @"iVBORw0KGgoAAAANSUhEUgAAAD8AAAAkCAYAAAA+TuKHAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAAGmklEQVRoBd1ZWWwbRRie2bVz27s2adPGxzqxqAQCIRA3CDVJGxpKaEtRoSAVISQQggdeQIIHeIAHkOCBFyQeKlARhaYHvUJa0ksVoIgKUKFqKWqdeG2nR1Lsdeo0h73D54iku7NO6ySOk3alyPN//+zM/81/7MyEkDl66j2eJXWK8vocTT82rTgXk/t8vqBNEI9QSp9zOeVkPJnomgs7ik5eUZQ6OxGOEEq9WcKUksdlWbqU0LRfi70ARSXv8Xi8dkE8CsJ+I1FK6BNYgCgW4A8jPtvtopFHqNeWCLbDIF6fkxQjK91O1z9IgRM59bMAFoV8YEFgka1EyBJfMhkH5L9ACFstS9IpRMDJyfoVEp918sGamoVCme0QyN3GG87wAKcTOBYA4hrJKf+VSCb+nsBnqYHVnr2ntra2mpWWH0BVu52fhRH2XSZDmsA/xensokC21Pv9T3J4wcWrq17gob1er7tEhMcJuYsfGoS3hdTweuBpxaM0iCJph8fLuX7DJMPWnI2GOzi8YOKseD4gB+RSQezMRRx5vRPEn88Sz7IIx8KHgT3FCBniWJUyke6o8/uXc3jBxIKTd7vdTsFJfkSo38NbCY/vPRsOPwt81KgLqeoBXc+sBjZsxLF4ZfgM7goqSqMRL1S7oOSrq6sdLodjH0rYfbyByPEOePwZ4CO8Liv3RCL70Wctr8+mA2NkT53P91iu92aCFYx8TU1NpbOi8gfs2R7iDYLxnXqYPg3c5Fm+Xygcbs/omXXATZGBBagQqNAe9Psf4d+ZiVwQ8qjqFVVl5dmi9ShvDEL90IieXtVDevic5ruOyYiAXYiA9YSxsZow0YnSKkKFjoAn8OAENsPGjKs9qnp5iSDuBXFLXsLjR4fSIy29vb2DU7UThW4d8n0zxjXtRVAYNaJnlocikWNTHZPvP1PPl2LLujM3cfbzwJXUyukQzxrZraptRCcbEDm60Wh4S0IE7McByVJQjf3yac+EfEm9ouxAcWu2TsS6koOplr6+vstWXf5IKBrejBR4ybIAlLpE1JE6j8eyh8h/dEKmS95e7w9sy57G+MkQ6sdYMrmiv79/gNdNR0YEbGKUvIIFQMRffRBtbkG0HQj6fHdcRafWmg55Gzy+BR5vtUzF2O96kjSH4nHNopsB0B0Ob6SEvcYvAPYS1UwQDyqLFcu5IZ/pTMUkjxfEoD/wLVY9+z02PXDL8RE9s0y9qMZNigIJcU37TZblfj7aUAMqURLXuqqq9sQHBi5NZbqpkBfh8a9BPLtDMz3wyImh9GhTLBab0uSmQfIQcNQ95pJkDVG3wtgdC1KFA+HaSodjdzKZ/Neou1Y7X/JC0K98BeIvWAdjp+jwUKN6/nyfVVd4JK4lunDrkwJhc6Gl1GGjwhqnLO3UNC2Rz8z5kKfw+EYQf5EfEKF+Wh+kDd0XYxd43WzKiIBfEAEjiIAm0zyUSFiU1XJF+feJy5evW3euR57C41+A+MumSbICY2dGmd6gnlPPWXRFABABP7llCXsA2mCcDjVAJoK4qryycsfAwEDSqOPb1yQPj38O4q/yL4F4aCiTXhqNRmMWXREBFMGjslOywUbToQeyyy4IrVVO53bUgEk/uZOSr/MHPsOd0hs8F4R6mI2ONKi9vRFeNxdyIqkddknOMhA2nyuy+wAqtEol8rbEYCLnZisneXj8UxB/00KGkUiGsqU90WiPRTeHACLgoNsp4eBDHzaagRS4RbCzle6ysq3xVIq/LiMW8ti5fYRVfMs4yFibsdgI05eqqhqy6OYBEE9qnSiCLhRB7tRHFzDR1oIasBU1wHTAMpHHjcmHIP4OzwXf8XMkk24IR6NneN18klEE97mc0gJwuN9oF+SFNlF8vNJR1YYacGVcN0Eet6XvY6Pw3rhi/Bc5fiEzShp7eiOnx7H5/IsI6EAELEIE3Gu0EymwyCbQZocktWEfMHa3MEa+zqe8KwjCB8bO/7f70kxvVGPqyRy6eQshAtpdsuTDN/9us5F0MQ4zTS5BaIsPDQ3jO+5/G+fjj82dIDF2CZeKjd3R6J8W3Y0BYFca+JJQssFqLuvSUqlmESHSiZywGzsgx+OZNFnWE4scN+I3WJshAnYjAm5FBNxptp16y+y2hICLEtOVMXJcI0xvDveGi/ofU7NxBZN0XIpuIIy0mUZkZNNZVf1kDAt6lZagEhjGnxbweh8wdbw5hOwdxHbwY/j9BpTM9xi4MGzFvZhpk3Bz8J5gkb19ym7cJr5w/wEmUjzJqoNVhwAAAABJRU5ErkJggg==";
NSData *data = [[NSData alloc] initWithBase64EncodedString:base64Data options:NSDataBase64DecodingIgnoreUnknownCharacters];
keyboardUpImage = [UIImage imageWithData:data scale:3];
//Support for RTL languages like Arabic, Persia etc... (Bug ID: #448)
keyboardUpImage = [keyboardUpImage imageFlippedForRightToLeftLayoutDirection];
}
return keyboardUpImage;
}
+(UIImage*)keyboardNextImage
{
static UIImage *keyboardDownImage = nil;
if (keyboardDownImage == nil)
{
NSString *base64Data = @"iVBORw0KGgoAAAANSUhEUgAAAD8AAAAkCAYAAAA+TuKHAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAAGp0lEQVRoBd1ZCWhcRRiemff25WrydmOtuXbfZlMo4lEpKkppm6TpZUovC4UqKlQoUhURqQcUBcWDIkhVUCuI9SpJa+2h0VZjUawUEUUUirLNXqmxSnc32WaT7O4bv0nd5R1bc+2maR8s7z9m5v+/+f/5Z94sIf89jW73Yp/bfUuWvwLfDp/H8zhwObLYmCCaPJ6FjLJPCWNHNU1bkFVeQW/Zp2l7KWUvNmlaB3DJAhvz1ntvI5R1EUpnUUKdEifHGuvr519BwKUmj/cDYNtwARNd5/NoH4GWKIhzlFKXCSzn/xCut/jD4V9N8suPYYj4ewC+2e46f55Rwp/geExKSmdzJn2l1WrXmuSXF8MQ8XfyAeeEn9KTyV3MHwq9RTh50IqLEjJHUkh3Y13dPKvuMuApIr6bUHKP1VeE+Y8MIa09Z8/+JQlltD/+Q7VaFcW6X2VsjFmbRRnbUFFZeai/v/+cUTeDaYqIv4GlfL/NR879I3qmORwOnxG6UfCCiMbjJ51VagKdlgs+91BaKVO6oVJVD8bj8WhOPkMJn1t7jTL6gNU9pHpgKJ1q7u3tjWR1OfBCEOuPf+9Sq4YwAW3ZBqNvSqsYpeuc5WUHYolE3KSbQYzP430FwB+yuoSCFtKHaXP4z3DIqDOBFwpkwHfVThXLgrYaG6IGOAmT1pZVVHw8MDDQb9TNBLrJre0E8EdtvnAeSRPeHOwN9lh1NvCiASbgG5fqRLDJEmMHsSU6GFuDGrAfNWDAqLuUNE5uL6A2bbf5wPkZrmdaAuGw36aDIC940TAajx1HBijIgEWmjpRWS4ytrnKq+1EDEibdJWAa3dqzjLGnrKaxxvt4OtXS09v7u1WX5S8KXjRABnQ7VbUCEV+Y7SDeWAJX4dfuLCnZFzt//rxRN500jqo74NvTVptY42fTnLcGI5FTVp2R/1/womEsHj/mwgxg27vd2BH8bCrLq0rKyjoTicSgUTcdNIrbkwD+nM2WOJ3qmaVI9d9sOotgTPCiPTLgi+oqdTbOAbea+lM6xyHLK8pnVXSiCCZNuiIyjZr2GArSS1YTOKie45n0UqT6L1ZdPn5c4EVHHIS6sA3WYLZvNg6E9L9GZmwZzgEdqAFDRl0xaET8EQB/2To21ngsQ0kbIv6zVXcxftzgxQDIgM+qVbUeGbDAPCCtxbfxUhdjHdGhoWGzrnAcIr4NwHflGbGf6PqyQCj0Yx7dRUUTAi9GwQQccapOL7bBm4yjIiPqSElpC5VYRzKZLPgE4M5hK0rt67CDZDM9A+k0XxmIhE6apONgJgxejBmLxw65VHUu/LjRaANeNZQpyhJZUToGBwdHjLqp0Ij4FgB/0wocaxw7DV8F4CcmM/6kwMMQRwYcrFad87DvXW8yTKlbkZVFSmlJB3bBlEk3CQYRvxfA3wbw0Vun7BAAPqjrmfaecPjbrGyib2sKTbS/LG5F4NhGe0d+fDiTuSMSiUx6F8Bn6V343N6TB3gSyb/aHwx22+2OX2KazfF3y7VMnw4FcUvCP8lJcgRtVph0yEu8pTnRBAiv270JwN+1AscQw5zr66YKXLgyVfBijBQc2YQ0PCIY4wPH2yQPERNTYpSPRSPid0qUvY/+1mU5QjJ8PVL96FhjjEdfCPDCzggyAKnPP7cZpWQFlsZ+yPGdMPaDiK/F6fEjbKeypXVK5/pGfyTYZZFPmi0UeOHAcCZI1+Oa6JjVG0SwHbcrnZDn7sytbQSPiLdLTBJXy+Z2nKcR8U09odDhfP0mKyskeBIggaERPb0WGfC1zSFK1gDcXsitER1t6m3wrkTEbRmC5ZTRCd+MiB+wjTlFwVSrfV7zdXV15aWy0oWKvNjWgJMOfyiAIklwYXLhwfd4G/47OAxnTMVRAKec3u0PB8SkFfyxFpSCGMBHTkpWHPsU2bEEKe8xDUrJdfhKnItzgiiEXKvXWhijR9CuzNgOwHWc1+87HQ5+aJQXki4KeOGgOOFJDkdnqeJowSGlweg00vsGHJAa1UpnTJKIAF5u1AM4R8S3APgeo7zQdFHS3uikz+VSSWXVlwBo+hoUbUR0ITfVHQEcEd+K4rbbOE4xaJPhYhg4HY3GcYG4HFB/so5vBT6q53TbdAAXtooe+SzghoaGakWSu2FwflZmfWMffxjAX7XKi8VPG3gBoKam5uoKpeQEDjBz7YD4dpwUd9rlxZMUPe2Nrvf19f2dTKdasap7jHIsiR3TDdxsfxq5xtpazad5g02al+Na6plpND0zTHk8Hp+4iLyU3vwLp0orLWXqrZQAAAAASUVORK5CYII=";
NSData *data = [[NSData alloc] initWithBase64EncodedString:base64Data options:NSDataBase64DecodingIgnoreUnknownCharacters];
keyboardDownImage = [UIImage imageWithData:data scale:3];
//Support for RTL languages like Arabic, Persia etc... (Bug ID: #448)
keyboardDownImage = [keyboardDownImage imageFlippedForRightToLeftLayoutDirection];
}
return keyboardDownImage;
}
@end
/*UIKeyboardToolbar Category implementation*/
NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
@implementation UIView (IQToolbarAddition)
-(IQToolbar *)keyboardToolbar
{
IQToolbar *keyboardToolbar = nil;
if ([[self inputAccessoryView] isKindOfClass:[IQToolbar class]])
{
keyboardToolbar = [self inputAccessoryView];
}
else
{
keyboardToolbar = objc_getAssociatedObject(self, @selector(keyboardToolbar));
if (keyboardToolbar == nil)
{
CGFloat width = 0;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
if (@available(iOS 13.0, *))
{
width = self.window.windowScene.screen.bounds.size.width;
}
else
#endif
{
width = UIScreen.mainScreen.bounds.size.width;
}
CGRect frame = CGRectMake(0, 0, width, 44);
keyboardToolbar = [[IQToolbar alloc] initWithFrame:frame];
objc_setAssociatedObject(self, @selector(keyboardToolbar), keyboardToolbar, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
return keyboardToolbar;
}
-(void)setShouldHideToolbarPlaceholder:(BOOL)shouldHideToolbarPlaceholder
{
objc_setAssociatedObject(self, @selector(shouldHideToolbarPlaceholder), @(shouldHideToolbarPlaceholder), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.keyboardToolbar.titleBarButton.title = self.drawingToolbarPlaceholder;
}
-(BOOL)shouldHideToolbarPlaceholder
{
NSNumber *shouldHideToolbarPlaceholder = objc_getAssociatedObject(self, @selector(shouldHideToolbarPlaceholder));
return [shouldHideToolbarPlaceholder boolValue];
}
-(void)setToolbarPlaceholder:(NSString *)toolbarPlaceholder
{
objc_setAssociatedObject(self, @selector(toolbarPlaceholder), toolbarPlaceholder, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.keyboardToolbar.titleBarButton.title = self.drawingToolbarPlaceholder;
}
-(NSString *)toolbarPlaceholder
{
NSString *toolbarPlaceholder = objc_getAssociatedObject(self, @selector(toolbarPlaceholder));
return toolbarPlaceholder;
}
-(NSString *)drawingToolbarPlaceholder
{
if (self.shouldHideToolbarPlaceholder)
{
return nil;
}
else if (self.toolbarPlaceholder.length != 0)
{
return self.toolbarPlaceholder;
}
else if ([self respondsToSelector:@selector(placeholder)])
{
return [(UITextField*)self placeholder];
}
else
{
return nil;
}
}
#pragma mark - Private helper
+(IQBarButtonItem*)flexibleBarButtonItem
{
static IQBarButtonItem *nilButton = nil;
if (nilButton == nil)
{
nilButton = [[IQBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
}
return nilButton;
}
#pragma mark - Common
- (void)addKeyboardToolbarWithTarget:(id)target titleText:(NSString*)titleText rightBarButtonConfiguration:(IQBarButtonItemConfiguration*)rightBarButtonConfiguration previousBarButtonConfiguration:(IQBarButtonItemConfiguration*)previousBarButtonConfiguration nextBarButtonConfiguration:(IQBarButtonItemConfiguration*)nextBarButtonConfiguration
{
//If can't set InputAccessoryView. Then return
if (![self respondsToSelector:@selector(setInputAccessoryView:)]) return;
// Creating a toolBar for phoneNumber keyboard
IQToolbar *toolbar = self.keyboardToolbar;
NSMutableArray<UIBarButtonItem*> *items = [[NSMutableArray alloc] init];
if(previousBarButtonConfiguration)
{
IQBarButtonItem *prev = toolbar.previousBarButton;
if (prev.isSystemItem == NO && (previousBarButtonConfiguration.image || previousBarButtonConfiguration.title))
{
prev.title = previousBarButtonConfiguration.title;
prev.accessibilityLabel = previousBarButtonConfiguration.accessibilityLabel;
prev.accessibilityIdentifier = prev.accessibilityLabel;
prev.image = previousBarButtonConfiguration.image;
prev.target = target;
prev.action = previousBarButtonConfiguration.action;
}
else if (previousBarButtonConfiguration.image)
{
prev = [[IQBarButtonItem alloc] initWithImage:previousBarButtonConfiguration.image style:UIBarButtonItemStylePlain target:target action:previousBarButtonConfiguration.action];
prev.invocation = toolbar.previousBarButton.invocation;
prev.accessibilityLabel = previousBarButtonConfiguration.accessibilityLabel;
prev.accessibilityIdentifier = prev.accessibilityLabel;
prev.enabled = toolbar.previousBarButton.enabled;
prev.tag = toolbar.previousBarButton.tag;
toolbar.previousBarButton = prev;
}
else if (previousBarButtonConfiguration.title)
{
prev = [[IQBarButtonItem alloc] initWithTitle:previousBarButtonConfiguration.title style:UIBarButtonItemStylePlain target:target action:previousBarButtonConfiguration.action];
prev.invocation = toolbar.previousBarButton.invocation;
prev.accessibilityLabel = previousBarButtonConfiguration.accessibilityLabel;
prev.accessibilityIdentifier = prev.accessibilityLabel;
prev.enabled = toolbar.previousBarButton.enabled;
prev.tag = toolbar.previousBarButton.tag;
toolbar.previousBarButton = prev;
}
else
{
prev = [[IQBarButtonItem alloc] initWithBarButtonSystemItem:previousBarButtonConfiguration.barButtonSystemItem target:target action:previousBarButtonConfiguration.action];
prev.invocation = toolbar.previousBarButton.invocation;
prev.accessibilityLabel = previousBarButtonConfiguration.accessibilityLabel;
prev.accessibilityIdentifier = prev.accessibilityLabel;
prev.enabled = toolbar.previousBarButton.enabled;
prev.tag = toolbar.previousBarButton.tag;
toolbar.previousBarButton = prev;
}
[items addObject:prev];
}
if (previousBarButtonConfiguration != nil && nextBarButtonConfiguration != nil)
{
[items addObject:toolbar.fixedSpaceBarButton];
}
if(nextBarButtonConfiguration)
{
IQBarButtonItem *next = toolbar.nextBarButton;
if (next.isSystemItem == NO && (nextBarButtonConfiguration.image || nextBarButtonConfiguration.title))
{
next.title = nextBarButtonConfiguration.title;
next.accessibilityLabel = nextBarButtonConfiguration.accessibilityLabel;
next.accessibilityIdentifier = next.accessibilityLabel;
next.image = nextBarButtonConfiguration.image;
next.target = target;
next.action = nextBarButtonConfiguration.action;
}
else if (nextBarButtonConfiguration.image)
{
next = [[IQBarButtonItem alloc] initWithImage:nextBarButtonConfiguration.image style:UIBarButtonItemStylePlain target:target action:nextBarButtonConfiguration.action];
next.invocation = toolbar.nextBarButton.invocation;
next.accessibilityLabel = nextBarButtonConfiguration.accessibilityLabel;
next.accessibilityIdentifier = next.accessibilityLabel;
next.enabled = toolbar.nextBarButton.enabled;
next.tag = toolbar.nextBarButton.tag;
toolbar.nextBarButton = next;
}
else if (nextBarButtonConfiguration.title)
{
next = [[IQBarButtonItem alloc] initWithTitle:nextBarButtonConfiguration.title style:UIBarButtonItemStylePlain target:target action:nextBarButtonConfiguration.action];
next.invocation = toolbar.nextBarButton.invocation;
next.accessibilityLabel = nextBarButtonConfiguration.accessibilityLabel;
next.accessibilityIdentifier = next.accessibilityLabel;
next.enabled = toolbar.nextBarButton.enabled;
next.tag = toolbar.nextBarButton.tag;
toolbar.nextBarButton = next;
}
else
{
next = [[IQBarButtonItem alloc] initWithBarButtonSystemItem:nextBarButtonConfiguration.barButtonSystemItem target:target action:nextBarButtonConfiguration.action];
next.invocation = toolbar.nextBarButton.invocation;
next.accessibilityLabel = nextBarButtonConfiguration.accessibilityLabel;
next.accessibilityIdentifier = next.accessibilityLabel;
next.enabled = toolbar.nextBarButton.enabled;
next.tag = toolbar.nextBarButton.tag;
toolbar.nextBarButton = next;
}
[items addObject:next];
}
//Title
{
//Flexible space
[items addObject:[[self class] flexibleBarButtonItem]];
//Title button
toolbar.titleBarButton.title = titleText;
[items addObject:toolbar.titleBarButton];
//Flexible space
[items addObject:[[self class] flexibleBarButtonItem]];
}
if(rightBarButtonConfiguration)
{
IQBarButtonItem *done = toolbar.doneBarButton;
if (done.isSystemItem == NO && (rightBarButtonConfiguration.image || rightBarButtonConfiguration.title))
{
done.title = rightBarButtonConfiguration.title;
done.accessibilityLabel = rightBarButtonConfiguration.accessibilityLabel;
done.accessibilityIdentifier = done.accessibilityLabel;
done.image = rightBarButtonConfiguration.image;
done.target = target;
done.action = rightBarButtonConfiguration.action;
}
else if (rightBarButtonConfiguration.image)
{
done = [[IQBarButtonItem alloc] initWithImage:rightBarButtonConfiguration.image style:UIBarButtonItemStylePlain target:target action:rightBarButtonConfiguration.action];
done.invocation = toolbar.doneBarButton.invocation;
done.accessibilityLabel = rightBarButtonConfiguration.accessibilityLabel;
done.accessibilityIdentifier = done.accessibilityLabel;
done.enabled = toolbar.doneBarButton.enabled;
done.tag = toolbar.doneBarButton.tag;
toolbar.doneBarButton = done;
}
else if (rightBarButtonConfiguration.title)
{
done = [[IQBarButtonItem alloc] initWithTitle:rightBarButtonConfiguration.title style:UIBarButtonItemStylePlain target:target action:rightBarButtonConfiguration.action];
done.invocation = toolbar.doneBarButton.invocation;
done.accessibilityLabel = rightBarButtonConfiguration.accessibilityLabel;
done.accessibilityIdentifier = done.accessibilityLabel;
done.enabled = toolbar.doneBarButton.enabled;
done.tag = toolbar.doneBarButton.tag;
toolbar.doneBarButton = done;
}
else
{
done = [[IQBarButtonItem alloc] initWithBarButtonSystemItem:rightBarButtonConfiguration.barButtonSystemItem target:target action:rightBarButtonConfiguration.action];
done.invocation = toolbar.doneBarButton.invocation;
done.accessibilityLabel = rightBarButtonConfiguration.accessibilityLabel;
done.accessibilityIdentifier = done.accessibilityLabel;
done.enabled = toolbar.doneBarButton.enabled;
done.tag = toolbar.doneBarButton.tag;
toolbar.doneBarButton = done;
}
[items addObject:done];
}
// Adding button to toolBar.
[toolbar setItems:items];
// Setting toolbar to keyboard.
[(UITextField*)self setInputAccessoryView:toolbar];
if ([self respondsToSelector:@selector(keyboardAppearance)])
{
switch ([(UITextField*)self keyboardAppearance])
{
case UIKeyboardAppearanceDark: toolbar.barStyle = UIBarStyleBlack; break;
default: toolbar.barStyle = UIBarStyleDefault; break;
}
}
[self reloadInputViews];
}
#pragma mark - Right
- (void)addRightButtonOnKeyboardWithText:(NSString*)text target:(id)target action:(SEL)action
{
[self addRightButtonOnKeyboardWithText:text target:target action:action titleText:nil];
}
- (void)addRightButtonOnKeyboardWithText:(NSString*)text target:(id)target action:(SEL)action shouldShowPlaceholder:(BOOL)shouldShowPlaceholder
{
[self addRightButtonOnKeyboardWithText:text target:target action:action titleText:(shouldShowPlaceholder?[self drawingToolbarPlaceholder]:nil)];
}
- (void)addRightButtonOnKeyboardWithText:(NSString*)text target:(id)target action:(SEL)action titleText:(NSString*)titleText
{
IQBarButtonItemConfiguration *rightConfiguration = [[IQBarButtonItemConfiguration alloc] initWithTitle:text action:action];
[self addKeyboardToolbarWithTarget:target titleText:titleText rightBarButtonConfiguration:rightConfiguration previousBarButtonConfiguration:nil nextBarButtonConfiguration:nil];
}
- (void)addRightButtonOnKeyboardWithImage:(UIImage*)image target:(id)target action:(SEL)action
{
[self addRightButtonOnKeyboardWithImage:image target:target action:action titleText:nil];
}
- (void)addRightButtonOnKeyboardWithImage:(UIImage*)image target:(id)target action:(SEL)action shouldShowPlaceholder:(BOOL)shouldShowPlaceholder
{
[self addRightButtonOnKeyboardWithImage:image target:target action:action titleText:(shouldShowPlaceholder?[self drawingToolbarPlaceholder]:nil)];
}
- (void)addRightButtonOnKeyboardWithImage:(UIImage*)image target:(id)target action:(SEL)action titleText:(NSString*)titleText
{
IQBarButtonItemConfiguration *rightConfiguration = [[IQBarButtonItemConfiguration alloc] initWithImage:image action:action];
[self addKeyboardToolbarWithTarget:target titleText:titleText rightBarButtonConfiguration:rightConfiguration previousBarButtonConfiguration:nil nextBarButtonConfiguration:nil];
}
-(void)addDoneOnKeyboardWithTarget:(id)target action:(SEL)action
{
[self addDoneOnKeyboardWithTarget:target action:action titleText:nil];
}
-(void)addDoneOnKeyboardWithTarget:(id)target action:(SEL)action shouldShowPlaceholder:(BOOL)shouldShowPlaceholder
{
[self addDoneOnKeyboardWithTarget:target action:action titleText:(shouldShowPlaceholder?[self drawingToolbarPlaceholder]:nil)];
}
- (void)addDoneOnKeyboardWithTarget:(id)target action:(SEL)action titleText:(NSString*)titleText
{
IQBarButtonItemConfiguration *rightConfiguration = [[IQBarButtonItemConfiguration alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone action:action];
[self addKeyboardToolbarWithTarget:target titleText:titleText rightBarButtonConfiguration:rightConfiguration previousBarButtonConfiguration:nil nextBarButtonConfiguration:nil];
}
- (void)addLeftRightOnKeyboardWithTarget:(id)target leftButtonTitle:(NSString*)leftTitle rightButtonTitle:(NSString*)rightTitle leftButtonAction:(SEL)leftAction rightButtonAction:(SEL)rightAction
{
[self addLeftRightOnKeyboardWithTarget:target leftButtonTitle:leftTitle rightButtonTitle:rightTitle leftButtonAction:leftAction rightButtonAction:rightAction titleText:nil];
}
- (void)addLeftRightOnKeyboardWithTarget:(id)target leftButtonTitle:(NSString*)leftTitle rightButtonTitle:(NSString*)rightTitle leftButtonAction:(SEL)leftAction rightButtonAction:(SEL)rightAction shouldShowPlaceholder:(BOOL)shouldShowPlaceholder
{
[self addLeftRightOnKeyboardWithTarget:target leftButtonTitle:leftTitle rightButtonTitle:rightTitle leftButtonAction:leftAction rightButtonAction:rightAction titleText:(shouldShowPlaceholder?[self drawingToolbarPlaceholder]:nil)];
}
- (void)addLeftRightOnKeyboardWithTarget:(id)target leftButtonTitle:(NSString*)leftTitle rightButtonTitle:(NSString*)rightTitle leftButtonAction:(SEL)leftAction rightButtonAction:(SEL)rightAction titleText:(NSString*)titleText
{
IQBarButtonItemConfiguration *leftConfiguration = [[IQBarButtonItemConfiguration alloc] initWithTitle:leftTitle action:leftAction];
IQBarButtonItemConfiguration *rightConfiguration = [[IQBarButtonItemConfiguration alloc] initWithTitle:rightTitle action:rightAction];
[self addKeyboardToolbarWithTarget:target titleText:titleText rightBarButtonConfiguration:rightConfiguration previousBarButtonConfiguration:leftConfiguration nextBarButtonConfiguration:nil];
}
-(void)addCancelDoneOnKeyboardWithTarget:(id)target cancelAction:(SEL)cancelAction doneAction:(SEL)doneAction
{
[self addCancelDoneOnKeyboardWithTarget:target cancelAction:cancelAction doneAction:doneAction titleText:nil];
}
-(void)addCancelDoneOnKeyboardWithTarget:(id)target cancelAction:(SEL)cancelAction doneAction:(SEL)doneAction shouldShowPlaceholder:(BOOL)shouldShowPlaceholder
{
[self addCancelDoneOnKeyboardWithTarget:target cancelAction:cancelAction doneAction:doneAction titleText:(shouldShowPlaceholder?[self drawingToolbarPlaceholder]:nil)];
}
- (void)addCancelDoneOnKeyboardWithTarget:(id)target cancelAction:(SEL)cancelAction doneAction:(SEL)doneAction titleText:(NSString*)titleText
{
IQBarButtonItemConfiguration *leftConfiguration = [[IQBarButtonItemConfiguration alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel action:cancelAction];
IQBarButtonItemConfiguration *rightConfiguration = [[IQBarButtonItemConfiguration alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone action:doneAction];
[self addKeyboardToolbarWithTarget:target titleText:titleText rightBarButtonConfiguration:rightConfiguration previousBarButtonConfiguration:leftConfiguration nextBarButtonConfiguration:nil];
}
-(void)addPreviousNextDoneOnKeyboardWithTarget:(id)target previousAction:(SEL)previousAction nextAction:(SEL)nextAction doneAction:(SEL)doneAction
{
[self addPreviousNextDoneOnKeyboardWithTarget:target previousAction:previousAction nextAction:nextAction doneAction:doneAction titleText:nil];
}
-(void)addPreviousNextDoneOnKeyboardWithTarget:(id)target previousAction:(SEL)previousAction nextAction:(SEL)nextAction doneAction:(SEL)doneAction shouldShowPlaceholder:(BOOL)shouldShowPlaceholder
{
[self addPreviousNextDoneOnKeyboardWithTarget:target previousAction:previousAction nextAction:nextAction doneAction:doneAction titleText:(shouldShowPlaceholder?[self drawingToolbarPlaceholder]:nil)];
}
- (void)addPreviousNextDoneOnKeyboardWithTarget:(id)target previousAction:(SEL)previousAction nextAction:(SEL)nextAction doneAction:(SEL)doneAction titleText:(NSString*)titleText
{
IQBarButtonItemConfiguration *previousConfiguration = [[IQBarButtonItemConfiguration alloc] initWithImage:[UIImage keyboardPreviousImage] action:previousAction];
IQBarButtonItemConfiguration *nextConfiguration = [[IQBarButtonItemConfiguration alloc] initWithImage:[UIImage keyboardNextImage] action:nextAction];
IQBarButtonItemConfiguration *rightConfiguration = [[IQBarButtonItemConfiguration alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone action:doneAction];
[self addKeyboardToolbarWithTarget:target titleText:titleText rightBarButtonConfiguration:rightConfiguration previousBarButtonConfiguration:previousConfiguration nextBarButtonConfiguration:nextConfiguration];
}
- (void)addPreviousNextRightOnKeyboardWithTarget:(nullable id)target rightButtonImage:(nullable UIImage*)rightButtonImage previousAction:(nullable SEL)previousAction nextAction:(nullable SEL)nextAction rightButtonAction:(nullable SEL)rightButtonAction
{
[self addPreviousNextRightOnKeyboardWithTarget:target rightButtonImage:rightButtonImage previousAction:previousAction nextAction:nextAction rightButtonAction:rightButtonAction titleText:nil];
}
- (void)addPreviousNextRightOnKeyboardWithTarget:(nullable id)target rightButtonImage:(nullable UIImage*)rightButtonImage previousAction:(nullable SEL)previousAction nextAction:(nullable SEL)nextAction rightButtonAction:(nullable SEL)rightButtonAction shouldShowPlaceholder:(BOOL)shouldShowPlaceholder
{
[self addPreviousNextRightOnKeyboardWithTarget:target rightButtonImage:rightButtonImage previousAction:previousAction nextAction:nextAction rightButtonAction:rightButtonAction titleText:(shouldShowPlaceholder?[self drawingToolbarPlaceholder]:nil)];
}
- (void)addPreviousNextRightOnKeyboardWithTarget:(id)target rightButtonImage:(UIImage*)rightButtonImage previousAction:(SEL)previousAction nextAction:(SEL)nextAction rightButtonAction:(SEL)rightButtonAction titleText:(NSString*)titleText
{
IQBarButtonItemConfiguration *previousConfiguration = [[IQBarButtonItemConfiguration alloc] initWithImage:[UIImage keyboardPreviousImage] action:previousAction];
IQBarButtonItemConfiguration *nextConfiguration = [[IQBarButtonItemConfiguration alloc] initWithImage:[UIImage keyboardNextImage] action:nextAction];
IQBarButtonItemConfiguration *rightConfiguration = [[IQBarButtonItemConfiguration alloc] initWithImage:rightButtonImage action:rightButtonAction];
[self addKeyboardToolbarWithTarget:target titleText:titleText rightBarButtonConfiguration:rightConfiguration previousBarButtonConfiguration:previousConfiguration nextBarButtonConfiguration:nextConfiguration];
}
- (void)addPreviousNextRightOnKeyboardWithTarget:(id)target rightButtonTitle:(NSString*)rightButtonTitle previousAction:(SEL)previousAction nextAction:(SEL)nextAction rightButtonAction:(SEL)rightButtonAction
{
[self addPreviousNextRightOnKeyboardWithTarget:target rightButtonTitle:rightButtonTitle previousAction:previousAction nextAction:nextAction rightButtonAction:rightButtonAction titleText:nil];
}
- (void)addPreviousNextRightOnKeyboardWithTarget:(id)target rightButtonTitle:(NSString*)rightButtonTitle previousAction:(SEL)previousAction nextAction:(SEL)nextAction rightButtonAction:(SEL)rightButtonAction shouldShowPlaceholder:(BOOL)shouldShowPlaceholder
{
[self addPreviousNextRightOnKeyboardWithTarget:target rightButtonTitle:rightButtonTitle previousAction:previousAction nextAction:nextAction rightButtonAction:rightButtonAction titleText:(shouldShowPlaceholder?[self drawingToolbarPlaceholder]:nil)];
}
- (void)addPreviousNextRightOnKeyboardWithTarget:(id)target rightButtonTitle:(NSString*)rightButtonTitle previousAction:(SEL)previousAction nextAction:(SEL)nextAction rightButtonAction:(SEL)rightButtonAction titleText:(NSString*)titleText
{
IQBarButtonItemConfiguration *previousConfiguration = [[IQBarButtonItemConfiguration alloc] initWithImage:[UIImage keyboardPreviousImage] action:previousAction];
IQBarButtonItemConfiguration *nextConfiguration = [[IQBarButtonItemConfiguration alloc] initWithImage:[UIImage keyboardNextImage] action:nextAction];
IQBarButtonItemConfiguration *rightConfiguration = [[IQBarButtonItemConfiguration alloc] initWithTitle:rightButtonTitle action:rightButtonAction];
[self addKeyboardToolbarWithTarget:target titleText:titleText rightBarButtonConfiguration:rightConfiguration previousBarButtonConfiguration:previousConfiguration nextBarButtonConfiguration:nextConfiguration];
}
@end

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array/>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyTracking</key>
<false/>
</dict>
</plist>

21
Pods/IQKeyboardManager/LICENSE.md generated Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013-2023 Iftekhar Qurashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

220
Pods/IQKeyboardManager/README.md generated Normal file
View File

@@ -0,0 +1,220 @@
<p align="center">
<img src="https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Demo/Resources/icon.png" alt="Icon"/>
</p>
<H1 align="center">IQKeyboardManager</H1>
<p align="center">
<img src="https://img.shields.io/github/license/hackiftekhar/IQKeyboardManager.svg"
alt="GitHub license"/>
[![Build Status](https://travis-ci.org/hackiftekhar/IQKeyboardManager.svg)](https://travis-ci.org/hackiftekhar/IQKeyboardManager)
While developing iOS apps, we often run into issues where the iPhone keyboard slides up and covers the `UITextField/UITextView`. `IQKeyboardManager` allows you to prevent this issue of keyboard sliding up and covering `UITextField/UITextView` without needing you to write any code or make any additional setup. To use `IQKeyboardManager` you simply need to add source files to your project.
#### Key Features
1) `**CODELESS**, Zero Lines of Code`
2) `Works Automatically`
3) `No More UIScrollView`
4) `No More Subclasses`
5) `No More Manual Work`
6) `No More #imports`
`IQKeyboardManager` works on all orientations, and with the toolbar. It also has nice optional features allowing you to customize the distance from the text field, behaviour of previous, next and done buttons in the keyboard toolbar, play sound when the user navigates through the form and more.
## Screenshot
[![Screenshot 1](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Screenshot/README_Screenshot1.png)](http://youtu.be/6nhLw6hju2A)
[![Screenshot 2](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Screenshot/README_Screenshot2.png)](http://youtu.be/6nhLw6hju2A)
[![Screenshot 3](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Screenshot/README_Screenshot3.png)](http://youtu.be/6nhLw6hju2A)
[![Screenshot 4](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Screenshot/README_Screenshot4.png)](http://youtu.be/6nhLw6hju2A)
[![Screenshot 5](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Screenshot/README_Screenshot5.png)](http://youtu.be/6nhLw6hju2A)
## GIF animation
[![IQKeyboardManager](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/v3.3.0/Screenshot/IQKeyboardManager.gif)](http://youtu.be/6nhLw6hju2A)
## Video
<a href="http://youtu.be/WAYc2Qj-OQg" target="_blank"><img src="http://img.youtube.com/vi/WAYc2Qj-OQg/0.jpg"
alt="IQKeyboardManager Demo Video" width="480" height="360" border="10" /></a>
## Tutorial video by @rebeloper ([#1135](https://github.com/hackiftekhar/IQKeyboardManager/issues/1135))
@rebeloper demonstrated two videos on how to implement **IQKeyboardManager** at it's core:
<a href="https://www.youtube.com/playlist?list=PL_csAAO9PQ8aTL87XnueOXi3RpWE2m_8v" target="_blank"><img src="https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Screenshot/ThirdPartyYoutubeTutorial.jpg"
alt="Youtube Tutorial Playlist"/></a>
https://www.youtube.com/playlist?list=PL_csAAO9PQ8aTL87XnueOXi3RpWE2m_8v
## Warning
- **If you're planning to build SDK/library/framework and want to handle UITextField/UITextView with IQKeyboardManager then you're totally going the wrong way.** I would never suggest to add **IQKeyboardManager** as **dependency/adding/shipping** with any third-party library. Instead of adding **IQKeyboardManager** you should implement your own solution to achieve same kind of results. **IQKeyboardManager** is totally designed for projects to help developers for their convenience, it's not designed for **adding/dependency/shipping** with any **third-party library**, because **doing this could block adoption by other developers for their projects as well (who are not using IQKeyboardManager and have implemented their custom solution to handle UITextField/UITextView in the project).**
- If **IQKeyboardManager** conflicts with other **third-party library**, then it's **developer responsibility** to **enable/disable IQKeyboardManager** when **presenting/dismissing** third-party library UI. Third-party libraries are not responsible to handle IQKeyboardManager.
## Requirements
[![Platform iOS](https://img.shields.io/badge/Platform-iOS-blue.svg?style=fla)]()
| | Language | Minimum iOS Target | Minimum Xcode Version |
|------------------------|----------|--------------------|-----------------------|
| IQKeyboardManager | Obj-C | iOS 11.0 | Xcode 13 |
| IQKeyboardManagerSwift | Swift | iOS 13.0 | Xcode 13 |
| Demo Project | | | Xcode 15 |
#### Swift versions support
| Swift | Xcode | IQKeyboardManagerSwift |
|-------------------|-------|------------------------|
| 5.9, 5.8, 5.7, 5.6| 15 | >= 7.0.0 |
| 5.5, 5.4, 5.3, 5.2, 5.1, 5.0, 4.2| 11 | >= 6.5.7 |
| 5.1, 5.0, 4.2, 4.0, 3.2, 3.0| 11 | >= 6.5.0 |
| 5.0,4.2, 4.0, 3.2, 3.0| 10.2 | >= 6.2.1 |
| 4.2, 4.0, 3.2, 3.0| 10.0 | >= 6.0.4 |
| 4.0, 3.2, 3.0 | 9.0 | 5.0.0 |
## 7.0.0 version notes
- In this major release, a lot of variables and functions have been moved here and there. We have mentioned most of the major things in the MIGRATION GUIDE. So please take a look to make changes in your project when upgrading to this version.
- The 7.0.0 version adopted the latest Swift Concurrency/Actor feature and only available iOS 13.0 and above.
- Internal keyboard management handling have been updated with a different and better approach than legacy versions. However when adopting 7.0.0, please verify if it is working as expected in your apps, if there are any serious problems with 7.0.0 please open an issue with all the details and switch back to legacy version temporarily.
Installation
==========================
#### Installation with CocoaPods
[![CocoaPods](https://img.shields.io/cocoapods/v/IQKeyboardManager.svg)](http://cocoadocs.org/docsets/IQKeyboardManager)
***IQKeyboardManager (Objective-C):*** IQKeyboardManager is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile: ([#9](https://github.com/hackiftekhar/IQKeyboardManager/issues/9))
```ruby
pod 'IQKeyboardManager' #iOS13 and later
```
***IQKeyboardManager (Swift):*** IQKeyboardManagerSwift is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile: ([#236](https://github.com/hackiftekhar/IQKeyboardManager/issues/236))
*Swift 5.9, 5.8, 5.7, 5.6, 5.5 (Xcode 15)*
```ruby
pod 'IQKeyboardManagerSwift'
```
*Or you can choose the version you need based on Swift support table from [Requirements](README.md#requirements)*
```ruby
pod 'IQKeyboardManagerSwift', '6.3.0'
```
In AppDelegate.swift, just import IQKeyboardManagerSwift framework and enable IQKeyboardManager.
```swift
import IQKeyboardManagerSwift
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
IQKeyboardManager.shared.enable = true
return true
}
}
```
#### Installation with Carthage
[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
```bash
$ brew update
$ brew install carthage
```
To integrate `IQKeyboardManger` or `IQKeyboardManagerSwift` into your Xcode project using Carthage, add the following line to your `Cartfile`:
```ogdl
github "hackiftekhar/IQKeyboardManager"
```
Run `carthage` to build the frameworks and drag the appropriate framework (`IQKeyboardManager.framework` or `IQKeyboardManagerSwift.framework`) into your Xcode project based on your need. Make sure to add only one framework and not both.
#### Installation with Source Code
[![Github tag](https://img.shields.io/github/tag/hackiftekhar/iqkeyboardmanager.svg)]()
***IQKeyboardManager (Objective-C):*** Just ***drag and drop*** `IQKeyboardManager` directory from demo project to your project. That's it.
***IQKeyboardManager (Swift):*** ***Drag and drop*** `IQKeyboardManagerSwift` directory from demo project to your project
In AppDelegate.swift, just enable IQKeyboardManager.
```swift
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
IQKeyboardManager.shared.enable = true
return true
}
}
```
#### Installation with Swift Package Manager
[Swift Package Manager(SPM)](https://swift.org/package-manager/) is Apple's dependency manager tool. It is now supported in Xcode 11. So it can be used in all appleOS types of projects. It can be used alongside other tools like CocoaPods and Carthage as well.
To install IQKeyboardManagerSwift package via Xcode
* Go to File -> Swift Packages -> Add Package Dependency...
* Then search for https://github.com/hackiftekhar/IQKeyboardManager.git
* And choose the version you want
Migration Guide
==========================
- [IQKeyboardManager 6.0.0 Migration Guide](https://github.com/hackiftekhar/IQKeyboardManager/wiki/IQKeyboardManager-6.0.0-Migration-Guide)
- [IQKeyboardManager 7.0.0 Migration Guide](https://github.com/hackiftekhar/IQKeyboardManager/wiki/IQKeyboardManager-7.0.0-Migration-Guide)
Other Links
==========================
- [Known Issues](https://github.com/hackiftekhar/IQKeyboardManager/wiki/Known-Issues)
- [Manual Management Tweaks](https://github.com/hackiftekhar/IQKeyboardManager/wiki/Manual-Management)
- [Properties and functions usage](https://github.com/hackiftekhar/IQKeyboardManager/wiki/Properties-&-Functions)
## Flow Diagram
[![IQKeyboardManager CFD](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Screenshot/IQKeyboardManagerFlowDiagram.jpg)](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Screenshot/IQKeyboardManagerFlowDiagram.jpg)
If you would like to see detailed Flow diagram then check [Detailed Flow Diagram](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/v3.3.0/Screenshot/IQKeyboardManagerCFD.jpg).
LICENSE
---
Distributed under the MIT License.
Contributions
---
Any contribution is more than welcome! You can contribute through pull requests and issues on GitHub.
Author
---
If you wish to contact me, email at: hack.iftekhar@gmail.com

33
Pods/Manifest.lock generated
View File

@@ -89,11 +89,14 @@ PODS:
- BRPickerView/TextPicker (2.9.1):
- BRPickerView/Core
- Bugly (2.6.1)
- DZNEmptyDataSet (1.8.1)
- FLAnimatedImage (1.0.17)
- FMDB (2.7.12):
- FMDB/standard (= 2.7.12)
- FMDB/Core (2.7.12)
- FMDB/standard (2.7.12):
- FMDB/Core
- IQKeyboardManager (6.5.19)
- JXCategoryView (1.6.8)
- JXPagingView/Pager (2.1.3)
- libwebp (1.5.0):
@@ -166,6 +169,9 @@ PODS:
- MQTTClient/Min
- MQTTClient/Min (0.15.3)
- Protobuf (3.29.3)
- QCloudCore/WithoutMTA (6.5.0)
- QCloudCOSXML/Transfer (6.5.0):
- QCloudCore/WithoutMTA (= 6.5.0)
- QGVAPlayer (1.0.19)
- ReactiveObjC (3.1.1)
- SDCycleScrollView (1.82):
@@ -185,6 +191,9 @@ PODS:
- SVGAPlayer/ProtoFiles
- SVGAPlayer/ProtoFiles (2.5.7):
- Protobuf (~> 3.4)
- SVProgressHUD (2.3.1):
- SVProgressHUD/Core (= 2.3.1)
- SVProgressHUD/Core (2.3.1)
- TencentCloudHuiyanSDKFace_framework (8.7.0)
- TIMCommon (8.5.6864):
- Masonry
@@ -218,6 +227,8 @@ PODS:
- TZImagePickerController/Basic (3.8.8)
- TZImagePickerController/Location (3.8.8)
- WechatOpenSDK-XCFramework (2.0.4)
- XHLaunchAd (3.9.12):
- FLAnimatedImage (~> 1.0.12)
- YBImageBrowser (3.0.9):
- YBImageBrowser/Core (= 3.0.9)
- YBImageBrowser/Core (3.0.9):
@@ -250,6 +261,9 @@ DEPENDENCIES:
- AvoidCrash
- BRPickerView
- Bugly
- DZNEmptyDataSet
- FMDB
- IQKeyboardManager
- JXCategoryView
- JXPagingView/Pager
- LLDebugTool
@@ -257,11 +271,13 @@ DEPENDENCIES:
- MBProgressHUD
- MJRefresh
- MQTTClient
- QCloudCOSXML/Transfer
- QGVAPlayer
- SDCycleScrollView
- SDWebImage
- SDWebImageWebPCoder
- SVGAPlayer
- SVProgressHUD
- TencentCloudHuiyanSDKFace_framework (from `./TencentCloudHuiyanSDKFace_framework`)
- TIMCommon (from `./TUIKit/TIMCommon`)
- TIMPush
@@ -270,6 +286,7 @@ DEPENDENCIES:
- TUICore (from `./TUIKit/TUICore`)
- TZImagePickerController
- WechatOpenSDK-XCFramework (~> 2.0.2)
- XHLaunchAd
- YBImageBrowser (~> 3.0.9)
- YBImageBrowser/Video
- YYCache
@@ -291,7 +308,10 @@ SPEC REPOS:
- AvoidCrash
- BRPickerView
- Bugly
- DZNEmptyDataSet
- FLAnimatedImage
- FMDB
- IQKeyboardManager
- JXCategoryView
- JXPagingView
- libwebp
@@ -301,6 +321,8 @@ SPEC REPOS:
- MJRefresh
- MQTTClient
- Protobuf
- QCloudCore
- QCloudCOSXML
- QGVAPlayer
- ReactiveObjC
- SDCycleScrollView
@@ -308,10 +330,12 @@ SPEC REPOS:
- SDWebImageWebPCoder
- SSZipArchive
- SVGAPlayer
- SVProgressHUD
- TIMPush
- TXIMSDK_Plus_iOS_XCFramework
- TZImagePickerController
- WechatOpenSDK-XCFramework
- XHLaunchAd
- YBImageBrowser
- YYCache
- YYCategories
@@ -344,7 +368,10 @@ SPEC CHECKSUMS:
AvoidCrash: 28c6916fe19bd2b84126b8dec7cbe61c9a12741d
BRPickerView: 6dd69ea2c48e0a0abf1d197a705050e13143ee63
Bugly: 217ac2ce5f0f2626d43dbaa4f70764c953a26a31
DZNEmptyDataSet: 9525833b9e68ac21c30253e1d3d7076cc828eaa7
FLAnimatedImage: bbf914596368867157cc71b38a8ec834b3eeb32b
FMDB: 728731dd336af3936ce00f91d9d8495f5718a0e6
IQKeyboardManager: c8665b3396bd0b79402b4c573eac345a31c7d485
JXCategoryView: 262d503acea0b1278c79a1c25b7332ffaef4d518
JXPagingView: afdd2e9af09c90160dd232b970d603cc6e7ddd0e
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
@@ -354,6 +381,8 @@ SPEC CHECKSUMS:
MJRefresh: ff9e531227924c84ce459338414550a05d2aea78
MQTTClient: 0dfa04e789479c2e461786464969895c1fcb0875
Protobuf: e2de96d939c642ce8da186343ff8929b35223212
QCloudCore: 8681c29dc68d783d07209e7a6ebd8b9058ec73e8
QCloudCOSXML: 26ba1918f7126e650e70f091fee4760cca38038a
QGVAPlayer: a0bca68c9bd6f1c8de5ac2d10ddf98be6038cce9
ReactiveObjC: 011caa393aa0383245f2dcf9bf02e86b80b36040
SDCycleScrollView: a0d74c3384caa72bdfc81470bdbc8c14b3e1fbcf
@@ -361,6 +390,7 @@ SPEC CHECKSUMS:
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
SVGAPlayer: 318b85a78b61292d6ae9dfcd651f3f0d1cdadd86
SVProgressHUD: 4837c74bdfe2e51e8821c397825996a8d7de6e22
TencentCloudHuiyanSDKFace_framework: 1a9398776aac617c2ea7e580fbaf797be96256e1
TIMCommon: 23ed3f287fe194ce5139ce421f96cfb81232562f
TIMPush: e1d85ff0ee0c22cc3f51010ddd8de5316d60102c
@@ -370,6 +400,7 @@ SPEC CHECKSUMS:
TXIMSDK_Plus_iOS_XCFramework: ae200b67e618fef504947de6551b127d35b18d67
TZImagePickerController: d084a7b97c82d387e7669dd86dc9a9057500aacf
WechatOpenSDK-XCFramework: 36fb2bea0754266c17184adf4963d7e6ff98b69f
XHLaunchAd: 6320f9c68732667323072fc3a5f104e40fbe9cfe
YBImageBrowser: 7ecc8bf33ffa5f3b94c397c29b4f3638dd37f527
YYCache: 8105b6638f5e849296c71f331ff83891a4942952
YYCategories: 6bcd4314c6661a561410dce4a793379ebd306abd
@@ -379,6 +410,6 @@ SPEC CHECKSUMS:
YYWebImage: 5f7f36aee2ae293f016d418c7d6ba05c4863e928
Zip: b3fef584b147b6e582b2256a9815c897d60ddc67
PODFILE CHECKSUM: 9f0c224f530b4e6d6636265a8440d96ff79f0a83
PODFILE CHECKSUM: 068f3e03c3d864b09a8c3ffbb0275bbd06bd6ed7
COCOAPODS: 1.16.2

File diff suppressed because it is too large Load Diff

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0130B3724283586C0E9D2A112D4F2AA1"
BuildableName = "AFNetworking.framework"
BlueprintName = "AFNetworking"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "49F1FB331F497E32873611039BAF1C27"
BuildableName = "AgoraComponetLog"
BlueprintName = "AgoraComponetLog"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2F7F7AAA1C81C1ECBEECAABE3C3609AC"
BuildableName = "AgoraInfra_iOS"
BlueprintName = "AgoraInfra_iOS"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0409F31E66073F5C1A3BBA08171DD017"
BuildableName = "AgoraLyricsScoreBundle.bundle"
BlueprintName = "AgoraLyricsScore-AgoraLyricsScoreBundle"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "43B293ED3108BC77CE12D6449EF3AA5F"
BuildableName = "AgoraLyricsScore.framework"
BlueprintName = "AgoraLyricsScore"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "183C29DD6DE04D22CA29BAE61F5C530B"
BuildableName = "AgoraRtcEngine_iOS"
BlueprintName = "AgoraRtcEngine_iOS"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "4847ACB1E2799345AA74D9E317048D58"
BuildableName = "AlipaySDK-iOS"
BlueprintName = "AlipaySDK-iOS"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5D11A19D6943394EC1DDAB15A39FB827"
BuildableName = "AliyunOSSiOS_Privacy.bundle"
BlueprintName = "AliyunOSSiOS-AliyunOSSiOS_Privacy"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C1FD11F5F7EBFF63F845CDF963F73E1A"
BuildableName = "AliyunOSSiOS.framework"
BlueprintName = "AliyunOSSiOS"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3FA1E8E62E0263A4B0CA32E2D30D5BFC"
BuildableName = "AvoidCrash.framework"
BlueprintName = "AvoidCrash"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "941104F296513E17138328984384286C"
BuildableName = "BRPickerView.Privacy.bundle"
BlueprintName = "BRPickerView-BRPickerView.Privacy"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D505645C3F99EB1E8B6529D64D2C3E1C"
BuildableName = "BRPickerView.framework"
BlueprintName = "BRPickerView"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "4A68CFD979D413A619DF631BB121D98F"
BuildableName = "Bugly"
BlueprintName = "Bugly"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "973B9A51B49701F13767694DCAF5C37D"
BuildableName = "FMDB_Privacy.bundle"
BlueprintName = "FMDB-FMDB_Privacy"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8592E0E389D40AC17881400ADC67ABC0"
BuildableName = "FMDB.framework"
BlueprintName = "FMDB"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E79E29F55A2453370D27112480FCF6A2"
BuildableName = "JXCategoryView.bundle"
BlueprintName = "JXCategoryView-JXCategoryView"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8BEAFAA726C1965864B79B4B441AA513"
BuildableName = "JXCategoryView.framework"
BlueprintName = "JXCategoryView"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "16704C6B67439DA959DB043702C75A7A"
BuildableName = "JXPagerView.bundle"
BlueprintName = "JXPagingView-JXPagerView"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C4E1020AF425614337737213AA26DBD5"
BuildableName = "JXPagingView.framework"
BlueprintName = "JXPagingView"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5A3DE9AE6F3A36E1625275AD8CEC43B4"
BuildableName = "LLDebugTool.framework"
BlueprintName = "LLDebugTool"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "82B0A41D3031FF27D78E17B0A9A46FB0"
BuildableName = "MBProgressHUD.framework"
BlueprintName = "MBProgressHUD"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B26054DF1DEA11585A231AF6D1D80D5E"
BuildableName = "MJRefresh.Privacy.bundle"
BlueprintName = "MJRefresh-MJRefresh.Privacy"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6868056D761E163D10FDAF8CF1C4D9B8"
BuildableName = "MJRefresh.framework"
BlueprintName = "MJRefresh"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18D5937A1FF4E1064BDE592E5E9532EA"
BuildableName = "MQTTClient.framework"
BlueprintName = "MQTTClient"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "55AF53E6C77A10ED4985E04D74A8878E"
BuildableName = "Masonry.framework"
BlueprintName = "Masonry"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "27BBCC9A96380AA1F208DF828383B86B"
BuildableName = "Pods_QXLive.framework"
BlueprintName = "Pods-QXLive"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "4AAE1BEBA99B4F05AAA71842C4FD1A10"
BuildableName = "Pods_QXLiveDev.framework"
BlueprintName = "Pods-QXLiveDev"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331902F14E62290AA86FB37CAAC6ABB0"
BuildableName = "Protobuf_Privacy.bundle"
BlueprintName = "Protobuf-Protobuf_Privacy"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "470FE31978DC918618A329D8B55C85FF"
BuildableName = "Protobuf.framework"
BlueprintName = "Protobuf"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FBF544DF4EBDEAD14CE7D3656AF4A18B"
BuildableName = "QGVAPlayer.framework"
BlueprintName = "QGVAPlayer"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "438B238ACC7DF1178D1BCE1A31983146"
BuildableName = "ReactiveObjC.framework"
BlueprintName = "ReactiveObjC"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CBED833AAD6266F3AEFE9BE31C68E094"
BuildableName = "SDCycleScrollView.framework"
BlueprintName = "SDCycleScrollView"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "94CFBA7D633ECA58DF85C327B035E6A3"
BuildableName = "SDWebImage.bundle"
BlueprintName = "SDWebImage-SDWebImage"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3847153A6E5EEFB86565BA840768F429"
BuildableName = "SDWebImage.framework"
BlueprintName = "SDWebImage"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1953860EA9853AA2BC8022B242F08512"
BuildableName = "SDWebImageWebPCoder.framework"
BlueprintName = "SDWebImageWebPCoder"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F60E38364AFF5E1349FF07415B944396"
BuildableName = "SSZipArchive.framework"
BlueprintName = "SSZipArchive"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EF4EFD5BE77B99AACFB0B7ACE03D198F"
BuildableName = "SVGAPlayer.framework"
BlueprintName = "SVGAPlayer"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A9A94B2F47199AE29CDF9CF13F4D95CF"
BuildableName = "TIMCommon_Privacy.bundle"
BlueprintName = "TIMCommon-TIMCommon_Privacy"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "46196B0F15FB5453681E745A8C1700C8"
BuildableName = "TIMCommon.framework"
BlueprintName = "TIMCommon"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A3672D206FD781B76503C6F6C0C8191A"
BuildableName = "TIMPush_Privacy.bundle"
BlueprintName = "TIMPush-TIMPush_Privacy"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "4927495D845F79030185863355F5B96F"
BuildableName = "TIMPush"
BlueprintName = "TIMPush"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F56DFD738FAB4E4F07F2D2E962EA3946"
BuildableName = "TUIChat_Privacy.bundle"
BlueprintName = "TUIChat-TUIChat_Privacy"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "05927177BD6FAFDF573D533A8665C859"
BuildableName = "TUIChat.framework"
BlueprintName = "TUIChat"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FBC036FDFE401149B1CD899239D81CFA"
BuildableName = "TUIConversation_Privacy.bundle"
BlueprintName = "TUIConversation-TUIConversation_Privacy"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7E6A8D6108E4537DCBFAFA98674E2A71"
BuildableName = "TUIConversation.framework"
BlueprintName = "TUIConversation"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8F36C5664BEF7888A4F179030A11C598"
BuildableName = "TUICore_Privacy.bundle"
BlueprintName = "TUICore-TUICore_Privacy"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EB381B961261861E0643718E562995A8"
BuildableName = "TUICore.framework"
BlueprintName = "TUICore"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F71BF3AB61E6444B3140F0A3A86B2910"
BuildableName = "TXIMSDK_Plus_iOS_XCFramework_Privacy.bundle"
BlueprintName = "TXIMSDK_Plus_iOS_XCFramework-TXIMSDK_Plus_iOS_XCFramework_Privacy"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BB2154EC919CB4298C80ED396B322C5F"
BuildableName = "TXIMSDK_Plus_iOS_XCFramework"
BlueprintName = "TXIMSDK_Plus_iOS_XCFramework"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A96BBB982D62BB807B5BD10774BE2D07"
BuildableName = "TZImagePickerController.framework"
BlueprintName = "TZImagePickerController"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5E1A429206107DAD9DA8C2C93A5BAFC5"
BuildableName = "TencentCloudHuiyanSDKFace_framework"
BlueprintName = "TencentCloudHuiyanSDKFace_framework"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7F18C31A804695333763EEC303E265D7"
BuildableName = "WechatOpenSDK-XCFramework"
BlueprintName = "WechatOpenSDK-XCFramework"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B97FC50FB8C4390DDCCA281E85E7AD84"
BuildableName = "YBImageBrowser.framework"
BlueprintName = "YBImageBrowser"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CFDAE6EB02F58B9A37CADCF439AE6082"
BuildableName = "YYCache.framework"
BlueprintName = "YYCache"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

Some files were not shown because too many files have changed in this diff Show More