├── .gitignore ├── .travis.yml ├── JKBottomSearchView.podspec ├── JKBottomSearchView.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ └── JKBottomSearchView.xcscheme └── xcuserdata │ └── Jaro.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── LICENSE.md ├── README.md ├── Sources ├── Info.plist ├── JKBottomSearchView.h └── JKBottomSearchView.swift └── assets ├── FullyCollapsed.png ├── FullyExpanded.png ├── FullyExpandedAndFilled.png ├── MiddleExpanded.png └── example.gif /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | JKBottomSearchView.xcodeproj/xcuserdata/Jaro.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | -------------------------------------------------------------------------------- /JKBottomSearchView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'JKBottomSearchView' 3 | s.version = '1.1.1' 4 | s.summary = 'Search View similar to Apple maps solution' 5 | 6 | s.description = <<-DESC 7 | Simple view that contains searchBar and tableView and allows for full customization with native dataSource and delegate classes. SearchView can be dragged and after drop will snap to closest of 3 levels (fully collapsed, middle, fully expanded). 8 | DESC 9 | 10 | s.homepage = 'https://github.com/JaroVoltix/JKBottomSearchView' 11 | s.license = { :type => 'MIT', :file => 'LICENSE.md' } 12 | s.author = { 'Jarosław Krajewski' => 'jaroslaw.krajewski94@gmail.com' } 13 | s.source = { :git => 'https://github.com/JaroVoltix/JKBottomSearchView.git', :tag => "v#{s.version.to_s}" } 14 | 15 | s.ios.deployment_target = '9.0' 16 | s.source_files = 'Sources/*' 17 | 18 | end 19 | -------------------------------------------------------------------------------- /JKBottomSearchView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4A693B9B2077E14100B4FE37 /* JKBottomSearchView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A693B8D2077E14100B4FE37 /* JKBottomSearchView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 4A693BA52077E18E00B4FE37 /* JKBottomSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A693BA42077E18E00B4FE37 /* JKBottomSearchView.swift */; }; 12 | 4AA9AE0D207D375400829896 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 4AA9AE0C207D375400829896 /* README.md */; }; 13 | 4AA9AE0F207D376100829896 /* LICENSE.md in Resources */ = {isa = PBXBuildFile; fileRef = 4AA9AE0E207D376100829896 /* LICENSE.md */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXFileReference section */ 17 | 4A693B8A2077E14100B4FE37 /* JKBottomSearchView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JKBottomSearchView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 18 | 4A693B8D2077E14100B4FE37 /* JKBottomSearchView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JKBottomSearchView.h; sourceTree = ""; }; 19 | 4A693B8E2077E14100B4FE37 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 20 | 4A693BA42077E18E00B4FE37 /* JKBottomSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JKBottomSearchView.swift; sourceTree = ""; }; 21 | 4AA9AE0C207D375400829896 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 22 | 4AA9AE0E207D376100829896 /* LICENSE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = ""; }; 23 | /* End PBXFileReference section */ 24 | 25 | /* Begin PBXFrameworksBuildPhase section */ 26 | 4A693B862077E14100B4FE37 /* Frameworks */ = { 27 | isa = PBXFrameworksBuildPhase; 28 | buildActionMask = 2147483647; 29 | files = ( 30 | ); 31 | runOnlyForDeploymentPostprocessing = 0; 32 | }; 33 | /* End PBXFrameworksBuildPhase section */ 34 | 35 | /* Begin PBXGroup section */ 36 | 4A693B802077E14100B4FE37 = { 37 | isa = PBXGroup; 38 | children = ( 39 | 4AB17755207EB3DC00649B1A /* Sources */, 40 | 4AA9AE0E207D376100829896 /* LICENSE.md */, 41 | 4AA9AE0C207D375400829896 /* README.md */, 42 | 4A693B8B2077E14100B4FE37 /* Products */, 43 | ); 44 | sourceTree = ""; 45 | }; 46 | 4A693B8B2077E14100B4FE37 /* Products */ = { 47 | isa = PBXGroup; 48 | children = ( 49 | 4A693B8A2077E14100B4FE37 /* JKBottomSearchView.framework */, 50 | ); 51 | name = Products; 52 | sourceTree = ""; 53 | }; 54 | 4AB17755207EB3DC00649B1A /* Sources */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | 4A693B8D2077E14100B4FE37 /* JKBottomSearchView.h */, 58 | 4A693B8E2077E14100B4FE37 /* Info.plist */, 59 | 4A693BA42077E18E00B4FE37 /* JKBottomSearchView.swift */, 60 | ); 61 | path = Sources; 62 | sourceTree = ""; 63 | }; 64 | /* End PBXGroup section */ 65 | 66 | /* Begin PBXHeadersBuildPhase section */ 67 | 4A693B872077E14100B4FE37 /* Headers */ = { 68 | isa = PBXHeadersBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | 4A693B9B2077E14100B4FE37 /* JKBottomSearchView.h in Headers */, 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXHeadersBuildPhase section */ 76 | 77 | /* Begin PBXNativeTarget section */ 78 | 4A693B892077E14100B4FE37 /* JKBottomSearchView */ = { 79 | isa = PBXNativeTarget; 80 | buildConfigurationList = 4A693B9E2077E14100B4FE37 /* Build configuration list for PBXNativeTarget "JKBottomSearchView" */; 81 | buildPhases = ( 82 | 4A693B852077E14100B4FE37 /* Sources */, 83 | 4A693B862077E14100B4FE37 /* Frameworks */, 84 | 4A693B872077E14100B4FE37 /* Headers */, 85 | 4A693B882077E14100B4FE37 /* Resources */, 86 | ); 87 | buildRules = ( 88 | ); 89 | dependencies = ( 90 | ); 91 | name = JKBottomSearchView; 92 | productName = JKBottomSearchView; 93 | productReference = 4A693B8A2077E14100B4FE37 /* JKBottomSearchView.framework */; 94 | productType = "com.apple.product-type.framework"; 95 | }; 96 | /* End PBXNativeTarget section */ 97 | 98 | /* Begin PBXProject section */ 99 | 4A693B812077E14100B4FE37 /* Project object */ = { 100 | isa = PBXProject; 101 | attributes = { 102 | LastSwiftUpdateCheck = 0930; 103 | LastUpgradeCheck = 0930; 104 | ORGANIZATIONNAME = com.jerronimo; 105 | TargetAttributes = { 106 | 4A693B892077E14100B4FE37 = { 107 | CreatedOnToolsVersion = 9.3; 108 | LastSwiftMigration = 0930; 109 | }; 110 | }; 111 | }; 112 | buildConfigurationList = 4A693B842077E14100B4FE37 /* Build configuration list for PBXProject "JKBottomSearchView" */; 113 | compatibilityVersion = "Xcode 9.3"; 114 | developmentRegion = en; 115 | hasScannedForEncodings = 0; 116 | knownRegions = ( 117 | en, 118 | Base, 119 | ); 120 | mainGroup = 4A693B802077E14100B4FE37; 121 | productRefGroup = 4A693B8B2077E14100B4FE37 /* Products */; 122 | projectDirPath = ""; 123 | projectRoot = ""; 124 | targets = ( 125 | 4A693B892077E14100B4FE37 /* JKBottomSearchView */, 126 | ); 127 | }; 128 | /* End PBXProject section */ 129 | 130 | /* Begin PBXResourcesBuildPhase section */ 131 | 4A693B882077E14100B4FE37 /* Resources */ = { 132 | isa = PBXResourcesBuildPhase; 133 | buildActionMask = 2147483647; 134 | files = ( 135 | 4AA9AE0F207D376100829896 /* LICENSE.md in Resources */, 136 | 4AA9AE0D207D375400829896 /* README.md in Resources */, 137 | ); 138 | runOnlyForDeploymentPostprocessing = 0; 139 | }; 140 | /* End PBXResourcesBuildPhase section */ 141 | 142 | /* Begin PBXSourcesBuildPhase section */ 143 | 4A693B852077E14100B4FE37 /* Sources */ = { 144 | isa = PBXSourcesBuildPhase; 145 | buildActionMask = 2147483647; 146 | files = ( 147 | 4A693BA52077E18E00B4FE37 /* JKBottomSearchView.swift in Sources */, 148 | ); 149 | runOnlyForDeploymentPostprocessing = 0; 150 | }; 151 | /* End PBXSourcesBuildPhase section */ 152 | 153 | /* Begin XCBuildConfiguration section */ 154 | 4A693B9C2077E14100B4FE37 /* Debug */ = { 155 | isa = XCBuildConfiguration; 156 | buildSettings = { 157 | ALWAYS_SEARCH_USER_PATHS = NO; 158 | CLANG_ANALYZER_NONNULL = YES; 159 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 160 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 161 | CLANG_CXX_LIBRARY = "libc++"; 162 | CLANG_ENABLE_MODULES = YES; 163 | CLANG_ENABLE_OBJC_ARC = YES; 164 | CLANG_ENABLE_OBJC_WEAK = YES; 165 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 166 | CLANG_WARN_BOOL_CONVERSION = YES; 167 | CLANG_WARN_COMMA = YES; 168 | CLANG_WARN_CONSTANT_CONVERSION = YES; 169 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 170 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 171 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 172 | CLANG_WARN_EMPTY_BODY = YES; 173 | CLANG_WARN_ENUM_CONVERSION = YES; 174 | CLANG_WARN_INFINITE_RECURSION = YES; 175 | CLANG_WARN_INT_CONVERSION = YES; 176 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 177 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 178 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 179 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 180 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 181 | CLANG_WARN_STRICT_PROTOTYPES = YES; 182 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 183 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 184 | CLANG_WARN_UNREACHABLE_CODE = YES; 185 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 186 | CODE_SIGN_IDENTITY = "iPhone Developer"; 187 | COPY_PHASE_STRIP = NO; 188 | CURRENT_PROJECT_VERSION = 1; 189 | DEBUG_INFORMATION_FORMAT = dwarf; 190 | ENABLE_STRICT_OBJC_MSGSEND = YES; 191 | ENABLE_TESTABILITY = YES; 192 | GCC_C_LANGUAGE_STANDARD = gnu11; 193 | GCC_DYNAMIC_NO_PIC = NO; 194 | GCC_NO_COMMON_BLOCKS = YES; 195 | GCC_OPTIMIZATION_LEVEL = 0; 196 | GCC_PREPROCESSOR_DEFINITIONS = ( 197 | "DEBUG=1", 198 | "$(inherited)", 199 | ); 200 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 201 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 202 | GCC_WARN_UNDECLARED_SELECTOR = YES; 203 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 204 | GCC_WARN_UNUSED_FUNCTION = YES; 205 | GCC_WARN_UNUSED_VARIABLE = YES; 206 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 207 | MTL_ENABLE_DEBUG_INFO = YES; 208 | ONLY_ACTIVE_ARCH = YES; 209 | SDKROOT = iphoneos; 210 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 211 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 212 | VERSIONING_SYSTEM = "apple-generic"; 213 | VERSION_INFO_PREFIX = ""; 214 | }; 215 | name = Debug; 216 | }; 217 | 4A693B9D2077E14100B4FE37 /* Release */ = { 218 | isa = XCBuildConfiguration; 219 | buildSettings = { 220 | ALWAYS_SEARCH_USER_PATHS = NO; 221 | CLANG_ANALYZER_NONNULL = YES; 222 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 223 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 224 | CLANG_CXX_LIBRARY = "libc++"; 225 | CLANG_ENABLE_MODULES = YES; 226 | CLANG_ENABLE_OBJC_ARC = YES; 227 | CLANG_ENABLE_OBJC_WEAK = YES; 228 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 229 | CLANG_WARN_BOOL_CONVERSION = YES; 230 | CLANG_WARN_COMMA = YES; 231 | CLANG_WARN_CONSTANT_CONVERSION = YES; 232 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 233 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 234 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 235 | CLANG_WARN_EMPTY_BODY = YES; 236 | CLANG_WARN_ENUM_CONVERSION = YES; 237 | CLANG_WARN_INFINITE_RECURSION = YES; 238 | CLANG_WARN_INT_CONVERSION = YES; 239 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 240 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 241 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 242 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 243 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 244 | CLANG_WARN_STRICT_PROTOTYPES = YES; 245 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 246 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 247 | CLANG_WARN_UNREACHABLE_CODE = YES; 248 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 249 | CODE_SIGN_IDENTITY = "iPhone Developer"; 250 | COPY_PHASE_STRIP = NO; 251 | CURRENT_PROJECT_VERSION = 1; 252 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 253 | ENABLE_NS_ASSERTIONS = NO; 254 | ENABLE_STRICT_OBJC_MSGSEND = YES; 255 | GCC_C_LANGUAGE_STANDARD = gnu11; 256 | GCC_NO_COMMON_BLOCKS = YES; 257 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 258 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 259 | GCC_WARN_UNDECLARED_SELECTOR = YES; 260 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 261 | GCC_WARN_UNUSED_FUNCTION = YES; 262 | GCC_WARN_UNUSED_VARIABLE = YES; 263 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 264 | MTL_ENABLE_DEBUG_INFO = NO; 265 | SDKROOT = iphoneos; 266 | SWIFT_COMPILATION_MODE = wholemodule; 267 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 268 | VALIDATE_PRODUCT = YES; 269 | VERSIONING_SYSTEM = "apple-generic"; 270 | VERSION_INFO_PREFIX = ""; 271 | }; 272 | name = Release; 273 | }; 274 | 4A693B9F2077E14100B4FE37 /* Debug */ = { 275 | isa = XCBuildConfiguration; 276 | buildSettings = { 277 | CLANG_ENABLE_MODULES = YES; 278 | CODE_SIGN_IDENTITY = ""; 279 | CODE_SIGN_STYLE = Automatic; 280 | DEFINES_MODULE = YES; 281 | DEVELOPMENT_TEAM = 8K9V2VJ95T; 282 | DYLIB_COMPATIBILITY_VERSION = 1; 283 | DYLIB_CURRENT_VERSION = 1; 284 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 285 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; 286 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 287 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 288 | LD_RUNPATH_SEARCH_PATHS = ( 289 | "$(inherited)", 290 | "@executable_path/Frameworks", 291 | "@loader_path/Frameworks", 292 | ); 293 | PRODUCT_BUNDLE_IDENTIFIER = com.jerronimo.JKBottomSearchView; 294 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 295 | SKIP_INSTALL = YES; 296 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 297 | SWIFT_VERSION = 4.0; 298 | TARGETED_DEVICE_FAMILY = "1,2"; 299 | }; 300 | name = Debug; 301 | }; 302 | 4A693BA02077E14100B4FE37 /* Release */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | CLANG_ENABLE_MODULES = YES; 306 | CODE_SIGN_IDENTITY = ""; 307 | CODE_SIGN_STYLE = Automatic; 308 | DEFINES_MODULE = YES; 309 | DEVELOPMENT_TEAM = 8K9V2VJ95T; 310 | DYLIB_COMPATIBILITY_VERSION = 1; 311 | DYLIB_CURRENT_VERSION = 1; 312 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 313 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; 314 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 315 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 316 | LD_RUNPATH_SEARCH_PATHS = ( 317 | "$(inherited)", 318 | "@executable_path/Frameworks", 319 | "@loader_path/Frameworks", 320 | ); 321 | PRODUCT_BUNDLE_IDENTIFIER = com.jerronimo.JKBottomSearchView; 322 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 323 | SKIP_INSTALL = YES; 324 | SWIFT_VERSION = 4.0; 325 | TARGETED_DEVICE_FAMILY = "1,2"; 326 | }; 327 | name = Release; 328 | }; 329 | /* End XCBuildConfiguration section */ 330 | 331 | /* Begin XCConfigurationList section */ 332 | 4A693B842077E14100B4FE37 /* Build configuration list for PBXProject "JKBottomSearchView" */ = { 333 | isa = XCConfigurationList; 334 | buildConfigurations = ( 335 | 4A693B9C2077E14100B4FE37 /* Debug */, 336 | 4A693B9D2077E14100B4FE37 /* Release */, 337 | ); 338 | defaultConfigurationIsVisible = 0; 339 | defaultConfigurationName = Release; 340 | }; 341 | 4A693B9E2077E14100B4FE37 /* Build configuration list for PBXNativeTarget "JKBottomSearchView" */ = { 342 | isa = XCConfigurationList; 343 | buildConfigurations = ( 344 | 4A693B9F2077E14100B4FE37 /* Debug */, 345 | 4A693BA02077E14100B4FE37 /* Release */, 346 | ); 347 | defaultConfigurationIsVisible = 0; 348 | defaultConfigurationName = Release; 349 | }; 350 | /* End XCConfigurationList section */ 351 | }; 352 | rootObject = 4A693B812077E14100B4FE37 /* Project object */; 353 | } 354 | -------------------------------------------------------------------------------- /JKBottomSearchView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /JKBottomSearchView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /JKBottomSearchView.xcodeproj/xcshareddata/xcschemes/JKBottomSearchView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /JKBottomSearchView.xcodeproj/xcuserdata/Jaro.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | JKBottomSearchView.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 4A693B892077E14100B4FE37 16 | 17 | primary 18 | 19 | 20 | 4A693B922077E14100B4FE37 21 | 22 | primary 23 | 24 | 25 | 4A693BA92077E29B00B4FE37 26 | 27 | primary 28 | 29 | 30 | 4A693BBC2077E29D00B4FE37 31 | 32 | primary 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 JeyKeyCom 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JKBottomSearchView 2 | [![Build Status](https://travis-ci.org/JaroVoltix/JKBottomSearchView.svg?branch=master)](https://travis-ci.org/JaroVoltix/JKBottomSearchView) 3 |

4 | 5 | 6 | 7 | 8 | 9 |

10 | 11 | ## Installation 12 | 13 | ### Cocapods 14 | `pod "JKBottomSearchView"` 15 | 16 | ### Carthage 17 | ``` github "JeyKeyCom/JKBottomSearchView"``` 18 | 19 | ## Requirment 20 | - iOS 9+ 21 | - Xcode 9+ 22 | - Swift 4.1+ 23 | 24 | ## Example 25 | You can check out example project that use JKBottomSearchView here: 26 | https://github.com/JaroVoltix/JKBottomSearchViewExample 27 | 28 | ## Usage 29 | Create JKBottomSearchView 30 | ``` 31 | func viewDidLoad(){ 32 | super.viewDidLoad() 33 | let searchView = JKBottomSearchView() 34 | view.addSubview(searchView) 35 | } 36 | ``` 37 | ### DataSource & Delegate 38 | JKBottomSearchViewDataSource is and alias to UITableViewDataSource. 39 | 40 | ``` 41 | extension ViewController:JKBottomSearchViewDataSource{} 42 | ``` 43 | ``` 44 | searchView.dataSource = self 45 | ``` 46 | 47 | JKBottomSearchViewDelegate is an alias for UITableViewDelegate & UISearchBarDelegate. 48 | 49 | ``` 50 | extension ViewController:JKBottomSearchViewDelegate{} 51 | ``` 52 | ``` 53 | searchView.delegate = self 54 | ``` 55 | 56 | ### Customization 57 | ``` 58 | //Change blur effect. Default nil 59 | searchView.blurEffect = UIBlurEffect(style: .dark) 60 | 61 | //Any non tableView and searchBar customization should be performed on contentView 62 | searchView.contentView.backgroundColor = .red 63 | 64 | //Customizing searchBar 65 | searchView.barStyle = .black 66 | searchView.searchBarStyle = .minimal 67 | searchView.searchBarTintColor = .black 68 | searchView.placeholder = "What are you looking for" 69 | searchView.showsCancelButton = true 70 | searchView.enablesReturnKeyAutomatically = true 71 | 72 | //Customizing searchBar textField 73 | let textField = searchView.searchBarTextField 74 | textField.textColor = .red 75 | 76 | //Customizing tableView 77 | //You can access tableView and customize as You like by tableView property 78 | searchView.tableView.isScrollEnabled = false 79 | 80 | //Customizing expansion 81 | searchView.minimalYPosition = 100 // distance from top after fully expanding 82 | searchView.fastExpandindTime = 0.1 83 | searchView.slowExpandingTime = 2 84 | searchView.toggleExpand(.fullyExpanded,fast:false) // fast parameter is optional, default false 85 | ``` 86 | 87 | ## License 88 | Icons used in example available at: https://icons8.com 89 | 90 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details. 91 | -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.1.1 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/JKBottomSearchView.h: -------------------------------------------------------------------------------- 1 | // 2 | // JKBottomSearchView.h 3 | // JKBottomSearchView 4 | // 5 | // Created by Jarosław Krajewski on 06/04/2018. 6 | // Copyright © 2018 com.jerronimo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for JKBottomSearchView. 12 | FOUNDATION_EXPORT double JKBottomSearchViewVersionNumber; 13 | 14 | //! Project version string for JKBottomSearchView. 15 | FOUNDATION_EXPORT const unsigned char JKBottomSearchViewVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Sources/JKBottomSearchView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JKBottomSearchView.swift 3 | // JKBottomSearchView 4 | // 5 | // Created by Jarosław Krajewski on 06/04/2018. 6 | // Copyright © 2018 com.jerronimo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public enum JKBottomSearchViewExpandingState{ 12 | case fullyExpanded 13 | case middle 14 | case fullyCollapsed 15 | } 16 | 17 | public typealias JKBottomSearchViewDelegate = UISearchBarDelegate & UITableViewDelegate 18 | public typealias JKBottomSearchDataSource = UITableViewDataSource 19 | 20 | private class SearchBarProxy:NSObject,UISearchBarDelegate { 21 | var primaryDelegate:UISearchBarDelegate? 22 | var secondaryDelegate: UISearchBarDelegate? 23 | override func responds(to aSelector: Selector!) -> Bool { 24 | return primaryDelegate?.responds(to: aSelector) ?? false || secondaryDelegate?.responds(to: aSelector) ?? false 25 | } 26 | 27 | override func forwardingTarget(for aSelector: Selector!) -> Any? { 28 | if primaryDelegate?.responds(to: aSelector) == true { 29 | return primaryDelegate 30 | } 31 | return secondaryDelegate 32 | } 33 | } 34 | 35 | public class JKBottomSearchView: UIView{ 36 | 37 | public var blurEffect: UIBlurEffect?{ 38 | didSet{blurView.effect = blurEffect} 39 | } 40 | public var delegate:JKBottomSearchViewDelegate?{ 41 | didSet{ proxy.secondaryDelegate = delegate 42 | tableView.delegate = delegate} 43 | } 44 | public var dataSource:JKBottomSearchDataSource?{ 45 | didSet{ tableView.dataSource = dataSource} 46 | } 47 | public var contentView:UIView{ 48 | return blurView.contentView 49 | } 50 | public var tableView:UITableView! 51 | public var fastExpandingTime:Double = 0.25 52 | public var slowExpandingTime:Double = 1 53 | public var minimalYPosition:CGFloat 54 | 55 | private let paddingFromTop:CGFloat = 8 56 | private let maximalYPosition:CGFloat 57 | private var searchBar:UISearchBar! 58 | private var proxy = SearchBarProxy() 59 | private let blurView:UIVisualEffectView! = UIVisualEffectView(effect:nil) 60 | private var currentExpandedState: JKBottomSearchViewExpandingState = .fullyCollapsed 61 | private var startedDraggingOnSearchBar = false 62 | 63 | //MARK: - Search Bar Customization 64 | public var barStyle:UIBarStyle { 65 | get{return searchBar.barStyle} 66 | set{searchBar.barStyle = newValue} 67 | } 68 | public var searchBarStyle:UISearchBarStyle { 69 | get{ return searchBar.searchBarStyle} 70 | set{ searchBar.searchBarStyle = newValue} 71 | } 72 | public var searchBarTintColor:UIColor?{ 73 | get{ return searchBar.barTintColor} 74 | set{ searchBar.barTintColor = newValue} 75 | } 76 | public var placeholder:String?{ 77 | get{ return searchBar.placeholder} 78 | set{ searchBar.placeholder = newValue} 79 | } 80 | public var searchBarTextField:UITextField{ 81 | get{ return searchBar.value(forKey: "searchField") as! UITextField} 82 | } 83 | public var showsCancelButton:Bool{ 84 | get{ return searchBar.showsCancelButton} 85 | set{ searchBar.showsCancelButton = newValue} 86 | } 87 | public var enablesReturnKeyAutomatically:Bool{ 88 | get{ return searchBar.enablesReturnKeyAutomatically} 89 | set{ searchBar.enablesReturnKeyAutomatically = newValue} 90 | } 91 | 92 | public init(){ 93 | let windowFrame = UIWindow().frame 94 | let visibleHeight:CGFloat = 56 + paddingFromTop 95 | let frame = CGRect( 96 | x: 0, y: windowFrame.height - visibleHeight, 97 | width: windowFrame.width, height: windowFrame.height * CGFloat(0.8)) 98 | self.minimalYPosition = windowFrame.height - frame.height 99 | self.maximalYPosition = frame.origin.y 100 | super.init(frame: frame) 101 | 102 | setupView() 103 | } 104 | 105 | public required init?(coder aDecoder: NSCoder) { 106 | minimalYPosition = 0 107 | maximalYPosition = UIWindow().frame.height - 56 - 8 108 | super.init(coder: aDecoder) 109 | setupView() 110 | } 111 | 112 | private func setupView(){ 113 | 114 | let dragIndicationView = UIView(frame: .zero) 115 | dragIndicationView.backgroundColor = .lightGray 116 | dragIndicationView.translatesAutoresizingMaskIntoConstraints = false 117 | blurView.contentView.addSubview(dragIndicationView) 118 | dragIndicationView.centerXAnchor.constraint(equalTo: blurView.contentView.centerXAnchor).isActive = true 119 | dragIndicationView.topAnchor.constraint(equalTo: blurView.contentView.topAnchor, constant: 2).isActive = true 120 | dragIndicationView.widthAnchor.constraint(equalToConstant: UIWindow().frame.width / 15).isActive = true 121 | dragIndicationView.heightAnchor.constraint(equalToConstant: 4).isActive = true 122 | dragIndicationView.layer.cornerRadius = 1 123 | 124 | blurView.effect = blurEffect 125 | blurView.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: self.frame.size) 126 | blurView.autoresizingMask = [.flexibleWidth,.flexibleHeight] 127 | addSubview(blurView) 128 | 129 | searchBar = UISearchBar(frame: CGRect(x: 0, y: paddingFromTop, width: frame.width, height: 56)) 130 | blurView.contentView.addSubview(searchBar) 131 | 132 | proxy.primaryDelegate = self 133 | searchBar.delegate = proxy 134 | searchBar.enablesReturnKeyAutomatically = false 135 | 136 | let tableViewOriginY = searchBar.frame.origin.y + searchBar.frame.height 137 | tableView = UITableView(frame: CGRect( 138 | x:0, y: tableViewOriginY, 139 | width: frame.width, height:frame.height - tableViewOriginY )) 140 | tableView.backgroundColor = .clear 141 | tableView.bounces = false 142 | blurView.contentView.addSubview(tableView) 143 | 144 | 145 | 146 | let dragGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(userDidPan)) 147 | dragGestureRecognizer.delegate = self 148 | blurView.contentView.addGestureRecognizer(dragGestureRecognizer) 149 | } 150 | 151 | @objc private func userDidPan(_ sender: UIPanGestureRecognizer){ 152 | let senderView = sender.view 153 | let loc = sender.location(in: senderView) 154 | let tappedView = senderView?.hitTest(loc, with: nil) 155 | 156 | 157 | if sender.state == .began{ 158 | var viewToCheck:UIView? = tappedView 159 | while viewToCheck != nil { 160 | if viewToCheck is UISearchBar{ 161 | startedDraggingOnSearchBar = true 162 | break 163 | } 164 | viewToCheck = viewToCheck?.superview 165 | } 166 | } 167 | 168 | if sender.state == .ended{ 169 | startedDraggingOnSearchBar = false 170 | let currentYPosition = frame.origin.y 171 | let toTopDistance = abs(Int32(currentYPosition - minimalYPosition)) 172 | let toBottomDistance = abs(Int32(currentYPosition - maximalYPosition)) 173 | let toCenterDistance = abs(Int32(currentYPosition - (minimalYPosition + maximalYPosition) / 2)) 174 | let sortedDistances = [toTopDistance,toBottomDistance,toCenterDistance].sorted() 175 | if sortedDistances[0] == toTopDistance{ 176 | toggleExpand(.fullyExpanded,fast:true) 177 | }else if sortedDistances[0] == toBottomDistance{ 178 | toggleExpand(.fullyCollapsed,fast:true) 179 | }else{ 180 | toggleExpand(.middle,fast:true) 181 | } 182 | }else{ 183 | 184 | if tappedView?.superview is UITableViewCell && startedDraggingOnSearchBar == false{ 185 | if let visibleIndexPaths = tableView.indexPathsForVisibleRows{ 186 | if !visibleIndexPaths.contains(IndexPath(row: 0 , section: 0)) && tableView.isScrollEnabled == true{ 187 | sender.setTranslation(CGPoint.zero, in: self) 188 | return 189 | } 190 | } 191 | } 192 | let translation = sender.translation(in: self) 193 | 194 | var destinationY = self.frame.origin.y + translation.y 195 | if destinationY < minimalYPosition { 196 | destinationY = minimalYPosition 197 | }else if destinationY > maximalYPosition { 198 | destinationY = maximalYPosition 199 | } 200 | self.frame.origin.y = destinationY 201 | 202 | sender.setTranslation(CGPoint.zero, in: self) 203 | } 204 | } 205 | 206 | private func animationDuration(fast:Bool) -> Double { 207 | if fast { 208 | return fastExpandingTime 209 | }else{ 210 | return slowExpandingTime 211 | } 212 | } 213 | 214 | public func toggleExpand(_ state: JKBottomSearchViewExpandingState, fast:Bool = false){ 215 | let duration = animationDuration(fast: fast) 216 | UIView.animate(withDuration: duration) { 217 | switch state{ 218 | case .fullyExpanded: 219 | self.frame.origin.y = self.minimalYPosition 220 | self.tableView.isScrollEnabled = true 221 | case .middle: 222 | self.frame.origin.y = (self.minimalYPosition + self.maximalYPosition)/2 223 | self.tableView.isScrollEnabled = false 224 | case .fullyCollapsed: 225 | self.frame.origin.y = self.maximalYPosition 226 | self.tableView.isScrollEnabled = false 227 | } 228 | } 229 | self.currentExpandedState = state 230 | } 231 | } 232 | 233 | extension JKBottomSearchView: UIGestureRecognizerDelegate{ 234 | public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { 235 | return true 236 | } 237 | } 238 | 239 | extension JKBottomSearchView : UISearchBarDelegate { 240 | public func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { 241 | toggleExpand(.fullyExpanded) 242 | delegate?.searchBarTextDidBeginEditing?(searchBar) 243 | } 244 | 245 | public func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { 246 | toggleExpand(.fullyCollapsed) 247 | delegate?.searchBarTextDidEndEditing?(searchBar) 248 | } 249 | 250 | public func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { 251 | searchBar.resignFirstResponder() 252 | delegate?.searchBarSearchButtonClicked?(searchBar) 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /assets/FullyCollapsed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JaroVoltix/JKBottomSearchView/547bc959e1f0aaf0cf5ad64466c4d976c4fa51cc/assets/FullyCollapsed.png -------------------------------------------------------------------------------- /assets/FullyExpanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JaroVoltix/JKBottomSearchView/547bc959e1f0aaf0cf5ad64466c4d976c4fa51cc/assets/FullyExpanded.png -------------------------------------------------------------------------------- /assets/FullyExpandedAndFilled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JaroVoltix/JKBottomSearchView/547bc959e1f0aaf0cf5ad64466c4d976c4fa51cc/assets/FullyExpandedAndFilled.png -------------------------------------------------------------------------------- /assets/MiddleExpanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JaroVoltix/JKBottomSearchView/547bc959e1f0aaf0cf5ad64466c4d976c4fa51cc/assets/MiddleExpanded.png -------------------------------------------------------------------------------- /assets/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JaroVoltix/JKBottomSearchView/547bc959e1f0aaf0cf5ad64466c4d976c4fa51cc/assets/example.gif --------------------------------------------------------------------------------