├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── LICENCE ├── PYSearch.podspec ├── PYSearch ├── NSBundle+PYSearchExtension.h ├── NSBundle+PYSearchExtension.m ├── PYSearch.bundle │ ├── back@2x.png │ ├── back@3x.png │ ├── cell-content-line-vertical@2x.png │ ├── cell-content-line-vertical@3x.png │ ├── cell-content-line@2x.png │ ├── cell-content-line@3x.png │ ├── clearImage@2x.png │ ├── clearImage@3x.png │ ├── close@2x.png │ ├── close@3x.png │ ├── empty@2x.png │ ├── empty@3x.png │ ├── en.lproj │ │ └── Localizable.strings │ ├── es.lproj │ │ └── Localizable.strings │ ├── fr.lproj │ │ └── Localizable.strings │ ├── search@2x.png │ ├── search@3x.png │ ├── search_history@2x.png │ ├── search_history@3x.png │ ├── zh-Hans.lproj │ │ └── Localizable.strings │ └── zh-Hant.lproj │ │ └── Localizable.strings ├── PYSearch.h ├── PYSearchConst.h ├── PYSearchConst.m ├── PYSearchSuggestionViewController.h ├── PYSearchSuggestionViewController.m ├── PYSearchViewController.h ├── PYSearchViewController.m ├── UIColor+PYSearchExtension.h ├── UIColor+PYSearchExtension.m ├── UIView+PYSearchExtension.h └── UIView+PYSearchExtension.m ├── PYSearchExample ├── PYSearchExample.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ │ └── iphone5solo.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── iphone5solo.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── PYSearchExample.xcscheme │ │ └── xcschememanagement.plist ├── PYSearchExample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── Info.plist │ ├── PYSearchExampleController.h │ ├── PYSearchExampleController.m │ ├── PYTempViewController.h │ ├── PYTempViewController.m │ ├── en.lproj │ │ └── Localizable.strings │ ├── es.lproj │ │ ├── LaunchScreen.strings │ │ └── Localizable.strings │ ├── fr.lproj │ │ ├── LaunchScreen.strings │ │ └── Localizable.strings │ ├── main.m │ ├── zh-Hans.lproj │ │ ├── LaunchScreen.strings │ │ └── Localizable.strings │ └── zh-Hant.lproj │ │ ├── LaunchScreen.strings │ │ └── Localizable.strings ├── PYSearchExampleTests │ ├── Info.plist │ └── PYSearchExampleTests.m └── PYSearchExampleUITests │ ├── Info.plist │ └── PYSearchExampleUITests.m └── README.md /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Code owners for PYSearch 2 | * @ko1o 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | ### 问题描述 7 | 12 | #### 重现步骤 13 | 26 | 27 | 28 | 29 | #### 预期结果 30 | 34 | 35 | 36 | 37 | #### 实际结果 38 | 42 | 43 | 44 | 45 | ### 受影响的设备 46 | 53 | 54 | 55 | 56 | ### 版本信息 57 | 63 | 64 | - PYSearch:vX.X.X 65 | - Xcode:vX.X.X 66 | - macOS:vX.X.X 67 | - CocoaPods:vX.X.X 68 | 69 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Reference Issue 2 | 3 | -------------------------------------------------------------------------------- /.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/screenshots 54 | 55 | #Code Injection 56 | # 57 | # After new code Injection tools there's a generated folder /iOSInjectionProject 58 | # https://github.com/johnno1962/injectionforxcode 59 | 60 | iOSInjectionProject/ 61 | .DS_Store 62 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode8 2 | language: objective-c 3 | xcode_workspace: PYSearchExample/PYSearchExample.xcodeproj 4 | xcode_schemes: PYSearchExample 5 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 CoderKo1o 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PYSearch.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'PYSearch' 3 | s.version = '0.9.1' 4 | s.summary = 'An elegant search controller which replaces the UISearchController for iOS.' 5 | s.homepage = 'https://github.com/ko1o/PYSearch' 6 | s.license = 'MIT' 7 | s.authors = {'CoderKo1o' => '499491531@qq.com'} 8 | s.platform = :ios, '7.0' 9 | s.source = {:git => 'https://github.com/ko1o/PYSearch.git', :tag => s.version} 10 | s.source_files = 'PYSearch/**/*.{h,m}' 11 | s.resource = 'PYSearch/PYSearch.bundle' 12 | s.requires_arc = true 13 | end 14 | 15 | 16 | -------------------------------------------------------------------------------- /PYSearch/NSBundle+PYSearchExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import 8 | #import 9 | 10 | @interface NSBundle (PYSearchExtension) 11 | 12 | /** 13 | Get the localized string 14 | 15 | @param key key for localized string 16 | @return a localized string 17 | */ 18 | + (NSString *)py_localizedStringForKey:(NSString *)key; 19 | 20 | /** 21 | Get the path of `PYSearch.bundle`. 22 | 23 | @return path of the `PYSearch.bundle` 24 | */ 25 | + (NSBundle *)py_searchBundle; 26 | 27 | /** 28 | Get the image in the `PYSearch.bundle` path 29 | 30 | @param name name of image 31 | @return a image 32 | */ 33 | + (UIImage *)py_imageNamed:(NSString *)name; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /PYSearch/NSBundle+PYSearchExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import "NSBundle+PYSearchExtension.h" 8 | #import "PYSearchViewController.h" 9 | 10 | @implementation NSBundle (PYSearchExtension) 11 | 12 | + (NSBundle *)py_searchBundle 13 | { 14 | static NSBundle *searchBundle = nil; 15 | if (nil == searchBundle) { 16 | //Default use `[NSBundle mainBundle]`. 17 | searchBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"PYSearch" ofType:@"bundle"]]; 18 | /** 19 | If you use pod import and configure `use_frameworks` in Podfile, [NSBundle mainBundle] does not load the `PYSearch.fundle` resource file in `PYSearch.framework`. 20 | */ 21 | if (nil == searchBundle) { // Empty description resource file in `PYSearch.framework`. 22 | searchBundle = [NSBundle bundleWithPath:[[NSBundle bundleForClass:[PYSearchViewController class]] pathForResource:@"PYSearch" ofType:@"bundle"]]; 23 | } 24 | } 25 | return searchBundle; 26 | } 27 | 28 | + (NSString *)py_localizedStringForKey:(NSString *)key; 29 | { 30 | return [self py_localizedStringForKey:key value:nil]; 31 | } 32 | 33 | + (NSString *)py_localizedStringForKey:(NSString *)key value:(NSString *)value 34 | { 35 | static NSBundle *bundle = nil; 36 | if (nil == bundle) { 37 | NSString *language = [NSLocale preferredLanguages].firstObject; 38 | if ([language hasPrefix:@"en"]) language = @"en"; 39 | else if ([language hasPrefix:@"es"]) language = @"es"; 40 | else if ([language hasPrefix:@"fr"]) language = @"fr"; 41 | else if ([language hasPrefix:@"zh"]) { 42 | if ([language rangeOfString:@"Hans"].location != NSNotFound) { 43 | language = @"zh-Hans"; 44 | } else { 45 | language = @"zh-Hant"; 46 | } 47 | } else { 48 | language = @"en"; 49 | } 50 | 51 | // Find resources from `PYSearch.bundle` 52 | bundle = [NSBundle bundleWithPath:[[NSBundle py_searchBundle] pathForResource:language ofType:@"lproj"]]; 53 | } 54 | value = [bundle localizedStringForKey:key value:value table:nil]; 55 | return [[NSBundle mainBundle] localizedStringForKey:key value:value table:nil]; 56 | } 57 | 58 | + (UIImage *)py_imageNamed:(NSString *)name 59 | { 60 | CGFloat scale = [[UIScreen mainScreen] scale]; 61 | name = 3.0 == scale ? [NSString stringWithFormat:@"%@@3x.png", name] : [NSString stringWithFormat:@"%@@2x.png", name]; 62 | UIImage *image = [UIImage imageWithContentsOfFile:[[[NSBundle py_searchBundle] resourcePath] stringByAppendingPathComponent:name]]; 63 | return image; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/back@2x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/back@3x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/cell-content-line-vertical@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/cell-content-line-vertical@2x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/cell-content-line-vertical@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/cell-content-line-vertical@3x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/cell-content-line@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/cell-content-line@2x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/cell-content-line@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/cell-content-line@3x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/clearImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/clearImage@2x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/clearImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/clearImage@3x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/close@2x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/close@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/close@3x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/empty@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/empty@2x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/empty@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/empty@3x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "PYSearchSearchPlaceholderText" = "Search content"; 2 | "PYSearchHotSearchText" = "Popular searches"; 3 | "PYSearchSearchHistoryText" = "Search history"; 4 | "PYSearchEmptySearchHistoryText" = "Clear the search history"; 5 | 6 | "PYSearchEmptyButtonText" = "Clear"; 7 | "PYSearchEmptySearchHistoryLogText" = "Clear the search history"; 8 | "PYSearchCancelButtonText" = "Cancel"; 9 | "PYSearchBackButtonText" = "Back"; 10 | -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/es.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "PYSearchSearchPlaceholderText" = "Buscar contenido"; 2 | "PYSearchHotSearchText" = "Búsquedas populares"; 3 | "PYSearchSearchHistoryText" = "Buscar Historia"; 4 | "PYSearchEmptySearchHistoryText" = "Borrar el historial de búsqueda"; 5 | 6 | "PYSearchEmptyButtonText" = "Borrar"; 7 | "PYSearchEmptySearchHistoryLogText" = "Borrar el historial de búsqueda"; 8 | "PYSearchCancelButtonText" = "Cancelar"; 9 | "PYSearchBackButtonText" = "Volver a"; 10 | -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/fr.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "PYSearchSearchPlaceholderText" = "Recherche de contenu"; 2 | "PYSearchHotSearchText" = "Recherches populaires"; 3 | "PYSearchSearchHistoryText" = "Historique des recherches"; 4 | "PYSearchEmptySearchHistoryText" = "Effacer l'historique des recherches"; 5 | 6 | "PYSearchEmptyButtonText" = "Effacer"; 7 | "PYSearchEmptySearchHistoryLogText" = "Effacer l'historique des recherches"; 8 | "PYSearchCancelButtonText" = "Annuler"; 9 | "PYSearchBackButtonText" = "Retour à"; 10 | 11 | -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/search@2x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/search@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/search@3x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/search_history@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/search_history@2x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/search_history@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/search_history@3x.png -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearch/PYSearch.bundle/zh-Hans.lproj/Localizable.strings -------------------------------------------------------------------------------- /PYSearch/PYSearch.bundle/zh-Hant.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "PYSearchSearchPlaceholderText" = "搜索內容"; 2 | "PYSearchHotSearchText" = "熱門搜索"; 3 | "PYSearchSearchHistoryText" = "搜索歷史"; 4 | "PYSearchEmptySearchHistoryText" = "清空搜索歷史"; 5 | 6 | "PYSearchEmptyButtonText" = "清空"; 7 | "PYSearchEmptySearchHistoryLogText" = "清空搜索歷史"; 8 | "PYSearchCancelButtonText" = "取消"; 9 | "PYSearchBackButtonText" = "返回"; 10 | -------------------------------------------------------------------------------- /PYSearch/PYSearch.h: -------------------------------------------------------------------------------- 1 | // 2 | // 代码地址: https://github.com/iphone5solo/PYSearch 3 | // 代码地址: http://www.code4app.com/thread-11175-1-1.html 4 | // Created by CoderKo1o. 5 | // Copyright © 2016年 iphone5solo. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | #import "PYSearchViewController.h" 11 | -------------------------------------------------------------------------------- /PYSearch/PYSearchConst.h: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import 8 | #import "UIView+PYSearchExtension.h" 9 | #import "UIColor+PYSearchExtension.h" 10 | #import "NSBundle+PYSearchExtension.h" 11 | 12 | #define PYSEARCH_MARGIN 10 13 | #define PYSEARCH_BACKGROUND_COLOR PYSEARCH_COLOR(255, 255, 255) 14 | 15 | #ifdef DEBUG 16 | #define PYSEARCH_LOG(...) NSLog(__VA_ARGS__) 17 | #else 18 | #define PYSEARCH_LOG(...) 19 | #endif 20 | 21 | #define PYSEARCH_COLOR(r,g,b) [UIColor colorWithRed:(r/255.0) green:(g/255.0) blue:(b/255.0) alpha:1.0] 22 | #define PYSEARCH_RANDOM_COLOR PYSEARCH_COLOR(arc4random_uniform(256),arc4random_uniform(256),arc4random_uniform(256)) 23 | 24 | #define PYSEARCH_DEPRECATED(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead) 25 | 26 | #define PYSEARCH_REALY_SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width 27 | #define PYSEARCH_REALY_SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height 28 | #define PYScreenW (PYSEARCH_REALY_SCREEN_WIDTH < PYSEARCH_REALY_SCREEN_HEIGHT ? PYSEARCH_REALY_SCREEN_WIDTH : PYSEARCH_REALY_SCREEN_HEIGHT) 29 | #define PYScreenH (PYSEARCH_REALY_SCREEN_WIDTH > PYSEARCH_REALY_SCREEN_HEIGHT ? PYSEARCH_REALY_SCREEN_WIDTH : PYSEARCH_REALY_SCREEN_HEIGHT) 30 | #define PYSEARCH_SCREEN_SIZE CGSizeMake(PYScreenW, PYScreenH) 31 | 32 | #define PYSEARCH_SEARCH_HISTORY_CACHE_PATH [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"PYSearchhistories.plist"] // the path of search record cached 33 | 34 | UIKIT_EXTERN NSString *const PYSearchSearchPlaceholderText; 35 | UIKIT_EXTERN NSString *const PYSearchHotSearchText; 36 | UIKIT_EXTERN NSString *const PYSearchSearchHistoryText; 37 | UIKIT_EXTERN NSString *const PYSearchEmptySearchHistoryText; 38 | UIKIT_EXTERN NSString *const PYSearchEmptyButtonText; 39 | UIKIT_EXTERN NSString *const PYSearchEmptySearchHistoryLogText; 40 | UIKIT_EXTERN NSString *const PYSearchCancelButtonText; 41 | UIKIT_EXTERN NSString *const PYSearchBackButtonText; 42 | -------------------------------------------------------------------------------- /PYSearch/PYSearchConst.m: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import 8 | 9 | NSString *const PYSearchSearchPlaceholderText = @"PYSearchSearchPlaceholderText"; 10 | NSString *const PYSearchHotSearchText = @"PYSearchHotSearchText"; 11 | NSString *const PYSearchSearchHistoryText = @"PYSearchSearchHistoryText"; 12 | NSString *const PYSearchEmptySearchHistoryText = @"PYSearchEmptySearchHistoryText"; 13 | 14 | NSString *const PYSearchEmptyButtonText = @"PYSearchEmptyButtonText"; 15 | NSString *const PYSearchEmptySearchHistoryLogText = @"PYSearchEmptySearchHistoryLogText"; 16 | NSString *const PYSearchCancelButtonText = @"PYSearchCancelButtonText"; 17 | NSString *const PYSearchBackButtonText = @"PYSearchBackButtonText"; 18 | -------------------------------------------------------------------------------- /PYSearch/PYSearchSuggestionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import 8 | 9 | typedef void(^PYSearchSuggestionDidSelectCellBlock)(UITableViewCell *selectedCell); 10 | 11 | @protocol PYSearchSuggestionViewDataSource 12 | 13 | @required 14 | - (UITableViewCell *)searchSuggestionView:(UITableView *)searchSuggestionView cellForRowAtIndexPath:(NSIndexPath *)indexPath; 15 | - (NSInteger)searchSuggestionView:(UITableView *)searchSuggestionView numberOfRowsInSection:(NSInteger)section; 16 | @optional 17 | - (NSInteger)numberOfSectionsInSearchSuggestionView:(UITableView *)searchSuggestionView; 18 | - (CGFloat)searchSuggestionView:(UITableView *)searchSuggestionView heightForRowAtIndexPath:(NSIndexPath *)indexPath; 19 | 20 | @end 21 | 22 | @interface PYSearchSuggestionViewController : UITableViewController 23 | 24 | @property (nonatomic, weak) id dataSource; 25 | @property (nonatomic, copy) NSArray *searchSuggestions; 26 | @property (nonatomic, copy) PYSearchSuggestionDidSelectCellBlock didSelectCellBlock; 27 | 28 | + (instancetype)searchSuggestionViewControllerWithDidSelectCellBlock:(PYSearchSuggestionDidSelectCellBlock)didSelectCellBlock; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /PYSearch/PYSearchSuggestionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import "PYSearchSuggestionViewController.h" 8 | #import "PYSearchConst.h" 9 | 10 | @interface PYSearchSuggestionViewController () 11 | 12 | @property (nonatomic, assign) UIEdgeInsets originalContentInsetWhenKeyboardShow; 13 | @property (nonatomic, assign) UIEdgeInsets originalContentInsetWhenKeyboardHidden; 14 | 15 | @property (nonatomic, assign) BOOL keyboardDidShow; 16 | 17 | @end 18 | 19 | @implementation PYSearchSuggestionViewController 20 | 21 | + (instancetype)searchSuggestionViewControllerWithDidSelectCellBlock:(PYSearchSuggestionDidSelectCellBlock)didSelectCellBlock 22 | { 23 | PYSearchSuggestionViewController *searchSuggestionVC = [[self alloc] init]; 24 | searchSuggestionVC.didSelectCellBlock = didSelectCellBlock; 25 | searchSuggestionVC.automaticallyAdjustsScrollViewInsets = NO; 26 | return searchSuggestionVC; 27 | } 28 | 29 | - (void)viewDidLoad { 30 | [super viewDidLoad]; 31 | 32 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 33 | if ([self.tableView respondsToSelector:@selector(setCellLayoutMarginsFollowReadableWidth:)]) { // For the adapter iPad 34 | self.tableView.cellLayoutMarginsFollowReadableWidth = NO; 35 | } 36 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboradFrameDidShow:) name:UIKeyboardDidShowNotification object:nil]; 37 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboradFrameDidHidden:) name:UIKeyboardDidHideNotification object:nil]; 38 | } 39 | 40 | - (void)dealloc 41 | { 42 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 43 | } 44 | 45 | - (void)viewWillDisappear:(BOOL)animated 46 | { 47 | [super viewWillDisappear:animated]; 48 | 49 | if (self.keyboardDidShow) { 50 | self.originalContentInsetWhenKeyboardShow = self.tableView.contentInset; 51 | } else { 52 | self.originalContentInsetWhenKeyboardHidden = self.tableView.contentInset; 53 | } 54 | } 55 | 56 | - (void)keyboradFrameDidShow:(NSNotification *)notification 57 | { 58 | self.keyboardDidShow = YES; 59 | [self setSearchSuggestions:_searchSuggestions]; 60 | } 61 | 62 | - (void)keyboradFrameDidHidden:(NSNotification *)notification 63 | { 64 | self.keyboardDidShow = NO; 65 | self.originalContentInsetWhenKeyboardHidden = UIEdgeInsetsMake(-30, 0, 30, 0); 66 | [self setSearchSuggestions:_searchSuggestions]; 67 | } 68 | 69 | #pragma mark - setter 70 | - (void)setSearchSuggestions:(NSArray *)searchSuggestions 71 | { 72 | _searchSuggestions = [searchSuggestions copy]; 73 | 74 | [self.tableView reloadData]; 75 | 76 | /** 77 | * Adjust the searchSugesstionView when the keyboard changes. 78 | * more information can see : https://github.com/iphone5solo/PYSearch/issues/61 79 | */ 80 | if (self.keyboardDidShow && !UIEdgeInsetsEqualToEdgeInsets(self.originalContentInsetWhenKeyboardShow, UIEdgeInsetsZero) && !UIEdgeInsetsEqualToEdgeInsets(self.originalContentInsetWhenKeyboardShow, UIEdgeInsetsMake(-30, 0, 30 - CGRectGetMaxY(self.navigationController.navigationBar.frame), 0))) { 81 | self.tableView.contentInset = self.originalContentInsetWhenKeyboardShow; 82 | } else if (!self.keyboardDidShow && !UIEdgeInsetsEqualToEdgeInsets(self.originalContentInsetWhenKeyboardHidden, UIEdgeInsetsZero) && !UIEdgeInsetsEqualToEdgeInsets(self.originalContentInsetWhenKeyboardHidden, UIEdgeInsetsMake(-30, 0, 30 - CGRectGetMaxY(self.navigationController.navigationBar.frame), 0))) { 83 | self.tableView.contentInset = self.originalContentInsetWhenKeyboardHidden; 84 | } 85 | self.tableView.contentOffset = CGPointMake(0, -self.tableView.contentInset.top); 86 | 87 | if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 11.0) { // iOS 11 88 | self.tableView.contentInset = UIEdgeInsetsMake(-30, 0, 0, 0); 89 | } 90 | } 91 | 92 | #pragma mark - Table view data source 93 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 94 | if ([self.dataSource respondsToSelector:@selector(numberOfSectionsInSearchSuggestionView:)]) { 95 | return [self.dataSource numberOfSectionsInSearchSuggestionView:tableView]; 96 | } 97 | return 1; 98 | } 99 | 100 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 101 | if ([self.dataSource respondsToSelector:@selector(searchSuggestionView:numberOfRowsInSection:)]) { 102 | return [self.dataSource searchSuggestionView:tableView numberOfRowsInSection:section]; 103 | } 104 | return self.searchSuggestions.count; 105 | } 106 | 107 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 108 | if ([self.dataSource respondsToSelector:@selector(searchSuggestionView:cellForRowAtIndexPath:)]) { 109 | UITableViewCell *cell= [self.dataSource searchSuggestionView:tableView cellForRowAtIndexPath:indexPath]; 110 | if (cell) return cell; 111 | } 112 | 113 | static NSString *cellID = @"PYSearchSuggestionCellID"; 114 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID]; 115 | if (!cell) { 116 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID]; 117 | cell.textLabel.textColor = [UIColor darkGrayColor]; 118 | cell.textLabel.font = [UIFont systemFontOfSize:14]; 119 | cell.backgroundColor = [UIColor clearColor]; 120 | UIImageView *line = [[UIImageView alloc] initWithImage: [NSBundle py_imageNamed:@"cell-content-line"]]; 121 | line.py_height = 0.5; 122 | line.alpha = 0.7; 123 | line.py_x = PYSEARCH_MARGIN; 124 | line.py_y = 43; 125 | line.py_width = PYScreenW; 126 | [cell.contentView addSubview:line]; 127 | } 128 | cell.imageView.image = [NSBundle py_imageNamed:@"search"]; 129 | cell.textLabel.text = self.searchSuggestions[indexPath.row]; 130 | return cell; 131 | } 132 | 133 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 134 | { 135 | if ([self.dataSource respondsToSelector:@selector(searchSuggestionView:heightForRowAtIndexPath:)]) { 136 | return [self.dataSource searchSuggestionView:tableView heightForRowAtIndexPath:indexPath]; 137 | } 138 | return 44.0; 139 | } 140 | 141 | #pragma mark - UITableViewDelegate 142 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 143 | { 144 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 145 | 146 | if (self.didSelectCellBlock) self.didSelectCellBlock([tableView cellForRowAtIndexPath:indexPath]); 147 | } 148 | 149 | @end 150 | -------------------------------------------------------------------------------- /PYSearch/PYSearchViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import 8 | #import "PYSearchConst.h" 9 | 10 | @class PYSearchViewController, PYSearchSuggestionViewController; 11 | 12 | typedef void(^PYDidSearchBlock)(PYSearchViewController *searchViewController, UISearchBar *searchBar, NSString *searchText); 13 | 14 | /** 15 | style of popular search 16 | */ 17 | typedef NS_ENUM(NSInteger, PYHotSearchStyle) { 18 | PYHotSearchStyleNormalTag, // normal tag without border 19 | PYHotSearchStyleColorfulTag, // colorful tag without border, color of background is randrom and can be custom by `colorPol` 20 | PYHotSearchStyleBorderTag, // border tag, color of background is `clearColor` 21 | PYHotSearchStyleARCBorderTag, // broder tag with ARC, color of background is `clearColor` 22 | PYHotSearchStyleRankTag, // rank tag, color of background can be custom by `rankTagBackgroundColorHexStrings` 23 | PYHotSearchStyleRectangleTag, // rectangle tag, color of background is `clearColor` 24 | PYHotSearchStyleDefault = PYHotSearchStyleNormalTag // default is `PYHotSearchStyleNormalTag` 25 | }; 26 | 27 | /** 28 | style of search history 29 | */ 30 | typedef NS_ENUM(NSInteger, PYSearchHistoryStyle) { 31 | PYSearchHistoryStyleCell, // style of UITableViewCell 32 | PYSearchHistoryStyleNormalTag, // style of PYHotSearchStyleNormalTag 33 | PYSearchHistoryStyleColorfulTag, // style of PYHotSearchStyleColorfulTag 34 | PYSearchHistoryStyleBorderTag, // style of PYHotSearchStyleBorderTag 35 | PYSearchHistoryStyleARCBorderTag, // style of PYHotSearchStyleARCBorderTag 36 | PYSearchHistoryStyleDefault = PYSearchHistoryStyleCell // default is `PYSearchHistoryStyleCell` 37 | }; 38 | 39 | /** 40 | mode of search result view controller display 41 | */ 42 | typedef NS_ENUM(NSInteger, PYSearchResultShowMode) { 43 | PYSearchResultShowModeCustom, // custom, can be push or pop and so on. 44 | PYSearchResultShowModePush, // push, dispaly the view of search result by push 45 | PYSearchResultShowModeEmbed, // embed, dispaly the view of search result by embed 46 | PYSearchResultShowModeDefault = PYSearchResultShowModeCustom // defualt is `PYSearchResultShowModeCustom` 47 | }; 48 | /** 49 | mode of search view controller display 50 | */ 51 | typedef NS_ENUM(NSInteger, PYSearchViewControllerShowMode) { 52 | PYSearchViewControllerShowModeModal, // modal, dispaly the view of searchViewController by modal 53 | PYSearchViewControllerShowModePush, // push, dispaly the view of searchViewController by push 54 | PYSearchViewControllerShowDefault = PYSearchViewControllerShowModeModal // defualt is `PYSearchViewControllerShowModeModal` 55 | }; 56 | 57 | 58 | /** 59 | The protocol of data source, you can custom the suggestion view by implement these methods the data scource. 60 | */ 61 | @protocol PYSearchViewControllerDataSource 62 | 63 | @optional 64 | 65 | /** 66 | Return a `UITableViewCell` object. 67 | 68 | @param searchSuggestionView view which display search suggestions 69 | @param indexPath indexPath of row 70 | @return a `UITableViewCell` object 71 | */ 72 | - (UITableViewCell *)searchSuggestionView:(UITableView *)searchSuggestionView cellForRowAtIndexPath:(NSIndexPath *)indexPath; 73 | 74 | /** 75 | Return number of rows in section. 76 | 77 | @param searchSuggestionView view which display search suggestions 78 | @param section index of section 79 | @return number of rows in section 80 | */ 81 | - (NSInteger)searchSuggestionView:(UITableView *)searchSuggestionView numberOfRowsInSection:(NSInteger)section; 82 | 83 | /** 84 | Return number of sections in search suggestion view. 85 | 86 | @param searchSuggestionView view which display search suggestions 87 | @return number of sections 88 | */ 89 | - (NSInteger)numberOfSectionsInSearchSuggestionView:(UITableView *)searchSuggestionView; 90 | 91 | /** 92 | Return height for row. 93 | 94 | @param searchSuggestionView view which display search suggestions 95 | @param indexPath indexPath of row 96 | @return height of row 97 | */ 98 | - (CGFloat)searchSuggestionView:(UITableView *)searchSuggestionView heightForRowAtIndexPath:(NSIndexPath *)indexPath; 99 | 100 | @end 101 | 102 | 103 | /** 104 | The protocol of delegate 105 | */ 106 | @protocol PYSearchViewControllerDelegate 107 | 108 | @optional 109 | 110 | /** 111 | Called when search begain. 112 | 113 | @param searchViewController search view controller 114 | @param searchBar search bar 115 | @param searchText text for search 116 | */ 117 | - (void)searchViewController:(PYSearchViewController *)searchViewController 118 | didSearchWithSearchBar:(UISearchBar *)searchBar 119 | searchText:(NSString *)searchText; 120 | 121 | /** 122 | Called when popular search is selected. 123 | 124 | @param searchViewController search view controller 125 | @param index index of tag 126 | @param searchText text for search 127 | 128 | Note: `searchViewController:didSearchWithSearchBar:searchText:` will not be called when this method is implemented. 129 | */ 130 | - (void)searchViewController:(PYSearchViewController *)searchViewController 131 | didSelectHotSearchAtIndex:(NSInteger)index 132 | searchText:(NSString *)searchText; 133 | 134 | /** 135 | Called when search history is selected. 136 | 137 | @param searchViewController search view controller 138 | @param index index of tag or row 139 | @param searchText text for search 140 | 141 | Note: `searchViewController:didSearchWithSearchBar:searchText:` will not be called when this method is implemented. 142 | */ 143 | - (void)searchViewController:(PYSearchViewController *)searchViewController 144 | didSelectSearchHistoryAtIndex:(NSInteger)index 145 | searchText:(NSString *)searchText; 146 | 147 | /** 148 | Called when search suggestion is selected. 149 | 150 | @param searchViewController search view controller 151 | @param index index of row 152 | @param searchText text for search 153 | 154 | Note: `searchViewController:didSearchWithSearchBar:searchText:` will not be called when this method is implemented. 155 | */ 156 | - (void)searchViewController:(PYSearchViewController *)searchViewController 157 | didSelectSearchSuggestionAtIndex:(NSInteger)index 158 | searchText:(NSString *)searchText PYSEARCH_DEPRECATED("Use searchViewController:didSelectSearchSuggestionAtIndexPath:searchText:"); 159 | 160 | /** 161 | Called when search suggestion is selected, the method support more custom of search suggestion view. 162 | 163 | @param searchViewController search view controller 164 | @param indexPath indexPath of row 165 | @param searchBar search bar 166 | 167 | Note: `searchViewController:didSearchWithSearchBar:searchText:` and `searchViewController:didSelectSearchSuggestionAtIndex:searchText:` will not be called when this method is implemented. 168 | Suggestion: To ensure that can cache selected custom search suggestion records, you need to set `searchBar.text` = "custom search text". 169 | */ 170 | - (void)searchViewController:(PYSearchViewController *)searchViewController didSelectSearchSuggestionAtIndexPath:(NSIndexPath *)indexPath 171 | searchBar:(UISearchBar *)searchBar; 172 | 173 | /** 174 | Called when search text did change, you can reload data of suggestion view thought this method. 175 | 176 | @param searchViewController search view controller 177 | @param searchBar search bar 178 | @param searchText text for search 179 | */ 180 | - (void)searchViewController:(PYSearchViewController *)searchViewController 181 | searchTextDidChange:(UISearchBar *)searchBar 182 | searchText:(NSString *)searchText; 183 | 184 | /** 185 | Called when cancel item did press, default execute `[self dismissViewControllerAnimated:YES completion:nil]`. 186 | 187 | @param searchViewController search view controller 188 | */ 189 | - (void)didClickCancel:(PYSearchViewController *)searchViewController; 190 | 191 | /** 192 | Called when back item did press, default execute `[self.navigationController popViewControllerAnimated:YES]`. 193 | 194 | @param searchViewController search view controller 195 | */ 196 | - (void)didClickBack:(PYSearchViewController *)searchViewController; 197 | 198 | @end 199 | 200 | @interface PYSearchViewController : UIViewController 201 | 202 | /** 203 | The delegate 204 | */ 205 | @property (nonatomic, weak) id delegate; 206 | 207 | /** 208 | The data source 209 | */ 210 | @property (nonatomic, weak) id dataSource; 211 | 212 | /** 213 | Ranking the background color of the corresponding hexadecimal string (eg: @"#ffcc99") array (just four colors) when `hotSearchStyle` is `PYHotSearchStyleRankTag`. 214 | */ 215 | @property (nonatomic, strong) NSArray *rankTagBackgroundColorHexStrings; 216 | 217 | /** 218 | The pool of color which are use in colorful tag when `hotSearchStyle` is `PYHotSearchStyleColorfulTag`. 219 | */ 220 | @property (nonatomic, strong) NSMutableArray *colorPol; 221 | 222 | /** 223 | Whether swap the popular search and search history location, default is NO. 224 | 225 | Note: It is‘t effective when `searchHistoryStyle` is `PYSearchHistoryStyleCell`. 226 | */ 227 | @property (nonatomic, assign) BOOL swapHotSeachWithSearchHistory; 228 | 229 | /** 230 | The element of popular search 231 | */ 232 | @property (nonatomic, copy) NSArray *hotSearches; 233 | 234 | /** 235 | The tags of popular search 236 | */ 237 | @property (nonatomic, copy) NSArray *hotSearchTags; 238 | 239 | /** 240 | The label of popular search header 241 | */ 242 | @property (nonatomic, weak) UILabel *hotSearchHeader; 243 | 244 | /** 245 | Whether show popular search, default is YES. 246 | */ 247 | @property (nonatomic, assign) BOOL showHotSearch; 248 | 249 | /** 250 | The title of popular search 251 | */ 252 | @property (nonatomic, copy) NSString *hotSearchTitle; 253 | 254 | /** 255 | The tags of search history 256 | */ 257 | @property (nonatomic, copy) NSArray *searchHistoryTags; 258 | 259 | /** 260 | The label of search history header 261 | */ 262 | @property (nonatomic, weak) UILabel *searchHistoryHeader; 263 | 264 | /** 265 | The title of search history 266 | */ 267 | @property (nonatomic, copy) NSString *searchHistoryTitle; 268 | 269 | /** 270 | Whether show search history, default is YES. 271 | 272 | Note: search record is not cache when it is NO. 273 | */ 274 | @property (nonatomic, assign) BOOL showSearchHistory; 275 | 276 | /** 277 | The path of cache search record, default is `PYSEARCH_SEARCH_HISTORY_CACHE_PATH`. 278 | */ 279 | @property (nonatomic, copy) NSString *searchHistoriesCachePath; 280 | 281 | /** 282 | The number of cache search record, default is 20. 283 | */ 284 | @property (nonatomic, assign) NSUInteger searchHistoriesCount; 285 | 286 | /** 287 | Whether remove the space of search string, default is YES. 288 | */ 289 | @property (nonatomic, assign) BOOL removeSpaceOnSearchString; 290 | 291 | /** 292 | The button of empty search record when `searchHistoryStyle` is’t `PYSearchHistoryStyleCell`. 293 | */ 294 | @property (nonatomic, weak) UIButton *emptyButton; 295 | 296 | /** 297 | The label od empty search record when `searchHistoryStyle` is `PYSearchHistoryStyleCell`. 298 | */ 299 | @property (nonatomic, weak) UILabel *emptySearchHistoryLabel; 300 | 301 | /** 302 | The style of popular search, default is `PYHotSearchStyleNormalTag`. 303 | */ 304 | @property (nonatomic, assign) PYHotSearchStyle hotSearchStyle; 305 | 306 | /** 307 | The style of search histrory, default is `PYSearchHistoryStyleCell`. 308 | */ 309 | @property (nonatomic, assign) PYSearchHistoryStyle searchHistoryStyle; 310 | 311 | /** 312 | The mode of display search result view controller, default is `PYSearchResultShowModeCustom`. 313 | */ 314 | @property (nonatomic, assign) PYSearchResultShowMode searchResultShowMode; 315 | 316 | /** 317 | The mode of display search view controller, default is `PYSearchViewControllerShowModeModal`. 318 | */ 319 | @property (nonatomic, assign) PYSearchViewControllerShowMode searchViewControllerShowMode; 320 | 321 | /** 322 | The search bar 323 | */ 324 | @property (nonatomic, weak) UISearchBar *searchBar; 325 | 326 | /** 327 | The text field of search bar 328 | */ 329 | @property (nonatomic, weak) UITextField *searchTextField; 330 | 331 | /** 332 | The background color of search bar. 333 | */ 334 | @property (nonatomic, strong) UIColor *searchBarBackgroundColor; 335 | 336 | /** 337 | The cornerRadius of `_UISearchBarSearchFieldBackgroundView` which from `self.searchTextField.subviews`, default is 0.0. 338 | */ 339 | @property (nonatomic, assign) CGFloat searchBarCornerRadius; 340 | 341 | /** 342 | The barButtonItem of cancel 343 | */ 344 | @property (nonatomic, strong) UIBarButtonItem *cancelBarButtonItem; 345 | 346 | /** 347 | The customView of cancelBarButtonItem 348 | */ 349 | @property (nonatomic, weak) UIButton *cancelButton; 350 | 351 | /** 352 | The barButtonItem of back 353 | */ 354 | @property (nonatomic, strong) UIBarButtonItem *backBarButtonItem; 355 | 356 | /** 357 | The customView of backBarButtonItem 358 | */ 359 | @property (nonatomic, weak) UIButton *backButton; 360 | 361 | /** 362 | The search suggestion view 363 | */ 364 | @property (nonatomic, weak, readonly) UITableView *searchSuggestionView; 365 | 366 | /** 367 | The block which invoked when search begain. 368 | */ 369 | @property (nonatomic, copy) PYDidSearchBlock didSearchBlock; 370 | 371 | /** 372 | The element of search suggestions 373 | 374 | Note: it is't effective when `searchSuggestionHidden` is NO or cell of suggestion view is custom. 375 | */ 376 | @property (nonatomic, copy) NSArray *searchSuggestions; 377 | 378 | /** 379 | Whether hidden search suggstion view, default is NO. 380 | */ 381 | @property (nonatomic, assign) BOOL searchSuggestionHidden; 382 | 383 | /** 384 | The view controller of search result. 385 | */ 386 | @property (nonatomic, strong) UIViewController *searchResultController; 387 | 388 | /** 389 | Whether show search result view when search text did change, default is NO. 390 | 391 | Note: it is effective only when `searchResultShowMode` is `PYSearchResultShowModeEmbed`. 392 | */ 393 | @property (nonatomic, assign) BOOL showSearchResultWhenSearchTextChanged; 394 | 395 | /** 396 | Whether show search result view when search bar become first responder again. 397 | 398 | Note: it is effective only when `searchResultShowMode` is `PYSearchResultShowModeEmbed`. 399 | */ 400 | @property (nonatomic, assign) BOOL showSearchResultWhenSearchBarRefocused; 401 | 402 | /** 403 | Whether show keyboard when return to search result, default is YES. 404 | */ 405 | @property (nonatomic, assign) BOOL showKeyboardWhenReturnSearchResult; 406 | 407 | /** 408 | Creates an instance of searchViewContoller with popular searches and search bar's placeholder. 409 | 410 | @param hotSearches popular searchs 411 | @param placeholder placeholder of search bar 412 | @return new instance of `PYSearchViewController` class 413 | */ 414 | + (instancetype)searchViewControllerWithHotSearches:(NSArray *)hotSearches 415 | searchBarPlaceholder:(NSString *)placeholder; 416 | 417 | /** 418 | Creates an instance of searchViewContoller with popular searches, search bar's placeholder and the block which invoked when search begain. 419 | 420 | @param hotSearches popular searchs 421 | @param placeholder placeholder of search bar 422 | @param block block which invoked when search begain 423 | @return new instance of `PYSearchViewController` class 424 | 425 | Note: The `delegate` has a priority greater than the `block`, `block` is't effective when `searchViewController:didSearchWithSearchBar:searchText:` is implemented. 426 | */ 427 | + (instancetype)searchViewControllerWithHotSearches:(NSArray *)hotSearches 428 | searchBarPlaceholder:(NSString *)placeholder 429 | didSearchBlock:(PYDidSearchBlock)block; 430 | 431 | @end 432 | -------------------------------------------------------------------------------- /PYSearch/PYSearchViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import "PYSearchViewController.h" 8 | #import "PYSearchConst.h" 9 | #import "PYSearchSuggestionViewController.h" 10 | 11 | #define PYRectangleTagMaxCol 3 12 | #define PYTextColor PYSEARCH_COLOR(113, 113, 113) 13 | #define PYSEARCH_COLORPolRandomColor self.colorPol[arc4random_uniform((uint32_t)self.colorPol.count)] 14 | 15 | @interface PYSearchViewController () { 16 | id _previousInteractivePopGestureRecognizerDelegate; 17 | } 18 | 19 | /** 20 | The header view of search view 21 | */ 22 | @property (nonatomic, weak) UIView *headerView; 23 | 24 | /** 25 | The view of popular search 26 | */ 27 | @property (nonatomic, weak) UIView *hotSearchView; 28 | 29 | /** 30 | The view of search history 31 | */ 32 | @property (nonatomic, weak) UIView *searchHistoryView; 33 | 34 | /** 35 | The records of search 36 | */ 37 | @property (nonatomic, strong) NSMutableArray *searchHistories; 38 | 39 | /** 40 | Whether keyboard is showing. 41 | */ 42 | @property (nonatomic, assign) BOOL keyboardShowing; 43 | 44 | /** 45 | The height of keyborad 46 | */ 47 | @property (nonatomic, assign) CGFloat keyboardHeight; 48 | 49 | /** 50 | The search suggestion view contoller 51 | */ 52 | @property (nonatomic, weak) PYSearchSuggestionViewController *searchSuggestionVC; 53 | 54 | /** 55 | The content view of popular search tags 56 | */ 57 | @property (nonatomic, weak) UIView *hotSearchTagsContentView; 58 | 59 | /** 60 | The tags of rank 61 | */ 62 | @property (nonatomic, copy) NSArray *rankTags; 63 | 64 | /** 65 | The text labels of rank 66 | */ 67 | @property (nonatomic, copy) NSArray *rankTextLabels; 68 | 69 | /** 70 | The view of rank which contain tag and text label. 71 | */ 72 | @property (nonatomic, copy) NSArray *rankViews; 73 | 74 | /** 75 | The content view of search history tags. 76 | */ 77 | @property (nonatomic, weak) UIView *searchHistoryTagsContentView; 78 | 79 | /** 80 | The base table view of search view controller 81 | */ 82 | @property (nonatomic, strong) UITableView *baseSearchTableView; 83 | 84 | /** 85 | Whether did press suggestion cell 86 | */ 87 | @property (nonatomic, assign) BOOL didClickSuggestionCell; 88 | 89 | /** 90 | The current orientation of device 91 | */ 92 | @property (nonatomic, assign) UIDeviceOrientation currentOrientation; 93 | 94 | /** 95 | The width of cancel button 96 | */ 97 | @property (nonatomic, assign) CGFloat cancelButtonWidth; 98 | 99 | @end 100 | 101 | @implementation PYSearchViewController 102 | 103 | - (instancetype)init 104 | { 105 | if (self = [super init]) { 106 | [self setup]; 107 | } 108 | return self; 109 | } 110 | 111 | - (void)awakeFromNib 112 | { 113 | [super awakeFromNib]; 114 | 115 | [self setup]; 116 | } 117 | 118 | - (void)viewDidLayoutSubviews 119 | { 120 | [super viewDidLayoutSubviews]; 121 | 122 | if (self.currentOrientation != [[UIDevice currentDevice] orientation]) { // orientation changed, reload layout 123 | self.hotSearches = self.hotSearches; 124 | self.searchHistories = self.searchHistories; 125 | self.currentOrientation = [[UIDevice currentDevice] orientation]; 126 | } 127 | 128 | CGFloat adaptWidth = 0.0; 129 | UISearchBar *searchBar = self.searchBar; 130 | UITextField *searchField = self.searchTextField; 131 | UIView *titleView = self.navigationItem.titleView; 132 | UIButton *backButton = self.navigationItem.leftBarButtonItem.customView; 133 | UIButton *cancelButton = self.navigationItem.rightBarButtonItem.customView; 134 | UIEdgeInsets backButtonLayoutMargins = UIEdgeInsetsZero; 135 | UIEdgeInsets cancelButtonLayoutMargins = UIEdgeInsetsZero; 136 | UIEdgeInsets navigationBarLayoutMargins = UIEdgeInsetsZero; 137 | UINavigationBar *navigationBar = self.navigationController.navigationBar; 138 | 139 | if (@available(iOS 8.0, *)) { 140 | backButton.layoutMargins = UIEdgeInsetsMake(8, 0, 8, 8); 141 | backButtonLayoutMargins = backButton.layoutMargins; 142 | cancelButton.layoutMargins = UIEdgeInsetsMake(8, 8, 8, 0); 143 | cancelButtonLayoutMargins = cancelButton.layoutMargins; 144 | navigationBarLayoutMargins = navigationBar.layoutMargins; 145 | } 146 | 147 | if (self.searchViewControllerShowMode == PYSearchViewControllerShowModePush) { 148 | UIButton *backButton = self.navigationItem.leftBarButtonItem.customView; 149 | UIImageView *imageView = backButton.imageView; 150 | UIView *titleLabel = backButton.titleLabel; 151 | 152 | [backButton sizeToFit]; 153 | [imageView sizeToFit]; 154 | [titleLabel sizeToFit]; 155 | 156 | backButton.py_height = navigationBar.py_height; 157 | backButton.py_width = titleLabel.py_width + imageView.py_width / 2.0 + backButtonLayoutMargins.left + backButtonLayoutMargins.right; 158 | adaptWidth = backButton.py_width + 8; 159 | } else { // Default is PYSearchViewControllerShowModeModal 160 | [cancelButton sizeToFit]; 161 | [cancelButton.titleLabel sizeToFit]; 162 | self.cancelButtonWidth = cancelButton.py_width + cancelButtonLayoutMargins.left + cancelButtonLayoutMargins.right; 163 | adaptWidth = self.cancelButtonWidth + 8; 164 | } 165 | 166 | adaptWidth = adaptWidth + navigationBarLayoutMargins.left + navigationBarLayoutMargins.right; 167 | // Adapt the search bar layout problem in the navigation bar on iOS 11 168 | // More details : https://github.com/iphone5solo/PYSearch/issues/108 169 | if (@available(iOS 11.0, *)) { // iOS 11 170 | if (self.searchViewControllerShowMode == PYSearchViewControllerShowModeModal) { 171 | NSLayoutConstraint *leftLayoutConstraint = [searchBar.leftAnchor constraintEqualToAnchor:titleView.leftAnchor]; 172 | if (navigationBarLayoutMargins.left > PYSEARCH_MARGIN) { 173 | [leftLayoutConstraint setConstant:0]; 174 | } else { 175 | [leftLayoutConstraint setConstant:PYSEARCH_MARGIN - navigationBarLayoutMargins.left]; 176 | } 177 | } 178 | searchBar.py_height = self.view.py_width > self.view.py_height ? 24 : 30; 179 | searchBar.py_width = self.view.py_width - adaptWidth - PYSEARCH_MARGIN; 180 | searchField.frame = searchBar.bounds; 181 | cancelButton.py_width = self.cancelButtonWidth; 182 | } else { 183 | titleView.py_y = self.view.py_width > self.view.py_height ? 4 : 7; 184 | titleView.py_height = self.view.py_width > self.view.py_height ? 24 : 30; 185 | if (self.searchViewControllerShowMode == PYSearchViewControllerShowModePush) { 186 | titleView.py_width = self.view.py_width - adaptWidth - PYSEARCH_MARGIN; 187 | } else { 188 | titleView.py_x = PYSEARCH_MARGIN * 1.5; 189 | titleView.py_width = self.view.py_width - self.cancelButtonWidth - titleView.py_x * 2 - 3; 190 | } 191 | } 192 | } 193 | 194 | - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { 195 | [self viewDidLayoutSubviews]; 196 | } 197 | 198 | - (BOOL)prefersStatusBarHidden 199 | { 200 | return NO; 201 | } 202 | 203 | - (void)viewWillAppear:(BOOL)animated 204 | { 205 | [super viewWillAppear:animated]; 206 | 207 | // Fixed search history view may not be displayed or other problem at the first time. 208 | [self setSearchHistoryStyle:self.searchHistoryStyle]; // in method viewDidAppear,the view flashes when searchHistory count > 0 209 | 210 | if (self.cancelButtonWidth == 0) { // Just adapt iOS 11.2 211 | [self viewDidLayoutSubviews]; 212 | } 213 | 214 | // Adjust the view according to the `navigationBar.translucent` 215 | if (NO == self.navigationController.navigationBar.translucent) { 216 | self.baseSearchTableView.contentInset = UIEdgeInsetsMake(0, 0, self.view.py_y, 0); 217 | self.searchSuggestionVC.view.frame = CGRectMake(0, CGRectGetMaxY(self.navigationController.navigationBar.frame) - self.view.py_y, self.view.py_width, self.view.py_height + self.view.py_y); 218 | if (!self.navigationController.navigationBar.barTintColor) { 219 | self.navigationController.navigationBar.barTintColor = PYSEARCH_COLOR(249, 249, 249); 220 | } 221 | } 222 | 223 | if (NULL == self.searchResultController.parentViewController) { 224 | [self.searchBar becomeFirstResponder]; 225 | } else if (YES == self.showKeyboardWhenReturnSearchResult) { 226 | [self.searchBar becomeFirstResponder]; 227 | } 228 | if (_searchViewControllerShowMode == PYSearchViewControllerShowModePush) { 229 | if (self.navigationController.viewControllers.count > 1) { 230 | _previousInteractivePopGestureRecognizerDelegate = self.navigationController.interactivePopGestureRecognizer.delegate; 231 | self.navigationController.interactivePopGestureRecognizer.delegate = self; 232 | } 233 | } 234 | } 235 | 236 | - (void)viewDidAppear:(BOOL)animated 237 | { 238 | [super viewDidAppear:animated]; 239 | 240 | // Fixed search history view may not be displayed or other problem at the first time. 241 | [self setSearchHistoryStyle:self.searchHistoryStyle]; 242 | } 243 | 244 | - (void)viewWillDisappear:(BOOL)animated 245 | { 246 | [super viewWillDisappear:animated]; 247 | 248 | [self.searchBar resignFirstResponder]; 249 | 250 | if (_searchViewControllerShowMode == PYSearchViewControllerShowModePush) { 251 | self.navigationController.interactivePopGestureRecognizer.delegate = _previousInteractivePopGestureRecognizerDelegate; 252 | } 253 | } 254 | 255 | - (void)dealloc 256 | { 257 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 258 | } 259 | 260 | + (instancetype)searchViewControllerWithHotSearches:(NSArray *)hotSearches searchBarPlaceholder:(NSString *)placeholder 261 | { 262 | PYSearchViewController *searchVC = [[self alloc] init]; 263 | searchVC.hotSearches = hotSearches; 264 | searchVC.searchBar.placeholder = placeholder; 265 | return searchVC; 266 | } 267 | 268 | + (instancetype)searchViewControllerWithHotSearches:(NSArray *)hotSearches searchBarPlaceholder:(NSString *)placeholder didSearchBlock:(PYDidSearchBlock)block 269 | { 270 | PYSearchViewController *searchVC = [self searchViewControllerWithHotSearches:hotSearches searchBarPlaceholder:placeholder]; 271 | searchVC.didSearchBlock = [block copy]; 272 | return searchVC; 273 | } 274 | 275 | #pragma mark - Lazy 276 | - (UITableView *)baseSearchTableView 277 | { 278 | if (!_baseSearchTableView) { 279 | UITableView *baseSearchTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; 280 | baseSearchTableView.backgroundColor = [UIColor clearColor]; 281 | baseSearchTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 282 | if ([baseSearchTableView respondsToSelector:@selector(setCellLayoutMarginsFollowReadableWidth:)]) { // For the adapter iPad 283 | baseSearchTableView.cellLayoutMarginsFollowReadableWidth = NO; 284 | } 285 | baseSearchTableView.delegate = self; 286 | baseSearchTableView.dataSource = self; 287 | [self.view addSubview:baseSearchTableView]; 288 | _baseSearchTableView = baseSearchTableView; 289 | } 290 | return _baseSearchTableView; 291 | } 292 | 293 | - (PYSearchSuggestionViewController *)searchSuggestionVC 294 | { 295 | if (!_searchSuggestionVC) { 296 | PYSearchSuggestionViewController *searchSuggestionVC = [[PYSearchSuggestionViewController alloc] initWithStyle:UITableViewStyleGrouped]; 297 | __weak typeof(self) _weakSelf = self; 298 | searchSuggestionVC.didSelectCellBlock = ^(UITableViewCell *didSelectCell) { 299 | __strong typeof(_weakSelf) _swSelf = _weakSelf; 300 | _swSelf.searchBar.text = didSelectCell.textLabel.text; 301 | NSIndexPath *indexPath = [_swSelf.searchSuggestionVC.tableView indexPathForCell:didSelectCell]; 302 | 303 | if ([_swSelf.delegate respondsToSelector:@selector(searchViewController:didSelectSearchSuggestionAtIndexPath:searchBar:)]) { 304 | [_swSelf.delegate searchViewController:_swSelf didSelectSearchSuggestionAtIndexPath:indexPath searchBar:_swSelf.searchBar]; 305 | [_swSelf saveSearchCacheAndRefreshView]; 306 | } else if ([_swSelf.delegate respondsToSelector:@selector(searchViewController:didSelectSearchSuggestionAtIndex:searchText:)]) { 307 | #pragma clang diagnostic push 308 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 309 | [_swSelf.delegate searchViewController:_swSelf didSelectSearchSuggestionAtIndex:indexPath.row searchText:_swSelf.searchBar.text]; 310 | #pragma clang diagnostic pop 311 | [_swSelf saveSearchCacheAndRefreshView]; 312 | } else { 313 | [_swSelf searchBarSearchButtonClicked:_swSelf.searchBar]; 314 | } 315 | }; 316 | searchSuggestionVC.view.frame = CGRectMake(0, CGRectGetMaxY(self.navigationController.navigationBar.frame), PYScreenW, PYScreenH); 317 | searchSuggestionVC.view.backgroundColor = self.baseSearchTableView.backgroundColor; 318 | searchSuggestionVC.view.hidden = YES; 319 | _searchSuggestionView = (UITableView *)searchSuggestionVC.view; 320 | searchSuggestionVC.dataSource = self; 321 | [self.view addSubview:searchSuggestionVC.view]; 322 | [self addChildViewController:searchSuggestionVC]; 323 | _searchSuggestionVC = searchSuggestionVC; 324 | } 325 | return _searchSuggestionVC; 326 | } 327 | 328 | - (UIButton *)emptyButton 329 | { 330 | if (!_emptyButton) { 331 | UIButton *emptyButton = [[UIButton alloc] init]; 332 | emptyButton.titleLabel.font = self.searchHistoryHeader.font; 333 | [emptyButton setTitleColor:PYTextColor forState:UIControlStateNormal]; 334 | [emptyButton setTitle:[NSBundle py_localizedStringForKey:PYSearchEmptyButtonText] forState:UIControlStateNormal]; 335 | [emptyButton setImage:[NSBundle py_imageNamed:@"empty"] forState:UIControlStateNormal]; 336 | [emptyButton addTarget:self action:@selector(emptySearchHistoryDidClick) forControlEvents:UIControlEventTouchUpInside]; 337 | [emptyButton sizeToFit]; 338 | emptyButton.py_width += PYSEARCH_MARGIN; 339 | emptyButton.py_height += PYSEARCH_MARGIN; 340 | emptyButton.py_centerY = self.searchHistoryHeader.py_centerY; 341 | emptyButton.py_x = self.searchHistoryView.py_width - emptyButton.py_width; 342 | emptyButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; 343 | [self.searchHistoryView addSubview:emptyButton]; 344 | _emptyButton = emptyButton; 345 | } 346 | return _emptyButton; 347 | } 348 | 349 | - (UIView *)searchHistoryTagsContentView 350 | { 351 | if (!_searchHistoryTagsContentView) { 352 | UIView *searchHistoryTagsContentView = [[UIView alloc] init]; 353 | searchHistoryTagsContentView.py_width = self.searchHistoryView.py_width; 354 | searchHistoryTagsContentView.autoresizingMask = UIViewAutoresizingFlexibleWidth; 355 | searchHistoryTagsContentView.py_y = CGRectGetMaxY(self.hotSearchTagsContentView.frame) + PYSEARCH_MARGIN; 356 | [self.searchHistoryView addSubview:searchHistoryTagsContentView]; 357 | _searchHistoryTagsContentView = searchHistoryTagsContentView; 358 | } 359 | return _searchHistoryTagsContentView; 360 | } 361 | 362 | - (UILabel *)searchHistoryHeader 363 | { 364 | if (!_searchHistoryHeader) { 365 | UILabel *titleLabel = [self setupTitleLabel:[NSBundle py_localizedStringForKey:PYSearchSearchHistoryText]]; 366 | [self.searchHistoryView addSubview:titleLabel]; 367 | _searchHistoryHeader = titleLabel; 368 | } 369 | return _searchHistoryHeader; 370 | } 371 | 372 | - (UIView *)searchHistoryView 373 | { 374 | if (!_searchHistoryView) { 375 | UIView *searchHistoryView = [[UIView alloc] init]; 376 | searchHistoryView.py_x = self.hotSearchView.py_x; 377 | searchHistoryView.py_y = self.hotSearchView.py_y; 378 | searchHistoryView.py_width = self.headerView.py_width - searchHistoryView.py_x * 2; 379 | searchHistoryView.autoresizingMask = UIViewAutoresizingFlexibleWidth; 380 | [self.headerView addSubview:searchHistoryView]; 381 | _searchHistoryView = searchHistoryView; 382 | } 383 | return _searchHistoryView; 384 | } 385 | 386 | - (NSMutableArray *)searchHistories 387 | { 388 | if (!_searchHistories) { 389 | _searchHistories = [NSMutableArray arrayWithArray:[NSKeyedUnarchiver unarchiveObjectWithFile:self.searchHistoriesCachePath]]; 390 | } 391 | return _searchHistories; 392 | } 393 | 394 | - (NSMutableArray *)colorPol 395 | { 396 | if (!_colorPol) { 397 | NSArray *colorStrPol = @[@"009999", @"0099cc", @"0099ff", @"00cc99", @"00cccc", @"336699", @"3366cc", @"3366ff", @"339966", @"666666", @"666699", @"6666cc", @"6666ff", @"996666", @"996699", @"999900", @"999933", @"99cc00", @"99cc33", @"660066", @"669933", @"990066", @"cc9900", @"cc6600" , @"cc3300", @"cc3366", @"cc6666", @"cc6699", @"cc0066", @"cc0033", @"ffcc00", @"ffcc33", @"ff9900", @"ff9933", @"ff6600", @"ff6633", @"ff6666", @"ff6699", @"ff3366", @"ff3333"]; 398 | NSMutableArray *colorPolM = [NSMutableArray array]; 399 | for (NSString *colorStr in colorStrPol) { 400 | UIColor *color = [UIColor py_colorWithHexString:colorStr]; 401 | [colorPolM addObject:color]; 402 | } 403 | _colorPol = colorPolM; 404 | } 405 | return _colorPol; 406 | } 407 | 408 | - (void)setup 409 | { 410 | self.view.backgroundColor = [UIColor whiteColor]; 411 | self.baseSearchTableView.separatorStyle = UITableViewCellSeparatorStyleNone; 412 | self.navigationController.navigationBar.backIndicatorImage = nil; 413 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil]; 414 | UIButton *cancleButton = [UIButton buttonWithType:UIButtonTypeSystem]; 415 | cancleButton.titleLabel.font = [UIFont systemFontOfSize:17]; 416 | [cancleButton setTitle:[NSBundle py_localizedStringForKey:PYSearchCancelButtonText] forState:UIControlStateNormal]; 417 | [cancleButton addTarget:self action:@selector(cancelDidClick) forControlEvents:UIControlEventTouchUpInside]; 418 | cancleButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; 419 | cancleButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight; 420 | [cancleButton sizeToFit]; 421 | cancleButton.py_width += PYSEARCH_MARGIN; 422 | self.cancelButton = cancleButton; 423 | self.cancelBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:cancleButton]; 424 | UIButton *backButton = [UIButton buttonWithType:UIButtonTypeSystem]; 425 | UIImage *backImage = [NSBundle py_imageNamed:@"back"]; 426 | backButton.titleLabel.font = [UIFont systemFontOfSize:17]; 427 | [backButton setTitle:[NSBundle py_localizedStringForKey:PYSearchBackButtonText] forState:UIControlStateNormal]; 428 | [backButton setImage:backImage forState:UIControlStateNormal]; 429 | [backButton addTarget:self action:@selector(backDidClick) forControlEvents:UIControlEventTouchUpInside]; 430 | backButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; 431 | backButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; 432 | [backButton.imageView sizeToFit]; 433 | backButton.contentEdgeInsets = UIEdgeInsetsMake(0, -ceil(backImage.size.width / 2.0), 0, 0); 434 | backButton.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0); 435 | [backButton sizeToFit]; 436 | backButton.py_width += 3; 437 | self.backButton = backButton; 438 | self.backBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton]; 439 | 440 | /** 441 | * Initialize settings 442 | */ 443 | self.hotSearchStyle = PYHotSearchStyleDefault; 444 | self.searchHistoryStyle = PYHotSearchStyleDefault; 445 | self.searchResultShowMode = PYSearchResultShowModeDefault; 446 | self.searchViewControllerShowMode = PYSearchViewControllerShowDefault; 447 | self.searchSuggestionHidden = NO; 448 | self.searchHistoriesCachePath = PYSEARCH_SEARCH_HISTORY_CACHE_PATH; 449 | self.searchHistoriesCount = 20; 450 | self.showSearchHistory = YES; 451 | self.showHotSearch = YES; 452 | self.showSearchResultWhenSearchTextChanged = NO; 453 | self.showSearchResultWhenSearchBarRefocused = NO; 454 | self.showKeyboardWhenReturnSearchResult = YES; 455 | self.removeSpaceOnSearchString = YES; 456 | self.searchBarCornerRadius = 0.0; 457 | 458 | UIView *titleView = [[UIView alloc] init]; 459 | UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:titleView.bounds]; 460 | [titleView addSubview:searchBar]; 461 | if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 11.0) { // iOS 11 462 | [NSLayoutConstraint activateConstraints:@[ 463 | [searchBar.topAnchor constraintEqualToAnchor:titleView.topAnchor], 464 | [searchBar.leftAnchor constraintEqualToAnchor:titleView.leftAnchor], 465 | [searchBar.rightAnchor constraintEqualToAnchor:titleView.rightAnchor], 466 | [searchBar.bottomAnchor constraintEqualToAnchor:titleView.bottomAnchor] 467 | ]]; 468 | } else { 469 | searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 470 | } 471 | self.navigationItem.titleView = titleView; 472 | searchBar.placeholder = [NSBundle py_localizedStringForKey:PYSearchSearchPlaceholderText]; 473 | searchBar.backgroundImage = [NSBundle py_imageNamed:@"clearImage"]; 474 | searchBar.delegate = self; 475 | for (UIView *subView in [[searchBar.subviews lastObject] subviews]) { 476 | if ([[subView class] isSubclassOfClass:[UITextField class]]) { 477 | UITextField *textField = (UITextField *)subView; 478 | textField.font = [UIFont systemFontOfSize:16]; 479 | _searchTextField = textField; 480 | break; 481 | } 482 | } 483 | self.searchBar = searchBar; 484 | 485 | UIView *headerView = [[UIView alloc] init]; 486 | headerView.py_width = PYScreenW; 487 | headerView.autoresizingMask = UIViewAutoresizingFlexibleWidth; 488 | UIView *hotSearchView = [[UIView alloc] init]; 489 | hotSearchView.py_x = PYSEARCH_MARGIN * 1.5; 490 | hotSearchView.py_width = headerView.py_width - hotSearchView.py_x * 2; 491 | hotSearchView.autoresizingMask = UIViewAutoresizingFlexibleWidth; 492 | UILabel *titleLabel = [self setupTitleLabel:[NSBundle py_localizedStringForKey:PYSearchHotSearchText]]; 493 | self.hotSearchHeader = titleLabel; 494 | [hotSearchView addSubview:titleLabel]; 495 | UIView *hotSearchTagsContentView = [[UIView alloc] init]; 496 | hotSearchTagsContentView.py_width = hotSearchView.py_width; 497 | hotSearchTagsContentView.py_y = CGRectGetMaxY(titleLabel.frame) + PYSEARCH_MARGIN; 498 | hotSearchTagsContentView.autoresizingMask = UIViewAutoresizingFlexibleWidth; 499 | [hotSearchView addSubview:hotSearchTagsContentView]; 500 | [headerView addSubview:hotSearchView]; 501 | self.hotSearchTagsContentView = hotSearchTagsContentView; 502 | self.hotSearchView = hotSearchView; 503 | self.headerView = headerView; 504 | self.baseSearchTableView.tableHeaderView = headerView; 505 | 506 | UIView *footerView = [[UIView alloc] init]; 507 | footerView.py_width = PYScreenW; 508 | UILabel *emptySearchHistoryLabel = [[UILabel alloc] init]; 509 | emptySearchHistoryLabel.textColor = [UIColor darkGrayColor]; 510 | emptySearchHistoryLabel.font = [UIFont systemFontOfSize:13]; 511 | emptySearchHistoryLabel.userInteractionEnabled = YES; 512 | emptySearchHistoryLabel.text = [NSBundle py_localizedStringForKey:PYSearchEmptySearchHistoryText]; 513 | emptySearchHistoryLabel.textAlignment = NSTextAlignmentCenter; 514 | emptySearchHistoryLabel.py_height = 49; 515 | [emptySearchHistoryLabel addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(emptySearchHistoryDidClick)]]; 516 | emptySearchHistoryLabel.py_width = footerView.py_width; 517 | emptySearchHistoryLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; 518 | self.emptySearchHistoryLabel = emptySearchHistoryLabel; 519 | [footerView addSubview:emptySearchHistoryLabel]; 520 | footerView.py_height = emptySearchHistoryLabel.py_height; 521 | self.baseSearchTableView.tableFooterView = footerView; 522 | 523 | self.hotSearches = nil; 524 | } 525 | 526 | - (UILabel *)setupTitleLabel:(NSString *)title 527 | { 528 | UILabel *titleLabel = [[UILabel alloc] init]; 529 | titleLabel.text = title; 530 | titleLabel.font = [UIFont systemFontOfSize:13]; 531 | titleLabel.tag = 1; 532 | titleLabel.textColor = PYTextColor; 533 | [titleLabel sizeToFit]; 534 | titleLabel.py_x = 0; 535 | titleLabel.py_y = 0; 536 | return titleLabel; 537 | } 538 | 539 | - (void)setupHotSearchRectangleTags 540 | { 541 | UIView *contentView = self.hotSearchTagsContentView; 542 | contentView.py_width = PYSEARCH_REALY_SCREEN_WIDTH; 543 | contentView.py_x = -PYSEARCH_MARGIN * 1.5; 544 | contentView.py_y += 2; 545 | contentView.backgroundColor = [UIColor whiteColor]; 546 | self.baseSearchTableView.backgroundColor = [UIColor py_colorWithHexString:@"#efefef"]; 547 | // remove all subviews in hotSearchTagsContentView 548 | [self.hotSearchTagsContentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 549 | 550 | CGFloat rectangleTagH = 40; 551 | NSMutableArray *rectangleTagLabelsM = [NSMutableArray array]; 552 | for (int i = 0; i < self.hotSearches.count; i++) { 553 | UILabel *rectangleTagLabel = [[UILabel alloc] init]; 554 | rectangleTagLabel.userInteractionEnabled = YES; 555 | rectangleTagLabel.font = [UIFont systemFontOfSize:14]; 556 | rectangleTagLabel.textColor = PYTextColor; 557 | rectangleTagLabel.backgroundColor = [UIColor clearColor]; 558 | rectangleTagLabel.text = self.hotSearches[i]; 559 | rectangleTagLabel.py_width = contentView.py_width / PYRectangleTagMaxCol; 560 | rectangleTagLabel.py_height = rectangleTagH; 561 | rectangleTagLabel.textAlignment = NSTextAlignmentCenter; 562 | [rectangleTagLabel addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tagDidCLick:)]]; 563 | rectangleTagLabel.py_x = rectangleTagLabel.py_width * (i % PYRectangleTagMaxCol); 564 | rectangleTagLabel.py_y = rectangleTagLabel.py_height * (i / PYRectangleTagMaxCol); 565 | [contentView addSubview:rectangleTagLabel]; 566 | [rectangleTagLabelsM addObject:rectangleTagLabel]; 567 | } 568 | self.hotSearchTags = [rectangleTagLabelsM copy]; 569 | contentView.py_height = CGRectGetMaxY(contentView.subviews.lastObject.frame); 570 | 571 | self.hotSearchView.py_height = CGRectGetMaxY(contentView.frame) + PYSEARCH_MARGIN * 2; 572 | self.baseSearchTableView.tableHeaderView.py_height = self.headerView.py_height = MAX(CGRectGetMaxY(self.hotSearchView.frame), CGRectGetMaxY(self.searchHistoryView.frame)); 573 | 574 | for (int i = 0; i < PYRectangleTagMaxCol - 1; i++) { 575 | UIImageView *verticalLine = [[UIImageView alloc] initWithImage:[NSBundle py_imageNamed:@"cell-content-line-vertical"]]; 576 | verticalLine.py_height = contentView.py_height; 577 | verticalLine.alpha = 0.7; 578 | verticalLine.py_x = contentView.py_width / PYRectangleTagMaxCol * (i + 1); 579 | verticalLine.py_width = 0.5; 580 | [contentView addSubview:verticalLine]; 581 | } 582 | 583 | for (int i = 0; i < ceil(((double)self.hotSearches.count / PYRectangleTagMaxCol)) - 1; i++) { 584 | UIImageView *verticalLine = [[UIImageView alloc] initWithImage:[NSBundle py_imageNamed:@"cell-content-line"]]; 585 | verticalLine.py_height = 0.5; 586 | verticalLine.alpha = 0.7; 587 | verticalLine.py_y = rectangleTagH * (i + 1); 588 | verticalLine.py_width = contentView.py_width; 589 | [contentView addSubview:verticalLine]; 590 | } 591 | [self layoutForDemand]; 592 | // Note:When the operating system for the iOS 9.x series tableHeaderView height settings are invalid, you need to reset the tableHeaderView 593 | [self.baseSearchTableView setTableHeaderView:self.baseSearchTableView.tableHeaderView]; 594 | } 595 | 596 | - (void)setupHotSearchRankTags 597 | { 598 | UIView *contentView = self.hotSearchTagsContentView; 599 | [self.hotSearchTagsContentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 600 | 601 | NSMutableArray *rankTextLabelsM = [NSMutableArray array]; 602 | NSMutableArray *rankTagM = [NSMutableArray array]; 603 | NSMutableArray *rankViewM = [NSMutableArray array]; 604 | for (int i = 0; i < self.hotSearches.count; i++) { 605 | UIView *rankView = [[UIView alloc] init]; 606 | rankView.py_height = 40; 607 | rankView.py_width = (self.baseSearchTableView.py_width - PYSEARCH_MARGIN * 3) * 0.5; 608 | rankView.autoresizingMask = UIViewAutoresizingFlexibleWidth; 609 | [contentView addSubview:rankView]; 610 | // rank tag 611 | UILabel *rankTag = [[UILabel alloc] init]; 612 | rankTag.textAlignment = NSTextAlignmentCenter; 613 | rankTag.font = [UIFont systemFontOfSize:10]; 614 | rankTag.layer.cornerRadius = 3; 615 | rankTag.clipsToBounds = YES; 616 | rankTag.text = [NSString stringWithFormat:@"%d", i + 1]; 617 | [rankTag sizeToFit]; 618 | rankTag.py_width = rankTag.py_height += PYSEARCH_MARGIN * 0.5; 619 | rankTag.py_y = (rankView.py_height - rankTag.py_height) * 0.5; 620 | [rankView addSubview:rankTag]; 621 | [rankTagM addObject:rankTag]; 622 | // rank text 623 | UILabel *rankTextLabel = [[UILabel alloc] init]; 624 | rankTextLabel.text = self.hotSearches[i]; 625 | rankTextLabel.userInteractionEnabled = YES; 626 | [rankTextLabel addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tagDidCLick:)]]; 627 | rankTextLabel.textAlignment = NSTextAlignmentLeft; 628 | rankTextLabel.backgroundColor = [UIColor clearColor]; 629 | rankTextLabel.textColor = PYTextColor; 630 | rankTextLabel.font = [UIFont systemFontOfSize:14]; 631 | rankTextLabel.py_x = CGRectGetMaxX(rankTag.frame) + PYSEARCH_MARGIN; 632 | rankTextLabel.py_width = (self.baseSearchTableView.py_width - PYSEARCH_MARGIN * 3) * 0.5 - rankTextLabel.py_x; 633 | rankTextLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; 634 | rankTextLabel.py_height = rankView.py_height; 635 | [rankTextLabelsM addObject:rankTextLabel]; 636 | [rankView addSubview:rankTextLabel]; 637 | 638 | UIImageView *line = [[UIImageView alloc] initWithImage:[NSBundle py_imageNamed:@"cell-content-line"]]; 639 | line.py_height = 0.5; 640 | line.alpha = 0.7; 641 | line.py_x = -PYScreenW * 0.5; 642 | line.py_y = rankView.py_height - 1; 643 | line.py_width = self.baseSearchTableView.py_width; 644 | line.autoresizingMask = UIViewAutoresizingFlexibleWidth; 645 | [rankView addSubview:line]; 646 | [rankViewM addObject:rankView]; 647 | 648 | // set tag's background color and text color 649 | switch (i) { 650 | case 0: // NO.1 651 | rankTag.backgroundColor = [UIColor py_colorWithHexString:self.rankTagBackgroundColorHexStrings[0]]; 652 | rankTag.textColor = [UIColor whiteColor]; 653 | break; 654 | case 1: // NO.2 655 | rankTag.backgroundColor = [UIColor py_colorWithHexString:self.rankTagBackgroundColorHexStrings[1]]; 656 | rankTag.textColor = [UIColor whiteColor]; 657 | break; 658 | case 2: // NO.3 659 | rankTag.backgroundColor = [UIColor py_colorWithHexString:self.rankTagBackgroundColorHexStrings[2]]; 660 | rankTag.textColor = [UIColor whiteColor]; 661 | break; 662 | default: // Other 663 | rankTag.backgroundColor = [UIColor py_colorWithHexString:self.rankTagBackgroundColorHexStrings[3]]; 664 | rankTag.textColor = PYTextColor; 665 | break; 666 | } 667 | } 668 | self.rankTextLabels = rankTextLabelsM; 669 | self.rankTags = rankTagM; 670 | self.rankViews = rankViewM; 671 | 672 | for (int i = 0; i < self.rankViews.count; i++) { // default is two column 673 | UIView *rankView = self.rankViews[i]; 674 | rankView.py_x = (PYSEARCH_MARGIN + rankView.py_width) * (i % 2); 675 | rankView.py_y = rankView.py_height * (i / 2); 676 | } 677 | 678 | contentView.py_height = CGRectGetMaxY(self.rankViews.lastObject.frame); 679 | self.hotSearchView.py_height = CGRectGetMaxY(contentView.frame) + PYSEARCH_MARGIN * 2; 680 | self.baseSearchTableView.tableHeaderView.py_height = self.headerView.py_height = MAX(CGRectGetMaxY(self.hotSearchView.frame), CGRectGetMaxY(self.searchHistoryView.frame)); 681 | [self layoutForDemand]; 682 | 683 | // Note:When the operating system for the iOS 9.x series tableHeaderView height settings are invalid, you need to reset the tableHeaderView 684 | [self.baseSearchTableView setTableHeaderView:self.baseSearchTableView.tableHeaderView]; 685 | } 686 | 687 | - (void)setupHotSearchNormalTags 688 | { 689 | self.hotSearchTags = [self addAndLayoutTagsWithTagsContentView:self.hotSearchTagsContentView tagTexts:self.hotSearches]; 690 | [self setHotSearchStyle:self.hotSearchStyle]; 691 | } 692 | 693 | - (void)setupSearchHistoryTags 694 | { 695 | self.baseSearchTableView.tableFooterView = nil; 696 | self.searchHistoryTagsContentView.py_y = PYSEARCH_MARGIN; 697 | self.emptyButton.py_y = self.searchHistoryHeader.py_y - PYSEARCH_MARGIN * 0.5; 698 | self.searchHistoryTagsContentView.py_y = CGRectGetMaxY(self.emptyButton.frame) + PYSEARCH_MARGIN; 699 | self.searchHistoryTags = [self addAndLayoutTagsWithTagsContentView:self.searchHistoryTagsContentView tagTexts:[self.searchHistories copy]]; 700 | } 701 | 702 | - (NSArray *)addAndLayoutTagsWithTagsContentView:(UIView *)contentView tagTexts:(NSArray *)tagTexts; 703 | { 704 | [contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 705 | NSMutableArray *tagsM = [NSMutableArray array]; 706 | for (int i = 0; i < tagTexts.count; i++) { 707 | UILabel *label = [self labelWithTitle:tagTexts[i]]; 708 | [label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tagDidCLick:)]]; 709 | [contentView addSubview:label]; 710 | [tagsM addObject:label]; 711 | } 712 | 713 | CGFloat currentX = 0; 714 | CGFloat currentY = 0; 715 | CGFloat countRow = 0; 716 | CGFloat countCol = 0; 717 | 718 | for (int i = 0; i < contentView.subviews.count; i++) { 719 | UILabel *subView = contentView.subviews[i]; 720 | // When the number of search words is too large, the width is width of the contentView 721 | if (subView.py_width > contentView.py_width) subView.py_width = contentView.py_width; 722 | if (currentX + subView.py_width + PYSEARCH_MARGIN * countRow > contentView.py_width) { 723 | subView.py_x = 0; 724 | subView.py_y = (currentY += subView.py_height) + PYSEARCH_MARGIN * ++countCol; 725 | currentX = subView.py_width; 726 | countRow = 1; 727 | } else { 728 | subView.py_x = (currentX += subView.py_width) - subView.py_width + PYSEARCH_MARGIN * countRow; 729 | subView.py_y = currentY + PYSEARCH_MARGIN * countCol; 730 | countRow ++; 731 | } 732 | } 733 | 734 | contentView.py_height = CGRectGetMaxY(contentView.subviews.lastObject.frame); 735 | if (self.hotSearchTagsContentView == contentView) { // popular search tag 736 | self.hotSearchView.py_height = CGRectGetMaxY(contentView.frame) + PYSEARCH_MARGIN * 2; 737 | } else if (self.searchHistoryTagsContentView == contentView) { // search history tag 738 | self.searchHistoryView.py_height = CGRectGetMaxY(contentView.frame) + PYSEARCH_MARGIN * 2; 739 | } 740 | 741 | [self layoutForDemand]; 742 | self.baseSearchTableView.tableHeaderView.py_height = self.headerView.py_height = MAX(CGRectGetMaxY(self.hotSearchView.frame), CGRectGetMaxY(self.searchHistoryView.frame)); 743 | self.baseSearchTableView.tableHeaderView.hidden = NO; 744 | 745 | // Note:When the operating system for the iOS 9.x series tableHeaderView height settings are invalid, you need to reset the tableHeaderView 746 | [self.baseSearchTableView setTableHeaderView:self.baseSearchTableView.tableHeaderView]; 747 | return [tagsM copy]; 748 | } 749 | 750 | - (void)layoutForDemand { 751 | if (NO == self.swapHotSeachWithSearchHistory) { 752 | self.hotSearchView.py_y = PYSEARCH_MARGIN * 2; 753 | self.searchHistoryView.py_y = self.hotSearches.count > 0 && self.showHotSearch ? CGRectGetMaxY(self.hotSearchView.frame) : PYSEARCH_MARGIN * 1.5; 754 | } else { // swap popular search whith search history 755 | self.searchHistoryView.py_y = PYSEARCH_MARGIN * 1.5; 756 | self.hotSearchView.py_y = self.searchHistories.count > 0 && self.showSearchHistory ? CGRectGetMaxY(self.searchHistoryView.frame) : PYSEARCH_MARGIN * 2; 757 | } 758 | } 759 | 760 | #pragma mark - setter 761 | - (void)setRankTextLabels:(NSArray *)rankTextLabels 762 | { 763 | // popular search tagLabel's tag is 1, search history tagLabel's tag is 0. 764 | for (UILabel *rankLabel in rankTextLabels) { 765 | rankLabel.tag = 1; 766 | } 767 | _rankTextLabels= rankTextLabels; 768 | } 769 | 770 | - (void)setSearchBarCornerRadius:(CGFloat)searchBarCornerRadius 771 | { 772 | _searchBarCornerRadius = searchBarCornerRadius; 773 | 774 | for (UIView *subView in self.searchTextField.subviews) { 775 | if ([NSStringFromClass([subView class]) isEqualToString:@"_UISearchBarSearchFieldBackgroundView"]) { 776 | subView.layer.cornerRadius = searchBarCornerRadius; 777 | subView.clipsToBounds = YES; 778 | break; 779 | } 780 | } 781 | } 782 | 783 | - (void)setSwapHotSeachWithSearchHistory:(BOOL)swapHotSeachWithSearchHistory 784 | { 785 | _swapHotSeachWithSearchHistory = swapHotSeachWithSearchHistory; 786 | 787 | self.hotSearches = self.hotSearches; 788 | self.searchHistories = self.searchHistories; 789 | } 790 | 791 | - (void)setHotSearchTitle:(NSString *)hotSearchTitle 792 | { 793 | _hotSearchTitle = [hotSearchTitle copy]; 794 | 795 | self.hotSearchHeader.text = _hotSearchTitle; 796 | } 797 | 798 | - (void)setSearchHistoryTitle:(NSString *)searchHistoryTitle 799 | { 800 | _searchHistoryTitle = [searchHistoryTitle copy]; 801 | 802 | if (PYSearchHistoryStyleCell == self.searchHistoryStyle) { 803 | [self.baseSearchTableView reloadData]; 804 | } else { 805 | self.searchHistoryHeader.text = _searchHistoryTitle; 806 | } 807 | } 808 | 809 | - (void)setShowSearchResultWhenSearchTextChanged:(BOOL)showSearchResultWhenSearchTextChanged 810 | { 811 | _showSearchResultWhenSearchTextChanged = showSearchResultWhenSearchTextChanged; 812 | 813 | if (YES == _showSearchResultWhenSearchTextChanged) { 814 | self.searchSuggestionHidden = YES; 815 | } 816 | } 817 | 818 | - (void)setShowHotSearch:(BOOL)showHotSearch 819 | { 820 | _showHotSearch = showHotSearch; 821 | 822 | [self setHotSearches:self.hotSearches]; 823 | [self setSearchHistoryStyle:self.searchHistoryStyle]; 824 | } 825 | 826 | - (void)setShowSearchHistory:(BOOL)showSearchHistory 827 | { 828 | _showSearchHistory = showSearchHistory; 829 | 830 | [self setHotSearches:self.hotSearches]; 831 | [self setSearchHistoryStyle:self.searchHistoryStyle]; 832 | } 833 | 834 | - (void)setCancelBarButtonItem:(UIBarButtonItem *)cancelBarButtonItem 835 | { 836 | _cancelBarButtonItem = cancelBarButtonItem; 837 | self.navigationItem.rightBarButtonItem = cancelBarButtonItem; 838 | } 839 | 840 | - (void)setCancelButton:(UIButton *)cancelButton 841 | { 842 | _cancelButton = cancelButton; 843 | self.navigationItem.rightBarButtonItem.customView = cancelButton; 844 | } 845 | 846 | - (void)setSearchHistoriesCachePath:(NSString *)searchHistoriesCachePath 847 | { 848 | _searchHistoriesCachePath = [searchHistoriesCachePath copy]; 849 | 850 | self.searchHistories = nil; 851 | if (PYSearchHistoryStyleCell == self.searchHistoryStyle) { 852 | [self.baseSearchTableView reloadData]; 853 | } else { 854 | [self setSearchHistoryStyle:self.searchHistoryStyle]; 855 | } 856 | } 857 | 858 | - (void)setHotSearchTags:(NSArray *)hotSearchTags 859 | { 860 | // popular search tagLabel's tag is 1, search history tagLabel's tag is 0. 861 | for (UILabel *tagLabel in hotSearchTags) { 862 | tagLabel.tag = 1; 863 | } 864 | _hotSearchTags = hotSearchTags; 865 | } 866 | 867 | - (void)setSearchBarBackgroundColor:(UIColor *)searchBarBackgroundColor 868 | { 869 | _searchBarBackgroundColor = searchBarBackgroundColor; 870 | _searchTextField.backgroundColor = searchBarBackgroundColor; 871 | } 872 | 873 | - (void)setSearchSuggestions:(NSArray *)searchSuggestions 874 | { 875 | if ([self.dataSource respondsToSelector:@selector(searchSuggestionView:cellForRowAtIndexPath:)]) { 876 | // set searchSuggestion is nil when cell of suggestion view is custom. 877 | _searchSuggestions = nil; 878 | return; 879 | } 880 | 881 | _searchSuggestions = [searchSuggestions copy]; 882 | self.searchSuggestionVC.searchSuggestions = [searchSuggestions copy]; 883 | 884 | self.baseSearchTableView.hidden = !self.searchSuggestionHidden && [self.searchSuggestionVC.tableView numberOfRowsInSection:0]; 885 | self.searchSuggestionVC.view.hidden = self.searchSuggestionHidden || ![self.searchSuggestionVC.tableView numberOfRowsInSection:0]; 886 | } 887 | 888 | - (void)setRankTagBackgroundColorHexStrings:(NSArray *)rankTagBackgroundColorHexStrings 889 | { 890 | if (rankTagBackgroundColorHexStrings.count < 4) { 891 | NSArray *colorStrings = @[@"#f14230", @"#ff8000", @"#ffcc01", @"#ebebeb"]; 892 | _rankTagBackgroundColorHexStrings = colorStrings; 893 | } else { 894 | _rankTagBackgroundColorHexStrings = @[rankTagBackgroundColorHexStrings[0], rankTagBackgroundColorHexStrings[1], rankTagBackgroundColorHexStrings[2], rankTagBackgroundColorHexStrings[3]]; 895 | } 896 | 897 | self.hotSearches = self.hotSearches; 898 | } 899 | 900 | - (void)setHotSearches:(NSArray *)hotSearches 901 | { 902 | _hotSearches = hotSearches; 903 | if (0 == hotSearches.count || !self.showHotSearch) { 904 | self.hotSearchHeader.hidden = YES; 905 | self.hotSearchTagsContentView.hidden = YES; 906 | if (PYSearchHistoryStyleCell == self.searchHistoryStyle) { 907 | UIView *tableHeaderView = self.baseSearchTableView.tableHeaderView; 908 | tableHeaderView.py_height = PYSEARCH_MARGIN * 1.5; 909 | [self.baseSearchTableView setTableHeaderView:tableHeaderView]; 910 | } 911 | return; 912 | }; 913 | 914 | self.baseSearchTableView.tableHeaderView.hidden = NO; 915 | self.hotSearchHeader.hidden = NO; 916 | self.hotSearchTagsContentView.hidden = NO; 917 | if (PYHotSearchStyleDefault == self.hotSearchStyle 918 | || PYHotSearchStyleColorfulTag == self.hotSearchStyle 919 | || PYHotSearchStyleBorderTag == self.hotSearchStyle 920 | || PYHotSearchStyleARCBorderTag == self.hotSearchStyle) { 921 | [self setupHotSearchNormalTags]; 922 | } else if (PYHotSearchStyleRankTag == self.hotSearchStyle) { 923 | [self setupHotSearchRankTags]; 924 | } else if (PYHotSearchStyleRectangleTag == self.hotSearchStyle) { 925 | [self setupHotSearchRectangleTags]; 926 | } 927 | [self setSearchHistoryStyle:self.searchHistoryStyle]; 928 | } 929 | 930 | - (void)setSearchHistoryStyle:(PYSearchHistoryStyle)searchHistoryStyle 931 | { 932 | _searchHistoryStyle = searchHistoryStyle; 933 | 934 | if (!self.searchHistories.count || !self.showSearchHistory || UISearchBarStyleDefault == searchHistoryStyle) { 935 | self.searchHistoryHeader.hidden = YES; 936 | self.searchHistoryTagsContentView.hidden = YES; 937 | self.searchHistoryView.hidden = YES; 938 | self.emptyButton.hidden = YES; 939 | return; 940 | }; 941 | 942 | self.searchHistoryHeader.hidden = NO; 943 | self.searchHistoryTagsContentView.hidden = NO; 944 | self.searchHistoryView.hidden = NO; 945 | self.emptyButton.hidden = NO; 946 | [self setupSearchHistoryTags]; 947 | 948 | switch (searchHistoryStyle) { 949 | case PYSearchHistoryStyleColorfulTag: 950 | for (UILabel *tag in self.searchHistoryTags) { 951 | tag.textColor = [UIColor whiteColor]; 952 | tag.layer.borderColor = nil; 953 | tag.layer.borderWidth = 0.0; 954 | tag.backgroundColor = PYSEARCH_COLORPolRandomColor; 955 | } 956 | break; 957 | case PYSearchHistoryStyleBorderTag: 958 | for (UILabel *tag in self.searchHistoryTags) { 959 | tag.backgroundColor = [UIColor clearColor]; 960 | tag.layer.borderColor = PYSEARCH_COLOR(223, 223, 223).CGColor; 961 | tag.layer.borderWidth = 0.5; 962 | } 963 | break; 964 | case PYSearchHistoryStyleARCBorderTag: 965 | for (UILabel *tag in self.searchHistoryTags) { 966 | tag.backgroundColor = [UIColor clearColor]; 967 | tag.layer.borderColor = PYSEARCH_COLOR(223, 223, 223).CGColor; 968 | tag.layer.borderWidth = 0.5; 969 | tag.layer.cornerRadius = tag.py_height * 0.5; 970 | } 971 | break; 972 | default: 973 | break; 974 | } 975 | } 976 | 977 | - (void)setHotSearchStyle:(PYHotSearchStyle)hotSearchStyle 978 | { 979 | _hotSearchStyle = hotSearchStyle; 980 | 981 | switch (hotSearchStyle) { 982 | case PYHotSearchStyleColorfulTag: 983 | for (UILabel *tag in self.hotSearchTags) { 984 | tag.textColor = [UIColor whiteColor]; 985 | tag.layer.borderColor = nil; 986 | tag.layer.borderWidth = 0.0; 987 | tag.backgroundColor = PYSEARCH_COLORPolRandomColor; 988 | } 989 | break; 990 | case PYHotSearchStyleBorderTag: 991 | for (UILabel *tag in self.hotSearchTags) { 992 | tag.backgroundColor = [UIColor clearColor]; 993 | tag.layer.borderColor = PYSEARCH_COLOR(223, 223, 223).CGColor; 994 | tag.layer.borderWidth = 0.5; 995 | } 996 | break; 997 | case PYHotSearchStyleARCBorderTag: 998 | for (UILabel *tag in self.hotSearchTags) { 999 | tag.backgroundColor = [UIColor clearColor]; 1000 | tag.layer.borderColor = PYSEARCH_COLOR(223, 223, 223).CGColor; 1001 | tag.layer.borderWidth = 0.5; 1002 | tag.layer.cornerRadius = tag.py_height * 0.5; 1003 | } 1004 | break; 1005 | case PYHotSearchStyleRectangleTag: 1006 | self.hotSearches = self.hotSearches; 1007 | break; 1008 | case PYHotSearchStyleRankTag: 1009 | self.rankTagBackgroundColorHexStrings = nil; 1010 | break; 1011 | 1012 | default: 1013 | break; 1014 | } 1015 | } 1016 | 1017 | - (void)setSearchViewControllerShowMode:(PYSearchViewControllerShowMode)searchViewControllerShowMode 1018 | { 1019 | _searchViewControllerShowMode = searchViewControllerShowMode; 1020 | if (_searchViewControllerShowMode == PYSearchViewControllerShowModeModal) { // modal 1021 | self.navigationItem.hidesBackButton = YES; 1022 | self.navigationItem.rightBarButtonItem = _cancelBarButtonItem; 1023 | self.navigationItem.leftBarButtonItem = nil; 1024 | } else if (_searchViewControllerShowMode == PYSearchViewControllerShowModePush) { // push 1025 | self.navigationItem.hidesBackButton = YES; 1026 | self.navigationItem.leftBarButtonItem = _backBarButtonItem; 1027 | self.navigationItem.rightBarButtonItem = nil; 1028 | } 1029 | } 1030 | 1031 | - (void)cancelDidClick 1032 | { 1033 | [self.searchBar resignFirstResponder]; 1034 | 1035 | if ([self.delegate respondsToSelector:@selector(didClickCancel:)]) { 1036 | [self.delegate didClickCancel:self]; 1037 | return; 1038 | } 1039 | 1040 | [self dismissViewControllerAnimated:YES completion:nil]; 1041 | } 1042 | 1043 | - (void)backDidClick 1044 | { 1045 | [self.searchBar resignFirstResponder]; 1046 | 1047 | if ([self.delegate respondsToSelector:@selector(didClickBack:)]) { 1048 | [self.delegate didClickBack:self]; 1049 | return; 1050 | } 1051 | 1052 | [self.navigationController popViewControllerAnimated:YES]; 1053 | } 1054 | 1055 | - (void)keyboardDidShow:(NSNotification *)noti 1056 | { 1057 | NSDictionary *info = noti.userInfo; 1058 | self.keyboardHeight = [info[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; 1059 | self.keyboardShowing = YES; 1060 | // Adjust the content inset of suggestion view 1061 | self.searchSuggestionVC.tableView.contentInset = UIEdgeInsetsMake(-30, 0, self.keyboardHeight + 30, 0); 1062 | } 1063 | 1064 | 1065 | - (void)emptySearchHistoryDidClick 1066 | { 1067 | [self.searchHistories removeAllObjects]; 1068 | [NSKeyedArchiver archiveRootObject:self.searchHistories toFile:self.searchHistoriesCachePath]; 1069 | if (PYSearchHistoryStyleCell == self.searchHistoryStyle) { 1070 | [self.baseSearchTableView reloadData]; 1071 | } else { 1072 | self.searchHistoryStyle = self.searchHistoryStyle; 1073 | } 1074 | if (YES == self.swapHotSeachWithSearchHistory) { 1075 | self.hotSearches = self.hotSearches; 1076 | } 1077 | PYSEARCH_LOG(@"%@", [NSBundle py_localizedStringForKey:PYSearchEmptySearchHistoryLogText]); 1078 | } 1079 | 1080 | - (void)tagDidCLick:(UITapGestureRecognizer *)gr 1081 | { 1082 | UILabel *label = (UILabel *)gr.view; 1083 | self.searchBar.text = label.text; 1084 | // popular search tagLabel's tag is 1, search history tagLabel's tag is 0. 1085 | if (1 == label.tag) { 1086 | if ([self.delegate respondsToSelector:@selector(searchViewController:didSelectHotSearchAtIndex:searchText:)]) { 1087 | [self.delegate searchViewController:self didSelectHotSearchAtIndex:[self.hotSearchTags indexOfObject:label] searchText:label.text]; 1088 | [self saveSearchCacheAndRefreshView]; 1089 | } else { 1090 | [self searchBarSearchButtonClicked:self.searchBar]; 1091 | } 1092 | } else { 1093 | if ([self.delegate respondsToSelector:@selector(searchViewController:didSelectSearchHistoryAtIndex:searchText:)]) { 1094 | [self.delegate searchViewController:self didSelectSearchHistoryAtIndex:[self.searchHistoryTags indexOfObject:label] searchText:label.text]; 1095 | [self saveSearchCacheAndRefreshView]; 1096 | } else { 1097 | [self searchBarSearchButtonClicked:self.searchBar]; 1098 | } 1099 | } 1100 | PYSEARCH_LOG(@"Search %@", label.text); 1101 | } 1102 | 1103 | - (UILabel *)labelWithTitle:(NSString *)title 1104 | { 1105 | UILabel *label = [[UILabel alloc] init]; 1106 | label.userInteractionEnabled = YES; 1107 | label.font = [UIFont systemFontOfSize:12]; 1108 | label.text = title; 1109 | label.textColor = [UIColor grayColor]; 1110 | label.backgroundColor = [UIColor py_colorWithHexString:@"#fafafa"]; 1111 | label.layer.cornerRadius = 3; 1112 | label.clipsToBounds = YES; 1113 | label.textAlignment = NSTextAlignmentCenter; 1114 | [label sizeToFit]; 1115 | label.py_width += 20; 1116 | label.py_height += 14; 1117 | return label; 1118 | } 1119 | 1120 | - (void)saveSearchCacheAndRefreshView 1121 | { 1122 | UISearchBar *searchBar = self.searchBar; 1123 | [searchBar resignFirstResponder]; 1124 | NSString *searchText = searchBar.text; 1125 | if (self.removeSpaceOnSearchString) { // remove sapce on search string 1126 | searchText = [searchBar.text stringByReplacingOccurrencesOfString:@" " withString:@""]; 1127 | } 1128 | if (self.showSearchHistory && searchText.length > 0) { 1129 | [self.searchHistories removeObject:searchText]; 1130 | [self.searchHistories insertObject:searchText atIndex:0]; 1131 | 1132 | if (self.searchHistories.count > self.searchHistoriesCount) { 1133 | [self.searchHistories removeLastObject]; 1134 | } 1135 | [NSKeyedArchiver archiveRootObject:self.searchHistories toFile:self.searchHistoriesCachePath]; 1136 | 1137 | if (PYSearchHistoryStyleCell == self.searchHistoryStyle) { 1138 | [self.baseSearchTableView reloadData]; 1139 | } else { 1140 | self.searchHistoryStyle = self.searchHistoryStyle; 1141 | } 1142 | } 1143 | 1144 | [self handleSearchResultShow]; 1145 | } 1146 | 1147 | - (void)handleSearchResultShow 1148 | { 1149 | switch (self.searchResultShowMode) { 1150 | case PYSearchResultShowModePush: 1151 | self.searchResultController.view.hidden = NO; 1152 | [self.navigationController pushViewController:self.searchResultController animated:YES]; 1153 | break; 1154 | case PYSearchResultShowModeEmbed: 1155 | if (self.searchResultController) { 1156 | [self.view addSubview:self.searchResultController.view]; 1157 | [self addChildViewController:self.searchResultController]; 1158 | self.searchResultController.view.hidden = NO; 1159 | self.searchResultController.view.py_y = NO == self.navigationController.navigationBar.translucent ? 0 : CGRectGetMaxY(self.navigationController.navigationBar.frame); 1160 | self.searchResultController.view.py_height = self.view.py_height - self.searchResultController.view.py_y; 1161 | self.searchSuggestionVC.view.hidden = YES; 1162 | } else { 1163 | PYSEARCH_LOG(@"PYSearchDebug: searchResultController cannot be nil when searchResultShowMode is PYSearchResultShowModeEmbed."); 1164 | } 1165 | break; 1166 | case PYSearchResultShowModeCustom: 1167 | 1168 | break; 1169 | default: 1170 | break; 1171 | } 1172 | } 1173 | 1174 | #pragma mark - PYSearchSuggestionViewDataSource 1175 | - (NSInteger)numberOfSectionsInSearchSuggestionView:(UITableView *)searchSuggestionView 1176 | { 1177 | if ([self.dataSource respondsToSelector:@selector(numberOfSectionsInSearchSuggestionView:)]) { 1178 | return [self.dataSource numberOfSectionsInSearchSuggestionView:searchSuggestionView]; 1179 | } 1180 | return 1; 1181 | } 1182 | 1183 | - (NSInteger)searchSuggestionView:(UITableView *)searchSuggestionView numberOfRowsInSection:(NSInteger)section 1184 | { 1185 | if ([self.dataSource respondsToSelector:@selector(searchSuggestionView:numberOfRowsInSection:)]) { 1186 | NSInteger numberOfRow = [self.dataSource searchSuggestionView:searchSuggestionView numberOfRowsInSection:section]; 1187 | searchSuggestionView.hidden = self.searchSuggestionHidden || !self.searchBar.text.length || 0 == numberOfRow; 1188 | self.baseSearchTableView.hidden = !searchSuggestionView.hidden; 1189 | return numberOfRow; 1190 | } 1191 | return self.searchSuggestions.count; 1192 | } 1193 | 1194 | - (UITableViewCell *)searchSuggestionView:(UITableView *)searchSuggestionView cellForRowAtIndexPath:(NSIndexPath *)indexPath 1195 | { 1196 | if ([self.dataSource respondsToSelector:@selector(searchSuggestionView:cellForRowAtIndexPath:)]) { 1197 | return [self.dataSource searchSuggestionView:searchSuggestionView cellForRowAtIndexPath:indexPath]; 1198 | } 1199 | return nil; 1200 | } 1201 | 1202 | - (CGFloat)searchSuggestionView:(UITableView *)searchSuggestionView heightForRowAtIndexPath:(NSIndexPath *)indexPath 1203 | { 1204 | if ([self.dataSource respondsToSelector:@selector(searchSuggestionView:heightForRowAtIndexPath:)]) { 1205 | return [self.dataSource searchSuggestionView:searchSuggestionView heightForRowAtIndexPath:indexPath]; 1206 | } 1207 | return 44.0; 1208 | } 1209 | 1210 | #pragma mark - UISearchBarDelegate 1211 | - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar 1212 | { 1213 | if ([self.delegate respondsToSelector:@selector(searchViewController:didSearchWithSearchBar:searchText:)]) { 1214 | [self.delegate searchViewController:self didSearchWithSearchBar:searchBar searchText:searchBar.text]; 1215 | [self saveSearchCacheAndRefreshView]; 1216 | return; 1217 | } 1218 | if (self.didSearchBlock) self.didSearchBlock(self, searchBar, searchBar.text); 1219 | [self saveSearchCacheAndRefreshView]; 1220 | } 1221 | 1222 | - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText 1223 | { 1224 | if (PYSearchResultShowModeEmbed == self.searchResultShowMode && self.showSearchResultWhenSearchTextChanged) { 1225 | [self handleSearchResultShow]; 1226 | self.searchResultController.view.hidden = 0 == searchText.length; 1227 | } else if (self.searchResultController) { 1228 | self.searchResultController.view.hidden = YES; 1229 | } 1230 | self.baseSearchTableView.hidden = searchText.length && !self.searchSuggestionHidden && [self.searchSuggestionVC.tableView numberOfRowsInSection:0]; 1231 | self.searchSuggestionVC.view.hidden = self.searchSuggestionHidden || !searchText.length || ![self.searchSuggestionVC.tableView numberOfRowsInSection:0]; 1232 | if (self.searchSuggestionVC.view.hidden) { 1233 | self.searchSuggestions = nil; 1234 | } 1235 | [self.view bringSubviewToFront:self.searchSuggestionVC.view]; 1236 | if ([self.delegate respondsToSelector:@selector(searchViewController:searchTextDidChange:searchText:)]) { 1237 | [self.delegate searchViewController:self searchTextDidChange:searchBar searchText:searchText]; 1238 | } 1239 | } 1240 | 1241 | - (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar 1242 | { 1243 | if (PYSearchResultShowModeEmbed == self.searchResultShowMode) { 1244 | self.searchResultController.view.hidden = 0 == searchBar.text.length || !self.showSearchResultWhenSearchBarRefocused; 1245 | self.searchSuggestionVC.view.hidden = self.searchSuggestionHidden || !searchBar.text.length || ![self.searchSuggestionVC.tableView numberOfRowsInSection:0]; 1246 | if (self.searchSuggestionVC.view.hidden) { 1247 | self.searchSuggestions = nil; 1248 | } 1249 | self.baseSearchTableView.hidden = searchBar.text.length && !self.searchSuggestionHidden && ![self.searchSuggestionVC.tableView numberOfRowsInSection:0]; 1250 | } 1251 | [self setSearchSuggestions:self.searchSuggestions]; 1252 | return YES; 1253 | } 1254 | 1255 | - (void)closeDidClick:(UIButton *)sender 1256 | { 1257 | UITableViewCell *cell = (UITableViewCell *)sender.superview; 1258 | [self.searchHistories removeObject:cell.textLabel.text]; 1259 | [NSKeyedArchiver archiveRootObject:self.searchHistories toFile:self.searchHistoriesCachePath]; 1260 | [self.baseSearchTableView reloadData]; 1261 | } 1262 | 1263 | #pragma mark - Table view data source 1264 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 1265 | return 1; 1266 | } 1267 | 1268 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 1269 | self.baseSearchTableView.tableFooterView.hidden = 0 == self.searchHistories.count || !self.showSearchHistory; 1270 | return self.showSearchHistory && PYSearchHistoryStyleCell == self.searchHistoryStyle ? self.searchHistories.count : 0; 1271 | } 1272 | 1273 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 1274 | { 1275 | static NSString *cellID = @"PYSearchHistoryCellID"; 1276 | 1277 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID]; 1278 | if (!cell) { 1279 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID]; 1280 | cell.textLabel.textColor = PYTextColor; 1281 | cell.textLabel.font = [UIFont systemFontOfSize:14]; 1282 | cell.backgroundColor = [UIColor clearColor]; 1283 | 1284 | UIButton *closetButton = [[UIButton alloc] init]; 1285 | closetButton.py_size = CGSizeMake(cell.py_height, cell.py_height); 1286 | [closetButton setImage:[NSBundle py_imageNamed:@"close"] forState:UIControlStateNormal]; 1287 | UIImageView *closeView = [[UIImageView alloc] initWithImage:[NSBundle py_imageNamed:@"close"]]; 1288 | [closetButton addTarget:self action:@selector(closeDidClick:) forControlEvents:UIControlEventTouchUpInside]; 1289 | closeView.contentMode = UIViewContentModeCenter; 1290 | cell.accessoryView = closetButton; 1291 | UIImageView *line = [[UIImageView alloc] initWithImage:[NSBundle py_imageNamed:@"cell-content-line"]]; 1292 | line.py_height = 0.5; 1293 | line.alpha = 0.7; 1294 | line.py_x = PYSEARCH_MARGIN; 1295 | line.py_y = 43; 1296 | line.py_width = tableView.py_width; 1297 | line.autoresizingMask = UIViewAutoresizingFlexibleWidth; 1298 | [cell.contentView addSubview:line]; 1299 | } 1300 | 1301 | cell.imageView.image = [NSBundle py_imageNamed:@"search_history"]; 1302 | cell.textLabel.text = self.searchHistories[indexPath.row]; 1303 | 1304 | return cell; 1305 | } 1306 | 1307 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 1308 | { 1309 | return self.showSearchHistory && self.searchHistories.count && PYSearchHistoryStyleCell == self.searchHistoryStyle ? (self.searchHistoryTitle.length ? self.searchHistoryTitle : [NSBundle py_localizedStringForKey:PYSearchSearchHistoryText]) : nil; 1310 | } 1311 | 1312 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 1313 | { 1314 | return self.searchHistories.count && self.showSearchHistory && PYSearchHistoryStyleCell == self.searchHistoryStyle ? 25 : 0.01; 1315 | } 1316 | 1317 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section 1318 | { 1319 | return 0.01; 1320 | } 1321 | 1322 | #pragma mark - UITableViewDelegate 1323 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 1324 | { 1325 | UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; 1326 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 1327 | self.searchBar.text = cell.textLabel.text; 1328 | 1329 | if ([self.delegate respondsToSelector:@selector(searchViewController:didSelectSearchHistoryAtIndex:searchText:)]) { 1330 | [self.delegate searchViewController:self didSelectSearchHistoryAtIndex:indexPath.row searchText:cell.textLabel.text]; 1331 | [self saveSearchCacheAndRefreshView]; 1332 | } else { 1333 | [self searchBarSearchButtonClicked:self.searchBar]; 1334 | } 1335 | } 1336 | 1337 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 1338 | { 1339 | if (self.keyboardShowing) { 1340 | // Adjust the content inset of suggestion view 1341 | self.searchSuggestionVC.tableView.contentInset = UIEdgeInsetsMake(-30, 0, 30, 0); 1342 | [self.searchBar resignFirstResponder]; 1343 | } 1344 | } 1345 | 1346 | #pragma mark - UIGestureRecognizerDelegate 1347 | 1348 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer*)gestureRecognizer 1349 | { 1350 | return self.navigationController.childViewControllers.count > 1; 1351 | } 1352 | 1353 | - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer 1354 | { 1355 | return self.navigationController.viewControllers.count > 1; 1356 | } 1357 | 1358 | @end 1359 | -------------------------------------------------------------------------------- /PYSearch/UIColor+PYSearchExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import 8 | 9 | @interface UIColor (PYSearchExtension) 10 | 11 | /** 12 | Returns the corresponding color according to the hexadecimal string. 13 | 14 | @param hexString hexadecimal string(eg:@"#ccff88") 15 | @return new instance of `UIColor` class 16 | */ 17 | + (instancetype)py_colorWithHexString:(NSString *)hexString; 18 | 19 | /** 20 | Returns the corresponding color according to the hexadecimal string and alpha. 21 | 22 | @param hexString hexadecimal string(eg:@"#ccff88") 23 | @param alpha alpha 24 | @return new instance of `UIColor` class 25 | */ 26 | + (instancetype)py_colorWithHexString:(NSString *)hexString alpha:(CGFloat)alpha; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /PYSearch/UIColor+PYSearchExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import "UIColor+PYSearchExtension.h" 8 | 9 | @implementation UIColor (PYSearchExtension) 10 | 11 | + (instancetype)py_colorWithHexString:(NSString *)hexString 12 | { 13 | NSString *colorString = [[hexString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString]; 14 | 15 | if (colorString.length < 6) { 16 | return [UIColor clearColor]; 17 | } 18 | 19 | if ([colorString hasPrefix:@"0X"]) { 20 | colorString = [colorString substringFromIndex:2]; 21 | } 22 | 23 | if ([colorString hasPrefix:@"#"]) { 24 | colorString = [colorString substringFromIndex:1]; 25 | } 26 | 27 | if (colorString.length != 6) { 28 | return [UIColor clearColor]; 29 | } 30 | 31 | NSRange range; 32 | range.location = 0; 33 | range.length = 2; 34 | // r 35 | NSString *rString = [colorString substringWithRange:range]; 36 | 37 | // g 38 | range.location = 2; 39 | NSString *gString = [colorString substringWithRange:range]; 40 | 41 | // b 42 | range.location = 4; 43 | NSString *bString = [colorString substringWithRange:range]; 44 | 45 | unsigned int r, g, b; 46 | [[NSScanner scannerWithString:rString] scanHexInt:&r]; 47 | [[NSScanner scannerWithString:gString] scanHexInt:&g]; 48 | [[NSScanner scannerWithString:bString] scanHexInt:&b]; 49 | 50 | return [UIColor colorWithRed:(float)r / 255.0 green:(float)g / 255.0 blue:(float)b / 255.0 alpha:1.0]; 51 | } 52 | 53 | + (instancetype)py_colorWithHexString:(NSString *)hexString alpha:(CGFloat)alpha 54 | { 55 | return [[self py_colorWithHexString:hexString] colorWithAlphaComponent:alpha]; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /PYSearch/UIView+PYSearchExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import 8 | 9 | @interface UIView (PYSearchExtension) 10 | 11 | @property (nonatomic, assign) CGFloat py_x; 12 | @property (nonatomic, assign) CGFloat py_y; 13 | @property (nonatomic, assign) CGFloat py_centerX; 14 | @property (nonatomic, assign) CGFloat py_centerY; 15 | @property (nonatomic, assign) CGFloat py_width; 16 | @property (nonatomic, assign) CGFloat py_height; 17 | @property (nonatomic, assign) CGSize py_size; 18 | @property (nonatomic, assign) CGPoint py_origin; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /PYSearch/UIView+PYSearchExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import "UIView+PYSearchExtension.h" 8 | 9 | @implementation UIView (PYSearchExtension) 10 | 11 | - (void)setPy_x:(CGFloat)py_x 12 | { 13 | CGRect frame = self.frame; 14 | frame.origin.x = py_x; 15 | self.frame = frame; 16 | } 17 | 18 | - (CGFloat)py_x 19 | { 20 | return self.py_origin.x; 21 | } 22 | 23 | - (void)setPy_centerX:(CGFloat)py_centerX 24 | { 25 | CGPoint center = self.center; 26 | center.x = py_centerX; 27 | self.center = center; 28 | } 29 | 30 | - (CGFloat)py_centerX 31 | { 32 | return self.center.x; 33 | } 34 | 35 | -(void)setPy_centerY:(CGFloat)py_centerY 36 | { 37 | CGPoint center = self.center; 38 | center.y = py_centerY; 39 | self.center = center; 40 | } 41 | 42 | - (CGFloat)py_centerY 43 | { 44 | return self.center.y; 45 | } 46 | 47 | - (void)setPy_y:(CGFloat)py_y 48 | { 49 | CGRect frame = self.frame; 50 | frame.origin.y = py_y; 51 | self.frame = frame; 52 | } 53 | 54 | - (CGFloat)py_y 55 | { 56 | return self.frame.origin.y; 57 | } 58 | 59 | - (void)setPy_size:(CGSize)py_size 60 | { 61 | CGRect frame = self.frame; 62 | frame.size = py_size; 63 | self.frame = frame; 64 | 65 | } 66 | 67 | - (CGSize)py_size 68 | { 69 | return self.frame.size; 70 | } 71 | 72 | - (void)setPy_height:(CGFloat)py_height 73 | { 74 | CGRect frame = self.frame; 75 | frame.size.height = py_height; 76 | self.frame = frame; 77 | } 78 | 79 | - (CGFloat)py_height 80 | { 81 | return self.frame.size.height; 82 | } 83 | 84 | - (void)setPy_width:(CGFloat)py_width 85 | { 86 | CGRect frame = self.frame; 87 | frame.size.width = py_width; 88 | self.frame = frame; 89 | 90 | } 91 | 92 | -(CGFloat)py_width 93 | { 94 | return self.frame.size.width; 95 | } 96 | 97 | - (void)setPy_origin:(CGPoint)py_origin 98 | { 99 | CGRect frame = self.frame; 100 | frame.origin = py_origin; 101 | self.frame = frame; 102 | } 103 | 104 | - (CGPoint)py_origin 105 | { 106 | return self.frame.origin; 107 | } 108 | 109 | @end 110 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6B3EB2111DC498370080DF86 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3EB2101DC498370080DF86 /* AppDelegate.m */; }; 11 | 6B3EB21F1DC498410080DF86 /* PYSearch.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 6B3EB2131DC498410080DF86 /* PYSearch.bundle */; }; 12 | 6B3EB2201DC498410080DF86 /* PYSearchConst.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3EB2161DC498410080DF86 /* PYSearchConst.m */; }; 13 | 6B3EB2211DC498410080DF86 /* PYSearchSuggestionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3EB2181DC498410080DF86 /* PYSearchSuggestionViewController.m */; }; 14 | 6B3EB2221DC498410080DF86 /* PYSearchViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3EB21A1DC498410080DF86 /* PYSearchViewController.m */; }; 15 | 6B3EB2231DC498410080DF86 /* UIColor+PYSearchExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3EB21C1DC498410080DF86 /* UIColor+PYSearchExtension.m */; }; 16 | 6B3EB2241DC498410080DF86 /* UIView+PYSearchExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3EB21E1DC498410080DF86 /* UIView+PYSearchExtension.m */; }; 17 | 6B8FA3851E1B781F00DFD42A /* NSBundle+PYSearchExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B8FA3841E1B781F00DFD42A /* NSBundle+PYSearchExtension.m */; }; 18 | 6B9717681DC48FA800B2DAA1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B9717671DC48FA800B2DAA1 /* main.m */; }; 19 | 6B9717761DC48FA800B2DAA1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6B9717751DC48FA800B2DAA1 /* Assets.xcassets */; }; 20 | 6B9717791DC48FA800B2DAA1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6B9717771DC48FA800B2DAA1 /* LaunchScreen.storyboard */; }; 21 | 6B9717841DC48FA800B2DAA1 /* PYSearchExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B9717831DC48FA800B2DAA1 /* PYSearchExampleTests.m */; }; 22 | 6B97178F1DC48FA800B2DAA1 /* PYSearchExampleUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B97178E1DC48FA800B2DAA1 /* PYSearchExampleUITests.m */; }; 23 | 6B97179E1DC48FE800B2DAA1 /* PYSearchExampleController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B97179D1DC48FE800B2DAA1 /* PYSearchExampleController.m */; }; 24 | 6B9DA6011DC8415700D07A16 /* PYTempViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B9DA6001DC8415700D07A16 /* PYTempViewController.m */; }; 25 | A6324EC11E3130FB00BAA226 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A6324EC31E3130FB00BAA226 /* Localizable.strings */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 6B9717801DC48FA800B2DAA1 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 6B97175B1DC48FA700B2DAA1 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 6B9717621DC48FA800B2DAA1; 34 | remoteInfo = PYSearchExample; 35 | }; 36 | 6B97178B1DC48FA800B2DAA1 /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = 6B97175B1DC48FA700B2DAA1 /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = 6B9717621DC48FA800B2DAA1; 41 | remoteInfo = PYSearchExample; 42 | }; 43 | /* End PBXContainerItemProxy section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | 6B3EB20F1DC498370080DF86 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 47 | 6B3EB2101DC498370080DF86 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 48 | 6B3EB2131DC498410080DF86 /* PYSearch.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = PYSearch.bundle; sourceTree = ""; }; 49 | 6B3EB2141DC498410080DF86 /* PYSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PYSearch.h; sourceTree = ""; }; 50 | 6B3EB2151DC498410080DF86 /* PYSearchConst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PYSearchConst.h; sourceTree = ""; }; 51 | 6B3EB2161DC498410080DF86 /* PYSearchConst.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PYSearchConst.m; sourceTree = ""; }; 52 | 6B3EB2171DC498410080DF86 /* PYSearchSuggestionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PYSearchSuggestionViewController.h; sourceTree = ""; }; 53 | 6B3EB2181DC498410080DF86 /* PYSearchSuggestionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PYSearchSuggestionViewController.m; sourceTree = ""; }; 54 | 6B3EB2191DC498410080DF86 /* PYSearchViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PYSearchViewController.h; sourceTree = ""; }; 55 | 6B3EB21A1DC498410080DF86 /* PYSearchViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PYSearchViewController.m; sourceTree = ""; }; 56 | 6B3EB21B1DC498410080DF86 /* UIColor+PYSearchExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+PYSearchExtension.h"; sourceTree = ""; }; 57 | 6B3EB21C1DC498410080DF86 /* UIColor+PYSearchExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+PYSearchExtension.m"; sourceTree = ""; }; 58 | 6B3EB21D1DC498410080DF86 /* UIView+PYSearchExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+PYSearchExtension.h"; sourceTree = ""; }; 59 | 6B3EB21E1DC498410080DF86 /* UIView+PYSearchExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+PYSearchExtension.m"; sourceTree = ""; }; 60 | 6B8FA3831E1B781F00DFD42A /* NSBundle+PYSearchExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+PYSearchExtension.h"; sourceTree = ""; }; 61 | 6B8FA3841E1B781F00DFD42A /* NSBundle+PYSearchExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+PYSearchExtension.m"; sourceTree = ""; }; 62 | 6B9717631DC48FA800B2DAA1 /* PYSearchExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PYSearchExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 6B9717671DC48FA800B2DAA1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 64 | 6B9717751DC48FA800B2DAA1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 65 | 6B9717781DC48FA800B2DAA1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 66 | 6B97177A1DC48FA800B2DAA1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | 6B97177F1DC48FA800B2DAA1 /* PYSearchExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PYSearchExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | 6B9717831DC48FA800B2DAA1 /* PYSearchExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PYSearchExampleTests.m; sourceTree = ""; }; 69 | 6B9717851DC48FA800B2DAA1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 70 | 6B97178A1DC48FA800B2DAA1 /* PYSearchExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PYSearchExampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | 6B97178E1DC48FA800B2DAA1 /* PYSearchExampleUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PYSearchExampleUITests.m; sourceTree = ""; }; 72 | 6B9717901DC48FA800B2DAA1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 73 | 6B97179C1DC48FE800B2DAA1 /* PYSearchExampleController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PYSearchExampleController.h; sourceTree = ""; }; 74 | 6B97179D1DC48FE800B2DAA1 /* PYSearchExampleController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PYSearchExampleController.m; sourceTree = ""; }; 75 | 6B9DA5FF1DC8415700D07A16 /* PYTempViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PYTempViewController.h; sourceTree = ""; }; 76 | 6B9DA6001DC8415700D07A16 /* PYTempViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PYTempViewController.m; sourceTree = ""; }; 77 | A6324EBB1E312F1100BAA226 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/LaunchScreen.strings; sourceTree = ""; }; 78 | A6324EBC1E312F1600BAA226 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/LaunchScreen.strings; sourceTree = ""; }; 79 | A6324EBD1E312F1C00BAA226 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/LaunchScreen.strings"; sourceTree = ""; }; 80 | A6324EBE1E312F2000BAA226 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/LaunchScreen.strings"; sourceTree = ""; }; 81 | A6324EC21E3130FB00BAA226 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 82 | A6324EC41E3130FE00BAA226 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; 83 | A6324EC51E3130FE00BAA226 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; 84 | A6324EC61E3130FF00BAA226 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; 85 | A6324EC71E31310000BAA226 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; 86 | /* End PBXFileReference section */ 87 | 88 | /* Begin PBXFrameworksBuildPhase section */ 89 | 6B9717601DC48FA800B2DAA1 /* Frameworks */ = { 90 | isa = PBXFrameworksBuildPhase; 91 | buildActionMask = 2147483647; 92 | files = ( 93 | ); 94 | runOnlyForDeploymentPostprocessing = 0; 95 | }; 96 | 6B97177C1DC48FA800B2DAA1 /* Frameworks */ = { 97 | isa = PBXFrameworksBuildPhase; 98 | buildActionMask = 2147483647; 99 | files = ( 100 | ); 101 | runOnlyForDeploymentPostprocessing = 0; 102 | }; 103 | 6B9717871DC48FA800B2DAA1 /* Frameworks */ = { 104 | isa = PBXFrameworksBuildPhase; 105 | buildActionMask = 2147483647; 106 | files = ( 107 | ); 108 | runOnlyForDeploymentPostprocessing = 0; 109 | }; 110 | /* End PBXFrameworksBuildPhase section */ 111 | 112 | /* Begin PBXGroup section */ 113 | 6B3EB2121DC498410080DF86 /* PYSearch */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 6B3EB2131DC498410080DF86 /* PYSearch.bundle */, 117 | 6B3EB2141DC498410080DF86 /* PYSearch.h */, 118 | 6B3EB2151DC498410080DF86 /* PYSearchConst.h */, 119 | 6B3EB2161DC498410080DF86 /* PYSearchConst.m */, 120 | 6B3EB2171DC498410080DF86 /* PYSearchSuggestionViewController.h */, 121 | 6B3EB2181DC498410080DF86 /* PYSearchSuggestionViewController.m */, 122 | 6B3EB2191DC498410080DF86 /* PYSearchViewController.h */, 123 | 6B3EB21A1DC498410080DF86 /* PYSearchViewController.m */, 124 | 6B3EB21B1DC498410080DF86 /* UIColor+PYSearchExtension.h */, 125 | 6B3EB21C1DC498410080DF86 /* UIColor+PYSearchExtension.m */, 126 | 6B3EB21D1DC498410080DF86 /* UIView+PYSearchExtension.h */, 127 | 6B3EB21E1DC498410080DF86 /* UIView+PYSearchExtension.m */, 128 | 6B8FA3831E1B781F00DFD42A /* NSBundle+PYSearchExtension.h */, 129 | 6B8FA3841E1B781F00DFD42A /* NSBundle+PYSearchExtension.m */, 130 | ); 131 | name = PYSearch; 132 | path = ../../PYSearch; 133 | sourceTree = ""; 134 | }; 135 | 6B97175A1DC48FA700B2DAA1 = { 136 | isa = PBXGroup; 137 | children = ( 138 | 6B9717651DC48FA800B2DAA1 /* PYSearchExample */, 139 | 6B9717821DC48FA800B2DAA1 /* PYSearchExampleTests */, 140 | 6B97178D1DC48FA800B2DAA1 /* PYSearchExampleUITests */, 141 | 6B9717641DC48FA800B2DAA1 /* Products */, 142 | ); 143 | sourceTree = ""; 144 | }; 145 | 6B9717641DC48FA800B2DAA1 /* Products */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 6B9717631DC48FA800B2DAA1 /* PYSearchExample.app */, 149 | 6B97177F1DC48FA800B2DAA1 /* PYSearchExampleTests.xctest */, 150 | 6B97178A1DC48FA800B2DAA1 /* PYSearchExampleUITests.xctest */, 151 | ); 152 | name = Products; 153 | sourceTree = ""; 154 | }; 155 | 6B9717651DC48FA800B2DAA1 /* PYSearchExample */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | 6B3EB2121DC498410080DF86 /* PYSearch */, 159 | 6B3EB20F1DC498370080DF86 /* AppDelegate.h */, 160 | 6B3EB2101DC498370080DF86 /* AppDelegate.m */, 161 | 6B97179C1DC48FE800B2DAA1 /* PYSearchExampleController.h */, 162 | 6B97179D1DC48FE800B2DAA1 /* PYSearchExampleController.m */, 163 | 6B9DA5FF1DC8415700D07A16 /* PYTempViewController.h */, 164 | 6B9DA6001DC8415700D07A16 /* PYTempViewController.m */, 165 | 6B9717751DC48FA800B2DAA1 /* Assets.xcassets */, 166 | 6B9717661DC48FA800B2DAA1 /* Supporting Files */, 167 | ); 168 | path = PYSearchExample; 169 | sourceTree = ""; 170 | }; 171 | 6B9717661DC48FA800B2DAA1 /* Supporting Files */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | 6B9717771DC48FA800B2DAA1 /* LaunchScreen.storyboard */, 175 | 6B97177A1DC48FA800B2DAA1 /* Info.plist */, 176 | A6324EC31E3130FB00BAA226 /* Localizable.strings */, 177 | 6B9717671DC48FA800B2DAA1 /* main.m */, 178 | ); 179 | name = "Supporting Files"; 180 | sourceTree = ""; 181 | }; 182 | 6B9717821DC48FA800B2DAA1 /* PYSearchExampleTests */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | 6B9717831DC48FA800B2DAA1 /* PYSearchExampleTests.m */, 186 | 6B9717851DC48FA800B2DAA1 /* Info.plist */, 187 | ); 188 | path = PYSearchExampleTests; 189 | sourceTree = ""; 190 | }; 191 | 6B97178D1DC48FA800B2DAA1 /* PYSearchExampleUITests */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | 6B97178E1DC48FA800B2DAA1 /* PYSearchExampleUITests.m */, 195 | 6B9717901DC48FA800B2DAA1 /* Info.plist */, 196 | ); 197 | path = PYSearchExampleUITests; 198 | sourceTree = ""; 199 | }; 200 | /* End PBXGroup section */ 201 | 202 | /* Begin PBXNativeTarget section */ 203 | 6B9717621DC48FA800B2DAA1 /* PYSearchExample */ = { 204 | isa = PBXNativeTarget; 205 | buildConfigurationList = 6B9717931DC48FA800B2DAA1 /* Build configuration list for PBXNativeTarget "PYSearchExample" */; 206 | buildPhases = ( 207 | 6B97175F1DC48FA800B2DAA1 /* Sources */, 208 | 6B9717601DC48FA800B2DAA1 /* Frameworks */, 209 | 6B9717611DC48FA800B2DAA1 /* Resources */, 210 | ); 211 | buildRules = ( 212 | ); 213 | dependencies = ( 214 | ); 215 | name = PYSearchExample; 216 | productName = PYSearchExample; 217 | productReference = 6B9717631DC48FA800B2DAA1 /* PYSearchExample.app */; 218 | productType = "com.apple.product-type.application"; 219 | }; 220 | 6B97177E1DC48FA800B2DAA1 /* PYSearchExampleTests */ = { 221 | isa = PBXNativeTarget; 222 | buildConfigurationList = 6B9717961DC48FA800B2DAA1 /* Build configuration list for PBXNativeTarget "PYSearchExampleTests" */; 223 | buildPhases = ( 224 | 6B97177B1DC48FA800B2DAA1 /* Sources */, 225 | 6B97177C1DC48FA800B2DAA1 /* Frameworks */, 226 | 6B97177D1DC48FA800B2DAA1 /* Resources */, 227 | ); 228 | buildRules = ( 229 | ); 230 | dependencies = ( 231 | 6B9717811DC48FA800B2DAA1 /* PBXTargetDependency */, 232 | ); 233 | name = PYSearchExampleTests; 234 | productName = PYSearchExampleTests; 235 | productReference = 6B97177F1DC48FA800B2DAA1 /* PYSearchExampleTests.xctest */; 236 | productType = "com.apple.product-type.bundle.unit-test"; 237 | }; 238 | 6B9717891DC48FA800B2DAA1 /* PYSearchExampleUITests */ = { 239 | isa = PBXNativeTarget; 240 | buildConfigurationList = 6B9717991DC48FA800B2DAA1 /* Build configuration list for PBXNativeTarget "PYSearchExampleUITests" */; 241 | buildPhases = ( 242 | 6B9717861DC48FA800B2DAA1 /* Sources */, 243 | 6B9717871DC48FA800B2DAA1 /* Frameworks */, 244 | 6B9717881DC48FA800B2DAA1 /* Resources */, 245 | ); 246 | buildRules = ( 247 | ); 248 | dependencies = ( 249 | 6B97178C1DC48FA800B2DAA1 /* PBXTargetDependency */, 250 | ); 251 | name = PYSearchExampleUITests; 252 | productName = PYSearchExampleUITests; 253 | productReference = 6B97178A1DC48FA800B2DAA1 /* PYSearchExampleUITests.xctest */; 254 | productType = "com.apple.product-type.bundle.ui-testing"; 255 | }; 256 | /* End PBXNativeTarget section */ 257 | 258 | /* Begin PBXProject section */ 259 | 6B97175B1DC48FA700B2DAA1 /* Project object */ = { 260 | isa = PBXProject; 261 | attributes = { 262 | LastUpgradeCheck = 0730; 263 | ORGANIZATIONNAME = CoderKo1o; 264 | TargetAttributes = { 265 | 6B9717621DC48FA800B2DAA1 = { 266 | CreatedOnToolsVersion = 7.3; 267 | }; 268 | 6B97177E1DC48FA800B2DAA1 = { 269 | CreatedOnToolsVersion = 7.3; 270 | TestTargetID = 6B9717621DC48FA800B2DAA1; 271 | }; 272 | 6B9717891DC48FA800B2DAA1 = { 273 | CreatedOnToolsVersion = 7.3; 274 | TestTargetID = 6B9717621DC48FA800B2DAA1; 275 | }; 276 | }; 277 | }; 278 | buildConfigurationList = 6B97175E1DC48FA700B2DAA1 /* Build configuration list for PBXProject "PYSearchExample" */; 279 | compatibilityVersion = "Xcode 3.2"; 280 | developmentRegion = English; 281 | hasScannedForEncodings = 0; 282 | knownRegions = ( 283 | en, 284 | Base, 285 | fr, 286 | es, 287 | "zh-Hans", 288 | "zh-Hant", 289 | ); 290 | mainGroup = 6B97175A1DC48FA700B2DAA1; 291 | productRefGroup = 6B9717641DC48FA800B2DAA1 /* Products */; 292 | projectDirPath = ""; 293 | projectRoot = ""; 294 | targets = ( 295 | 6B9717621DC48FA800B2DAA1 /* PYSearchExample */, 296 | 6B97177E1DC48FA800B2DAA1 /* PYSearchExampleTests */, 297 | 6B9717891DC48FA800B2DAA1 /* PYSearchExampleUITests */, 298 | ); 299 | }; 300 | /* End PBXProject section */ 301 | 302 | /* Begin PBXResourcesBuildPhase section */ 303 | 6B9717611DC48FA800B2DAA1 /* Resources */ = { 304 | isa = PBXResourcesBuildPhase; 305 | buildActionMask = 2147483647; 306 | files = ( 307 | 6B9717791DC48FA800B2DAA1 /* LaunchScreen.storyboard in Resources */, 308 | A6324EC11E3130FB00BAA226 /* Localizable.strings in Resources */, 309 | 6B3EB21F1DC498410080DF86 /* PYSearch.bundle in Resources */, 310 | 6B9717761DC48FA800B2DAA1 /* Assets.xcassets in Resources */, 311 | ); 312 | runOnlyForDeploymentPostprocessing = 0; 313 | }; 314 | 6B97177D1DC48FA800B2DAA1 /* Resources */ = { 315 | isa = PBXResourcesBuildPhase; 316 | buildActionMask = 2147483647; 317 | files = ( 318 | ); 319 | runOnlyForDeploymentPostprocessing = 0; 320 | }; 321 | 6B9717881DC48FA800B2DAA1 /* Resources */ = { 322 | isa = PBXResourcesBuildPhase; 323 | buildActionMask = 2147483647; 324 | files = ( 325 | ); 326 | runOnlyForDeploymentPostprocessing = 0; 327 | }; 328 | /* End PBXResourcesBuildPhase section */ 329 | 330 | /* Begin PBXSourcesBuildPhase section */ 331 | 6B97175F1DC48FA800B2DAA1 /* Sources */ = { 332 | isa = PBXSourcesBuildPhase; 333 | buildActionMask = 2147483647; 334 | files = ( 335 | 6B3EB2201DC498410080DF86 /* PYSearchConst.m in Sources */, 336 | 6B3EB2241DC498410080DF86 /* UIView+PYSearchExtension.m in Sources */, 337 | 6B97179E1DC48FE800B2DAA1 /* PYSearchExampleController.m in Sources */, 338 | 6B3EB2211DC498410080DF86 /* PYSearchSuggestionViewController.m in Sources */, 339 | 6B3EB2221DC498410080DF86 /* PYSearchViewController.m in Sources */, 340 | 6B9DA6011DC8415700D07A16 /* PYTempViewController.m in Sources */, 341 | 6B9717681DC48FA800B2DAA1 /* main.m in Sources */, 342 | 6B3EB2231DC498410080DF86 /* UIColor+PYSearchExtension.m in Sources */, 343 | 6B8FA3851E1B781F00DFD42A /* NSBundle+PYSearchExtension.m in Sources */, 344 | 6B3EB2111DC498370080DF86 /* AppDelegate.m in Sources */, 345 | ); 346 | runOnlyForDeploymentPostprocessing = 0; 347 | }; 348 | 6B97177B1DC48FA800B2DAA1 /* Sources */ = { 349 | isa = PBXSourcesBuildPhase; 350 | buildActionMask = 2147483647; 351 | files = ( 352 | 6B9717841DC48FA800B2DAA1 /* PYSearchExampleTests.m in Sources */, 353 | ); 354 | runOnlyForDeploymentPostprocessing = 0; 355 | }; 356 | 6B9717861DC48FA800B2DAA1 /* Sources */ = { 357 | isa = PBXSourcesBuildPhase; 358 | buildActionMask = 2147483647; 359 | files = ( 360 | 6B97178F1DC48FA800B2DAA1 /* PYSearchExampleUITests.m in Sources */, 361 | ); 362 | runOnlyForDeploymentPostprocessing = 0; 363 | }; 364 | /* End PBXSourcesBuildPhase section */ 365 | 366 | /* Begin PBXTargetDependency section */ 367 | 6B9717811DC48FA800B2DAA1 /* PBXTargetDependency */ = { 368 | isa = PBXTargetDependency; 369 | target = 6B9717621DC48FA800B2DAA1 /* PYSearchExample */; 370 | targetProxy = 6B9717801DC48FA800B2DAA1 /* PBXContainerItemProxy */; 371 | }; 372 | 6B97178C1DC48FA800B2DAA1 /* PBXTargetDependency */ = { 373 | isa = PBXTargetDependency; 374 | target = 6B9717621DC48FA800B2DAA1 /* PYSearchExample */; 375 | targetProxy = 6B97178B1DC48FA800B2DAA1 /* PBXContainerItemProxy */; 376 | }; 377 | /* End PBXTargetDependency section */ 378 | 379 | /* Begin PBXVariantGroup section */ 380 | 6B9717771DC48FA800B2DAA1 /* LaunchScreen.storyboard */ = { 381 | isa = PBXVariantGroup; 382 | children = ( 383 | 6B9717781DC48FA800B2DAA1 /* Base */, 384 | A6324EBB1E312F1100BAA226 /* fr */, 385 | A6324EBC1E312F1600BAA226 /* es */, 386 | A6324EBD1E312F1C00BAA226 /* zh-Hans */, 387 | A6324EBE1E312F2000BAA226 /* zh-Hant */, 388 | ); 389 | name = LaunchScreen.storyboard; 390 | sourceTree = ""; 391 | }; 392 | A6324EC31E3130FB00BAA226 /* Localizable.strings */ = { 393 | isa = PBXVariantGroup; 394 | children = ( 395 | A6324EC21E3130FB00BAA226 /* en */, 396 | A6324EC41E3130FE00BAA226 /* fr */, 397 | A6324EC51E3130FE00BAA226 /* es */, 398 | A6324EC61E3130FF00BAA226 /* zh-Hans */, 399 | A6324EC71E31310000BAA226 /* zh-Hant */, 400 | ); 401 | name = Localizable.strings; 402 | sourceTree = ""; 403 | }; 404 | /* End PBXVariantGroup section */ 405 | 406 | /* Begin XCBuildConfiguration section */ 407 | 6B9717911DC48FA800B2DAA1 /* Debug */ = { 408 | isa = XCBuildConfiguration; 409 | buildSettings = { 410 | ALWAYS_SEARCH_USER_PATHS = NO; 411 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 412 | CLANG_ANALYZER_NONNULL = YES; 413 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 414 | CLANG_CXX_LIBRARY = "libc++"; 415 | CLANG_ENABLE_MODULES = YES; 416 | CLANG_ENABLE_OBJC_ARC = YES; 417 | CLANG_WARN_BOOL_CONVERSION = YES; 418 | CLANG_WARN_CONSTANT_CONVERSION = YES; 419 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 420 | CLANG_WARN_EMPTY_BODY = YES; 421 | CLANG_WARN_ENUM_CONVERSION = YES; 422 | CLANG_WARN_INT_CONVERSION = YES; 423 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 424 | CLANG_WARN_UNREACHABLE_CODE = YES; 425 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 426 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 427 | COPY_PHASE_STRIP = NO; 428 | DEBUG_INFORMATION_FORMAT = dwarf; 429 | ENABLE_STRICT_OBJC_MSGSEND = YES; 430 | ENABLE_TESTABILITY = YES; 431 | GCC_C_LANGUAGE_STANDARD = gnu99; 432 | GCC_DYNAMIC_NO_PIC = NO; 433 | GCC_NO_COMMON_BLOCKS = YES; 434 | GCC_OPTIMIZATION_LEVEL = 0; 435 | GCC_PREPROCESSOR_DEFINITIONS = ( 436 | "DEBUG=1", 437 | "$(inherited)", 438 | ); 439 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 440 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 441 | GCC_WARN_UNDECLARED_SELECTOR = YES; 442 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 443 | GCC_WARN_UNUSED_FUNCTION = YES; 444 | GCC_WARN_UNUSED_VARIABLE = YES; 445 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 446 | MTL_ENABLE_DEBUG_INFO = YES; 447 | ONLY_ACTIVE_ARCH = YES; 448 | SDKROOT = iphoneos; 449 | }; 450 | name = Debug; 451 | }; 452 | 6B9717921DC48FA800B2DAA1 /* Release */ = { 453 | isa = XCBuildConfiguration; 454 | buildSettings = { 455 | ALWAYS_SEARCH_USER_PATHS = NO; 456 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 457 | CLANG_ANALYZER_NONNULL = YES; 458 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 459 | CLANG_CXX_LIBRARY = "libc++"; 460 | CLANG_ENABLE_MODULES = YES; 461 | CLANG_ENABLE_OBJC_ARC = YES; 462 | CLANG_WARN_BOOL_CONVERSION = YES; 463 | CLANG_WARN_CONSTANT_CONVERSION = YES; 464 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 465 | CLANG_WARN_EMPTY_BODY = YES; 466 | CLANG_WARN_ENUM_CONVERSION = YES; 467 | CLANG_WARN_INT_CONVERSION = YES; 468 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 469 | CLANG_WARN_UNREACHABLE_CODE = YES; 470 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 471 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 472 | COPY_PHASE_STRIP = NO; 473 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 474 | ENABLE_NS_ASSERTIONS = NO; 475 | ENABLE_STRICT_OBJC_MSGSEND = YES; 476 | GCC_C_LANGUAGE_STANDARD = gnu99; 477 | GCC_NO_COMMON_BLOCKS = YES; 478 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 479 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 480 | GCC_WARN_UNDECLARED_SELECTOR = YES; 481 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 482 | GCC_WARN_UNUSED_FUNCTION = YES; 483 | GCC_WARN_UNUSED_VARIABLE = YES; 484 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 485 | MTL_ENABLE_DEBUG_INFO = NO; 486 | SDKROOT = iphoneos; 487 | VALIDATE_PRODUCT = YES; 488 | }; 489 | name = Release; 490 | }; 491 | 6B9717941DC48FA800B2DAA1 /* Debug */ = { 492 | isa = XCBuildConfiguration; 493 | buildSettings = { 494 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 495 | DEVELOPMENT_TEAM = ""; 496 | INFOPLIST_FILE = PYSearchExample/Info.plist; 497 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 498 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 499 | PRODUCT_BUNDLE_IDENTIFIER = com.iphone5solo.PYSearchExample; 500 | PRODUCT_NAME = "$(TARGET_NAME)"; 501 | TARGETED_DEVICE_FAMILY = "1,2"; 502 | }; 503 | name = Debug; 504 | }; 505 | 6B9717951DC48FA800B2DAA1 /* Release */ = { 506 | isa = XCBuildConfiguration; 507 | buildSettings = { 508 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 509 | DEVELOPMENT_TEAM = ""; 510 | INFOPLIST_FILE = PYSearchExample/Info.plist; 511 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 512 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 513 | PRODUCT_BUNDLE_IDENTIFIER = com.iphone5solo.PYSearchExample; 514 | PRODUCT_NAME = "$(TARGET_NAME)"; 515 | TARGETED_DEVICE_FAMILY = "1,2"; 516 | }; 517 | name = Release; 518 | }; 519 | 6B9717971DC48FA800B2DAA1 /* Debug */ = { 520 | isa = XCBuildConfiguration; 521 | buildSettings = { 522 | BUNDLE_LOADER = "$(TEST_HOST)"; 523 | INFOPLIST_FILE = PYSearchExampleTests/Info.plist; 524 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 525 | PRODUCT_BUNDLE_IDENTIFIER = com.iphone5solo.PYSearchExampleTests; 526 | PRODUCT_NAME = "$(TARGET_NAME)"; 527 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PYSearchExample.app/PYSearchExample"; 528 | }; 529 | name = Debug; 530 | }; 531 | 6B9717981DC48FA800B2DAA1 /* Release */ = { 532 | isa = XCBuildConfiguration; 533 | buildSettings = { 534 | BUNDLE_LOADER = "$(TEST_HOST)"; 535 | INFOPLIST_FILE = PYSearchExampleTests/Info.plist; 536 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 537 | PRODUCT_BUNDLE_IDENTIFIER = com.iphone5solo.PYSearchExampleTests; 538 | PRODUCT_NAME = "$(TARGET_NAME)"; 539 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PYSearchExample.app/PYSearchExample"; 540 | }; 541 | name = Release; 542 | }; 543 | 6B97179A1DC48FA800B2DAA1 /* Debug */ = { 544 | isa = XCBuildConfiguration; 545 | buildSettings = { 546 | INFOPLIST_FILE = PYSearchExampleUITests/Info.plist; 547 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 548 | PRODUCT_BUNDLE_IDENTIFIER = com.iphone5solo.PYSearchExampleUITests; 549 | PRODUCT_NAME = "$(TARGET_NAME)"; 550 | TEST_TARGET_NAME = PYSearchExample; 551 | }; 552 | name = Debug; 553 | }; 554 | 6B97179B1DC48FA800B2DAA1 /* Release */ = { 555 | isa = XCBuildConfiguration; 556 | buildSettings = { 557 | INFOPLIST_FILE = PYSearchExampleUITests/Info.plist; 558 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 559 | PRODUCT_BUNDLE_IDENTIFIER = com.iphone5solo.PYSearchExampleUITests; 560 | PRODUCT_NAME = "$(TARGET_NAME)"; 561 | TEST_TARGET_NAME = PYSearchExample; 562 | }; 563 | name = Release; 564 | }; 565 | /* End XCBuildConfiguration section */ 566 | 567 | /* Begin XCConfigurationList section */ 568 | 6B97175E1DC48FA700B2DAA1 /* Build configuration list for PBXProject "PYSearchExample" */ = { 569 | isa = XCConfigurationList; 570 | buildConfigurations = ( 571 | 6B9717911DC48FA800B2DAA1 /* Debug */, 572 | 6B9717921DC48FA800B2DAA1 /* Release */, 573 | ); 574 | defaultConfigurationIsVisible = 0; 575 | defaultConfigurationName = Release; 576 | }; 577 | 6B9717931DC48FA800B2DAA1 /* Build configuration list for PBXNativeTarget "PYSearchExample" */ = { 578 | isa = XCConfigurationList; 579 | buildConfigurations = ( 580 | 6B9717941DC48FA800B2DAA1 /* Debug */, 581 | 6B9717951DC48FA800B2DAA1 /* Release */, 582 | ); 583 | defaultConfigurationIsVisible = 0; 584 | defaultConfigurationName = Release; 585 | }; 586 | 6B9717961DC48FA800B2DAA1 /* Build configuration list for PBXNativeTarget "PYSearchExampleTests" */ = { 587 | isa = XCConfigurationList; 588 | buildConfigurations = ( 589 | 6B9717971DC48FA800B2DAA1 /* Debug */, 590 | 6B9717981DC48FA800B2DAA1 /* Release */, 591 | ); 592 | defaultConfigurationIsVisible = 0; 593 | defaultConfigurationName = Release; 594 | }; 595 | 6B9717991DC48FA800B2DAA1 /* Build configuration list for PBXNativeTarget "PYSearchExampleUITests" */ = { 596 | isa = XCConfigurationList; 597 | buildConfigurations = ( 598 | 6B97179A1DC48FA800B2DAA1 /* Debug */, 599 | 6B97179B1DC48FA800B2DAA1 /* Release */, 600 | ); 601 | defaultConfigurationIsVisible = 0; 602 | defaultConfigurationName = Release; 603 | }; 604 | /* End XCConfigurationList section */ 605 | }; 606 | rootObject = 6B97175B1DC48FA700B2DAA1 /* Project object */; 607 | } 608 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample.xcodeproj/project.xcworkspace/xcuserdata/iphone5solo.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ko1o/PYSearch/b62360587dae1e7aa7545d427de49916407f2a94/PYSearchExample/PYSearchExample.xcodeproj/project.xcworkspace/xcuserdata/iphone5solo.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample.xcodeproj/xcuserdata/iphone5solo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample.xcodeproj/xcuserdata/iphone5solo.xcuserdatad/xcschemes/PYSearchExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample.xcodeproj/xcuserdata/iphone5solo.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | PYSearchExample.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 6B9717621DC48FA800B2DAA1 16 | 17 | primary 18 | 19 | 20 | 6B97177E1DC48FA800B2DAA1 21 | 22 | primary 23 | 24 | 25 | 6B9717891DC48FA800B2DAA1 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | #import 7 | 8 | @interface AppDelegate : UIResponder 9 | 10 | @property (strong, nonatomic) UIWindow *window; 11 | 12 | @end 13 | 14 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import "AppDelegate.h" 8 | #import "PYSearchExampleController.h" 9 | 10 | @interface AppDelegate () 11 | 12 | @end 13 | 14 | @implementation AppDelegate 15 | 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | if (!self.window) { 19 | UIWindow *keyWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 20 | keyWindow.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[PYSearchExampleController alloc] init]]; 21 | [keyWindow makeKeyAndVisible]; 22 | self.window = keyWindow; 23 | } 24 | return YES; 25 | } 26 | 27 | 28 | - (void)applicationWillResignActive:(UIApplication *)application { 29 | // 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. 30 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 31 | } 32 | 33 | 34 | - (void)applicationDidEnterBackground:(UIApplication *)application { 35 | // 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. 36 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 37 | } 38 | 39 | 40 | - (void)applicationWillEnterForeground:(UIApplication *)application { 41 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 42 | } 43 | 44 | 45 | - (void)applicationDidBecomeActive:(UIApplication *)application { 46 | // 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. 47 | } 48 | 49 | 50 | - (void)applicationWillTerminate:(UIApplication *)application { 51 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 52 | } 53 | 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | PYSearch 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarTintParameters 34 | 35 | UINavigationBar 36 | 37 | Style 38 | UIBarStyleDefault 39 | Translucent 40 | 41 | 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | UIInterfaceOrientationPortraitUpsideDown 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/PYSearchExampleController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import 8 | 9 | @interface PYSearchExampleController : UITableViewController 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/PYSearchExampleController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import "PYSearchExampleController.h" 8 | #import "PYSearch.h" 9 | #import "PYTempViewController.h" 10 | 11 | @interface PYSearchExampleController () 12 | 13 | @end 14 | 15 | @implementation PYSearchExampleController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | 20 | // set title 21 | self.title = @"PYSearch Example"; 22 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; 23 | if ([self.tableView respondsToSelector:@selector(setCellLayoutMarginsFollowReadableWidth:)]) { // Adjust for iPad 24 | self.tableView.cellLayoutMarginsFollowReadableWidth = NO; 25 | } 26 | } 27 | 28 | - (void)didReceiveMemoryWarning { 29 | [super didReceiveMemoryWarning]; 30 | // Dispose of any resources that can be recreated. 31 | } 32 | 33 | #pragma mark - Table view data source 34 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 35 | { 36 | return 2; 37 | } 38 | 39 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 40 | return section ? 5 : 6; 41 | } 42 | 43 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 44 | UITableViewCell *cell = [[UITableViewCell alloc] init]; 45 | if (0 == indexPath.section) { 46 | cell.textLabel.text = @[@"PYHotSearchStyleDefault", @"PYHotSearchStyleColorfulTag", @"PYHotSearchStyleBorderTag", @"PYHotSearchStyleARCBorderTag", @"PYHotSearchStyleRankTag", @"PYHotSearchStyleRectangleTag"][indexPath.row]; 47 | } else { 48 | cell.textLabel.text = @[@"PYSearchHistoryStyleDefault", @"PYSearchHistoryStyleNormalTag", @"PYSearchHistoryStyleColorfulTag", @"PYSearchHistoryStyleBorderTag", @"PYSearchHistoryStyleARCBorderTag"][indexPath.row]; 49 | } 50 | return cell; 51 | } 52 | 53 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 54 | { 55 | // 1. Create an Array of popular search 56 | NSArray *hotSeaches = @[@"Java", @"Python", @"Objective-C", @"Swift", @"C", @"C++", @"PHP", @"C#", @"Perl", @"Go", @"JavaScript", @"R", @"Ruby", @"MATLAB"]; 57 | // 2. Create a search view controller 58 | PYSearchViewController *searchViewController = [PYSearchViewController searchViewControllerWithHotSearches:hotSeaches searchBarPlaceholder:NSLocalizedString(@"PYExampleSearchPlaceholderText", @"搜索编程语言") didSearchBlock:^(PYSearchViewController *searchViewController, UISearchBar *searchBar, NSString *searchText) { 59 | // Called when search begain. 60 | // eg:Push to a temp view controller 61 | [searchViewController.navigationController pushViewController:[[PYTempViewController alloc] init] animated:YES]; 62 | }]; 63 | // 3. Set style for popular search and search history 64 | if (0 == indexPath.section) { 65 | searchViewController.hotSearchStyle = (NSInteger)indexPath.row; 66 | searchViewController.searchHistoryStyle = PYHotSearchStyleDefault; 67 | } else { 68 | searchViewController.hotSearchStyle = PYHotSearchStyleDefault; 69 | searchViewController.searchHistoryStyle = (NSInteger)indexPath.row; 70 | } 71 | // 4. Set delegate 72 | searchViewController.delegate = self; 73 | // 5. Present(Modal) or push search view controller 74 | // Present(Modal) 75 | // UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:searchViewController]; 76 | // [self presentViewController:nav animated:YES completion:nil]; 77 | // Push 78 | // Set mode of show search view controller, default is `PYSearchViewControllerShowModeModal` 79 | searchViewController.searchViewControllerShowMode = PYSearchViewControllerShowModePush; 80 | // // Push search view controller 81 | [self.navigationController pushViewController:searchViewController animated:YES]; 82 | } 83 | 84 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 85 | { 86 | return section ? NSLocalizedString(@"PYExampleTableSectionZeroTitle", @"选择搜索历史风格(热门搜索为默认风格)") : NSLocalizedString(@"PYExampleTableSectionZeroTitle", @"选择热门搜索风格(搜索历史为默认风格)"); 87 | } 88 | 89 | #pragma mark - PYSearchViewControllerDelegate 90 | - (void)searchViewController:(PYSearchViewController *)searchViewController searchTextDidChange:(UISearchBar *)seachBar searchText:(NSString *)searchText 91 | { 92 | if (searchText.length) { 93 | // Simulate a send request to get a search suggestions 94 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 95 | NSMutableArray *searchSuggestionsM = [NSMutableArray array]; 96 | for (int i = 0; i < arc4random_uniform(5) + 10; i++) { 97 | NSString *searchSuggestion = [NSString stringWithFormat:@"Search suggestion %d", i]; 98 | [searchSuggestionsM addObject:searchSuggestion]; 99 | } 100 | // Refresh and display the search suggustions 101 | searchViewController.searchSuggestions = searchSuggestionsM; 102 | }); 103 | } 104 | } 105 | @end 106 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/PYTempViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import 8 | 9 | @interface PYTempViewController : UIViewController 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/PYTempViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub: https://github.com/iphone5solo/PYSearch 3 | // Created by CoderKo1o. 4 | // Copyright © 2016 iphone5solo. All rights reserved. 5 | // 6 | 7 | #import "PYTempViewController.h" 8 | #import "PYSearchConst.h" 9 | 10 | @interface PYTempViewController () 11 | 12 | @end 13 | 14 | @implementation PYTempViewController 15 | 16 | - (void)viewDidLoad { 17 | [super viewDidLoad]; 18 | 19 | self.title = @"SearchResultViewController"; 20 | self.view.backgroundColor = PYSEARCH_RANDOM_COLOR; 21 | } 22 | 23 | - (void)didReceiveMemoryWarning { 24 | [super didReceiveMemoryWarning]; 25 | // Dispose of any resources that can be recreated. 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | PYSearchExample 4 | 5 | Created by Sam Spencer on 1/19/17. 6 | Copyright © 2017 CoderKo1o. All rights reserved. 7 | */ 8 | 9 | "PYExampleSearchPlaceholderText" = "Search programming language"; 10 | "PYExampleTableSectionZeroTitle" = "Select a Search History Style (Popular Search is default)"; 11 | "PYExampleTableSectionOneTitle" = "Select Top Search Style (Search History is default)"; 12 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/es.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/es.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | PYSearchExample 4 | 5 | Created by Sam Spencer on 1/19/17. 6 | Copyright © 2017 CoderKo1o. All rights reserved. 7 | */ 8 | 9 | "PYExampleSearchPlaceholderText" = "Buscar lenguaje de programación"; 10 | "PYExampleTableSectionZeroTitle" = "Elija el estilo Historial de Búsqueda"; 11 | "PYExampleTableSectionOneTitle" = "Elija el estilo Popular de Búsquedas"; 12 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/fr.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/fr.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | PYSearchExample 4 | 5 | Created by Sam Spencer on 1/19/17. 6 | Copyright © 2017 CoderKo1o. All rights reserved. 7 | */ 8 | 9 | "PYExampleSearchPlaceholderText" = "Rechercher un langage de programmation"; 10 | "PYExampleTableSectionZeroTitle" = "Choisissez le style de Historique de la Recherche"; 11 | "PYExampleTableSectionOneTitle" = "Choisissez le style de Recherches Populaires"; 12 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PYSearchExample 4 | // 5 | // Created by 谢培艺 on 2016/10/29. 6 | // Copyright © 2016年 CoderKo1o. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/zh-Hans.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | PYSearchExample 4 | 5 | Created by Sam Spencer on 1/19/17. 6 | Copyright © 2017 CoderKo1o. All rights reserved. 7 | */ 8 | 9 | "PYExampleSearchPlaceholderText" = "搜索编程语言"; 10 | "PYExampleTableSectionZeroTitle" = "选择搜索历史风格(热门搜索为默认风格)"; 11 | "PYExampleTableSectionOneTitle" = ""; 12 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/zh-Hant.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExample/zh-Hant.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | PYSearchExample 4 | 5 | Created by Sam Spencer on 1/19/17. 6 | Copyright © 2017 CoderKo1o. All rights reserved. 7 | */ 8 | 9 | "PYExampleSearchPlaceholderText" = "搜索编程语言"; 10 | "PYExampleTableSectionZeroTitle" = "选择搜索历史风格(热门搜索为默认风格)"; 11 | "PYExampleTableSectionOneTitle" = ""; 12 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExampleTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExampleTests/PYSearchExampleTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PYSearchExampleTests.m 3 | // PYSearchExampleTests 4 | // 5 | // Created by 谢培艺 on 2016/10/29. 6 | // Copyright © 2016年 CoderKo1o. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PYSearchExampleTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation PYSearchExampleTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExampleUITests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /PYSearchExample/PYSearchExampleUITests/PYSearchExampleUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PYSearchExampleUITests.m 3 | // PYSearchExampleUITests 4 | // 5 | // Created by 谢培艺 on 2016/10/29. 6 | // Copyright © 2016年 CoderKo1o. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PYSearchExampleUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation PYSearchExampleUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | [![Apps Using](https://img.shields.io/badge/Apps%20Using-%3E%203,000-00BFFF.svg?style=plastic)](https://cocoapods.org/pods/PYSearch) 5 | [![Total Downloads](https://img.shields.io/badge/Total%20Downloads-%3E%2060,000-00BFFF.svg?style=plastic)](https://cocoapods.org/pods/PYSearch) 6 |
7 | [![Build Status](https://travis-ci.org/ko1o/PYSearch.svg?branch=master)](https://travis-ci.org/ko1o/PYSearch) 8 | [![Pod Version](http://img.shields.io/cocoapods/v/PYSearch.svg?style=flat)](http://cocoadocs.org/docsets/PYSearch/) 9 | [![Pod Platform](https://img.shields.io/badge/platform-iOS%207%2B-blue.svg?style=flat)](http://cocoadocs.org/docsets/PYSearch/) 10 | [![Pod License](http://img.shields.io/cocoapods/l/PYSearch.svg?style=flat)](https://opensource.org/licenses/MIT) 11 | 12 | - 🔍 An elegant search controller for iOS. 13 | 14 | ## QQ chat room 15 |   16 | 17 | ## Features 18 | - [x] Support a variety of hot search style 19 | - [x] Support a variety of search history style 20 | - [x] Support a variety of search results display mode 21 | - [x] Support a variety of search view controller display mode 22 | - [x] Support search suggestions 23 | - [x] Support search history (record) cache 24 | - [x] Support callback using delegate or block completion search 25 | - [x] Support CocoaPods 26 | - [x] Support localization 27 | - [x] Support vertical and horizontal screen on iPhone and iPad 28 | 29 | ## Requirements 30 | * iOS 7.0 or later 31 | * Xcode 7.0 or later 32 | 33 | ## Architecture 34 | ### Main 35 | - `PYSearch` 36 | - `PYSearchConst` 37 | - `PYSearchViewController` 38 | - `PYSearchSuggestionViewController` 39 | 40 | ### Category 41 | - `UIColor+PYSearchExtension` 42 | - `UIView+PYSearchExtension` 43 | - `NSBundle+PYSearchExtension` 44 | 45 | ## Contents 46 | * Getting Started 47 | * [Renderings](#Renderings) 48 | * [Styles](#Styles) 49 | 50 | * Usage 51 | * [How to use](#Howtouse) 52 | * [Details (See the example program PYSearchExample for details)](#Details) 53 | * [Custom](#Custom) 54 | 55 | * [Hope](#Hope) 56 | 57 | ## Renderings 58 | 59 | 60 | 61 | ## Styles 62 | 63 | #### Hot search style 64 |

65 | 66 | #### Search history style 67 |

68 | 69 | ## How to use 70 | * Use CocoaPods: 71 | - `pod "PYSearch"` 72 | - Import the main file:`#import ` 73 | * Manual import: 74 | - Drag All files in the `PYSearch` folder to project 75 | - Import the main file:`#import "PYSearch.h"` 76 | 77 | 78 | ## Details (See the example program PYSearchExample for details) 79 | ```objc 80 | // 1. Create hotSearches array 81 | NSArray *hotSeaches = @[@"Java", @"Python", @"Objective-C", @"Swift", @"C", @"C++", @"PHP", @"C#", @"Perl", @"Go", @"JavaScript", @"R", @"Ruby", @"MATLAB"]; 82 | // 2. Create searchViewController 83 | PYSearchViewController *searchViewController = [PYSearchViewController searchViewControllerWithHotSearches:hotSeaches searchBarPlaceholder:@"Search programming language" didSearchBlock:^(PYSearchViewController *searchViewController, UISearchBar *searchBar, NSString *searchText) { 84 | // Call this Block when completion search automatically 85 | // Such as: Push to a view controller 86 | [searchViewController.navigationController pushViewController:[[UIViewController alloc] init] animated:YES]; 87 | 88 | }]; 89 | // 3. present the searchViewController 90 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:searchViewController]; 91 | [self presentViewController:nav animated:NO completion:nil]; 92 | 93 | ``` 94 | 95 | ## Custom 96 | 97 | * Custom search suggestions display 98 | ```objc 99 | // 1. Set dataSource 100 | searchViewController.dataSource = self; 101 | // 2. Implement dataSource method 102 | ``` 103 | 104 | * Custom search result dispaly 105 | ```objc 106 | // 1. Set searchResultShowMode 107 | searchViewController.searchResultShowMode = PYSearchResultShowModeEmbed; 108 | // 2. Set searchResultController 109 | searchViewController.searchResultController = [[UIViewController alloc] init]; 110 | ``` 111 | 112 | * Set hotSearchStyle(default is PYHotSearchStyleNormalTag) 113 | ```objc 114 | // Set hotSearchStyle 115 | searchViewController.hotSearchStyle = PYHotSearchStyleColorfulTag; 116 | ``` 117 | 118 | * Set searchHistoryStyle(default is PYSearchHistoryStyleCell) 119 | ```objc 120 | // Set searchHistoryStyle 121 | searchViewController.searchHistoryStyle = PYSearchHistoryStyleBorderTag; 122 | ``` 123 | 124 | * Set searchHistoriesCachePath(default is PYSEARCH_SEARCH_HISTORY_CACHE_PATH) 125 | ```objc 126 | // Set searchHistoriesCachePath 127 | searchViewController.searchHistoriesCachePath = @"The cache path"; 128 | ``` 129 | 130 | * Set searchHistoriesCount(default is 20) 131 | ```objc 132 | // Set searchHistoriesCount 133 | searchViewController. searchHistoriesCount = 6; 134 | ``` 135 | 136 | * Set searchResultShowMode(default is PYSearchResultShowModeCustom) 137 | ```objc 138 | // Set searchResultShowMode 139 | searchViewController.searchResultShowMode = PYSearchResultShowModeEmbed; 140 | ``` 141 | 142 | * Set searchSuggestionHidden(deafult is NO) 143 | ```objc 144 | // Set searchSuggestionHidden 145 | searchViewController.searchSuggestionHidden = YES; 146 | ``` 147 | 148 | ## Hope 149 | 150 | - If you have any questions during the process or want more interfaces to customize,you can [issues me](https://github.com/iphone5solo/PYSearch/issues/new)! 151 | - Instead of giving me star, it is better to throw a bug to me! 152 | - If you want to participate in the maintenance of this project or have a good design style, welcome to pull request! 153 | - If you feel slightly discomfort in use, please contact me QQ:499491531 or Email:499491531@qq.com. 154 | - Hope to improve this project together, let it become more powerful, able to meet the needs of most users! 155 | 156 | ## Licenses 157 | All source code is licensed under the MIT License. 158 | --------------------------------------------------------------------------------