├── TODocumentPickerViewControllerExample
├── ja.lproj
│ ├── LaunchScreen.strings
│ └── Main.strings
├── TOViewController.h
├── TOAppDelegate.h
├── TODocumentsDataSource.h
├── main.m
├── Base.lproj
│ ├── LaunchScreen.xib
│ └── Main.storyboard
├── Images.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
├── TOViewController.m
├── TOAppDelegate.m
└── TODocumentsDataSource.m
├── .github
└── FUNDING.yml
├── TOSearchBar
├── ko.lproj
│ └── TOSearchBarLocalizable.strings
├── ja.lproj
│ └── TOSearchBarLocalizable.strings
├── zh-Hans.lproj
│ └── TOSearchBarLocalizable.strings
├── zh-Hant.lproj
│ └── TOSearchBarLocalizable.strings
├── Base.lproj
│ └── TOSearchBarLocalizable.strings
├── da.lproj
│ └── TOSearchBarLocalizable.strings
├── en.lproj
│ └── TOSearchBarLocalizable.strings
├── es.lproj
│ └── TOSearchBarLocalizable.strings
├── it.lproj
│ └── TOSearchBarLocalizable.strings
├── nl.lproj
│ └── TOSearchBarLocalizable.strings
├── pl.lproj
│ └── TOSearchBarLocalizable.strings
├── ru.lproj
│ └── TOSearchBarLocalizable.strings
├── de.lproj
│ └── TOSearchBarLocalizable.strings
├── fr.lproj
│ └── TOSearchBarLocalizable.strings
├── TOSearchBar+Assets.h
├── TOSearchBar.h
├── TOSearchBar+Assets.m
└── TOSearchBar.m
├── screenshot.jpg
├── screenshot2016.jpg
├── Design
└── FileIcons.pcvd
├── TODocumentPickerViewControllerExample.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
├── TODocumentPickerViewController.podspec
├── LICENSE
├── CHANGELOG.md
├── TODocumentPickerViewController
├── Models
│ ├── TODocumentPickerTheme.m
│ ├── TODocumentPickerItem.h
│ ├── TODocumentPickerLocalDiskDataSource.h
│ ├── TODocumentPickerConstants.m
│ ├── TODocumentPickerTheme.h
│ ├── TODocumentPickerItemManager.h
│ ├── TODocumentPickerConfiguration.m
│ ├── TODocumentPickerConfiguration.h
│ ├── TODocumentPickerItem.m
│ ├── TODocumentPickerLocalDiskDataSource.m
│ ├── TODocumentPickerConstants.h
│ └── TODocumentPickerItemManager.m
├── Views
│ ├── TODocumentPickerTableView.h
│ ├── TODocumentPickerSegmentedControl.h
│ ├── TODocumentPickerTableViewCell.h
│ ├── TODocumentPickerTableView.m
│ ├── TODocumentPickerHeaderView.h
│ ├── TODocumentPickerTableViewCell.m
│ ├── TODocumentPickerHeaderView.m
│ └── TODocumentPickerSegmentedControl.m
├── Categories
│ └── UIImage+TODocumentPickerIcons.h
└── TODocumentPickerViewController.h
├── .gitignore
└── README.md
/TODocumentPickerViewControllerExample/ja.lproj/LaunchScreen.strings:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: timoliver
2 | custom: https://tim.dev/paypal
3 |
--------------------------------------------------------------------------------
/TOSearchBar/ko.lproj/TOSearchBarLocalizable.strings:
--------------------------------------------------------------------------------
1 | "Search" = "검색";
2 | "Cancel" = "취소";
3 |
--------------------------------------------------------------------------------
/TOSearchBar/ja.lproj/TOSearchBarLocalizable.strings:
--------------------------------------------------------------------------------
1 | "Search" = "検索";
2 | "Cancel" = "キャンセル";
3 |
--------------------------------------------------------------------------------
/TOSearchBar/zh-Hans.lproj/TOSearchBarLocalizable.strings:
--------------------------------------------------------------------------------
1 | "Search" = "搜索";
2 | "Cancel" = "取消";
3 |
--------------------------------------------------------------------------------
/TOSearchBar/zh-Hant.lproj/TOSearchBarLocalizable.strings:
--------------------------------------------------------------------------------
1 | "Search" = "搜尋";
2 | "Cancel" = "取消";
3 |
--------------------------------------------------------------------------------
/TOSearchBar/Base.lproj/TOSearchBarLocalizable.strings:
--------------------------------------------------------------------------------
1 | "Search" = "Search";
2 | "Cancel" = "Cancel";
3 |
--------------------------------------------------------------------------------
/TOSearchBar/da.lproj/TOSearchBarLocalizable.strings:
--------------------------------------------------------------------------------
1 | "Search" = "Søg";
2 | "Cancel" = "Annuller";
3 |
--------------------------------------------------------------------------------
/TOSearchBar/en.lproj/TOSearchBarLocalizable.strings:
--------------------------------------------------------------------------------
1 | "Search" = "Search";
2 | "Cancel" = "Cancel";
3 |
--------------------------------------------------------------------------------
/TOSearchBar/es.lproj/TOSearchBarLocalizable.strings:
--------------------------------------------------------------------------------
1 | "Search" = "Buscar";
2 | "Cancel" = "Cancelar";
3 |
--------------------------------------------------------------------------------
/TOSearchBar/it.lproj/TOSearchBarLocalizable.strings:
--------------------------------------------------------------------------------
1 | "Search" = "Cerca";
2 | "Cancel" = "Annulla";
3 |
--------------------------------------------------------------------------------
/TOSearchBar/nl.lproj/TOSearchBarLocalizable.strings:
--------------------------------------------------------------------------------
1 | "Search" = "Zoek";
2 | "Cancel" = "Annuleer";
3 |
--------------------------------------------------------------------------------
/TOSearchBar/pl.lproj/TOSearchBarLocalizable.strings:
--------------------------------------------------------------------------------
1 | "Search" = "Szukaj";
2 | "Cancel" = "Anuluj";
3 |
--------------------------------------------------------------------------------
/TOSearchBar/ru.lproj/TOSearchBarLocalizable.strings:
--------------------------------------------------------------------------------
1 | "Search" = "Поиск";
2 | "Cancel" = "Отменить";
3 |
--------------------------------------------------------------------------------
/TOSearchBar/de.lproj/TOSearchBarLocalizable.strings:
--------------------------------------------------------------------------------
1 | "Search" = "Suchen";
2 | "Cancel" = "Abbrechen";
3 |
--------------------------------------------------------------------------------
/TOSearchBar/fr.lproj/TOSearchBarLocalizable.strings:
--------------------------------------------------------------------------------
1 | "Search" = "Recherche";
2 | "Cancel" = "Annuler";
3 |
--------------------------------------------------------------------------------
/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TimOliver/TODocumentPickerViewController/HEAD/screenshot.jpg
--------------------------------------------------------------------------------
/screenshot2016.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TimOliver/TODocumentPickerViewController/HEAD/screenshot2016.jpg
--------------------------------------------------------------------------------
/Design/FileIcons.pcvd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TimOliver/TODocumentPickerViewController/HEAD/Design/FileIcons.pcvd
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample/ja.lproj/Main.strings:
--------------------------------------------------------------------------------
1 |
2 | /* Class = "IBUIButton"; normalTitle = "Show Document Picker"; ObjectID = "G6f-9A-QZJ"; */
3 | "G6f-9A-QZJ.normalTitle" = "Show Document Picker";
4 |
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample/TOViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // TODocumentPickerViewControllerExample
4 | //
5 | // Created by Tim Oliver on 12/31/14.
6 | // Copyright (c) 2014 Tim Oliver. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TOViewController : UIViewController
12 |
13 | - (IBAction)buttonTapped:(id)sender;
14 |
15 | @end
16 |
17 |
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample/TOAppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // TODocumentPickerViewControllerExample
4 | //
5 | // Created by Tim Oliver on 12/31/14.
6 | // Copyright (c) 2014 Tim Oliver. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TOAppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample/TODocumentsDataSource.h:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentsDataSource.h
3 | // TODocumentPickerViewControllerExample
4 | //
5 | // Created by Tim Oliver on 1/1/15.
6 | // Copyright (c) 2015 Tim Oliver. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "TODocumentPickerViewController.h"
11 |
12 | @interface TODocumentsDataSource : NSObject
13 | @end
14 |
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // TODocumentPickerViewControllerExample
4 | //
5 | // Created by Tim Oliver on 12/31/14.
6 | // Copyright (c) 2014 Tim Oliver. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "TOAppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([TOAppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = 'TODocumentPickerViewController'
3 | spec.version = '0.2.2'
4 | spec.platform = :ios, '9.0'
5 | spec.license = { :type => 'MIT', :file => 'LICENSE' }
6 | spec.homepage = 'https://github.com/TimOliver/TODocumentPickerViewController'
7 | spec.authors = { 'Tim Oliver' => 'info@timoliver.com.au' }
8 | spec.summary = 'A view controller for interacting with file systems on iOS.'
9 | spec.source = { :git => 'https://github.com/TimOliver/TODocumentPickerViewController.git', :tag => spec.version }
10 | spec.source_files = 'TODocumentPickerViewController/**/*.{h,m}'
11 | spec.dependency 'TOSearchBar'
12 | end
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2015 Timothy Oliver. All rights reserved.
2 |
3 | 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:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | 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.
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 |
9 | ## 0.2.1 - 2017-12-30
10 |
11 | ### Fixed
12 | - Table header view not being properly obscured under the navigation bar on iPhone X.
13 | - Properly aligned the text label in the toolbar at the bottom.
14 |
15 | ## 0.2.0 - 2017-12-30
16 |
17 | ### Added
18 | - New icon design to align with iOS 11's new overall feel.
19 | - A system to automatically generate icons for specific file formats.
20 |
21 | ### Changed
22 | - Changed theme settings from `NSDictionary` to a dedicated object with properties.
23 | - Removed the alphabetical sectioning of the files list in favour of integrating `TOSearchBar` in a later release.
24 |
25 | ### Fixed
26 | - Fixed scrolling inset bugs introduced due to iOS 11.
27 | - Fixed header view insetting behaviour to be more consistent, especially when the refresh control is visible.
28 |
29 |
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Models/TODocumentPickerTheme.m:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerTheme.m
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TODocumentPickerTheme.h"
24 |
25 | @implementation TODocumentPickerTheme
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Views/TODocumentPickerTableView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerTableView.h
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import
24 |
25 | @interface TODocumentPickerTableView : UITableView
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Views/TODocumentPickerSegmentedControl.h:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerSegmentedControl.h
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import
24 | #import "TODocumentPickerConstants.h"
25 |
26 | @interface TODocumentPickerSegmentedControl : UISegmentedControl
27 |
28 | @property (nonatomic, assign) TODocumentPickerSortType sortingType;
29 | @property (nonatomic, copy) void (^sortTypeChangedHandler)(void);
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xcuserstate
23 |
24 | ## Obj-C/Swift specific
25 | *.hmap
26 | *.ipa
27 | *.dSYM.zip
28 | *.dSYM
29 |
30 | # CocoaPods
31 | #
32 | # We recommend against adding the Pods directory to your .gitignore. However
33 | # you should judge for yourself, the pros and cons are mentioned at:
34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
35 | #
36 | # Pods/
37 |
38 | # Carthage
39 | #
40 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
41 | # Carthage/Checkouts
42 |
43 | Carthage/Build
44 |
45 | # fastlane
46 | #
47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
48 | # screenshots whenever they are needed.
49 | # For more information about the recommended setup visit:
50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
51 |
52 | fastlane/report.xml
53 | fastlane/Preview.html
54 | fastlane/screenshots
55 | fastlane/test_output
56 |
57 | # Code Injection
58 | #
59 | # After new code Injection tools there's a generated folder /iOSInjectionProject
60 | # https://github.com/johnno1962/injectionforxcode
61 |
62 | iOSInjectionProject/
63 | .DS_Store
64 |
--------------------------------------------------------------------------------
/TOSearchBar/TOSearchBar+Assets.h:
--------------------------------------------------------------------------------
1 | //
2 | // TOSearchBar+ImageAssets.h
3 | //
4 | // Copyright 2015-2016 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TOSearchBar.h"
24 |
25 | @interface TOSearchBar (ImageAssets)
26 |
27 | + (UIImage *)sharedSearchBarBackground; /* The rounded rectangle for the background */
28 | + (UIImage *)sharedSearchIcon; /* The magnifying glads icon */
29 | + (UIImage *)sharedClearIcon; /* The clear button icon. */
30 |
31 | + (void)cleanUpSharedAssets; /* Called upon each `dealloc` method call to clean up any freed assets. */
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample/TOViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // TODocumentPickerViewControllerExample
4 | //
5 | // Created by Tim Oliver on 12/31/14.
6 | // Copyright (c) 2014 Tim Oliver. All rights reserved.
7 | //
8 |
9 | #import "TOViewController.h"
10 | #import "TODocumentPickerViewController.h"
11 | #import "TODocumentsDataSource.h"
12 |
13 | @interface TOViewController ()
14 |
15 | @end
16 |
17 | @implementation TOViewController
18 |
19 | - (IBAction)buttonTapped:(id)sender
20 | {
21 | TODocumentPickerViewController *documentPicker = [[TODocumentPickerViewController alloc] initWithFilePath:nil];
22 | documentPicker.dataSource = [[TODocumentsDataSource alloc] init];
23 | documentPicker.documentPickerDelegate = self;
24 |
25 | UINavigationController *controller = [[UINavigationController alloc] initWithRootViewController:documentPicker];
26 | controller.modalPresentationStyle = UIModalPresentationFormSheet;
27 |
28 | [self presentViewController:controller animated:YES completion:nil];
29 | }
30 |
31 | - (void)documentPickerViewController:(TODocumentPickerViewController *)documentPicker didSelectItems:(nonnull NSArray *)items inFilePath:(NSString *)filePath
32 | {
33 | [self dismissViewControllerAnimated:YES completion:nil];
34 |
35 | NSMutableArray *absoluteItemPaths = [NSMutableArray array];
36 | for (TODocumentPickerItem *item in items) {
37 | NSString *absoluteFilePath = [filePath stringByAppendingPathComponent:item.fileName];
38 | [absoluteItemPaths addObject:absoluteFilePath];
39 | }
40 |
41 | NSLog(@"Paths for items selected: %@", absoluteItemPaths);
42 | }
43 |
44 | @end
45 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Views/TODocumentPickerTableViewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerTableViewCell.h
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import
24 | #import "TODocumentPickerConstants.h"
25 |
26 | @interface TODocumentPickerTableViewCell : UITableViewCell
27 |
28 | @property (nonatomic, readonly) UILabel *textLabel;
29 | @property (nonatomic, readonly) UILabel *detailTextLabel;
30 | @property (nonatomic, readonly) UIImageView *imageView;
31 |
32 | - (void)configureCellForStyle:(TODocumentPickerViewControllerStyle)style;
33 |
34 | @end
35 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Views/TODocumentPickerTableView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerTableView.m
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TODocumentPickerTableView.h"
24 |
25 | @implementation TODocumentPickerTableView
26 |
27 | - (void)setContentSize:(CGSize)contentSize
28 | {
29 | CGFloat scrollInset = self.contentInset.top + self.contentInset.bottom;
30 | if (@available(iOS 11.0, *)) {
31 | scrollInset = self.adjustedContentInset.top + self.adjustedContentInset.bottom;
32 | }
33 | CGFloat height = (CGRectGetHeight(self.bounds) - scrollInset) + CGRectGetHeight(self.tableHeaderView.frame);
34 | contentSize.height = MAX(height, contentSize.height);
35 | [super setContentSize:contentSize];
36 | }
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Models/TODocumentPickerItem.h:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerItem.h
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import
24 |
25 | @class TODocumentPickerViewController;
26 |
27 | @interface TODocumentPickerItem : NSObject
28 |
29 | @property (nonatomic, copy) NSString *fileName; /** The full name of this file */
30 | @property (nonatomic, assign) NSUInteger fileSize; /** The size of the file in bytes */
31 | @property (nonatomic, strong) NSDate *lastModifiedDate; /** Where possible, the modification date of this file */
32 | @property (nonatomic, assign) BOOL isFolder; /** Whether the file is a folder or now */
33 |
34 | /** A pre-generated description of the item that is displayed in the picker view */
35 | @property (nonatomic, copy, readonly) NSString *localizedMetadata;
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Models/TODocumentPickerLocalDiskDataSource.h:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerFileDataSource.h
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import
24 | #import "TODocumentPickerConstants.h"
25 |
26 | @interface TODocumentPickerLocalDiskDataSource : NSObject
27 |
28 | /** Allows you to override the title of the root folder displayed initially. */
29 | @property (nonatomic, copy) NSString *rootFolderName;
30 |
31 | /** Shows files that have been prefixed with a '.' character. */
32 | @property (nonatomic, assign) BOOL showHiddenFiles;
33 |
34 | /* Create a new instance of this data source, with the supplied file path
35 | pointing to the folder that will be initially displayed.
36 | If the file path is relative (eg, starts with '/'), the app's top-most directory
37 | in the sandbox will be used.
38 | */
39 | - (instancetype)initWithBaseFolderPath:(NSString *)folderPath;
40 |
41 | @end
42 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Views/TODocumentPickerHeaderView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerHeaderView.h
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import
24 | #import "TODocumentPickerSegmentedControl.h"
25 |
26 | @class TOSearchBar;
27 |
28 | @interface TODocumentPickerHeaderView : UIView
29 |
30 | /* Views */
31 | @property (nonatomic, readonly) TOSearchBar *searchBar;
32 | @property (nonatomic, readonly) TODocumentPickerSegmentedControl *sortControl;
33 |
34 | /* The height from the top of the view that is clipped */
35 | @property (nonatomic, assign) CGFloat clippingHeight;
36 |
37 | /* Whether to show the search bar or not */
38 | @property (nonatomic, assign) BOOL showsSearchBar;
39 |
40 | /* Responding to text inserted into the search bar */
41 | @property (nonatomic, copy) void (^searchTextChangedHandler)(NSString *searchText);
42 |
43 | /* Cancelling the search bar (i.e the user scrolls the table view) */
44 | - (void)dismissKeyboard;
45 |
46 | @end
47 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Models/TODocumentPickerConstants.m:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerDefines.h
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TODocumentPickerConstants.h"
24 |
25 | NSString * const TODocumentPickerViewControllerThemeAttributeBackgroundColor = @"";
26 | NSString * const TODocumentPickerViewControllerThemeAttributeTableSeparatorColor = @"";
27 | NSString * const TODocumentPickerViewControllerThemeAttributeTableCellTitleColor = @"";
28 | NSString * const TODocumentPickerViewControllerThemeAttributeTableCellDetailTextColor = @"";
29 | NSString * const TODocumentPickerViewControllerThemeAttributeTableCellAccessoryTintColor = @"";
30 | NSString * const TODocumentPickerViewControllerThemeAttributeTableCellIconTintColor = @"";
31 | NSString * const TODocumentPickerViewControllerThemeAttributeTableSectionHeaderBackgroundColor = @"";
32 | NSString * const TODocumentPickerViewControllerThemeAttributeTableSectionTitleColor = @"";
33 | NSString * const TODocumentPickerViewControllerThemeAttributeTableSectionIndexColor = @"";
34 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Models/TODocumentPickerTheme.h:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerTheme.h
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import
24 | #import
25 |
26 | NS_ASSUME_NONNULL_BEGIN
27 |
28 | @interface TODocumentPickerTheme : NSObject
29 |
30 | @property (nonatomic, strong, nullable) UIColor *backgroundColor; /* Background color of the table view */
31 | @property (nonatomic, strong, nullable) UIColor *tableSeparatorColor; /* Color of the table cell divider lines */
32 | @property (nonatomic, strong, nullable) UIColor *tableCellTitleColor; /* Color of the title text label in each cell */
33 | @property (nonatomic, strong, nullable) UIColor *tableCellDetailColor; /* Color of the subtitle text label in each cell */
34 | @property (nonatomic, strong, nullable) UIColor *tableAccessoryViewColor; /* Color of the arrow accessory icon */
35 | @property (nonatomic, strong, nullable) UIColor *scrollBarTintColor; /* Color of the scroll bar */
36 |
37 | @end
38 |
39 | NS_ASSUME_NONNULL_END
40 |
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample/TOAppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // TODocumentPickerViewControllerExample
4 | //
5 | // Created by Tim Oliver on 12/31/14.
6 | // Copyright (c) 2014 Tim Oliver. All rights reserved.
7 | //
8 |
9 | #import "TOAppDelegate.h"
10 |
11 | @interface TOAppDelegate ()
12 |
13 | @end
14 |
15 | @implementation TOAppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 | - (void)applicationWillResignActive:(UIApplication *)application {
24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
26 | }
27 |
28 | - (void)applicationDidEnterBackground:(UIApplication *)application {
29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
31 | }
32 |
33 | - (void)applicationWillEnterForeground:(UIApplication *)application {
34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
35 | }
36 |
37 | - (void)applicationDidBecomeActive:(UIApplication *)application {
38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
39 | }
40 |
41 | - (void)applicationWillTerminate:(UIApplication *)application {
42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TODocumentPickerViewController
2 |
3 |
4 |
5 |
6 |
7 | [](https://beerpay.io/TimOliver/TODocumentPickerViewController)
8 | [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=M4RKULAVKV7K8)
9 |
10 | `TODocumentPickerViewController` is an open source `UIViewController` framework that takes a list of files as input, and displays a navigable file system on iOS devices.
11 |
12 | The goal of this project is to provide a completely generic, modular frontend UI for a variety of file services, both online, and locally. With services like Dropbox and Google Drive exposing their file systems via a REST API, and more low-level protocols like SMB and FTP requiring direct object serialisation, `TODocumentPickerViewController` is being engineered in such a way that any file source can be integrated.
13 |
14 | # Features
15 | * Uses a delegate / data source model in order to integrate with any file source.
16 | * Can be configured using a configuration object passed to it upon init.
17 | * Self-replicates as users drill down folders, keeping configuration consistent between copies.
18 | * Data source provides an asynchronous mechanism to allow long lasting requests (like REST APIs) to not block the main thread.
19 |
20 | ## Installation
21 |
22 | #### As a CocoaPods Dependency
23 |
24 | Add the following to your Podfile:
25 | ``` ruby
26 | pod 'TODocumentPickerViewController'
27 | ```
28 |
29 | #### Manual Installation
30 |
31 | All of the necessary source files are in `TODocumentPickerViewController`. Simply copy that folder to your app project folder, and then import it into Xcode.
32 |
33 | `TODocumentPickerViewController` also relies on the following libraries:
34 |
35 | * [`TOSearchBar`](https://github.com/TimOliver/TOSearchBar)
36 |
37 | While copies have been placed in this repo to minimise the need to download multiple other repos, be sure to check to see if there have been updates on their main repos.
38 |
39 | ## Technical Requirements
40 | iOS 9.0 or above.
41 |
42 | ## License
43 | TODocumentPickerViewControlleris licensed under the MIT License. Please see the LICENSE file for more information. 
44 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Models/TODocumentPickerItemManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerItemManager.h
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import
24 | #import
25 | #import "TODocumentPickerConstants.h"
26 |
27 | @class TODocumentPickerItem;
28 |
29 | @interface TODocumentPickerItemManager : NSObject
30 |
31 | /* Main configuration properties (Changing any of these properties will cause a reload) */
32 | @property (nonatomic, strong) NSArray *items; /* Items are passed to the manager by setting this property. */
33 | @property (nonatomic, assign) TODocumentPickerSortType sortingType; /* The order in which items are displayed. */
34 | @property (nonatomic, copy) NSString *searchString; /* Filter the items with this string */
35 |
36 | @property (nonatomic, weak) UITableView *tableView; /* A weak reference to the relating table view. */
37 | @property (nonatomic, copy) void (^contentReloadedHandler)(void); /* A handler called each time the manager decides to reload the table view. */
38 |
39 | @property (nonatomic, readonly) NSInteger numberOfItems; /* Number of visible rows depending on search string */
40 |
41 | /* Start sorting items, if not already done so. */
42 | - (void)reloadItems;
43 |
44 | /* Retrieve the appropriate object at the provided index */
45 | - (TODocumentPickerItem *)itemForIndexPath:(NSIndexPath *)indexPath;
46 |
47 | @end
48 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Categories/UIImage+TODocumentPickerIcons.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+TODocumentPickerIcons.h
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import
24 | #import "TODocumentPickerConstants.h"
25 |
26 | @interface UIImage (TODocumentPickerIcons)
27 |
28 | + (UIImage *)TO_downloadIcon;
29 |
30 | + (UIImage *)TO_documentPickerDefaultFolderForStyle:(TODocumentPickerViewControllerStyle)style;
31 |
32 | + (UIImage *)TO_documentPickerFolderIconWithSize:(CGSize)size
33 | backgroundColor:(UIColor *)backgroundColor
34 | foregroundBottomColor:(UIColor *)foregroundBottomColor
35 | foregroundTopColor:(UIColor *)foregroundTopColor;
36 |
37 | + (UIImage *)TO_documentPickerDefaultFileIconWithExtension:(NSString *)extension
38 | tintColor:(UIColor *)tintColor
39 | style:(TODocumentPickerViewControllerStyle)style;
40 |
41 | + (UIImage *)TO_documentPickerIconWithSize:(CGSize)size
42 | outlineColor:(UIColor *)outlineColor
43 | backgroundColor:(UIColor *)backgroundColor
44 | cornerColor:(UIColor *)cornerColor
45 | formatNameString:(NSString *)formatNameString
46 | formatNameFont:(UIFont *)formatNameFont
47 | formatNameColor:(UIColor *)formatNameColor;
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Models/TODocumentPickerConfiguration.m:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerConfiguration.m
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TODocumentPickerConstants.h"
24 |
25 | #import "TODocumentPickerConfiguration.h"
26 | #import "TODocumentPickerTheme.h"
27 | #import "UIImage+TODocumentPickerIcons.h"
28 |
29 | @interface TODocumentPickerConfiguration ()
30 |
31 | @property (nonatomic, strong, readwrite) TODocumentPickerTheme *theme;
32 |
33 | @end
34 |
35 | @implementation TODocumentPickerConfiguration
36 |
37 | - (instancetype)init
38 | {
39 | if (self = [super init]) {
40 | _showToolbar = YES;
41 | _theme = [[TODocumentPickerTheme alloc] init];
42 | _fileIcons = [NSMutableDictionary dictionary];
43 | }
44 |
45 | return self;
46 | }
47 |
48 | - (UIImage *)defaultIcon
49 | {
50 | if (_defaultIcon == nil) {
51 | _defaultIcon = [UIImage TO_documentPickerDefaultFileIconWithExtension:nil
52 | tintColor:nil
53 | style:self.style];
54 | }
55 |
56 | return _defaultIcon;
57 | }
58 |
59 |
60 | - (void)setStyle:(TODocumentPickerViewControllerStyle)style
61 | {
62 | if (style == _style) { return; }
63 | _style = style;
64 |
65 | _defaultIcon = nil;
66 | _defaultIcon = nil;
67 | }
68 |
69 | - (void)setFileIcons:(NSMutableDictionary *)fileIcons
70 | {
71 | if (fileIcons == _fileIcons) { return; }
72 |
73 | _fileIcons = fileIcons;
74 |
75 | if (_fileIcons == nil) {
76 | _fileIcons = [NSMutableDictionary dictionary];
77 | }
78 | }
79 |
80 | @end
81 |
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Models/TODocumentPickerConfiguration.h:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerConfiguration.h
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TODocumentPickerConstants.h"
24 |
25 | #import
26 | #import
27 |
28 | @class TODocumentPickerTheme;
29 |
30 | NS_ASSUME_NONNULL_BEGIN
31 |
32 | @interface TODocumentPickerConfiguration : NSObject
33 |
34 | /* Sets the style of the view controller to be light or dark. (Can be customized with the `theme` property) */
35 | @property (nonatomic, assign) TODocumentPickerViewControllerStyle style;
36 |
37 | /* Whether this controller shows and manages the navigation controller toolbar (Default is YES) */
38 | @property (nonatomic, assign) BOOL showToolbar;
39 |
40 | /* When not in 'Select' mode, the bar button items on the left hand side of the toolbar */
41 | @property (nonatomic, strong) NSArray *toolbarLeftItems;
42 |
43 | /* When not in 'Select' mode, the bar button items on the right hand side of the toolbar */
44 | @property (nonatomic, strong) NSArray *toolbarRightItems;
45 |
46 | /* File formats that may be selected by this controller. (If nil, all files may be selected) */
47 | @property (nonatomic, strong, nullable) NSArray *allowedFileExtensions;
48 |
49 | /* Shows files that weren't on the allowed extensions list, but grayed out (Default is NO) */
50 | @property (nonatomic, assign) BOOL showExcludedFileExtensions;
51 |
52 | /* The default icon if the file format isn't recognized, or other icons aren't available. */
53 | @property (nonatomic, strong, nullable) UIImage *defaultIcon;
54 |
55 | /* The icon used for folder entries. */
56 | @property (nonatomic, strong, nullable) UIImage *folderIcon;
57 |
58 | /* A dictionary of file extension strings to images to use as icons for each file format. */
59 | @property (nonatomic, strong, null_resettable) NSMutableDictionary *fileIcons;
60 |
61 | /* If desired, a custom table view cell class that can contain additional controls or formatting. */
62 | @property (nonatomic, assign, nullable) Class tableViewCellClass;
63 |
64 | /* After setting the `style` property, certain visual aspects of the controller can be fine-tuned with this property. */
65 | @property (nonatomic, readonly) TODocumentPickerTheme *theme;
66 |
67 | @end
68 |
69 | NS_ASSUME_NONNULL_END
70 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/TODocumentPickerViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerTableViewController.h
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import
24 | #import "TODocumentPickerConstants.h"
25 | #import "TODocumentPickerItem.h"
26 | #import "TODocumentPickerConfiguration.h"
27 | #import "TODocumentPickerTheme.h"
28 |
29 | @interface TODocumentPickerViewController : UITableViewController
30 |
31 | @property (nonatomic, readonly, nonnull) TODocumentPickerConfiguration *configuration; /* An object that holds the configuration for all controllers */
32 |
33 | @property (nonatomic, strong, nullable) id dataSource; /* Data source for file info. Retained by the document picker and shared among all children. */
34 | @property (nonatomic, weak, nullable) id documentPickerDelegate; /* Sends out delegate events to the assigned object */
35 |
36 | @property (nonatomic, readonly, nullable) NSString *filePath; /* The file path that this view controller corresponds to */
37 | @property (nonatomic, strong, nullable) NSArray *items; /* All of the items displayed by this view controller. (Setting this will trigger a UI refresh) */
38 |
39 | @property (nonatomic, readonly, nonnull) TODocumentPickerViewController *rootViewController; /* In a navigation chain of picker controllers, the root controller at the front. */
40 | @property (nonatomic, readonly, nonnull) NSArray *viewControllers; /* The chain of document picker view controllers in the navigation stack */
41 |
42 | /* Create the initial view controller with the file path string that will be the initial starting point */
43 | - (nullable instancetype)initWithFilePath:(nullable NSString *)filePath;
44 |
45 | /* Create an instance with a custom created configuration object */
46 | - (nullable instancetype)initWithConfiguration:(nullable TODocumentPickerConfiguration *)configuration filePath:(nullable NSString *)filePath;
47 |
48 | /* Sets the items for the view controller in this chain, controlling that file path */
49 | - (void)setItems:(nullable NSArray *)items forFilePath:(nullable NSString *)filePath;
50 |
51 | /* An initial file path must be specified, so use initWithFilePath: */
52 | - (nonnull instancetype)init __attribute__((unavailable("Must use initWithFilePath: instead.")));
53 | + (nonnull instancetype)new __attribute__((unavailable("Must use initWithFilePath: instead.")));
54 |
55 | @end
56 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Models/TODocumentPickerItem.m:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerItem.m
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TODocumentPickerItem.h"
24 |
25 | @interface TODocumentPickerItem ()
26 |
27 | @property (nonatomic, readonly) NSByteCountFormatter *sharedFileSizeFormatter;
28 | @property (nonatomic, readonly) NSDateFormatter *sharedDateFormatter;
29 | @property (nonatomic, copy, readwrite) NSString *localizedMetadata;
30 |
31 | /* Rebuild the localized description string */
32 | - (void)formatMetadata;
33 |
34 | @end
35 |
36 | @implementation TODocumentPickerItem
37 |
38 | #pragma mark Accessors -
39 | - (NSString *)localizedMetadata
40 | {
41 | if (_localizedMetadata == nil)
42 | [self formatMetadata];
43 |
44 | return _localizedMetadata;
45 | }
46 |
47 | #pragma mark - Info formatter -
48 | - (void)formatMetadata
49 | {
50 | NSString *formattedSize = [self.sharedFileSizeFormatter stringFromByteCount:(long long)self.fileSize];
51 | NSString *formattedDate = [self.sharedDateFormatter stringFromDate:self.lastModifiedDate];
52 |
53 | if (!self.isFolder && self.lastModifiedDate && self.fileSize) {
54 | _localizedMetadata = [NSString stringWithFormat:@"%@ • %@", formattedSize, formattedDate];
55 | }
56 | else if (self.lastModifiedDate) {
57 | _localizedMetadata = formattedDate;
58 | }
59 | else if (self.fileSize) {
60 | _localizedMetadata = formattedSize;
61 | }
62 | }
63 |
64 | - (NSByteCountFormatter *)sharedFileSizeFormatter
65 | {
66 | static NSByteCountFormatter *_sharedFileSizeFormatter = nil;
67 | static dispatch_once_t onceToken;
68 | dispatch_once(&onceToken, ^{
69 | _sharedFileSizeFormatter = [[NSByteCountFormatter alloc] init];
70 | _sharedFileSizeFormatter.allowsNonnumericFormatting = NO;
71 | });
72 |
73 | return _sharedFileSizeFormatter;
74 | }
75 |
76 | - (NSDateFormatter *)sharedDateFormatter
77 | {
78 | static NSDateFormatter *_sharedDateFormatter = nil;
79 | static dispatch_once_t onceToken;
80 | dispatch_once(&onceToken, ^{
81 | _sharedDateFormatter = [[NSDateFormatter alloc] init];
82 | _sharedDateFormatter.dateStyle = NSDateFormatterMediumStyle;
83 | _sharedDateFormatter.timeStyle = NSDateFormatterShortStyle;
84 | });
85 |
86 | return _sharedDateFormatter;
87 | }
88 |
89 | #pragma mark - System Description -
90 | - (NSString *)description
91 | {
92 | return [NSString stringWithFormat:@"Name: %@ Size: %ld Folder: %d", self.fileName, (long)self.fileSize, self.isFolder];
93 | }
94 |
95 | @end
96 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Models/TODocumentPickerLocalDiskDataSource.m:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerFileDataSource.m
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TODocumentPickerLocalDiskDataSource.h"
24 | #import "TODocumentPickerViewController.h"
25 |
26 | @interface TODocumentPickerLocalDiskDataSource ()
27 |
28 | @property (nonatomic, copy) NSString *rootFilePath;
29 |
30 | @end
31 |
32 | @implementation TODocumentPickerLocalDiskDataSource
33 |
34 | - (instancetype)init
35 | {
36 | if (self = [self initWithBaseFolderPath:nil]) {
37 |
38 | }
39 |
40 | return self;
41 | }
42 |
43 | - (instancetype)initWithBaseFolderPath:(NSString *)folderPath
44 | {
45 | if (self = [super init]) {
46 | _rootFilePath = [TODocumentPickerLocalDiskDataSource formattedRootFilePath:folderPath];
47 | self.rootFolderName = _rootFilePath.lastPathComponent;
48 | }
49 |
50 | return self;
51 | }
52 |
53 | + (NSString *)formattedRootFilePath:(NSString *)filePath
54 | {
55 | // If it doesn't start with '/', assume absolute
56 | if (filePath.length && [filePath characterAtIndex:0] != '/') {
57 | return filePath;
58 | }
59 |
60 | NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
61 | NSString *applicationSandboxPath = [documentPath stringByDeletingLastPathComponent];
62 | return applicationSandboxPath;
63 | }
64 |
65 | #pragma mark - Data Source Protocol Methods-
66 |
67 | - (NSString *)documentPickerViewController:(TODocumentPickerViewController *)documentPicker titleForFilePath:(NSString *)filePath
68 | {
69 | if (!self.rootFolderName) {
70 | return filePath.lastPathComponent;
71 | }
72 | else {
73 | if (filePath.length == 0 || [filePath isEqualToString:@"/"]) {
74 | return self.rootFolderName;
75 | }
76 | }
77 |
78 | return filePath.lastPathComponent;
79 | }
80 |
81 | - (void)documentPickerViewController:(TODocumentPickerViewController *)documentPicker
82 | requestItemsForFilePath:(NSString *)filePath
83 | completionHandler:(void (^)(NSArray * _Nullable))completionHandler
84 | {
85 | NSString *fullFilePath = [self.rootFilePath stringByAppendingPathComponent:filePath];
86 | NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:fullFilePath error:nil];
87 |
88 | NSMutableArray *items = [NSMutableArray array];
89 | for (NSString *file in files) {
90 | // Skip hidden files if required
91 | if (!self.showHiddenFiles && [file characterAtIndex:0] == '.') {
92 | continue;
93 | }
94 |
95 | NSString *path = [fullFilePath stringByAppendingPathComponent:file];
96 |
97 | NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
98 | TODocumentPickerItem *item = [TODocumentPickerItem new];
99 | item.fileName = file;
100 | item.isFolder = (attributes.fileType == NSFileTypeDirectory);
101 | item.fileSize = item.isFolder ? 0 : (NSUInteger)attributes.fileSize;
102 | item.lastModifiedDate = attributes.fileModificationDate;
103 | [items addObject:item];
104 | }
105 |
106 | completionHandler(items);
107 | }
108 |
109 | @end
110 |
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample/TODocumentsDataSource.m:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentsDataSource.m
3 | // TODocumentPickerViewControllerExample
4 | //
5 | // Created by Tim Oliver on 1/1/15.
6 | // Copyright (c) 2015 Tim Oliver. All rights reserved.
7 | //
8 |
9 | #import "TODocumentsDataSource.h"
10 | #import "TODocumentPickerConstants.h"
11 | #import "TODocumentPickerItem.h"
12 |
13 | @interface TODocumentsDataSource ()
14 |
15 | - (void)createTestData;
16 | - (NSString *)documentsPath;
17 |
18 | @end
19 |
20 | @implementation TODocumentsDataSource
21 |
22 | - (instancetype)init
23 | {
24 | if (self = [super init]) {
25 | [self createTestData];
26 | }
27 |
28 | return self;
29 | }
30 |
31 | #pragma mark - Data Source Methods -
32 | - (NSString *)documentPickerViewController:(TODocumentPickerViewController *)documentPicker titleForFilePath:(NSString *)filePath
33 | {
34 | if (filePath.length == 0|| [filePath isEqualToString:@"/"]) {
35 | return @"Documents";
36 | }
37 |
38 | return filePath.lastPathComponent;
39 | }
40 |
41 | - (void)documentPickerViewController:(TODocumentPickerViewController *)documentPicker
42 | requestItemsForFilePath:(NSString *)filePath
43 | completionHandler:(void (^)(NSArray * _Nullable))completionHandler
44 | {
45 | NSString *fullFilePath = [self.documentsPath stringByAppendingPathComponent:filePath];
46 | NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:fullFilePath error:nil];
47 |
48 | NSMutableArray *items = [NSMutableArray array];
49 | for (NSString *file in files) {
50 | NSString *path = [fullFilePath stringByAppendingPathComponent:file];
51 |
52 | NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
53 | TODocumentPickerItem *item = [TODocumentPickerItem new];
54 | item.fileName = file;
55 | item.isFolder = (attributes.fileType == NSFileTypeDirectory);
56 | item.fileSize = item.isFolder ? 0 : (NSUInteger)attributes.fileSize;
57 | item.lastModifiedDate = attributes.fileModificationDate;
58 | [items addObject:item];
59 | }
60 |
61 | //Perform after a 1 second delay to simulate a web request
62 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
63 | completionHandler(items);
64 | });
65 | }
66 |
67 | #pragma mark - Class Setup / Management -
68 | - (void)createTestData
69 | {
70 | NSString *documentsFilePath = [self documentsPath];
71 |
72 | NSArray *folders = @[@"Apps",
73 | @"Archive",
74 | @"Books",
75 | @"Comics",
76 | @"Documents",
77 | @"Examples",
78 | @"Music",
79 | @"Photos",
80 | @"Pictures",
81 | @"Programs",
82 | @"Public",
83 | @"Save Files",
84 | @"Shared",
85 | @"Sites",
86 | @"Writing",
87 | @"Apps/iComics",
88 | @"Apps/Dropbox",
89 | @"Archive/Design Docs",
90 | @"Comics/Adventures in Space",
91 | @"Documents/Invoices",
92 | @"Examples/PSDs",];
93 |
94 | for (NSString *folder in folders) {
95 | NSString *filePath = [documentsFilePath stringByAppendingPathComponent:folder];
96 | [[NSFileManager defaultManager] createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil];
97 | }
98 |
99 | NSArray *textFiles = @[@"DesignPlan.txt",
100 | @"HelloWorld.txt",
101 | @"Blog Posts.txt",
102 | @"Test Document.txt",
103 | @"Upcoming Projects.txt"
104 | ];
105 |
106 | for (NSString *file in textFiles) {
107 | NSString *filePath = [documentsFilePath stringByAppendingPathComponent:file];
108 | [file writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
109 | }
110 | }
111 |
112 | - (NSString *)documentsPath
113 | {
114 | static NSString *sharedDocumentsDirectoryPath = nil;
115 | static dispatch_once_t token;
116 | dispatch_once(&token, ^{
117 | sharedDocumentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
118 | });
119 | return sharedDocumentsDirectoryPath;
120 | }
121 |
122 | @end
123 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Views/TODocumentPickerTableViewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerTableViewCell.m
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TODocumentPickerTableViewCell.h"
24 |
25 | static const CGFloat kTODocumentPickerTableViewCellImagePadding = 15.0f;
26 |
27 | @interface TODocumentPickerTableViewCell ()
28 |
29 | @property (nonatomic, assign) CGFloat textFontHeight;
30 | @property (nonatomic, assign) CGFloat detailTextFontHeight;
31 |
32 | @end
33 |
34 | @implementation TODocumentPickerTableViewCell
35 |
36 | @synthesize imageView = __imageView;
37 | @synthesize textLabel = __textLabel;
38 | @synthesize detailTextLabel = __detailTextLabel;
39 |
40 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
41 | {
42 | if (self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]) {
43 | [self commonInit];
44 | }
45 |
46 | return self;
47 | }
48 |
49 | - (void)commonInit
50 | {
51 | __textLabel = [[UILabel alloc] initWithFrame:CGRectZero];
52 | __textLabel.font = [UIFont systemFontOfSize:18.0f weight:UIFontWeightRegular];
53 | __textLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
54 | [self.contentView addSubview:__textLabel];
55 |
56 | __detailTextLabel = [[UILabel alloc] initWithFrame:CGRectZero];
57 | __detailTextLabel.font = [UIFont systemFontOfSize:13.0f weight:UIFontWeightRegular];
58 | __detailTextLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
59 | [self.contentView addSubview:__detailTextLabel];
60 |
61 | __imageView = [[UIImageView alloc] initWithImage:nil];
62 | __imageView.contentMode = UIViewContentModeScaleAspectFit;
63 | [self.contentView addSubview:__imageView];
64 |
65 | _textFontHeight = __textLabel.font.ascender - __textLabel.font.descender;
66 | _detailTextFontHeight = __detailTextLabel.font.ascender - __detailTextLabel.font.descender;
67 | }
68 |
69 | - (void)configureCellForStyle:(TODocumentPickerViewControllerStyle)style
70 | {
71 | BOOL darkMode = (style == TODocumentPickerViewControllerStyleDark);
72 |
73 | self.textLabel.textColor = darkMode ? [UIColor whiteColor] : [UIColor blackColor];
74 | self.detailTextLabel.textColor = darkMode ? [UIColor colorWithWhite:0.6f alpha:1.0f] : [UIColor colorWithWhite:0.5f alpha:1.0f];
75 | }
76 |
77 | - (void)layoutSubviews
78 | {
79 | [super layoutSubviews];
80 |
81 | CGRect contentFrame = CGRectZero;
82 | contentFrame.origin.x = self.layoutMargins.left;
83 | contentFrame.origin.y = self.layoutMargins.top;
84 | contentFrame.size.width = CGRectGetWidth(self.contentView.frame) - (self.layoutMargins.left + self.layoutMargins.right);
85 | contentFrame.size.height = CGRectGetHeight(self.contentView.frame) - (self.layoutMargins.top + self.layoutMargins.bottom);
86 |
87 | CGRect frame = CGRectZero;
88 | frame.origin.x = contentFrame.origin.x;
89 | frame.origin.y = contentFrame.origin.y;
90 | frame.size.height = contentFrame.size.height;
91 | frame.size.width = contentFrame.size.height;
92 | self.imageView.frame = CGRectIntegral(frame);
93 |
94 | CGFloat midY = CGRectGetMidY(contentFrame);
95 | frame.origin.x = CGRectGetMaxX(self.imageView.frame) + kTODocumentPickerTableViewCellImagePadding;
96 | frame.origin.y = ceilf(midY - self.textFontHeight);
97 | frame.size.width = contentFrame.size.width - frame.origin.x;
98 | frame.size.height = self.textFontHeight;
99 | self.textLabel.frame = CGRectIntegral(frame);
100 |
101 | frame.origin.x = CGRectGetMaxX(self.imageView.frame) + kTODocumentPickerTableViewCellImagePadding;
102 | frame.origin.y = ceilf(midY);
103 | frame.size.width = contentFrame.size.width - frame.origin.x;
104 | frame.size.height = self.detailTextFontHeight;
105 | self.detailTextLabel.frame = CGRectIntegral(frame);
106 | }
107 |
108 | @end
109 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Models/TODocumentPickerConstants.h:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerDefines.h
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import
24 | #import
25 |
26 | @class TODocumentPickerViewController;
27 | @class TODocumentPickerItem;
28 |
29 | /*
30 | The default content styles that the controller implements.
31 | */
32 | typedef NS_ENUM(NSInteger, TODocumentPickerViewControllerStyle) {
33 | TODocumentPickerViewControllerStyleLight,
34 | TODocumentPickerViewControllerStyleDark
35 | };
36 |
37 | /*
38 | The various sorting orders in which the picker can display the files.
39 | */
40 | typedef NS_ENUM(NSInteger, TODocumentPickerSortType) {
41 | TODocumentPickerSortTypeNameAscending=0,
42 | TODocumentPickerSortTypeNameDescending,
43 | TODocumentPickerSortTypeDateAscending,
44 | TODocumentPickerSortTypeDateDescending,
45 | TODocumentPickerSortTypeSizeAscending,
46 | TODocumentPickerSortTypeSizeDescending
47 | };
48 |
49 | //-------------------------------------------------------------------------
50 | // The data source protocol for objects charged with loading the file information to display
51 |
52 | @protocol TODocumentPickerViewControllerDataSource
53 |
54 | @required
55 |
56 | /**
57 | Called by the view controller when it wants to obtain a list of items for the folder at the end of the file path.
58 | Whether the data source subsequently hands off this request asynchronously or not, when completed, the data source
59 | must call the 'updateItemsForFilePath' block.
60 |
61 | @param filePath The file path with which to download file information
62 | */
63 | - (void)documentPickerViewController:(nonnull TODocumentPickerViewController *)documentPicker
64 | requestItemsForFilePath:(nonnull NSString *)filePath
65 | completionHandler:(nonnull void (^)(NSArray * _Nullable items))completionHandler;
66 |
67 | @optional
68 |
69 | /**
70 | If an asynchronous request for files is currently in progress and the representing view controller is canceled,
71 | (eg, if the user hits 'back' before it completes), this method will be called to give the request a chance to cancel.
72 |
73 | @param filePath The file path with which to cancel loading
74 | */
75 | - (void)documentPickerViewController:(nonnull TODocumentPickerViewController *)documentPicker cancelRequestForFilePath:(nonnull NSString *)filePath;
76 |
77 | /**
78 | After a table cell has been configured for display, this method allows for additional custom configuration of the cell after the fact.
79 | */
80 | - (void)documentPickerViewController:(nonnull TODocumentPickerViewController *)documentPicker configureCell:(nonnull UITableViewCell *)cell withItem:(nonnull TODocumentPickerItem *)item;
81 |
82 | /**
83 | The title that will appear in the navigation bar for the folder at this file path.
84 | If not implemented, defautl behaviour is to return simply the folder name from the filePath string
85 | */
86 | - (nullable NSString *)documentPickerViewController:(nonnull TODocumentPickerViewController *)documentPicker titleForFilePath:(nullable NSString *)filePath;
87 |
88 | /**
89 | If desired, this method may be overridden to provide a custom icon for items that represent folders in the file system.
90 | */
91 | - (nonnull UIImage *)documentPickerViewController:(nonnull TODocumentPickerViewController *)documentPicker folderIconForStyle:(TODocumentPickerViewControllerStyle)style;
92 |
93 | /**
94 | If desired, a custom icon can be provided by default for each type of file that the view controller is displaying. `extension` can be `nil` for unknown file types.
95 | */
96 | - (nonnull UIImage *)documentPickerViewController:(nonnull TODocumentPickerViewController *)documentPicker fileIconForExtension:(nullable NSString *)extension style:(TODocumentPickerViewControllerStyle)style;
97 |
98 | @end
99 |
100 | //-------------------------------------------------------------------------
101 |
102 | @protocol TODocumentPickerViewControllerDelegate
103 |
104 | @optional
105 |
106 | /* User either tapped a single item, or selected multiple items and hit 'choose' */
107 | - (void)documentPickerViewController:(nonnull TODocumentPickerViewController *)documentPicker didSelectItems:(nonnull NSArray *)items inFilePath:(nullable NSString *)filePath;
108 |
109 | @end
110 |
111 | //-------------------------------------------------------------------------
112 | // Size of the default file and folder icons
113 |
114 | static const CGSize kTODocumentPickerDefaultFolderIconSize = (CGSize){47,37};
115 | static const CGSize kTODocumentPickerDefaultFileIconSize = (CGSize){37,49};
116 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Models/TODocumentPickerItemManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerItemManager.m
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TODocumentPickerItemManager.h"
24 | #import "TODocumentPickerItem.h"
25 |
26 | @interface TODocumentPickerItemManager ()
27 |
28 | @property (nonatomic, strong) NSArray *sortedItems; /* A single list of sorted items */
29 | @property (nonatomic, strong) NSArray *filteredItems; /* A single list of filtered items */
30 |
31 | @end
32 |
33 | @implementation TODocumentPickerItemManager
34 |
35 | #pragma mark - Item Sorting -
36 | - (void)reloadItems
37 | {
38 | [self rebuildItems];
39 | }
40 |
41 | - (void)rebuildItems
42 | {
43 | /* Clear out all of the lists */
44 | self.sortedItems = nil;
45 | self.filteredItems = nil;
46 |
47 | //A search string overrides all other lists
48 | if (self.searchString.length > 0) {
49 | self.filteredItems = [self filteredItemsWithItems:self.items searchString:self.searchString];
50 | self.filteredItems = [self sortedItemsArrayWithArray:self.filteredItems];
51 | }
52 | else {
53 | self.sortedItems = [self sortedItemsArrayWithArray:self.items];
54 | }
55 |
56 | [self updateTableView];
57 | }
58 |
59 | - (void)updateTableView
60 | {
61 | dispatch_async(dispatch_get_main_queue(), ^{
62 | [self.tableView reloadData];
63 | if (self.contentReloadedHandler) {
64 | self.contentReloadedHandler();
65 | }
66 | });
67 | }
68 |
69 | - (NSArray *)sortedItemsArrayWithArray:(NSArray *)items
70 | {
71 | BOOL reverse = NO;
72 | NSString *sortKey = nil;
73 |
74 | switch (self.sortingType) {
75 | //File name needs to be explicitly sorted, so additional flags like
76 | //case-insensitive and numeric handling are properly set
77 | case TODocumentPickerSortTypeNameDescending: reverse = YES;
78 | case TODocumentPickerSortTypeNameAscending:
79 | items = [items sortedArrayUsingComparator:^NSComparisonResult(TODocumentPickerItem *first, TODocumentPickerItem *second) {
80 | return [first.fileName compare:second.fileName options:NSCaseInsensitiveSearch|NSNumericSearch|NSDiacriticInsensitiveSearch];
81 | }];
82 | if (reverse)
83 | items = [[items reverseObjectEnumerator] allObjects];
84 |
85 | return items;
86 |
87 | case TODocumentPickerSortTypeDateDescending: reverse = YES;
88 | case TODocumentPickerSortTypeDateAscending: sortKey = @"lastModifiedDate";
89 | break;
90 |
91 | case TODocumentPickerSortTypeSizeDescending: reverse = YES;
92 | case TODocumentPickerSortTypeSizeAscending: sortKey = @"fileSize";
93 | break;
94 | }
95 |
96 | //Sort the files
97 | NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:sortKey ascending:!reverse];
98 | items = [items sortedArrayUsingDescriptors:@[sortDescriptor]];
99 | return items;
100 | }
101 |
102 | - (NSArray *)filteredItemsWithItems:(NSArray *)items searchString:(NSString *)searchString
103 | {
104 | NSMutableArray *filteredItems = [NSMutableArray array];
105 | [items enumerateObjectsUsingBlock:^(TODocumentPickerItem *item, NSUInteger i, BOOL *stop) {
106 | if ([item.fileName rangeOfString:searchString options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch)].location != NSNotFound) {
107 | [filteredItems addObject:item];
108 | }
109 | }];
110 |
111 | return [NSArray arrayWithArray:filteredItems];
112 | }
113 |
114 | #pragma mark - Item Serving -
115 | - (TODocumentPickerItem *)itemForIndexPath:(NSIndexPath *)indexPath
116 | {
117 | if (self.filteredItems) {
118 | return self.filteredItems[indexPath.row];
119 | }
120 |
121 | if (self.sortedItems) {
122 | return self.sortedItems[indexPath.row];
123 | }
124 |
125 | return nil;
126 | }
127 |
128 | #pragma mark - Accessors -
129 |
130 | - (NSInteger)numberOfItems
131 | {
132 | if (self.filteredItems) {
133 | return self.filteredItems.count;
134 | }
135 |
136 | return self.items.count;
137 | }
138 |
139 | - (void)setItems:(NSArray *)items
140 | {
141 | if (items == _items) { return; }
142 |
143 | _items = items;
144 |
145 | if (self.tableView == nil) {
146 | return;
147 | }
148 |
149 | [self rebuildItems];
150 | }
151 |
152 | - (void)setSortingType:(TODocumentPickerSortType)sortingType
153 | {
154 | if (sortingType == _sortingType) { return; }
155 |
156 | _sortingType = sortingType;
157 |
158 | if (self.tableView == nil)
159 | return;
160 |
161 | [self rebuildItems];
162 | }
163 |
164 | - (void)setSearchString:(NSString *)searchString
165 | {
166 | if (searchString == _searchString) { return; }
167 |
168 | _searchString = searchString;
169 |
170 | if (self.tableView == nil)
171 | return;
172 |
173 | [self rebuildItems];
174 | }
175 |
176 | @end
177 |
--------------------------------------------------------------------------------
/TOSearchBar/TOSearchBar.h:
--------------------------------------------------------------------------------
1 | //
2 | // TOSearchBar.h
3 | //
4 | // Copyright 2015-2016 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import
24 |
25 | NS_ASSUME_NONNULL_BEGIN
26 |
27 | typedef NS_ENUM(NSInteger, TOSearchBarStyle) {
28 | TOSearchBarStyleLight = 0,
29 | TOSearchBarStyleDark = 1
30 | };
31 |
32 | @class TOSearchBar;
33 |
34 | @protocol TOSearchBarDelegate
35 |
36 | @optional
37 | /**
38 | Called before the text field gains focus, and can optionally reject doing so.
39 | */
40 | - (BOOL)searchBarShouldBeginEditing:(TOSearchBar *)searchBar;
41 |
42 | /**
43 | Called just after the text field gains focus.
44 | */
45 | - (void)searchBarDidBeginEditing:(TOSearchBar *)searchBar;
46 |
47 | /**
48 | Called before the text field loses focus, and can optionally reject doing so.
49 | */
50 | - (BOOL)searchBarShouldEndEditing:(TOSearchBar *)searchBar;
51 |
52 | /**
53 | Called just after the text field loses focus.
54 | */
55 | - (void)searchBarDidEndEditing:(TOSearchBar *)searchBar;
56 |
57 | /**
58 | Called just after each time the text in the text field is changed.
59 | */
60 | - (void)searchBar:(TOSearchBar *)searchBar textDidChange:(nullable NSString *)searchText;
61 |
62 | /**
63 | Called during text editing and can be used to optionally constrain the type of characters the field can accept.
64 | */
65 | - (BOOL)searchBar:(TOSearchBar *)searchBar shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;
66 |
67 | /**
68 | Called when the 'Clear' button is tapped to confirm it may go ahead.
69 | */
70 | - (void)searchBarClearButtonTapped:(TOSearchBar *)searchBar;
71 |
72 | /**
73 | Called after the user hits the 'Search' button. Can be used to set up actions for that event.
74 | */
75 | - (void)searchBarSearchButtonTapped:(TOSearchBar *)searchBar;
76 |
77 | @end
78 |
79 | IB_DESIGNABLE
80 | @interface TOSearchBar : UIControl
81 |
82 | /** The color style of this view. Setting this will set a series of default color values to each subview.
83 | The individual views can then further be custom configured if desired.
84 | */
85 | @property (nonatomic, assign) IBInspectable TOSearchBarStyle style;
86 |
87 | /** The delegate object for responding to events */
88 | @property (nullable, nonatomic, weak) id delegate;
89 |
90 | /** The search text, entered by the user */
91 | @property (nullable, nonatomic, copy) IBInspectable NSString *text;
92 |
93 | /** The contents of the placeholder text. Defaults to 'Search' */
94 | @property (null_resettable, nonatomic, copy) IBInspectable NSString *placeholderText;
95 |
96 | /** How many points on each side the search bar is inset */
97 | @property (nonatomic, assign) IBInspectable CGFloat horizontalInset;
98 |
99 | /** Whether the text field is currently presenting the keyboard */
100 | @property (nonatomic, assign) BOOL editing;
101 |
102 | /** Whether the text field contains any text or not */
103 | @property (nonatomic, readonly) BOOL hasSearchText;
104 |
105 | /** Shows a 'Cancel' button while editing */
106 | @property (nonatomic, assign) IBInspectable BOOL showsCancelButton;
107 |
108 | /** The tint color of the placeholder content (Both the icon and the text) */
109 | @property (nullable, nonatomic, strong) IBInspectable UIColor *placeholderTintColor UI_APPEARANCE_SELECTOR;
110 |
111 | /** The tint color of the rounded background rectangle */
112 | @property (null_resettable, nonatomic, strong) IBInspectable UIColor *barBackgroundTintColor UI_APPEARANCE_SELECTOR;
113 |
114 | /** The tint color of the background when the text field is tapped by the user */
115 | @property (nullable, nonatomic, strong) IBInspectable UIColor *selectedBarBackgroundTintColor UI_APPEARANCE_SELECTOR;
116 |
117 | /** The tint color of the background when the text field is currently, or has content */
118 | @property (nullable, nonatomic, strong) IBInspectable UIColor *highlightedBarBackgroundTintColor UI_APPEARANCE_SELECTOR;
119 |
120 | /** The main text field that the user will input search text */
121 | @property (nonatomic, readonly) UITextField *searchTextField;
122 |
123 | /** The auxiliary icon view to the left of the text field text */
124 | @property (nullable, nonatomic, readonly) UIImageView *iconView;
125 |
126 | /** The initial 'Search' text label displayed when the search field is empty. */
127 | @property (nonatomic, strong, readonly) UILabel *placeholderLabel;
128 |
129 | /** If specified, the 'Cancel' button that can cancel out of text input */
130 | @property (nullable, nonatomic, readonly) UIButton *cancelButton;
131 |
132 | /** The 'clear' button that will clear any text in the text field */
133 | @property (nonatomic, strong, readonly) UIButton *clearButton;
134 |
135 | /**
136 | Create a new instance of this class, specifying the frame and style.
137 |
138 | @param frame The entire frame for this view (Ideally of a height of 44 points)
139 | @param style The color style of the view (light or dark)
140 | */
141 | - (instancetype)initWithFrame:(CGRect)frame style:(TOSearchBarStyle)style;
142 |
143 | /**
144 | Create a new instance of this class, specifying the style.
145 |
146 | @param style The color style of the view (light or dark)
147 | */
148 | - (instancetype)initWithStyle:(TOSearchBarStyle)style;
149 |
150 | @end
151 |
152 | NS_ASSUME_NONNULL_END
153 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Views/TODocumentPickerHeaderView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerHeaderView.m
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TODocumentPickerHeaderView.h"
24 | #import "TOSearchBar.h"
25 |
26 | static const CGFloat kTODocumentPickerHeaderViewPadding = 5.0f;
27 | static const CGFloat kTODocumentPickerHeaderMaxWidth = 640.0f;
28 |
29 | @interface TODocumentPickerHeaderView ()
30 |
31 | @property (nonatomic, strong, readwrite) UIView *clippingView;
32 | @property (nonatomic, strong, readwrite) UIView *containerView;
33 | @property (nonatomic, strong, readwrite) TOSearchBar *searchBar;
34 | @property (nonatomic, strong, readwrite) TODocumentPickerSegmentedControl *sortControl;
35 |
36 | @end
37 |
38 | @implementation TODocumentPickerHeaderView
39 |
40 | - (instancetype)init
41 | {
42 | if (self = [super initWithFrame:(CGRect){0,0,320,44}]) {
43 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
44 | self.backgroundColor = [UIColor clearColor];
45 |
46 | [self setupViews];
47 | }
48 |
49 | return self;
50 | }
51 |
52 | - (void)setupViews
53 | {
54 | self.clippingView = [[UIView alloc] initWithFrame:self.bounds];
55 | self.clippingView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
56 | self.clippingView.clipsToBounds = YES;
57 | [self addSubview:self.clippingView];
58 |
59 | self.containerView = [[UIView alloc] initWithFrame:self.bounds];
60 | self.containerView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
61 | [self.clippingView addSubview:self.containerView];
62 |
63 | self.sortControl = [[TODocumentPickerSegmentedControl alloc] init];
64 | [self.containerView addSubview:self.sortControl];
65 | }
66 |
67 | - (void)sizeToFit
68 | {
69 | CGFloat height = self.layoutMargins.top + self.layoutMargins.bottom;
70 | height += self.sortControl.frame.size.height;
71 |
72 | if (self.searchBar) {
73 | height += kTODocumentPickerHeaderViewPadding;
74 | height += self.searchBar.frame.size.height;
75 | }
76 |
77 | CGRect frame = self.frame;
78 | frame.size.height = height;
79 | self.frame = frame;
80 | }
81 |
82 | - (void)didMoveToSuperview
83 | {
84 | [super didMoveToSuperview];
85 | self.backgroundColor = self.superview.backgroundColor;
86 | [self needsUpdateConstraints];
87 | }
88 |
89 | - (void)layoutSubviews
90 | {
91 | [super layoutSubviews];
92 |
93 | CGFloat originalHeight = self.bounds.size.height;
94 | CGFloat newHeight = originalHeight - self.clippingHeight;
95 | CGFloat clampedHeight = MAX(self.clippingHeight, 0);
96 |
97 | //clamp the height
98 | newHeight = MAX(0, newHeight);
99 | newHeight = MIN(originalHeight, newHeight);
100 |
101 | //Set the clipping view to lower
102 | CGRect frame = self.bounds;
103 | frame.size.height = newHeight;
104 | frame.origin.y = clampedHeight;
105 | self.clippingView.frame = frame;
106 |
107 | //At the same time, raise the container view to create the illusion
108 | //that the content isn't being lowered
109 | frame = self.bounds;
110 | frame.origin.y = -clampedHeight;
111 | self.containerView.frame = frame;
112 |
113 | CGFloat midY = floorf(originalHeight * 0.5f);
114 |
115 | // Layout the child views
116 | if (self.searchBar) {
117 | frame = self.searchBar.frame;
118 | frame.size.width = MIN(kTODocumentPickerHeaderMaxWidth, self.bounds.size.width - (self.layoutMargins.left + self.layoutMargins.right));
119 | frame.size.height = 44.0f;
120 | frame.origin.y = self.layoutMargins.top;
121 | frame.origin.x = CGRectGetMidX(self.bounds) - (frame.size.width * 0.5f);
122 | self.searchBar.frame = CGRectIntegral(frame);
123 | }
124 |
125 | frame = self.sortControl.frame;
126 | frame.size.width = MIN(kTODocumentPickerHeaderMaxWidth, self.bounds.size.width - (self.layoutMargins.left + self.layoutMargins.right));
127 | frame.origin.x = CGRectGetMidX(self.bounds) - (frame.size.width * 0.5f);
128 | frame.size.height = 33.0f;
129 | if (self.searchBar) {
130 | frame.origin.y = CGRectGetMaxY(self.searchBar.frame) + kTODocumentPickerHeaderViewPadding;
131 | }
132 | else {
133 | frame.origin.y = midY - (frame.size.height * 0.5f);
134 | }
135 |
136 | self.sortControl.frame = CGRectIntegral(frame);
137 | }
138 |
139 | - (void)layoutMarginsDidChange
140 | {
141 | [super layoutMarginsDidChange];
142 | [self setNeedsLayout];
143 | }
144 |
145 | #pragma mark - Search Bar Delegate -
146 | - (void)searchBar:(TOSearchBar *)searchBar textDidChange:(NSString *)searchText
147 | {
148 | if (self.searchTextChangedHandler) {
149 | self.searchTextChangedHandler(searchText);
150 | }
151 | }
152 |
153 | - (void)searchBarSearchButtonTapped:(TOSearchBar *)searchBar
154 | {
155 | [self.searchBar resignFirstResponder];
156 | }
157 |
158 | #pragma mark - External Interactions -
159 | - (void)dismissKeyboard
160 | {
161 | if ([self.searchBar isFirstResponder])
162 | [self.searchBar resignFirstResponder];
163 | }
164 |
165 | - (void)setClippingHeight:(CGFloat)clippingHeight
166 | {
167 | //Don't bother doing any layout if the we're beyond the clipping threshold
168 | clippingHeight = MAX(0, clippingHeight);
169 | if (_clippingHeight == clippingHeight) {
170 | return;
171 | }
172 |
173 | _clippingHeight = clippingHeight;
174 |
175 | if (clippingHeight > self.frame.size.height) {
176 | self.hidden = YES;
177 | return;
178 | }
179 |
180 | self.hidden = NO;
181 | [self setNeedsLayout];
182 | }
183 |
184 | - (void)setShowsSearchBar:(BOOL)showsSearchBar
185 | {
186 | if (_showsSearchBar == showsSearchBar) {
187 | return;
188 | }
189 |
190 | _showsSearchBar = showsSearchBar;
191 |
192 | if (_showsSearchBar) {
193 | self.searchBar = [[TOSearchBar alloc] initWithFrame:(CGRect){0,0,320,44}];
194 | self.searchBar.delegate = self;
195 | self.searchBar.placeholderText = @"Search";
196 | [self.containerView addSubview:self.searchBar];
197 | }
198 | else {
199 | [self.searchBar removeFromSuperview];
200 | self.searchBar = nil;
201 | }
202 |
203 | [self sizeToFit];
204 | }
205 |
206 | @end
207 |
--------------------------------------------------------------------------------
/TODocumentPickerViewController/Views/TODocumentPickerSegmentedControl.m:
--------------------------------------------------------------------------------
1 | //
2 | // TODocumentPickerSegmentedControl.m
3 | //
4 | // Copyright 2015-2018 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TODocumentPickerSegmentedControl.h"
24 |
25 | #define ARROW_WIDTH 7
26 | #define ARROW_HEIGHT 4
27 |
28 | typedef NS_ENUM(NSInteger, TODocumentPickerSegmentedControlImage) {
29 | TODocumentPickerSegmentedControlImageDefault=0,
30 | TODocumentPickerSegmentedControlImageDescending=1,
31 | TODocumentPickerSegmentedControlImageAscending=2
32 | };
33 |
34 | @interface TODocumentPickerSegmentedControl ()
35 |
36 | @property (nonatomic, readonly) NSArray *segmentedControlImages;
37 |
38 | - (void)segmentedControlTapped:(id)sender;
39 | - (void)updateItemsForCurrentSortType;
40 |
41 | /* Generates an 'off', 'descending' and 'ascending' images of the supplied text */
42 | + (NSArray *)TO_documentPickerSegmentedControlItemsForTitle:(NSString *)title;
43 | + (void)drawArrowAtPoint:(CGPoint)point ascending:(BOOL)ascending;
44 |
45 | @end
46 |
47 | @implementation TODocumentPickerSegmentedControl
48 |
49 | #pragma mark - Class Creation -
50 |
51 | - (instancetype)init
52 | {
53 | NSArray *items = @[self.segmentedControlImages[0][0], self.segmentedControlImages[1][0], self.segmentedControlImages[2][0]];
54 |
55 | if (self = [super initWithItems:items]) {
56 | [self addTarget:self action:@selector(segmentedControlTapped:) forControlEvents:UIControlEventValueChanged];
57 | }
58 |
59 | return self;
60 | }
61 |
62 | - (void)didMoveToSuperview
63 | {
64 | [super didMoveToSuperview];
65 |
66 | self.selectedSegmentIndex = 0;
67 | [self updateItemsForCurrentSortType];
68 | }
69 |
70 | #pragma mark - Interaction Detection -
71 |
72 | //Detects when the same segment is tapped twice
73 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
74 | {
75 | NSInteger oldValue = self.selectedSegmentIndex;
76 | [super touchesEnded:touches withEvent:event];
77 |
78 | if (oldValue == self.selectedSegmentIndex) {
79 | switch (oldValue) {
80 | case 0:
81 | if (self.sortingType == TODocumentPickerSortTypeNameAscending) {
82 | self.sortingType = TODocumentPickerSortTypeNameDescending;
83 | }
84 | else {
85 | self.sortingType = TODocumentPickerSortTypeNameAscending;
86 | }
87 | break;
88 | case 1:
89 | if (self.sortingType == TODocumentPickerSortTypeSizeAscending) {
90 | self.sortingType = TODocumentPickerSortTypeSizeDescending;
91 | }
92 | else {
93 | self.sortingType = TODocumentPickerSortTypeSizeAscending;
94 | }
95 | break;
96 | case 2:
97 | if (self.sortingType == TODocumentPickerSortTypeDateAscending) {
98 | self.sortingType = TODocumentPickerSortTypeDateDescending;
99 | }
100 | else {
101 | self.sortingType = TODocumentPickerSortTypeDateAscending;
102 | }
103 | break;
104 | }
105 |
106 | [self updateItemsForCurrentSortType];
107 |
108 | if (self.sortTypeChangedHandler)
109 | self.sortTypeChangedHandler();
110 | }
111 | }
112 |
113 | - (void)segmentedControlTapped:(id)sender
114 | {
115 | switch (self.selectedSegmentIndex) {
116 | case 0: //Name
117 | self.sortingType = TODocumentPickerSortTypeNameAscending;
118 | break;
119 | case 1: //Size
120 | self.sortingType = TODocumentPickerSortTypeSizeAscending;
121 | break;
122 | case 2: //Date
123 | self.sortingType = TODocumentPickerSortTypeDateAscending;
124 | break;
125 | }
126 |
127 | [self updateItemsForCurrentSortType];
128 |
129 | if (self.sortTypeChangedHandler)
130 | self.sortTypeChangedHandler();
131 | }
132 |
133 | #pragma mark - Update Items -
134 | - (void)updateItemsForCurrentSortType
135 | {
136 | //Reset all of the items
137 | for (NSInteger i = 0; i < self.segmentedControlImages.count; i++)
138 | [self setImage:[self.segmentedControlImages[i] firstObject] forSegmentAtIndex:i];
139 |
140 | switch (self.sortingType) {
141 | case TODocumentPickerSortTypeNameAscending: [self setImage:self.segmentedControlImages[0][TODocumentPickerSegmentedControlImageAscending] forSegmentAtIndex:0]; break;
142 | case TODocumentPickerSortTypeNameDescending: [self setImage:self.segmentedControlImages[0][TODocumentPickerSegmentedControlImageDescending] forSegmentAtIndex:0]; break;
143 | case TODocumentPickerSortTypeSizeAscending: [self setImage:self.segmentedControlImages[1][TODocumentPickerSegmentedControlImageAscending] forSegmentAtIndex:1]; break;
144 | case TODocumentPickerSortTypeSizeDescending: [self setImage:self.segmentedControlImages[1][TODocumentPickerSegmentedControlImageDescending] forSegmentAtIndex:1]; break;
145 | case TODocumentPickerSortTypeDateAscending: [self setImage:self.segmentedControlImages[2][TODocumentPickerSegmentedControlImageAscending] forSegmentAtIndex:2]; break;
146 | case TODocumentPickerSortTypeDateDescending: [self setImage:self.segmentedControlImages[2][TODocumentPickerSegmentedControlImageDescending] forSegmentAtIndex:2]; break;
147 | }
148 | }
149 |
150 | #pragma mark - Accessors -
151 | - (NSArray *)segmentedControlImages
152 | {
153 | static NSArray *_segmentedControlImages;
154 | static dispatch_once_t onceToken;
155 | dispatch_once(&onceToken, ^{
156 | NSMutableArray *segmentedControlImages = [NSMutableArray array];
157 | [segmentedControlImages addObject:[TODocumentPickerSegmentedControl TO_documentPickerSegmentedControlItemsForTitle:NSLocalizedString(@"Name", @"Segmented Control Name")]];
158 | [segmentedControlImages addObject:[TODocumentPickerSegmentedControl TO_documentPickerSegmentedControlItemsForTitle:NSLocalizedString(@"Size", @"Segmented Control Size")]];
159 | [segmentedControlImages addObject:[TODocumentPickerSegmentedControl TO_documentPickerSegmentedControlItemsForTitle:NSLocalizedString(@"Date", @"Segmented Control Date")]];
160 | _segmentedControlImages = [NSArray arrayWithArray:segmentedControlImages];
161 | });
162 |
163 | return _segmentedControlImages;
164 | }
165 |
166 | #pragma mark - Segmented Control Image generation -
167 |
168 | //Annoyingly, [drawInRect:withAttributes] has no easy 'center' option. Sticking to the old way for now
169 | #pragma GCC diagnostic push
170 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
171 |
172 | + (NSArray *)TO_documentPickerSegmentedControlItemsForTitle:(NSString *)title
173 | {
174 | UIFont *font = [UIFont systemFontOfSize:14.0f];
175 | NSMutableArray *images = [NSMutableArray array];
176 |
177 | //Work out the size of all of the images
178 | CGSize imageSize = [title sizeWithAttributes:@{NSFontAttributeName:font}];
179 | imageSize.width += (ARROW_WIDTH + 3) * 2; //Add enough padding on either side so the content is still center
180 |
181 | CGRect frame = (CGRect){CGPointZero, imageSize};
182 |
183 | for (NSInteger i = 0; i <= 2; i++) {
184 | UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0f);
185 | {
186 | //draw the text
187 | [[UIColor blackColor] set];
188 | [title drawInRect:frame withFont:font lineBreakMode:NSLineBreakByClipping alignment:NSTextAlignmentCenter];
189 |
190 | //depending on the iteration, draw the arrow
191 | if (i > 0) {
192 | CGPoint arrowPoint = CGPointZero;
193 | arrowPoint.x = CGRectGetMaxX(frame) - ARROW_WIDTH;
194 | arrowPoint.y = ceil((CGRectGetHeight(frame) - ARROW_HEIGHT) * 0.5f);
195 |
196 | [TODocumentPickerSegmentedControl drawArrowAtPoint:arrowPoint ascending:(i==2)];
197 | }
198 |
199 | [images addObject:UIGraphicsGetImageFromCurrentImageContext()];
200 | }
201 | UIGraphicsEndImageContext();
202 | }
203 |
204 | return images;
205 | }
206 |
207 | #pragma GCC diagnostic pop
208 |
209 | + (void)drawArrowAtPoint:(CGPoint)point ascending:(BOOL)ascending
210 | {
211 | CGRect frame = {(CGPoint)point, {ARROW_WIDTH,ARROW_HEIGHT}};
212 |
213 | UIBezierPath* arrowPath = UIBezierPath.bezierPath;
214 | if (!ascending) {
215 | [arrowPath moveToPoint: CGPointMake(CGRectGetMinX(frame), CGRectGetMinY(frame))];
216 | [arrowPath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 7, CGRectGetMinY(frame))];
217 | [arrowPath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 3.5, CGRectGetMinY(frame) + 4)];
218 | [arrowPath addLineToPoint: CGPointMake(CGRectGetMinX(frame), CGRectGetMinY(frame))];
219 | }
220 | else {
221 | [arrowPath moveToPoint: CGPointMake(CGRectGetMinX(frame), CGRectGetMaxY(frame))];
222 | [arrowPath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 7, CGRectGetMaxY(frame))];
223 | [arrowPath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 3.5, CGRectGetMinY(frame))];
224 | [arrowPath addLineToPoint: CGPointMake(CGRectGetMinX(frame), CGRectGetMaxY(frame))];
225 | }
226 |
227 | [arrowPath closePath];
228 | [[UIColor blackColor] setFill];
229 | [arrowPath fill];
230 | }
231 |
232 | @end
233 |
--------------------------------------------------------------------------------
/TOSearchBar/TOSearchBar+Assets.m:
--------------------------------------------------------------------------------
1 | //
2 | // TOSearchBar+ImageAssets.m
3 | //
4 | // Copyright 2015-2016 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TOSearchBar+Assets.h"
24 | @import UIKit;
25 |
26 | /* A statically held map table that holds one instance of every image generated. */
27 | /* Once all images are released, the map table is also cleaned up */
28 | static NSMapTable *imageTable = nil;
29 |
30 | static NSString * const kSharedBackgroundKey = @"SharedSearchBackground";
31 | static NSString * const kSharedSearchIconKey = @"SharedSearchIcon";
32 | static NSString * const kSharedClearIconKey = @"SharedClearIcon";
33 |
34 | @implementation TOSearchBar (ImageAssets)
35 |
36 | + (void)setSharedImage:(UIImage *)image forKey:(NSString *)key
37 | {
38 | if (imageTable == nil) {
39 | imageTable = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsWeakMemory capacity:3];
40 | }
41 |
42 | [imageTable setObject:image forKey:key];
43 | }
44 |
45 | + (void)cleanUpSharedAssets
46 | {
47 | if (imageTable.count > 0) {
48 | return;
49 | }
50 |
51 | imageTable = nil;
52 | }
53 |
54 | + (UIImage *)sharedSearchBarBackground
55 | {
56 | UIImage *image = [imageTable objectForKey:kSharedBackgroundKey];
57 | if (image) {
58 | return image;
59 | }
60 |
61 | CGFloat cornerRadius = 4.5f;
62 |
63 | if (@available(iOS 11.0, *)) {
64 | cornerRadius = 15.0f;
65 | }
66 |
67 | CGRect frame = (CGRect){0, 0, (cornerRadius*2.0f) + 1, (cornerRadius*2.0f) + 1};
68 | UIGraphicsBeginImageContextWithOptions(frame.size, NO, 0.0f);
69 | UIBezierPath *path = [TOSearchBar bezierPathWithIOS7RoundedRect:frame cornerRadius:cornerRadius];
70 | [[UIColor blackColor] set];
71 | [path fill];
72 | image = UIGraphicsGetImageFromCurrentImageContext();
73 | UIGraphicsEndImageContext();
74 |
75 | image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
76 | image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(cornerRadius, cornerRadius, cornerRadius, cornerRadius)];
77 |
78 | [TOSearchBar setSharedImage:image forKey:kSharedBackgroundKey];
79 |
80 | return image;
81 | }
82 |
83 | + (UIImage *)sharedSearchIcon
84 | {
85 | UIImage *image = [imageTable objectForKey:kSharedSearchIconKey];
86 | if (image) {
87 | return image;
88 | }
89 |
90 | if (@available(iOS 11.0, *)) {
91 | UIGraphicsBeginImageContextWithOptions((CGSize){14,14}, NO, 0.0f);
92 |
93 | CGContextRef context = UIGraphicsGetCurrentContext();
94 |
95 | //// Circle Drawing
96 | UIBezierPath* circlePath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(0.55, 0.55, 9.95, 9.95)];
97 | CGContextSaveGState(context);
98 | [circlePath addClip];
99 | CGContextTranslateCTM(context, 1, 86);
100 | CGContextScaleCTM(context, 1, -1);
101 | CGContextTranslateCTM(context, 0, -image.size.height);
102 | CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage);
103 | CGContextRestoreGState(context);
104 | [UIColor.blackColor setStroke];
105 | circlePath.lineWidth = 1.1;
106 | [circlePath stroke];
107 |
108 |
109 | //// Handle Drawing
110 | UIBezierPath* handlePath = [UIBezierPath bezierPath];
111 | [handlePath moveToPoint: CGPointMake(13.21, 12.2)];
112 | [handlePath addLineToPoint: CGPointMake(13.21, 12.2)];
113 | [handlePath addCurveToPoint: CGPointMake(13.74, 12.73) controlPoint1: CGPointMake(13.74, 12.73) controlPoint2: CGPointMake(13.74, 12.73)];
114 | [handlePath addLineToPoint: CGPointMake(13.74, 12.73)];
115 | [handlePath addCurveToPoint: CGPointMake(13.74, 13.78) controlPoint1: CGPointMake(14.03, 13.02) controlPoint2: CGPointMake(14.03, 13.49)];
116 | [handlePath addLineToPoint: CGPointMake(13.74, 13.78)];
117 | [handlePath addLineToPoint: CGPointMake(13.74, 13.78)];
118 | [handlePath addLineToPoint: CGPointMake(13.74, 13.78)];
119 | [handlePath addLineToPoint: CGPointMake(13.74, 13.78)];
120 | [handlePath addLineToPoint: CGPointMake(13.74, 13.78)];
121 | [handlePath addCurveToPoint: CGPointMake(12.68, 13.78) controlPoint1: CGPointMake(13.45, 14.07) controlPoint2: CGPointMake(12.97, 14.07)];
122 | [handlePath addLineToPoint: CGPointMake(12.68, 13.78)];
123 | [handlePath addLineToPoint: CGPointMake(12.68, 13.78)];
124 | [handlePath addLineToPoint: CGPointMake(8.33, 9.46)];
125 | [handlePath addLineToPoint: CGPointMake(9.38, 8.41)];
126 | [handlePath addLineToPoint: CGPointMake(13.21, 12.2)];
127 | [handlePath closePath];
128 | [UIColor.blackColor setFill];
129 | [handlePath fill];
130 |
131 | image = UIGraphicsGetImageFromCurrentImageContext();
132 | UIGraphicsEndImageContext();
133 | }
134 | else { // iOS 10 and down
135 | UIGraphicsBeginImageContextWithOptions((CGSize){15,15}, NO, 0.0f);
136 |
137 | //// General Declarations
138 | CGContextRef context = UIGraphicsGetCurrentContext();
139 |
140 | //// Oval Drawing
141 | UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(1, 1, 10, 10)];
142 | [UIColor.grayColor setStroke];
143 | ovalPath.lineWidth = 1.3;
144 | [ovalPath stroke];
145 |
146 |
147 | //// Rectangle Drawing
148 | CGContextSaveGState(context);
149 | CGContextTranslateCTM(context, 8.8, 9.87);
150 | CGContextRotateCTM(context, -45 * M_PI / 180);
151 |
152 | UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(0, 0, 1.4, 6.55)];
153 | [UIColor.grayColor setFill];
154 | [rectanglePath fill];
155 |
156 | CGContextRestoreGState(context);
157 |
158 | image = UIGraphicsGetImageFromCurrentImageContext();
159 | UIGraphicsEndImageContext();
160 | }
161 |
162 |
163 | image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
164 |
165 | return image;
166 | }
167 |
168 | + (UIImage *)sharedClearIcon
169 | {
170 | UIImage *image = [imageTable objectForKey:kSharedClearIconKey];
171 | if (image) {
172 | return image;
173 | }
174 |
175 | if (@available(iOS 11.0, *)) {
176 | UIGraphicsBeginImageContextWithOptions((CGSize){16,16}, NO, 0.0f);
177 |
178 | //// Bezier 2 Drawing
179 | UIBezierPath* deletePath = [UIBezierPath bezierPath];
180 | [deletePath moveToPoint: CGPointMake(10.96, 4.31)];
181 | [deletePath addCurveToPoint: CGPointMake(10.47, 4.53) controlPoint1: CGPointMake(10.76, 4.33) controlPoint2: CGPointMake(10.6, 4.4)];
182 | [deletePath addCurveToPoint: CGPointMake(8, 7) controlPoint1: CGPointMake(10.47, 4.53) controlPoint2: CGPointMake(10.47, 4.53)];
183 | [deletePath addCurveToPoint: CGPointMake(5.53, 4.53) controlPoint1: CGPointMake(7.15, 6.15) controlPoint2: CGPointMake(5.53, 4.53)];
184 | [deletePath addCurveToPoint: CGPointMake(4.89, 4.32) controlPoint1: CGPointMake(5.35, 4.35) controlPoint2: CGPointMake(5.11, 4.28)];
185 | [deletePath addCurveToPoint: CGPointMake(4.47, 4.53) controlPoint1: CGPointMake(4.73, 4.34) controlPoint2: CGPointMake(4.59, 4.41)];
186 | [deletePath addCurveToPoint: CGPointMake(4.47, 5.59) controlPoint1: CGPointMake(4.18, 4.82) controlPoint2: CGPointMake(4.18, 5.3)];
187 | [deletePath addCurveToPoint: CGPointMake(6.94, 8.06) controlPoint1: CGPointMake(4.47, 5.59) controlPoint2: CGPointMake(4.47, 5.59)];
188 | [deletePath addCurveToPoint: CGPointMake(5.85, 9.15) controlPoint1: CGPointMake(6.63, 8.37) controlPoint2: CGPointMake(6.23, 8.77)];
189 | [deletePath addCurveToPoint: CGPointMake(4.53, 10.47) controlPoint1: CGPointMake(5.17, 9.83) controlPoint2: CGPointMake(4.53, 10.47)];
190 | [deletePath addCurveToPoint: CGPointMake(4.53, 11.53) controlPoint1: CGPointMake(4.24, 10.76) controlPoint2: CGPointMake(4.24, 11.24)];
191 | [deletePath addCurveToPoint: CGPointMake(5.59, 11.53) controlPoint1: CGPointMake(4.82, 11.82) controlPoint2: CGPointMake(5.3, 11.82)];
192 | [deletePath addCurveToPoint: CGPointMake(8, 9.12) controlPoint1: CGPointMake(5.59, 11.53) controlPoint2: CGPointMake(5.59, 11.53)];
193 | [deletePath addCurveToPoint: CGPointMake(8.43, 9.55) controlPoint1: CGPointMake(8.13, 9.25) controlPoint2: CGPointMake(8.28, 9.4)];
194 | [deletePath addCurveToPoint: CGPointMake(10.41, 11.53) controlPoint1: CGPointMake(9.29, 10.41) controlPoint2: CGPointMake(10.41, 11.53)];
195 | [deletePath addCurveToPoint: CGPointMake(11.47, 11.53) controlPoint1: CGPointMake(10.7, 11.82) controlPoint2: CGPointMake(11.18, 11.82)];
196 | [deletePath addCurveToPoint: CGPointMake(11.47, 10.47) controlPoint1: CGPointMake(11.76, 11.24) controlPoint2: CGPointMake(11.76, 10.76)];
197 | [deletePath addCurveToPoint: CGPointMake(9.06, 8.06) controlPoint1: CGPointMake(11.47, 10.47) controlPoint2: CGPointMake(11.47, 10.47)];
198 | [deletePath addCurveToPoint: CGPointMake(11.53, 5.59) controlPoint1: CGPointMake(9.91, 7.21) controlPoint2: CGPointMake(11.53, 5.59)];
199 | [deletePath addCurveToPoint: CGPointMake(11.53, 4.53) controlPoint1: CGPointMake(11.81, 5.31) controlPoint2: CGPointMake(11.82, 4.85)];
200 | [deletePath addCurveToPoint: CGPointMake(10.92, 4.31) controlPoint1: CGPointMake(11.34, 4.36) controlPoint2: CGPointMake(11.13, 4.29)];
201 | [deletePath addLineToPoint: CGPointMake(10.96, 4.31)];
202 | [deletePath closePath];
203 | [deletePath moveToPoint: CGPointMake(16, 8)];
204 | [deletePath addCurveToPoint: CGPointMake(8, 16) controlPoint1: CGPointMake(16, 12.42) controlPoint2: CGPointMake(12.42, 16)];
205 | [deletePath addCurveToPoint: CGPointMake(0, 8) controlPoint1: CGPointMake(3.58, 16) controlPoint2: CGPointMake(0, 12.42)];
206 | [deletePath addCurveToPoint: CGPointMake(3.13, 1.65) controlPoint1: CGPointMake(0, 5.41) controlPoint2: CGPointMake(1.23, 3.11)];
207 | [deletePath addCurveToPoint: CGPointMake(8, 0) controlPoint1: CGPointMake(4.48, 0.61) controlPoint2: CGPointMake(6.17, 0)];
208 | [deletePath addCurveToPoint: CGPointMake(16, 8) controlPoint1: CGPointMake(12.42, 0) controlPoint2: CGPointMake(16, 3.58)];
209 | [deletePath closePath];
210 | [UIColor.blackColor setFill];
211 | [deletePath fill];
212 |
213 | image = UIGraphicsGetImageFromCurrentImageContext();
214 | UIGraphicsEndImageContext();
215 |
216 | }
217 | else {
218 | UIGraphicsBeginImageContextWithOptions((CGSize){14,14}, NO, 0.0f);
219 |
220 | //// ClearIcon Drawing
221 | UIBezierPath* clearIconPath = [UIBezierPath bezierPath];
222 | [clearIconPath moveToPoint: CGPointMake(4.13, 4.12)];
223 | [clearIconPath addCurveToPoint: CGPointMake(4.08, 5.23) controlPoint1: CGPointMake(3.79, 4.46) controlPoint2: CGPointMake(3.79, 4.94)];
224 | [clearIconPath addCurveToPoint: CGPointMake(5.85, 7) controlPoint1: CGPointMake(4.08, 5.23) controlPoint2: CGPointMake(4.08, 5.23)];
225 | [clearIconPath addLineToPoint: CGPointMake(4.08, 8.77)];
226 | [clearIconPath addCurveToPoint: CGPointMake(4.08, 9.83) controlPoint1: CGPointMake(3.79, 9.06) controlPoint2: CGPointMake(3.79, 9.54)];
227 | [clearIconPath addCurveToPoint: CGPointMake(5.14, 9.83) controlPoint1: CGPointMake(4.37, 10.12) controlPoint2: CGPointMake(4.85, 10.12)];
228 | [clearIconPath addLineToPoint: CGPointMake(6.91, 8.06)];
229 | [clearIconPath addCurveToPoint: CGPointMake(8.67, 9.83) controlPoint1: CGPointMake(7.59, 8.74) controlPoint2: CGPointMake(8.67, 9.83)];
230 | [clearIconPath addCurveToPoint: CGPointMake(9.74, 9.83) controlPoint1: CGPointMake(8.97, 10.12) controlPoint2: CGPointMake(9.44, 10.12)];
231 | [clearIconPath addCurveToPoint: CGPointMake(9.74, 8.77) controlPoint1: CGPointMake(10.03, 9.54) controlPoint2: CGPointMake(10.03, 9.06)];
232 | [clearIconPath addCurveToPoint: CGPointMake(7.97, 7) controlPoint1: CGPointMake(9.74, 8.77) controlPoint2: CGPointMake(9.74, 8.77)];
233 | [clearIconPath addLineToPoint: CGPointMake(9.74, 5.23)];
234 | [clearIconPath addCurveToPoint: CGPointMake(9.74, 4.17) controlPoint1: CGPointMake(10.03, 4.94) controlPoint2: CGPointMake(10.03, 4.46)];
235 | [clearIconPath addCurveToPoint: CGPointMake(8.67, 4.17) controlPoint1: CGPointMake(9.44, 3.88) controlPoint2: CGPointMake(8.97, 3.88)];
236 | [clearIconPath addLineToPoint: CGPointMake(6.91, 5.94)];
237 | [clearIconPath addCurveToPoint: CGPointMake(5.14, 4.17) controlPoint1: CGPointMake(6.23, 5.26) controlPoint2: CGPointMake(5.14, 4.17)];
238 | [clearIconPath addCurveToPoint: CGPointMake(4.11, 4.14) controlPoint1: CGPointMake(4.85, 3.88) controlPoint2: CGPointMake(4.37, 3.88)];
239 | [clearIconPath addLineToPoint: CGPointMake(4.13, 4.12)];
240 | [clearIconPath closePath];
241 | [clearIconPath moveToPoint: CGPointMake(14, 7)];
242 | [clearIconPath addCurveToPoint: CGPointMake(7, 14) controlPoint1: CGPointMake(14, 10.87) controlPoint2: CGPointMake(10.87, 14)];
243 | [clearIconPath addCurveToPoint: CGPointMake(0, 7) controlPoint1: CGPointMake(3.13, 14) controlPoint2: CGPointMake(0, 10.87)];
244 | [clearIconPath addCurveToPoint: CGPointMake(2.74, 1.44) controlPoint1: CGPointMake(0, 4.74) controlPoint2: CGPointMake(1.08, 2.72)];
245 | [clearIconPath addCurveToPoint: CGPointMake(7, 0) controlPoint1: CGPointMake(3.92, 0.54) controlPoint2: CGPointMake(5.4, -0)];
246 | [clearIconPath addCurveToPoint: CGPointMake(14, 7) controlPoint1: CGPointMake(10.87, 0) controlPoint2: CGPointMake(14, 3.13)];
247 | [clearIconPath closePath];
248 | [UIColor.grayColor setFill];
249 | [clearIconPath fill];
250 |
251 | image = UIGraphicsGetImageFromCurrentImageContext();
252 | UIGraphicsEndImageContext();
253 | }
254 |
255 | image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
256 |
257 | return image;
258 | }
259 |
260 | // https://www.paintcodeapp.com/blogpost/code-for-ios-7-rounded-rectangles
261 |
262 | #define TOP_LEFT(X, Y)\
263 | CGPointMake(rect.origin.x + X * limitedRadius,\
264 | rect.origin.y + Y * limitedRadius)
265 | #define TOP_RIGHT(X, Y)\
266 | CGPointMake(rect.origin.x + rect.size.width - X * limitedRadius,\
267 | rect.origin.y + Y * limitedRadius)
268 | #define BOTTOM_RIGHT(X, Y)\
269 | CGPointMake(rect.origin.x + rect.size.width - X * limitedRadius,\
270 | rect.origin.y + rect.size.height - Y * limitedRadius)
271 | #define BOTTOM_LEFT(X, Y)\
272 | CGPointMake(rect.origin.x + X * limitedRadius,\
273 | rect.origin.y + rect.size.height - Y * limitedRadius)
274 |
275 |
276 | + (UIBezierPath*)bezierPathWithIOS7RoundedRect: (CGRect)rect
277 | cornerRadius: (CGFloat)radius
278 | {
279 | UIBezierPath* path = UIBezierPath.bezierPath;
280 | CGFloat limit = MIN(rect.size.width, rect.size.height) / 2 / 1.52866483;
281 | CGFloat limitedRadius = MIN(radius, limit);
282 |
283 | [path moveToPoint: TOP_LEFT(1.52866483, 0.00000000)];
284 | [path addLineToPoint: TOP_RIGHT(1.52866471, 0.00000000)];
285 | [path addCurveToPoint: TOP_RIGHT(0.66993427, 0.06549600)
286 | controlPoint1: TOP_RIGHT(1.08849323, 0.00000000)
287 | controlPoint2: TOP_RIGHT(0.86840689, 0.00000000)];
288 | [path addLineToPoint: TOP_RIGHT(0.63149399, 0.07491100)];
289 | [path addCurveToPoint: TOP_RIGHT(0.07491176, 0.63149399)
290 | controlPoint1: TOP_RIGHT(0.37282392, 0.16905899)
291 | controlPoint2: TOP_RIGHT(0.16906013, 0.37282401)];
292 | [path addCurveToPoint: TOP_RIGHT(0.00000000, 1.52866483)
293 | controlPoint1: TOP_RIGHT(0.00000000, 0.86840701)
294 | controlPoint2: TOP_RIGHT(0.00000000, 1.08849299)];
295 | [path addLineToPoint: BOTTOM_RIGHT(0.00000000, 1.52866471)];
296 | [path addCurveToPoint: BOTTOM_RIGHT(0.06549569, 0.66993493)
297 | controlPoint1: BOTTOM_RIGHT(0.00000000, 1.08849323)
298 | controlPoint2: BOTTOM_RIGHT(0.00000000, 0.86840689)];
299 | [path addLineToPoint: BOTTOM_RIGHT(0.07491111, 0.63149399)];
300 | [path addCurveToPoint: BOTTOM_RIGHT(0.63149399, 0.07491111)
301 | controlPoint1: BOTTOM_RIGHT(0.16905883, 0.37282392)
302 | controlPoint2: BOTTOM_RIGHT(0.37282392, 0.16905883)];
303 | [path addCurveToPoint: BOTTOM_RIGHT(1.52866471, 0.00000000)
304 | controlPoint1: BOTTOM_RIGHT(0.86840689, 0.00000000)
305 | controlPoint2: BOTTOM_RIGHT(1.08849323, 0.00000000)];
306 | [path addLineToPoint: BOTTOM_LEFT(1.52866483, 0.00000000)];
307 | [path addCurveToPoint: BOTTOM_LEFT(0.66993397, 0.06549569)
308 | controlPoint1: BOTTOM_LEFT(1.08849299, 0.00000000)
309 | controlPoint2: BOTTOM_LEFT(0.86840701, 0.00000000)];
310 | [path addLineToPoint: BOTTOM_LEFT(0.63149399, 0.07491111)];
311 | [path addCurveToPoint: BOTTOM_LEFT(0.07491100, 0.63149399)
312 | controlPoint1: BOTTOM_LEFT(0.37282401, 0.16905883)
313 | controlPoint2: BOTTOM_LEFT(0.16906001, 0.37282392)];
314 | [path addCurveToPoint: BOTTOM_LEFT(0.00000000, 1.52866471)
315 | controlPoint1: BOTTOM_LEFT(0.00000000, 0.86840689)
316 | controlPoint2: BOTTOM_LEFT(0.00000000, 1.08849323)];
317 | [path addLineToPoint: TOP_LEFT(0.00000000, 1.52866483)];
318 | [path addCurveToPoint: TOP_LEFT(0.06549600, 0.66993397)
319 | controlPoint1: TOP_LEFT(0.00000000, 1.08849299)
320 | controlPoint2: TOP_LEFT(0.00000000, 0.86840701)];
321 | [path addLineToPoint: TOP_LEFT(0.07491100, 0.63149399)];
322 | [path addCurveToPoint: TOP_LEFT(0.63149399, 0.07491100)
323 | controlPoint1: TOP_LEFT(0.16906001, 0.37282401)
324 | controlPoint2: TOP_LEFT(0.37282401, 0.16906001)];
325 | [path addCurveToPoint: TOP_LEFT(1.52866483, 0.00000000)
326 | controlPoint1: TOP_LEFT(0.86840701, 0.00000000)
327 | controlPoint2: TOP_LEFT(1.08849299, 0.00000000)];
328 | [path closePath];
329 | return path;
330 | }
331 |
332 | @end
333 |
--------------------------------------------------------------------------------
/TOSearchBar/TOSearchBar.m:
--------------------------------------------------------------------------------
1 | //
2 | // TOSearchBar.m
3 | //
4 | // Copyright 2015-2016 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TOSearchBar.h"
24 | #import "TOSearchBar+Assets.h"
25 |
26 | static const CGFloat kTOSearchBarInset = 8.0f; // inset from inside the bar
27 |
28 | // iOS 10 and down
29 | static const CGFloat kTOSearchBarIconMarginClassic = 5.0f; // spacing between icon and placeholder
30 | static const CGFloat kTOSearchBarFontSizeClassic = 15.0f;
31 | static const CGFloat kTOSearchBarBackgroundHeightClassic = 28.0f;
32 |
33 | // iOS 11 style constants
34 | static const CGFloat kTOSearchBarIconMarginModern = 7.0f;
35 | static const CGFloat kTOSearchBarFontSizeModern = 17.0f;
36 | static const CGFloat kTOSearchBarBackgroundHeightModern = 36.0f;
37 |
38 | @interface TOSearchBar ()
39 |
40 | // UI components
41 | @property (nonatomic, strong, readwrite) UIImageView *barBackgroundView;
42 | @property (nonatomic, strong, readwrite) UIView *containerView;
43 | @property (nonatomic, strong, readwrite) UILabel *placeholderLabel;
44 | @property (nonatomic, strong, readwrite) UITextField *searchTextField;
45 | @property (nonatomic, strong, readwrite) UIButton *cancelButton;
46 | @property (nonatomic, strong, readwrite) UIButton *clearButton;
47 | @property (nonatomic, strong, readwrite) UIImageView *iconView;
48 |
49 | // State
50 | @property (nonatomic, readonly) BOOL centerTextLabel;
51 |
52 | @end
53 |
54 | @implementation TOSearchBar
55 |
56 | @synthesize barBackgroundTintColor = _barBackgroundTintColor;
57 |
58 | - (instancetype)initWithFrame:(CGRect)frame style:(TOSearchBarStyle)style
59 | {
60 | if (self = [super initWithFrame:frame]) {
61 | _style = style;
62 | }
63 |
64 | return self;
65 | }
66 |
67 | - (instancetype)initWithStyle:(TOSearchBarStyle)style
68 | {
69 | if (self = [super initWithFrame:CGRectZero]) {
70 | _style = style;
71 | }
72 |
73 | return self;
74 | }
75 |
76 | - (void)dealloc
77 | {
78 | [TOSearchBar cleanUpSharedAssets];
79 | }
80 |
81 | #pragma mark - View Lifecycle -
82 | - (void)didMoveToSuperview
83 | {
84 | [super didMoveToSuperview];
85 | if (self.superview != nil) {
86 | [self setUpViews];
87 | }
88 | }
89 |
90 | #pragma mark - View Set-up -
91 | - (void)setUpViews
92 | {
93 | self.clipsToBounds = YES;
94 | self.userInteractionEnabled = YES;
95 |
96 | [self setUpBackgroundViews];
97 | [self setUpPlaceholderViews];
98 | [self setUpButtons];
99 | [self setUpTextField];
100 | [self setUpGestureRecognizers];
101 |
102 | [self configureThemeForCurrentStyle];
103 | }
104 |
105 | - (void)setUpBackgroundViews
106 | {
107 | if (self.barBackgroundView == nil) {
108 | self.barBackgroundView = [[UIImageView alloc] initWithImage:[TOSearchBar sharedSearchBarBackground]];
109 | }
110 | self.barBackgroundView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin |
111 | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth;
112 | self.barBackgroundView.tintColor = self.barBackgroundTintColor;
113 | self.barBackgroundView.userInteractionEnabled = NO;
114 | [self addSubview:self.barBackgroundView];
115 |
116 | if (self.containerView == nil) {
117 | self.containerView = [[UIView alloc] initWithFrame:self.barBackgroundView.frame];
118 | }
119 | self.containerView.autoresizingMask = self.barBackgroundView.autoresizingMask;
120 | [self addSubview:self.containerView];
121 | }
122 |
123 | - (void)setUpPlaceholderViews
124 | {
125 | // Set up the placeholder label view
126 | if (self.placeholderLabel == nil) {
127 | self.placeholderLabel = [[UILabel alloc] initWithFrame:CGRectZero];
128 | }
129 |
130 | CGFloat fontSize = kTOSearchBarFontSizeClassic;
131 | if (@available(iOS 11.0, *)) {
132 | fontSize = kTOSearchBarFontSizeModern;
133 | }
134 | self.placeholderLabel.font = [UIFont systemFontOfSize:fontSize];
135 | self.placeholderLabel.text = NSLocalizedStringFromTableInBundle(@"Search",
136 | @"TOSearchBarLocalizable",
137 | [TOSearchBar bundle],
138 | nil);
139 | [self.placeholderLabel sizeToFit];
140 | [self.containerView addSubview:self.placeholderLabel];
141 |
142 | // Set up the accessory icon view
143 | if (self.iconView == nil) {
144 | self.iconView = [[UIImageView alloc] initWithImage:[TOSearchBar sharedSearchIcon]];
145 | }
146 | [self.containerView addSubview:self.iconView];
147 | }
148 |
149 | - (void)setUpTextField
150 | {
151 | if (self.searchTextField == nil) {
152 | self.searchTextField = [[UITextField alloc] initWithFrame:self.containerView.bounds];
153 | }
154 |
155 | CGFloat fontSize = kTOSearchBarFontSizeClassic;
156 | if (@available(iOS 11.0, *)) {
157 | fontSize = kTOSearchBarFontSizeModern;
158 | }
159 | self.searchTextField.userInteractionEnabled = YES;
160 | self.searchTextField.backgroundColor = [UIColor clearColor];
161 | self.searchTextField.font = [UIFont systemFontOfSize:fontSize];
162 | self.searchTextField.delegate = self;
163 | self.searchTextField.returnKeyType = UIReturnKeySearch;
164 | [self.searchTextField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
165 | [self.containerView addSubview:self.searchTextField];
166 | }
167 |
168 | - (void)setUpButtons
169 | {
170 | CGFloat iconMargin = kTOSearchBarIconMarginClassic;
171 | if (@available(iOS 11.0, *)) {
172 | iconMargin = kTOSearchBarIconMarginModern;
173 | }
174 |
175 | if (self.showsCancelButton && self.cancelButton == nil) {
176 | self.cancelButton = [UIButton buttonWithType:UIButtonTypeSystem];
177 | }
178 | [self.cancelButton setTitle:NSLocalizedStringFromTableInBundle(@"Cancel",
179 | @"TOSearchBarLocalizable",
180 | [TOSearchBar bundle],
181 | nil) forState:UIControlStateNormal];
182 | self.cancelButton.titleLabel.font = [UIFont systemFontOfSize:17.0f];
183 | self.cancelButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
184 | [self.cancelButton addTarget:self action:@selector(cancelButttonTapped:) forControlEvents:UIControlEventTouchUpInside];
185 | [self.cancelButton sizeToFit];
186 | self.cancelButton.frame = CGRectInset(self.cancelButton.frame, -iconMargin, 0.0f);
187 | [self addSubview:self.cancelButton];
188 |
189 | if (self.clearButton) {
190 | return;
191 | }
192 |
193 | UIImage *clearButtonImage = [TOSearchBar sharedClearIcon];
194 | self.clearButton = [UIButton buttonWithType:UIButtonTypeSystem];
195 | [self.clearButton setImage:clearButtonImage forState:UIControlStateNormal];
196 | self.clearButton.frame = (CGRect){CGPointZero, {44.0f, 44.0f}};
197 | self.clearButton.enabled = NO;
198 | self.clearButton.hidden = YES;
199 | [self.clearButton addTarget:self action:@selector(clearButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
200 | [self.containerView addSubview:self.clearButton];
201 | }
202 |
203 | - (void)setUpGestureRecognizers
204 | {
205 | // Add recognizers to the text field itself (as it captures exclusive access to the view if tapped)
206 | [self.searchTextField addTarget:self action:@selector(didTapEnter:) forControlEvents:UIControlEventTouchDown];
207 | [self.searchTextField addTarget:self action:@selector(didTapUp:) forControlEvents:UIControlEventTouchUpInside];
208 | [self.searchTextField addTarget:self action:@selector(didTapExit:) forControlEvents:UIControlEventTouchCancel];
209 | [self.searchTextField addTarget:self action:@selector(didTapExit:) forControlEvents:UIControlEventTouchDragExit];
210 | [self.searchTextField addTarget:self action:@selector(didTapEnter:) forControlEvents:UIControlEventTouchDragEnter];
211 | }
212 |
213 | #pragma mark - View Management -
214 | - (void)layoutSubviews
215 | {
216 | CGRect frame;
217 | CGSize clearImageSize = self.clearButton.imageView.image.size;
218 |
219 | CGFloat iconMargin = kTOSearchBarIconMarginClassic;
220 | if (@available(iOS 11.0, *)) {
221 | iconMargin = kTOSearchBarIconMarginModern;
222 | }
223 |
224 | if (self.cancelButton) {
225 | self.cancelButton.alpha = self.editing ? 1.0f : 0.0f;
226 | frame = self.cancelButton.frame;
227 | frame.origin.y = (CGRectGetHeight(self.frame) - frame.size.height) * 0.5f;
228 | if (self.editing) {
229 | frame.origin.x = (CGRectGetWidth(self.frame) - (frame.size.width + self.horizontalInset));
230 | }
231 | else {
232 | frame.origin.x = CGRectGetWidth(self.frame);
233 | }
234 | self.cancelButton.frame = frame;
235 | }
236 |
237 | // Layout the background view (and content container)
238 | frame = self.barBackgroundView.frame;
239 | frame.size.width = (self.frame.size.width) - (self.horizontalInset * 2.0f);
240 | frame.size.height = kTOSearchBarBackgroundHeightClassic;
241 | if (@available(iOS 11.0, *)) {
242 | frame.size.height = kTOSearchBarBackgroundHeightModern;
243 | }
244 |
245 | if (self.editing && self.cancelButton) { frame.size.width -= self.cancelButton.frame.size.width; }
246 | frame.origin.x = self.horizontalInset;
247 | frame.origin.y = floorf((self.frame.size.height - frame.size.height) * 0.5f);
248 | self.barBackgroundView.frame = frame;
249 | self.containerView.frame = frame;
250 |
251 | // layout the place holder label
252 | frame = self.placeholderLabel.frame;
253 | if (self.centerTextLabel == NO) {
254 | frame.origin.x = (kTOSearchBarInset) + self.iconView.frame.size.width;
255 | if (@available(iOS 11.0, *)) {
256 | frame.origin.x += kTOSearchBarIconMarginModern;
257 | }
258 | else {
259 | frame.origin.x += kTOSearchBarIconMarginClassic;
260 | }
261 | }
262 | else {
263 | frame.origin.x = floorf((CGRectGetWidth(self.containerView.frame) - CGRectGetWidth(frame)) * 0.5f);
264 | }
265 | frame.origin.y = floorf((CGRectGetHeight(self.containerView.frame) - CGRectGetHeight(frame)) * 0.5f);
266 | self.placeholderLabel.frame = frame;
267 | self.placeholderLabel.hidden = self.hasSearchText;
268 |
269 | // layout the icon
270 | frame = self.iconView.frame;
271 | if (self.editing || self.hasSearchText) {
272 | frame.origin.x = kTOSearchBarInset;
273 | }
274 | else {
275 | frame.origin.x = CGRectGetMinX(self.placeholderLabel.frame) - (CGRectGetWidth(self.iconView.frame) + iconMargin);
276 | }
277 | frame.origin.y = floorf(CGRectGetMidY(self.placeholderLabel.frame) - (CGRectGetHeight(self.iconView.frame) * 0.5f));
278 | self.iconView.frame = frame;
279 |
280 | // lay out the text field
281 | frame = self.searchTextField.frame;
282 | frame.size = self.containerView.frame.size;
283 | frame.size.width = CGRectGetWidth(frame) - (self.placeholderLabel.frame.origin.x + iconMargin + clearImageSize.width);
284 | frame.origin.x = self.placeholderLabel.frame.origin.x;
285 | frame.origin.y = 0.0f;
286 | self.searchTextField.frame = frame;
287 |
288 | // layout the clear button
289 | CGPoint center = CGPointZero;
290 | center.x = (CGRectGetWidth(self.containerView.frame) - (kTOSearchBarInset + (clearImageSize.width * 0.5f)));
291 | center.y = (CGRectGetHeight(self.containerView.frame) * 0.5f);
292 | self.clearButton.center = center;
293 | }
294 |
295 | #pragma mark - Theme Management -
296 | - (void)configureThemeForCurrentStyle
297 | {
298 | BOOL darkMode = (self.style == TOSearchBarStyleDark);
299 |
300 | if (darkMode) {
301 | self.barBackgroundTintColor = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:0.065f];
302 | self.selectedBarBackgroundTintColor = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:0.15f];
303 | self.placeholderTintColor = [UIColor colorWithWhite:0.4f alpha:1.0f];
304 | self.searchTextField.textColor = [UIColor whiteColor];
305 | self.searchTextField.text = self.searchTextField.text; // Force the color to update
306 | self.clearButton.tintColor = [UIColor colorWithWhite:0.45f alpha:1.0f];
307 | self.searchTextField.keyboardAppearance = UIKeyboardAppearanceDark;
308 | return;
309 | }
310 |
311 | self.searchTextField.textColor = [UIColor blackColor];
312 | self.searchTextField.text = self.searchTextField.text; // Force the color to update
313 |
314 | self.placeholderTintColor = [UIColor colorWithRed:0.556863f green:0.556863f blue:0.576471f alpha:1.0f];
315 | self.clearButton.tintColor = [UIColor colorWithRed:0.556863f green:0.556863f blue:0.576471f alpha:1.0f];
316 |
317 | if (@available(iOS 11.0, *)) {
318 | self.barBackgroundTintColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.03922f alpha:0.055f];
319 | self.selectedBarBackgroundTintColor = [UIColor colorWithRed:0.04313f green:0.0392f blue:0.11765f alpha:0.2f];
320 | }
321 | else {
322 | self.barBackgroundTintColor = [UIColor colorWithRed:0.0f green:0.05f blue:0.13f alpha:0.083f];
323 | }
324 | self.searchTextField.keyboardAppearance = UIKeyboardAppearanceLight;
325 | }
326 |
327 | #pragma mark - Event Handling -
328 |
329 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
330 | {
331 | return !(self.searchTextField.isFirstResponder);
332 | }
333 |
334 | - (void)clearButtonTapped:(id)sender
335 | {
336 | self.text = nil;
337 | self.clearButton.enabled = NO;
338 | self.placeholderLabel.hidden = NO;
339 | [self setClearButtonHidden:YES animated:YES];
340 | [self becomeFirstResponder];
341 |
342 | if ([self.delegate respondsToSelector:@selector(searchBarClearButtonTapped:)]) {
343 | [self.delegate searchBarClearButtonTapped:self];
344 | }
345 |
346 | if ([self.delegate respondsToSelector:@selector(searchBar:textDidChange:)]) {
347 | [self.delegate searchBar:self textDidChange:self.text];
348 | }
349 | }
350 |
351 | - (void)cancelButttonTapped:(id)sender
352 | {
353 | [self resignFirstResponder];
354 | }
355 |
356 | - (BOOL)becomeFirstResponder
357 | {
358 | [self setEditing:YES animated:YES];
359 | return [self.searchTextField becomeFirstResponder];
360 | }
361 |
362 | - (BOOL)isFirstResponder
363 | {
364 | return self.searchTextField.isFirstResponder;
365 | }
366 |
367 | - (BOOL)resignFirstResponder
368 | {
369 | [self.searchTextField resignFirstResponder];
370 | [self setEditing:NO animated:YES];
371 | return [super resignFirstResponder];
372 | }
373 |
374 | - (void)didTapEnter:(id)sender
375 | {
376 | if (self.searchTextField.isFirstResponder) { return; }
377 | [self setSelected:YES animated:YES];
378 | }
379 |
380 | - (void)didTapUp:(id)sender
381 | {
382 | if (self.searchTextField.isFirstResponder) { return; }
383 | [self.searchTextField becomeFirstResponder];
384 | }
385 |
386 | - (void)didTapExit:(id)sender
387 | {
388 | if (self.searchTextField.isFirstResponder) { return; }
389 | [self setSelected:NO animated:YES];
390 | }
391 |
392 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
393 |
394 | // Make sure the 'clear' button receives priority touch events
395 | UIView *targetView = self.clearButton;
396 | CGPoint pointForTargetView = [targetView convertPoint:point fromView:self];
397 | if (targetView.hidden == NO && CGRectContainsPoint(targetView.bounds, pointForTargetView)) {
398 | return [targetView hitTest:pointForTargetView withEvent:event];
399 | }
400 |
401 | // If we touch anywhere in the rounded rectangle, assume we were aiming for the search field
402 | if (CGRectContainsPoint(self.barBackgroundView.frame, point)) {
403 | return self.searchTextField;
404 | }
405 |
406 | return [super hitTest:point withEvent:event];
407 | }
408 |
409 | #pragma mark - Editing -
410 | - (void)setEditing:(BOOL)editing animated:(BOOL)animated
411 | {
412 | self.editing = editing;
413 |
414 | if (animated == NO) {
415 | return;
416 | }
417 |
418 | [UIView animateWithDuration:0.4f
419 | delay:0.0f
420 | usingSpringWithDamping:1.0f
421 | initialSpringVelocity:0.1f
422 | options:UIViewAnimationOptionBeginFromCurrentState
423 | animations:
424 | ^{
425 | [self layoutIfNeeded];
426 | } completion:nil];
427 | }
428 |
429 | - (void)setClearButtonHidden:(BOOL)hidden animated:(BOOL)animated
430 | {
431 | if (self.clearButton.hidden == hidden) {
432 | return;
433 | }
434 |
435 | if (animated == NO) {
436 | self.clearButton.hidden = hidden;
437 | return;
438 | }
439 |
440 | self.clearButton.hidden = NO;
441 |
442 | CGAffineTransform visibleTransform = CGAffineTransformIdentity;
443 | CGAffineTransform hiddenTransform = CGAffineTransformScale(CGAffineTransformIdentity, 0.01f, 0.01f);
444 |
445 | CGFloat visibleAlpha = 1.0f;
446 | CGFloat hiddenAlpha = 0.0f;
447 |
448 | self.clearButton.transform = hidden ? visibleTransform : hiddenTransform;
449 | self.clearButton.alpha = hidden ? visibleAlpha : hiddenAlpha;
450 |
451 | [self.clearButton.layer removeAllAnimations];
452 | [UIView animateWithDuration:0.25f
453 | delay:0.0f
454 | usingSpringWithDamping:1.0f
455 | initialSpringVelocity:0.1f
456 | options:0
457 | animations:
458 | ^{
459 | self.clearButton.alpha = hidden ? hiddenAlpha : visibleAlpha;
460 | self.clearButton.transform = hidden ? hiddenTransform : visibleTransform;
461 | } completion:^(BOOL complete) {
462 | self.clearButton.hidden = hidden;
463 | }];
464 | }
465 |
466 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated
467 | {
468 | if (super.selected == selected) { return; }
469 |
470 | super.selected = selected;
471 |
472 | // The bar didn't change colour in iOS 10 and down
473 | if (@available(iOS 11.0, *)) { }
474 | else { return; }
475 |
476 | void (^selectedBlock)(void) = ^{
477 | self.barBackgroundView.tintColor = selected ? self.selectedBarBackgroundTintColor : self.barBackgroundTintColor;
478 | };
479 |
480 | if (!animated) {
481 | selectedBlock();
482 | return;
483 | }
484 |
485 | [UIView animateWithDuration:0.3f
486 | delay:0.0f
487 | options:0
488 | animations:selectedBlock
489 | completion:nil];
490 | }
491 |
492 | #pragma mark - Text Field Delegate -
493 | - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
494 | {
495 | BOOL result = YES;
496 |
497 | // Allow the delegate to override this value
498 | if ([self.delegate respondsToSelector:@selector(searchBarShouldBeginEditing:)]) {
499 | result = [self.delegate searchBarShouldBeginEditing:self];
500 | }
501 |
502 | // Kickstart the editing transition now. Any later creates strange stretch effects
503 | if (result) {
504 | [self setEditing:YES animated:YES];
505 | }
506 |
507 | return result;
508 | }
509 |
510 | - (void)textFieldDidBeginEditing:(UITextField *)textField
511 | {
512 | if ([self.delegate respondsToSelector:@selector(searchBarDidBeginEditing:)]) {
513 | [self.delegate searchBarDidBeginEditing:self];
514 | }
515 |
516 | [self setSelected:NO animated:YES];
517 | }
518 |
519 | - (BOOL)textFieldShouldEndEditing:(UITextField *)textField
520 | {
521 | BOOL result = YES;
522 |
523 | // Allow the delegate to override this value
524 | if ([self.delegate respondsToSelector:@selector(searchBarShouldEndEditing:)]) {
525 | result = [self.delegate searchBarShouldEndEditing:self];
526 | }
527 |
528 | // Kickstart the editing transition now. Any later creates strange stretch effects
529 | if (result) {
530 | [self setEditing:YES animated:YES];
531 | }
532 |
533 | return result;
534 | }
535 |
536 | - (void)textFieldDidEndEditing:(UITextField *)textField
537 | {
538 | [textField layoutIfNeeded]; // Necessary to stop bouncing animation glitch
539 | [self setEditing:NO animated:YES];
540 |
541 | if ([self.delegate respondsToSelector:@selector(searchBarDidEndEditing:)]) {
542 | [self.delegate searchBarDidEndEditing:self];
543 | }
544 | }
545 |
546 | - (void)textFieldDidChange:(UITextField *)textField
547 | {
548 | self.placeholderLabel.hidden = self.hasSearchText;
549 | self.clearButton.enabled = self.hasSearchText;
550 |
551 | [self setClearButtonHidden:!self.hasSearchText animated:YES];
552 |
553 | if ([self.delegate respondsToSelector:@selector(searchBar:textDidChange:)]) {
554 | [self.delegate searchBar:self textDidChange:self.text];
555 | }
556 | }
557 |
558 | - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
559 | {
560 | if ([self.delegate respondsToSelector:@selector(searchBar:shouldChangeTextInRange:replacementText:)]) {
561 | return [self.delegate searchBar:self shouldChangeCharactersInRange:range replacementString:string];
562 | }
563 |
564 | return YES;
565 | }
566 |
567 | - (BOOL)textFieldShouldReturn:(UITextField *)textField
568 | {
569 | if ([self.delegate respondsToSelector:@selector(searchBarSearchButtonTapped:)]) {
570 | [self.delegate searchBarSearchButtonTapped:self];
571 | }
572 |
573 | return YES;
574 | }
575 |
576 | #pragma mark - Accessors -
577 | - (void)setEditing:(BOOL)editing
578 | {
579 | _editing = editing;
580 | [self setNeedsLayout];
581 | }
582 |
583 | - (BOOL)hasSearchText
584 | {
585 | return self.searchTextField.text.length > 0;
586 | }
587 |
588 | - (void)setText:(NSString *)text
589 | {
590 | self.searchTextField.text = text;
591 | }
592 |
593 | - (NSString *)text
594 | {
595 | return self.searchTextField.text;
596 | }
597 |
598 | - (void)setPlaceholderText:(NSString *)placeholderText
599 | {
600 | self.placeholderLabel.text = placeholderText ?: NSLocalizedStringFromTableInBundle(@"Search",
601 | @"TOSearchBarLocalizable",
602 | [TOSearchBar bundle],
603 | nil);
604 | [self.placeholderLabel sizeToFit];
605 | [self setNeedsLayout];
606 | }
607 |
608 | - (NSString *)placeholderText
609 | {
610 | return self.placeholderLabel.text;
611 | }
612 |
613 | - (UIColor *)barBackgroundTintColor
614 | {
615 | return _barBackgroundTintColor;
616 | }
617 |
618 | - (void)setBarBackgroundTintColor:(UIColor *)barBackgroundTintColor
619 | {
620 | _barBackgroundTintColor = barBackgroundTintColor;
621 | self.barBackgroundView.tintColor = _barBackgroundTintColor;
622 | }
623 |
624 | - (void)setHorizontalInset:(CGFloat)horizontalInset
625 | {
626 | if (_horizontalInset == horizontalInset) {
627 | return;
628 | }
629 |
630 | _horizontalInset = horizontalInset;
631 | [self setNeedsLayout];
632 | }
633 |
634 | - (void)setPlaceholderTintColor:(UIColor *)placeholderTintColor
635 | {
636 | _placeholderTintColor = placeholderTintColor;
637 | self.placeholderLabel.textColor = placeholderTintColor;
638 | self.iconView.tintColor = placeholderTintColor;
639 | }
640 |
641 | - (void)setStyle:(TOSearchBarStyle)style
642 | {
643 | if (style == _style) {
644 | return;
645 | }
646 |
647 | _style = style;
648 | [self configureThemeForCurrentStyle];
649 | }
650 |
651 | - (void)setShowsCancelButton:(BOOL)showsCancelButton
652 | {
653 | _showsCancelButton = showsCancelButton;
654 |
655 | if (_showsCancelButton == NO) {
656 | [self.cancelButton removeFromSuperview];
657 | self.cancelButton = nil;
658 | }
659 | else {
660 | [self setUpButtons];
661 | }
662 |
663 | [self setNeedsLayout];
664 | }
665 |
666 | - (BOOL)centerTextLabel
667 | {
668 | if (@available(iOS 11.0, *)) {
669 | return NO;
670 | }
671 |
672 | return !self.editing && !self.hasSearchText;
673 | }
674 |
675 | + (NSBundle *)bundle
676 | {
677 | // Whether installed manually or via CocoaPods, this will configure the bundle paths properly
678 | NSBundle *resourceBundle = nil;
679 | NSBundle *classBundle = [NSBundle bundleForClass:[self class]];
680 | NSURL *resourceBundleURL = [classBundle URLForResource:@"TOSearchBarBundle" withExtension:@"bundle"];
681 | if (resourceBundleURL) {
682 | resourceBundle = [[NSBundle alloc] initWithURL:resourceBundleURL];
683 | }
684 | else {
685 | resourceBundle = classBundle;
686 | }
687 |
688 | return resourceBundle;
689 | }
690 |
691 | @end
692 |
--------------------------------------------------------------------------------
/TODocumentPickerViewControllerExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 2205C43E1EBF0B1600B20229 /* TODocumentsDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 2205C43D1EBF0B1600B20229 /* TODocumentsDataSource.m */; };
11 | 220D33011D33F0440004E4BE /* TODocumentPickerConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 220D33001D33F0440004E4BE /* TODocumentPickerConfiguration.m */; };
12 | 225C38F91A7E37DC004DA4D9 /* TODocumentPickerItemManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 225C38F81A7E37DC004DA4D9 /* TODocumentPickerItemManager.m */; };
13 | 22707A811A53BDDF008E66CC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 22707A801A53BDDF008E66CC /* main.m */; };
14 | 22707A841A53BDDF008E66CC /* TOAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 22707A831A53BDDF008E66CC /* TOAppDelegate.m */; };
15 | 22707A871A53BDDF008E66CC /* TOViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 22707A861A53BDDF008E66CC /* TOViewController.m */; };
16 | 22707A8A1A53BDDF008E66CC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 22707A881A53BDDF008E66CC /* Main.storyboard */; };
17 | 22707A8C1A53BDDF008E66CC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 22707A8B1A53BDDF008E66CC /* Images.xcassets */; };
18 | 22707A8F1A53BDDF008E66CC /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 22707A8D1A53BDDF008E66CC /* LaunchScreen.xib */; };
19 | 22A3E19D1A56857700C4839E /* TODocumentPickerItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 22A3E1991A56857700C4839E /* TODocumentPickerItem.m */; };
20 | 22A3E1A01A5685A000C4839E /* TODocumentPickerHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 22A3E19F1A5685A000C4839E /* TODocumentPickerHeaderView.m */; };
21 | 22A3E1A31A56880C00C4839E /* TODocumentPickerSegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 22A3E1A21A56880C00C4839E /* TODocumentPickerSegmentedControl.m */; };
22 | 22A5E42C1A7B17F8000CD302 /* TODocumentPickerTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 22A5E42B1A7B17F8000CD302 /* TODocumentPickerTableView.m */; };
23 | 22AF79871D2C01550066965F /* TODocumentPickerConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 22AF79861D2C01550066965F /* TODocumentPickerConstants.m */; };
24 | 22B177011FF60F6700F974BB /* TODocumentPickerTheme.m in Sources */ = {isa = PBXBuildFile; fileRef = 22B177001FF60F6700F974BB /* TODocumentPickerTheme.m */; };
25 | 22B177041FF6242600F974BB /* TODocumentPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 22B177031FF6242600F974BB /* TODocumentPickerViewController.m */; };
26 | 22B177071FF630E200F974BB /* TODocumentPickerTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 22B177061FF630E200F974BB /* TODocumentPickerTableViewCell.m */; };
27 | 22DF14A61A8104F8002E9DE4 /* UIImage+TODocumentPickerIcons.m in Sources */ = {isa = PBXBuildFile; fileRef = 22DF14A51A8104F8002E9DE4 /* UIImage+TODocumentPickerIcons.m */; };
28 | 22F806D71ECAC5EB0053C94E /* TODocumentPickerLocalDiskDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 22F806D61ECAC5EB0053C94E /* TODocumentPickerLocalDiskDataSource.m */; };
29 | 22FC4C611FF748A50055FDF5 /* TOSearchBarLocalizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 22FC4C4E1FF748A50055FDF5 /* TOSearchBarLocalizable.strings */; };
30 | 22FC4C621FF748A50055FDF5 /* TOSearchBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 22FC4C5D1FF748A50055FDF5 /* TOSearchBar.m */; };
31 | 22FC4C631FF748A50055FDF5 /* TOSearchBar+Assets.m in Sources */ = {isa = PBXBuildFile; fileRef = 22FC4C601FF748A50055FDF5 /* TOSearchBar+Assets.m */; };
32 | /* End PBXBuildFile section */
33 |
34 | /* Begin PBXFileReference section */
35 | 2205C43C1EBF0B1600B20229 /* TODocumentsDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TODocumentsDataSource.h; sourceTree = ""; };
36 | 2205C43D1EBF0B1600B20229 /* TODocumentsDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TODocumentsDataSource.m; sourceTree = ""; };
37 | 220D32FF1D33F0440004E4BE /* TODocumentPickerConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TODocumentPickerConfiguration.h; sourceTree = ""; };
38 | 220D33001D33F0440004E4BE /* TODocumentPickerConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TODocumentPickerConfiguration.m; sourceTree = ""; };
39 | 2211A3CF1A5B80900089106B /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Main.strings; sourceTree = ""; };
40 | 2211A3D01A5B80900089106B /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/LaunchScreen.strings; sourceTree = ""; };
41 | 225C38F71A7E37DC004DA4D9 /* TODocumentPickerItemManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TODocumentPickerItemManager.h; sourceTree = ""; };
42 | 225C38F81A7E37DC004DA4D9 /* TODocumentPickerItemManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TODocumentPickerItemManager.m; sourceTree = ""; };
43 | 225C38FA1A7E3AF0004DA4D9 /* TODocumentPickerConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TODocumentPickerConstants.h; sourceTree = ""; };
44 | 22707A7B1A53BDDF008E66CC /* TODocumentPickerViewControllerExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TODocumentPickerViewControllerExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
45 | 22707A7F1A53BDDF008E66CC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
46 | 22707A801A53BDDF008E66CC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
47 | 22707A821A53BDDF008E66CC /* TOAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TOAppDelegate.h; sourceTree = ""; };
48 | 22707A831A53BDDF008E66CC /* TOAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TOAppDelegate.m; sourceTree = ""; };
49 | 22707A851A53BDDF008E66CC /* TOViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TOViewController.h; sourceTree = ""; };
50 | 22707A861A53BDDF008E66CC /* TOViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TOViewController.m; sourceTree = ""; };
51 | 22707A891A53BDDF008E66CC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
52 | 22707A8B1A53BDDF008E66CC /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
53 | 22707A8E1A53BDDF008E66CC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; };
54 | 22A3E1981A56857700C4839E /* TODocumentPickerItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TODocumentPickerItem.h; sourceTree = ""; };
55 | 22A3E1991A56857700C4839E /* TODocumentPickerItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TODocumentPickerItem.m; sourceTree = ""; };
56 | 22A3E19E1A5685A000C4839E /* TODocumentPickerHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TODocumentPickerHeaderView.h; sourceTree = ""; };
57 | 22A3E19F1A5685A000C4839E /* TODocumentPickerHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TODocumentPickerHeaderView.m; sourceTree = ""; };
58 | 22A3E1A11A56880C00C4839E /* TODocumentPickerSegmentedControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TODocumentPickerSegmentedControl.h; sourceTree = ""; };
59 | 22A3E1A21A56880C00C4839E /* TODocumentPickerSegmentedControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TODocumentPickerSegmentedControl.m; sourceTree = ""; };
60 | 22A5E42A1A7B17F8000CD302 /* TODocumentPickerTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TODocumentPickerTableView.h; sourceTree = ""; };
61 | 22A5E42B1A7B17F8000CD302 /* TODocumentPickerTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TODocumentPickerTableView.m; sourceTree = ""; };
62 | 22AF79861D2C01550066965F /* TODocumentPickerConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TODocumentPickerConstants.m; sourceTree = ""; };
63 | 22B176FF1FF60F6700F974BB /* TODocumentPickerTheme.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TODocumentPickerTheme.h; sourceTree = ""; };
64 | 22B177001FF60F6700F974BB /* TODocumentPickerTheme.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TODocumentPickerTheme.m; sourceTree = ""; };
65 | 22B177021FF6242600F974BB /* TODocumentPickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TODocumentPickerViewController.h; path = TODocumentPickerViewController/TODocumentPickerViewController.h; sourceTree = ""; };
66 | 22B177031FF6242600F974BB /* TODocumentPickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TODocumentPickerViewController.m; path = TODocumentPickerViewController/TODocumentPickerViewController.m; sourceTree = ""; };
67 | 22B177051FF630E200F974BB /* TODocumentPickerTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TODocumentPickerTableViewCell.h; sourceTree = ""; };
68 | 22B177061FF630E200F974BB /* TODocumentPickerTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TODocumentPickerTableViewCell.m; sourceTree = ""; };
69 | 22DF14A41A8104F8002E9DE4 /* UIImage+TODocumentPickerIcons.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+TODocumentPickerIcons.h"; path = "TODocumentPickerViewController/Categories/UIImage+TODocumentPickerIcons.h"; sourceTree = ""; };
70 | 22DF14A51A8104F8002E9DE4 /* UIImage+TODocumentPickerIcons.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImage+TODocumentPickerIcons.m"; path = "TODocumentPickerViewController/Categories/UIImage+TODocumentPickerIcons.m"; sourceTree = ""; };
71 | 22F806D51ECAC5EB0053C94E /* TODocumentPickerLocalDiskDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TODocumentPickerLocalDiskDataSource.h; sourceTree = ""; };
72 | 22F806D61ECAC5EB0053C94E /* TODocumentPickerLocalDiskDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TODocumentPickerLocalDiskDataSource.m; sourceTree = ""; };
73 | 22FC4C4F1FF748A50055FDF5 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/TOSearchBarLocalizable.strings; sourceTree = ""; };
74 | 22FC4C501FF748A50055FDF5 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/TOSearchBarLocalizable.strings"; sourceTree = ""; };
75 | 22FC4C511FF748A50055FDF5 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/TOSearchBarLocalizable.strings; sourceTree = ""; };
76 | 22FC4C521FF748A50055FDF5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/TOSearchBarLocalizable.strings; sourceTree = ""; };
77 | 22FC4C531FF748A50055FDF5 /* TOSearchBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TOSearchBar.h; sourceTree = ""; };
78 | 22FC4C541FF748A50055FDF5 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/TOSearchBarLocalizable.strings; sourceTree = ""; };
79 | 22FC4C551FF748A50055FDF5 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/TOSearchBarLocalizable.strings; sourceTree = ""; };
80 | 22FC4C561FF748A50055FDF5 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/TOSearchBarLocalizable.strings; sourceTree = ""; };
81 | 22FC4C571FF748A50055FDF5 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/TOSearchBarLocalizable.strings; sourceTree = ""; };
82 | 22FC4C581FF748A50055FDF5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/TOSearchBarLocalizable.strings; sourceTree = ""; };
83 | 22FC4C591FF748A50055FDF5 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/TOSearchBarLocalizable.strings"; sourceTree = ""; };
84 | 22FC4C5A1FF748A50055FDF5 /* TOSearchBar+Assets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TOSearchBar+Assets.h"; sourceTree = ""; };
85 | 22FC4C5B1FF748A50055FDF5 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/TOSearchBarLocalizable.strings; sourceTree = ""; };
86 | 22FC4C5C1FF748A50055FDF5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/TOSearchBarLocalizable.strings; sourceTree = ""; };
87 | 22FC4C5D1FF748A50055FDF5 /* TOSearchBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TOSearchBar.m; sourceTree = ""; };
88 | 22FC4C5E1FF748A50055FDF5 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/TOSearchBarLocalizable.strings; sourceTree = ""; };
89 | 22FC4C5F1FF748A50055FDF5 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/TOSearchBarLocalizable.strings; sourceTree = ""; };
90 | 22FC4C601FF748A50055FDF5 /* TOSearchBar+Assets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "TOSearchBar+Assets.m"; sourceTree = ""; };
91 | /* End PBXFileReference section */
92 |
93 | /* Begin PBXFrameworksBuildPhase section */
94 | 22707A781A53BDDF008E66CC /* Frameworks */ = {
95 | isa = PBXFrameworksBuildPhase;
96 | buildActionMask = 2147483647;
97 | files = (
98 | );
99 | runOnlyForDeploymentPostprocessing = 0;
100 | };
101 | /* End PBXFrameworksBuildPhase section */
102 |
103 | /* Begin PBXGroup section */
104 | 225C38F31A7DFB7A004DA4D9 /* Categories */ = {
105 | isa = PBXGroup;
106 | children = (
107 | 22DF14A41A8104F8002E9DE4 /* UIImage+TODocumentPickerIcons.h */,
108 | 22DF14A51A8104F8002E9DE4 /* UIImage+TODocumentPickerIcons.m */,
109 | );
110 | name = Categories;
111 | sourceTree = "";
112 | };
113 | 22707A721A53BDDF008E66CC = {
114 | isa = PBXGroup;
115 | children = (
116 | 22707AA41A53BE85008E66CC /* TODocumentPickerViewController */,
117 | 22FC4C4D1FF748A50055FDF5 /* TOSearchBar */,
118 | 22707A7D1A53BDDF008E66CC /* TODocumentPickerViewControllerExample */,
119 | 22707A7C1A53BDDF008E66CC /* Products */,
120 | );
121 | sourceTree = "";
122 | };
123 | 22707A7C1A53BDDF008E66CC /* Products */ = {
124 | isa = PBXGroup;
125 | children = (
126 | 22707A7B1A53BDDF008E66CC /* TODocumentPickerViewControllerExample.app */,
127 | );
128 | name = Products;
129 | sourceTree = "";
130 | };
131 | 22707A7D1A53BDDF008E66CC /* TODocumentPickerViewControllerExample */ = {
132 | isa = PBXGroup;
133 | children = (
134 | 2205C43C1EBF0B1600B20229 /* TODocumentsDataSource.h */,
135 | 2205C43D1EBF0B1600B20229 /* TODocumentsDataSource.m */,
136 | 22707A821A53BDDF008E66CC /* TOAppDelegate.h */,
137 | 22707A831A53BDDF008E66CC /* TOAppDelegate.m */,
138 | 22707A851A53BDDF008E66CC /* TOViewController.h */,
139 | 22707A861A53BDDF008E66CC /* TOViewController.m */,
140 | 22707A881A53BDDF008E66CC /* Main.storyboard */,
141 | 22707A8B1A53BDDF008E66CC /* Images.xcassets */,
142 | 22707A8D1A53BDDF008E66CC /* LaunchScreen.xib */,
143 | 22707A7E1A53BDDF008E66CC /* Supporting Files */,
144 | );
145 | path = TODocumentPickerViewControllerExample;
146 | sourceTree = "";
147 | };
148 | 22707A7E1A53BDDF008E66CC /* Supporting Files */ = {
149 | isa = PBXGroup;
150 | children = (
151 | 22707A7F1A53BDDF008E66CC /* Info.plist */,
152 | 22707A801A53BDDF008E66CC /* main.m */,
153 | );
154 | name = "Supporting Files";
155 | sourceTree = "";
156 | };
157 | 22707AA41A53BE85008E66CC /* TODocumentPickerViewController */ = {
158 | isa = PBXGroup;
159 | children = (
160 | 22B177021FF6242600F974BB /* TODocumentPickerViewController.h */,
161 | 22B177031FF6242600F974BB /* TODocumentPickerViewController.m */,
162 | 225C38F31A7DFB7A004DA4D9 /* Categories */,
163 | 22A3E1971A56857700C4839E /* Models */,
164 | 22A3E19A1A56857700C4839E /* Views */,
165 | );
166 | name = TODocumentPickerViewController;
167 | sourceTree = "";
168 | };
169 | 22A3E1971A56857700C4839E /* Models */ = {
170 | isa = PBXGroup;
171 | children = (
172 | 225C38FA1A7E3AF0004DA4D9 /* TODocumentPickerConstants.h */,
173 | 22AF79861D2C01550066965F /* TODocumentPickerConstants.m */,
174 | 22F806D51ECAC5EB0053C94E /* TODocumentPickerLocalDiskDataSource.h */,
175 | 22F806D61ECAC5EB0053C94E /* TODocumentPickerLocalDiskDataSource.m */,
176 | 220D32FF1D33F0440004E4BE /* TODocumentPickerConfiguration.h */,
177 | 220D33001D33F0440004E4BE /* TODocumentPickerConfiguration.m */,
178 | 22B176FF1FF60F6700F974BB /* TODocumentPickerTheme.h */,
179 | 22B177001FF60F6700F974BB /* TODocumentPickerTheme.m */,
180 | 225C38F71A7E37DC004DA4D9 /* TODocumentPickerItemManager.h */,
181 | 225C38F81A7E37DC004DA4D9 /* TODocumentPickerItemManager.m */,
182 | 22A3E1981A56857700C4839E /* TODocumentPickerItem.h */,
183 | 22A3E1991A56857700C4839E /* TODocumentPickerItem.m */,
184 | );
185 | name = Models;
186 | path = TODocumentPickerViewController/Models;
187 | sourceTree = "";
188 | };
189 | 22A3E19A1A56857700C4839E /* Views */ = {
190 | isa = PBXGroup;
191 | children = (
192 | 22A3E19E1A5685A000C4839E /* TODocumentPickerHeaderView.h */,
193 | 22A3E19F1A5685A000C4839E /* TODocumentPickerHeaderView.m */,
194 | 22A3E1A11A56880C00C4839E /* TODocumentPickerSegmentedControl.h */,
195 | 22A3E1A21A56880C00C4839E /* TODocumentPickerSegmentedControl.m */,
196 | 22A5E42A1A7B17F8000CD302 /* TODocumentPickerTableView.h */,
197 | 22A5E42B1A7B17F8000CD302 /* TODocumentPickerTableView.m */,
198 | 22B177051FF630E200F974BB /* TODocumentPickerTableViewCell.h */,
199 | 22B177061FF630E200F974BB /* TODocumentPickerTableViewCell.m */,
200 | );
201 | name = Views;
202 | path = TODocumentPickerViewController/Views;
203 | sourceTree = "";
204 | };
205 | 22FC4C4D1FF748A50055FDF5 /* TOSearchBar */ = {
206 | isa = PBXGroup;
207 | children = (
208 | 22FC4C4E1FF748A50055FDF5 /* TOSearchBarLocalizable.strings */,
209 | 22FC4C531FF748A50055FDF5 /* TOSearchBar.h */,
210 | 22FC4C5A1FF748A50055FDF5 /* TOSearchBar+Assets.h */,
211 | 22FC4C5D1FF748A50055FDF5 /* TOSearchBar.m */,
212 | 22FC4C601FF748A50055FDF5 /* TOSearchBar+Assets.m */,
213 | );
214 | path = TOSearchBar;
215 | sourceTree = "";
216 | };
217 | /* End PBXGroup section */
218 |
219 | /* Begin PBXNativeTarget section */
220 | 22707A7A1A53BDDF008E66CC /* TODocumentPickerViewControllerExample */ = {
221 | isa = PBXNativeTarget;
222 | buildConfigurationList = 22707A9E1A53BDDF008E66CC /* Build configuration list for PBXNativeTarget "TODocumentPickerViewControllerExample" */;
223 | buildPhases = (
224 | 22707A771A53BDDF008E66CC /* Sources */,
225 | 22707A781A53BDDF008E66CC /* Frameworks */,
226 | 22707A791A53BDDF008E66CC /* Resources */,
227 | );
228 | buildRules = (
229 | );
230 | dependencies = (
231 | );
232 | name = TODocumentPickerViewControllerExample;
233 | productName = TODocumentPickerViewControllerExample;
234 | productReference = 22707A7B1A53BDDF008E66CC /* TODocumentPickerViewControllerExample.app */;
235 | productType = "com.apple.product-type.application";
236 | };
237 | /* End PBXNativeTarget section */
238 |
239 | /* Begin PBXProject section */
240 | 22707A731A53BDDF008E66CC /* Project object */ = {
241 | isa = PBXProject;
242 | attributes = {
243 | LastUpgradeCheck = 0900;
244 | ORGANIZATIONNAME = "Tim Oliver";
245 | TargetAttributes = {
246 | 22707A7A1A53BDDF008E66CC = {
247 | CreatedOnToolsVersion = 6.1.1;
248 | DevelopmentTeam = 6LF3GMKZAB;
249 | };
250 | };
251 | };
252 | buildConfigurationList = 22707A761A53BDDF008E66CC /* Build configuration list for PBXProject "TODocumentPickerViewControllerExample" */;
253 | compatibilityVersion = "Xcode 3.2";
254 | developmentRegion = English;
255 | hasScannedForEncodings = 0;
256 | knownRegions = (
257 | en,
258 | Base,
259 | de,
260 | "zh-Hans",
261 | ja,
262 | es,
263 | da,
264 | it,
265 | ko,
266 | "zh-Hant",
267 | pl,
268 | ru,
269 | fr,
270 | nl,
271 | );
272 | mainGroup = 22707A721A53BDDF008E66CC;
273 | productRefGroup = 22707A7C1A53BDDF008E66CC /* Products */;
274 | projectDirPath = "";
275 | projectRoot = "";
276 | targets = (
277 | 22707A7A1A53BDDF008E66CC /* TODocumentPickerViewControllerExample */,
278 | );
279 | };
280 | /* End PBXProject section */
281 |
282 | /* Begin PBXResourcesBuildPhase section */
283 | 22707A791A53BDDF008E66CC /* Resources */ = {
284 | isa = PBXResourcesBuildPhase;
285 | buildActionMask = 2147483647;
286 | files = (
287 | 22707A8A1A53BDDF008E66CC /* Main.storyboard in Resources */,
288 | 22707A8F1A53BDDF008E66CC /* LaunchScreen.xib in Resources */,
289 | 22FC4C611FF748A50055FDF5 /* TOSearchBarLocalizable.strings in Resources */,
290 | 22707A8C1A53BDDF008E66CC /* Images.xcassets in Resources */,
291 | );
292 | runOnlyForDeploymentPostprocessing = 0;
293 | };
294 | /* End PBXResourcesBuildPhase section */
295 |
296 | /* Begin PBXSourcesBuildPhase section */
297 | 22707A771A53BDDF008E66CC /* Sources */ = {
298 | isa = PBXSourcesBuildPhase;
299 | buildActionMask = 2147483647;
300 | files = (
301 | 22B177041FF6242600F974BB /* TODocumentPickerViewController.m in Sources */,
302 | 2205C43E1EBF0B1600B20229 /* TODocumentsDataSource.m in Sources */,
303 | 22F806D71ECAC5EB0053C94E /* TODocumentPickerLocalDiskDataSource.m in Sources */,
304 | 22DF14A61A8104F8002E9DE4 /* UIImage+TODocumentPickerIcons.m in Sources */,
305 | 220D33011D33F0440004E4BE /* TODocumentPickerConfiguration.m in Sources */,
306 | 22FC4C621FF748A50055FDF5 /* TOSearchBar.m in Sources */,
307 | 22707A871A53BDDF008E66CC /* TOViewController.m in Sources */,
308 | 22FC4C631FF748A50055FDF5 /* TOSearchBar+Assets.m in Sources */,
309 | 22A3E19D1A56857700C4839E /* TODocumentPickerItem.m in Sources */,
310 | 22B177011FF60F6700F974BB /* TODocumentPickerTheme.m in Sources */,
311 | 22A3E1A01A5685A000C4839E /* TODocumentPickerHeaderView.m in Sources */,
312 | 22AF79871D2C01550066965F /* TODocumentPickerConstants.m in Sources */,
313 | 22A3E1A31A56880C00C4839E /* TODocumentPickerSegmentedControl.m in Sources */,
314 | 22A5E42C1A7B17F8000CD302 /* TODocumentPickerTableView.m in Sources */,
315 | 22707A841A53BDDF008E66CC /* TOAppDelegate.m in Sources */,
316 | 22B177071FF630E200F974BB /* TODocumentPickerTableViewCell.m in Sources */,
317 | 225C38F91A7E37DC004DA4D9 /* TODocumentPickerItemManager.m in Sources */,
318 | 22707A811A53BDDF008E66CC /* main.m in Sources */,
319 | );
320 | runOnlyForDeploymentPostprocessing = 0;
321 | };
322 | /* End PBXSourcesBuildPhase section */
323 |
324 | /* Begin PBXVariantGroup section */
325 | 22707A881A53BDDF008E66CC /* Main.storyboard */ = {
326 | isa = PBXVariantGroup;
327 | children = (
328 | 22707A891A53BDDF008E66CC /* Base */,
329 | 2211A3CF1A5B80900089106B /* ja */,
330 | );
331 | name = Main.storyboard;
332 | sourceTree = "";
333 | };
334 | 22707A8D1A53BDDF008E66CC /* LaunchScreen.xib */ = {
335 | isa = PBXVariantGroup;
336 | children = (
337 | 22707A8E1A53BDDF008E66CC /* Base */,
338 | 2211A3D01A5B80900089106B /* ja */,
339 | );
340 | name = LaunchScreen.xib;
341 | sourceTree = "";
342 | };
343 | 22FC4C4E1FF748A50055FDF5 /* TOSearchBarLocalizable.strings */ = {
344 | isa = PBXVariantGroup;
345 | children = (
346 | 22FC4C4F1FF748A50055FDF5 /* de */,
347 | 22FC4C501FF748A50055FDF5 /* zh-Hans */,
348 | 22FC4C511FF748A50055FDF5 /* ja */,
349 | 22FC4C521FF748A50055FDF5 /* en */,
350 | 22FC4C541FF748A50055FDF5 /* es */,
351 | 22FC4C551FF748A50055FDF5 /* da */,
352 | 22FC4C561FF748A50055FDF5 /* it */,
353 | 22FC4C571FF748A50055FDF5 /* ko */,
354 | 22FC4C581FF748A50055FDF5 /* Base */,
355 | 22FC4C591FF748A50055FDF5 /* zh-Hant */,
356 | 22FC4C5B1FF748A50055FDF5 /* pl */,
357 | 22FC4C5C1FF748A50055FDF5 /* ru */,
358 | 22FC4C5E1FF748A50055FDF5 /* fr */,
359 | 22FC4C5F1FF748A50055FDF5 /* nl */,
360 | );
361 | name = TOSearchBarLocalizable.strings;
362 | sourceTree = "";
363 | };
364 | /* End PBXVariantGroup section */
365 |
366 | /* Begin XCBuildConfiguration section */
367 | 22707A9C1A53BDDF008E66CC /* Debug */ = {
368 | isa = XCBuildConfiguration;
369 | buildSettings = {
370 | ALWAYS_SEARCH_USER_PATHS = NO;
371 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
372 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
373 | CLANG_CXX_LIBRARY = "libc++";
374 | CLANG_ENABLE_MODULES = YES;
375 | CLANG_ENABLE_OBJC_ARC = YES;
376 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
377 | CLANG_WARN_BOOL_CONVERSION = YES;
378 | CLANG_WARN_COMMA = YES;
379 | CLANG_WARN_CONSTANT_CONVERSION = YES;
380 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
381 | CLANG_WARN_EMPTY_BODY = YES;
382 | CLANG_WARN_ENUM_CONVERSION = YES;
383 | CLANG_WARN_INFINITE_RECURSION = YES;
384 | CLANG_WARN_INT_CONVERSION = YES;
385 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
386 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
387 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
388 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
389 | CLANG_WARN_STRICT_PROTOTYPES = YES;
390 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
391 | CLANG_WARN_UNREACHABLE_CODE = YES;
392 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
393 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
394 | COPY_PHASE_STRIP = NO;
395 | ENABLE_STRICT_OBJC_MSGSEND = YES;
396 | ENABLE_TESTABILITY = YES;
397 | GCC_C_LANGUAGE_STANDARD = gnu99;
398 | GCC_DYNAMIC_NO_PIC = NO;
399 | GCC_NO_COMMON_BLOCKS = YES;
400 | GCC_OPTIMIZATION_LEVEL = 0;
401 | GCC_PREPROCESSOR_DEFINITIONS = (
402 | "DEBUG=1",
403 | "$(inherited)",
404 | );
405 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
406 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
407 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
408 | GCC_WARN_UNDECLARED_SELECTOR = YES;
409 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
410 | GCC_WARN_UNUSED_FUNCTION = YES;
411 | GCC_WARN_UNUSED_VARIABLE = YES;
412 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
413 | MTL_ENABLE_DEBUG_INFO = YES;
414 | ONLY_ACTIVE_ARCH = YES;
415 | SDKROOT = iphoneos;
416 | TARGETED_DEVICE_FAMILY = "1,2";
417 | };
418 | name = Debug;
419 | };
420 | 22707A9D1A53BDDF008E66CC /* Release */ = {
421 | isa = XCBuildConfiguration;
422 | buildSettings = {
423 | ALWAYS_SEARCH_USER_PATHS = NO;
424 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
425 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
426 | CLANG_CXX_LIBRARY = "libc++";
427 | CLANG_ENABLE_MODULES = YES;
428 | CLANG_ENABLE_OBJC_ARC = YES;
429 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
430 | CLANG_WARN_BOOL_CONVERSION = YES;
431 | CLANG_WARN_COMMA = YES;
432 | CLANG_WARN_CONSTANT_CONVERSION = YES;
433 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
434 | CLANG_WARN_EMPTY_BODY = YES;
435 | CLANG_WARN_ENUM_CONVERSION = YES;
436 | CLANG_WARN_INFINITE_RECURSION = YES;
437 | CLANG_WARN_INT_CONVERSION = YES;
438 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
439 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
440 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
441 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
442 | CLANG_WARN_STRICT_PROTOTYPES = YES;
443 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
444 | CLANG_WARN_UNREACHABLE_CODE = YES;
445 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
446 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
447 | COPY_PHASE_STRIP = YES;
448 | ENABLE_NS_ASSERTIONS = NO;
449 | ENABLE_STRICT_OBJC_MSGSEND = YES;
450 | GCC_C_LANGUAGE_STANDARD = gnu99;
451 | GCC_NO_COMMON_BLOCKS = YES;
452 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
453 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
454 | GCC_WARN_UNDECLARED_SELECTOR = YES;
455 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
456 | GCC_WARN_UNUSED_FUNCTION = YES;
457 | GCC_WARN_UNUSED_VARIABLE = YES;
458 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
459 | MTL_ENABLE_DEBUG_INFO = NO;
460 | SDKROOT = iphoneos;
461 | TARGETED_DEVICE_FAMILY = "1,2";
462 | VALIDATE_PRODUCT = YES;
463 | };
464 | name = Release;
465 | };
466 | 22707A9F1A53BDDF008E66CC /* Debug */ = {
467 | isa = XCBuildConfiguration;
468 | buildSettings = {
469 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
470 | DEVELOPMENT_TEAM = 6LF3GMKZAB;
471 | INFOPLIST_FILE = TODocumentPickerViewControllerExample/Info.plist;
472 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
473 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
474 | PRODUCT_BUNDLE_IDENTIFIER = "com.timoliver.$(PRODUCT_NAME:rfc1034identifier)";
475 | PRODUCT_NAME = "$(TARGET_NAME)";
476 | };
477 | name = Debug;
478 | };
479 | 22707AA01A53BDDF008E66CC /* Release */ = {
480 | isa = XCBuildConfiguration;
481 | buildSettings = {
482 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
483 | DEVELOPMENT_TEAM = 6LF3GMKZAB;
484 | INFOPLIST_FILE = TODocumentPickerViewControllerExample/Info.plist;
485 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
486 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
487 | PRODUCT_BUNDLE_IDENTIFIER = "com.timoliver.$(PRODUCT_NAME:rfc1034identifier)";
488 | PRODUCT_NAME = "$(TARGET_NAME)";
489 | };
490 | name = Release;
491 | };
492 | /* End XCBuildConfiguration section */
493 |
494 | /* Begin XCConfigurationList section */
495 | 22707A761A53BDDF008E66CC /* Build configuration list for PBXProject "TODocumentPickerViewControllerExample" */ = {
496 | isa = XCConfigurationList;
497 | buildConfigurations = (
498 | 22707A9C1A53BDDF008E66CC /* Debug */,
499 | 22707A9D1A53BDDF008E66CC /* Release */,
500 | );
501 | defaultConfigurationIsVisible = 0;
502 | defaultConfigurationName = Release;
503 | };
504 | 22707A9E1A53BDDF008E66CC /* Build configuration list for PBXNativeTarget "TODocumentPickerViewControllerExample" */ = {
505 | isa = XCConfigurationList;
506 | buildConfigurations = (
507 | 22707A9F1A53BDDF008E66CC /* Debug */,
508 | 22707AA01A53BDDF008E66CC /* Release */,
509 | );
510 | defaultConfigurationIsVisible = 0;
511 | defaultConfigurationName = Release;
512 | };
513 | /* End XCConfigurationList section */
514 | };
515 | rootObject = 22707A731A53BDDF008E66CC /* Project object */;
516 | }
517 |
--------------------------------------------------------------------------------