├── Image ├── IMG_4274.PNG ├── IMG_4275.PNG ├── IMG_4276.PNG ├── IMG_4277.PNG ├── filter.gif ├── filter2.gif └── filter3.gif ├── Podfile ├── Podfile.lock ├── Pods ├── MJExtension │ ├── LICENSE │ ├── MJExtension │ │ ├── MJExtension.h │ │ ├── MJExtensionConst.h │ │ ├── MJExtensionConst.m │ │ ├── MJFoundation.h │ │ ├── MJFoundation.m │ │ ├── MJProperty.h │ │ ├── MJProperty.m │ │ ├── MJPropertyKey.h │ │ ├── MJPropertyKey.m │ │ ├── MJPropertyType.h │ │ ├── MJPropertyType.m │ │ ├── NSObject+MJClass.h │ │ ├── NSObject+MJClass.m │ │ ├── NSObject+MJCoding.h │ │ ├── NSObject+MJCoding.m │ │ ├── NSObject+MJKeyValue.h │ │ ├── NSObject+MJKeyValue.m │ │ ├── NSObject+MJProperty.h │ │ ├── NSObject+MJProperty.m │ │ ├── NSString+MJExtension.h │ │ └── NSString+MJExtension.m │ └── README.md ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── zhouyn.xcuserdatad │ │ └── xcschemes │ │ ├── MJExtension.xcscheme │ │ ├── Pods-ZHFilterMenuView.xcscheme │ │ └── xcschememanagement.plist └── Target Support Files │ ├── MJExtension │ ├── Info.plist │ ├── MJExtension-dummy.m │ ├── MJExtension-prefix.pch │ ├── MJExtension-umbrella.h │ ├── MJExtension.modulemap │ └── MJExtension.xcconfig │ └── Pods-ZHFilterMenuView │ ├── Info.plist │ ├── Pods-ZHFilterMenuView-acknowledgements.markdown │ ├── Pods-ZHFilterMenuView-acknowledgements.plist │ ├── Pods-ZHFilterMenuView-dummy.m │ ├── Pods-ZHFilterMenuView-frameworks.sh │ ├── Pods-ZHFilterMenuView-resources.sh │ ├── Pods-ZHFilterMenuView-umbrella.h │ ├── Pods-ZHFilterMenuView.debug.xcconfig │ ├── Pods-ZHFilterMenuView.modulemap │ └── Pods-ZHFilterMenuView.release.xcconfig ├── README.md ├── ZHFilterMenuView.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── zhouyn.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── ZHFilterMenuView.xcworkspace ├── contents.xcworkspacedata ├── xcshareddata │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── zhouyn.xcuserdatad │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── ZHFilterMenuView ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon-1024.png │ │ ├── icon-20-ipad.png │ │ ├── icon-20@2x-ipad.png │ │ ├── icon-20@2x.png │ │ ├── icon-20@3x.png │ │ ├── icon-29-ipad.png │ │ ├── icon-29.png │ │ ├── icon-29@2x-ipad.png │ │ ├── icon-29@2x.png │ │ ├── icon-29@3x.png │ │ ├── icon-40.png │ │ ├── icon-40@2x.png │ │ ├── icon-40@3x.png │ │ ├── icon-50.png │ │ ├── icon-50@2x.png │ │ ├── icon-57.png │ │ ├── icon-57@2x.png │ │ ├── icon-60@2x.png │ │ ├── icon-60@3x.png │ │ ├── icon-72.png │ │ ├── icon-72@2x.png │ │ ├── icon-76.png │ │ ├── icon-76@2x.png │ │ └── icon-83.5@2x.png │ ├── Contents.json │ ├── x_arrow.imageset │ │ ├── Contents.json │ │ ├── x_arrow@2x.png │ │ └── x_arrow@3x.png │ ├── x_px.imageset │ │ ├── Contents.json │ │ ├── x_px@2x.png │ │ └── x_px@3x.png │ ├── x_syt.imageset │ │ ├── Contents.json │ │ ├── x_syt@2x.png │ │ └── x_syt@3x.png │ └── xq_ban.imageset │ │ ├── Contents.json │ │ ├── xq_ban@2x.png │ │ └── xq_ban@3x.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── ChangeViewController.h ├── ChangeViewController.m ├── FilterData.geojson ├── FilterDataUtil.h ├── FilterDataUtil.m ├── FilterViewController.h ├── FilterViewController.m ├── HeadFilterViewController.h ├── HeadFilterViewController.m ├── HouseTableViewCell.h ├── HouseTableViewCell.m ├── HouseTableViewCell.xib ├── Info.plist ├── ViewController.h ├── ViewController.m ├── ZHFilterMenuView │ ├── ZHFilterItemModel.h │ ├── ZHFilterItemModel.m │ ├── ZHFilterItemTableViewCell.h │ ├── ZHFilterItemTableViewCell.m │ ├── ZHFilterItemTableViewCell.xib │ ├── ZHFilterMenuView.h │ ├── ZHFilterMenuView.m │ ├── ZHFilterModel.h │ ├── ZHFilterModel.m │ ├── ZHFilterTitleTableViewCell.h │ └── ZHFilterTitleTableViewCell.m └── main.m ├── ZHFilterMenuViewTests ├── Info.plist └── ZHFilterMenuViewTests.m └── ZHFilterMenuViewUITests ├── Info.plist └── ZHFilterMenuViewUITests.m /Image/IMG_4274.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/Image/IMG_4274.PNG -------------------------------------------------------------------------------- /Image/IMG_4275.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/Image/IMG_4275.PNG -------------------------------------------------------------------------------- /Image/IMG_4276.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/Image/IMG_4276.PNG -------------------------------------------------------------------------------- /Image/IMG_4277.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/Image/IMG_4277.PNG -------------------------------------------------------------------------------- /Image/filter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/Image/filter.gif -------------------------------------------------------------------------------- /Image/filter2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/Image/filter2.gif -------------------------------------------------------------------------------- /Image/filter3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/Image/filter3.gif -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, ‘9.0’ 2 | use_frameworks! 3 | target ‘ZHFilterMenuView’ do 4 | pod 'MJExtension', '~> 3.0.10' 5 | end 6 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - MJExtension (3.0.17) 3 | 4 | DEPENDENCIES: 5 | - MJExtension (~> 3.0.10) 6 | 7 | SPEC CHECKSUMS: 8 | MJExtension: 74ec83124a68891619fb7ba9c5c811bbf1691076 9 | 10 | PODFILE CHECKSUM: 5dff578281463d3f8c3795963278b45fcbb4771e 11 | 12 | COCOAPODS: 1.4.0 13 | -------------------------------------------------------------------------------- /Pods/MJExtension/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2019 MJExtension (https://github.com/CoderMJLee/MJExtension) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/MJExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJExtension.h 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 代码地址:https://github.com/CoderMJLee/MJExtension 8 | // 代码地址:http://code4app.com/ios/%E5%AD%97%E5%85%B8-JSON-%E4%B8%8E%E6%A8%A1%E5%9E%8B%E7%9A%84%E8%BD%AC%E6%8D%A2/5339992a933bf062608b4c57 9 | 10 | #import "NSObject+MJCoding.h" 11 | #import "NSObject+MJProperty.h" 12 | #import "NSObject+MJClass.h" 13 | #import "NSObject+MJKeyValue.h" 14 | #import "NSString+MJExtension.h" 15 | #import "MJExtensionConst.h" 16 | 17 | #import "MJFoundation.h" 18 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/MJExtensionConst.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __MJExtensionConst__H__ 3 | #define __MJExtensionConst__H__ 4 | 5 | #import 6 | 7 | #ifndef MJ_LOCK 8 | #define MJ_LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); 9 | #endif 10 | 11 | #ifndef MJ_UNLOCK 12 | #define MJ_UNLOCK(lock) dispatch_semaphore_signal(lock); 13 | #endif 14 | 15 | // 信号量 16 | #define MJExtensionSemaphoreCreate \ 17 | static dispatch_semaphore_t signalSemaphore; \ 18 | static dispatch_once_t onceTokenSemaphore; \ 19 | dispatch_once(&onceTokenSemaphore, ^{ \ 20 | signalSemaphore = dispatch_semaphore_create(1); \ 21 | }); 22 | 23 | #define MJExtensionSemaphoreWait MJ_LOCK(signalSemaphore) 24 | #define MJExtensionSemaphoreSignal MJ_UNLOCK(signalSemaphore) 25 | 26 | // 过期 27 | #define MJExtensionDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead) 28 | 29 | // 构建错误 30 | #define MJExtensionBuildError(clazz, msg) \ 31 | NSError *error = [NSError errorWithDomain:msg code:250 userInfo:nil]; \ 32 | [clazz setMj_error:error]; 33 | 34 | // 日志输出 35 | #ifdef DEBUG 36 | #define MJExtensionLog(...) NSLog(__VA_ARGS__) 37 | #else 38 | #define MJExtensionLog(...) 39 | #endif 40 | 41 | /** 42 | * 断言 43 | * @param condition 条件 44 | * @param returnValue 返回值 45 | */ 46 | #define MJExtensionAssertError(condition, returnValue, clazz, msg) \ 47 | [clazz setMj_error:nil]; \ 48 | if ((condition) == NO) { \ 49 | MJExtensionBuildError(clazz, msg); \ 50 | return returnValue;\ 51 | } 52 | 53 | #define MJExtensionAssert2(condition, returnValue) \ 54 | if ((condition) == NO) return returnValue; 55 | 56 | /** 57 | * 断言 58 | * @param condition 条件 59 | */ 60 | #define MJExtensionAssert(condition) MJExtensionAssert2(condition, ) 61 | 62 | /** 63 | * 断言 64 | * @param param 参数 65 | * @param returnValue 返回值 66 | */ 67 | #define MJExtensionAssertParamNotNil2(param, returnValue) \ 68 | MJExtensionAssert2((param) != nil, returnValue) 69 | 70 | /** 71 | * 断言 72 | * @param param 参数 73 | */ 74 | #define MJExtensionAssertParamNotNil(param) MJExtensionAssertParamNotNil2(param, ) 75 | 76 | /** 77 | * 打印所有的属性 78 | */ 79 | #define MJLogAllIvars \ 80 | -(NSString *)description \ 81 | { \ 82 | return [self mj_keyValues].description; \ 83 | } 84 | #define MJExtensionLogAllProperties MJLogAllIvars 85 | 86 | /** 仅在 Debugger 展示所有的属性 */ 87 | #define MJImplementDebugDescription \ 88 | -(NSString *)debugDescription \ 89 | { \ 90 | return [self mj_keyValues].debugDescription; \ 91 | } 92 | 93 | /** 94 | * 类型(属性类型) 95 | */ 96 | FOUNDATION_EXPORT NSString *const MJPropertyTypeInt; 97 | FOUNDATION_EXPORT NSString *const MJPropertyTypeShort; 98 | FOUNDATION_EXPORT NSString *const MJPropertyTypeFloat; 99 | FOUNDATION_EXPORT NSString *const MJPropertyTypeDouble; 100 | FOUNDATION_EXPORT NSString *const MJPropertyTypeLong; 101 | FOUNDATION_EXPORT NSString *const MJPropertyTypeLongLong; 102 | FOUNDATION_EXPORT NSString *const MJPropertyTypeChar; 103 | FOUNDATION_EXPORT NSString *const MJPropertyTypeBOOL1; 104 | FOUNDATION_EXPORT NSString *const MJPropertyTypeBOOL2; 105 | FOUNDATION_EXPORT NSString *const MJPropertyTypePointer; 106 | 107 | FOUNDATION_EXPORT NSString *const MJPropertyTypeIvar; 108 | FOUNDATION_EXPORT NSString *const MJPropertyTypeMethod; 109 | FOUNDATION_EXPORT NSString *const MJPropertyTypeBlock; 110 | FOUNDATION_EXPORT NSString *const MJPropertyTypeClass; 111 | FOUNDATION_EXPORT NSString *const MJPropertyTypeSEL; 112 | FOUNDATION_EXPORT NSString *const MJPropertyTypeId; 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/MJExtensionConst.m: -------------------------------------------------------------------------------- 1 | #ifndef __MJExtensionConst__M__ 2 | #define __MJExtensionConst__M__ 3 | 4 | #import 5 | 6 | /** 7 | * 成员变量类型(属性类型) 8 | */ 9 | NSString *const MJPropertyTypeInt = @"i"; 10 | NSString *const MJPropertyTypeShort = @"s"; 11 | NSString *const MJPropertyTypeFloat = @"f"; 12 | NSString *const MJPropertyTypeDouble = @"d"; 13 | NSString *const MJPropertyTypeLong = @"l"; 14 | NSString *const MJPropertyTypeLongLong = @"q"; 15 | NSString *const MJPropertyTypeChar = @"c"; 16 | NSString *const MJPropertyTypeBOOL1 = @"c"; 17 | NSString *const MJPropertyTypeBOOL2 = @"b"; 18 | NSString *const MJPropertyTypePointer = @"*"; 19 | 20 | NSString *const MJPropertyTypeIvar = @"^{objc_ivar=}"; 21 | NSString *const MJPropertyTypeMethod = @"^{objc_method=}"; 22 | NSString *const MJPropertyTypeBlock = @"@?"; 23 | NSString *const MJPropertyTypeClass = @"#"; 24 | NSString *const MJPropertyTypeSEL = @":"; 25 | NSString *const MJPropertyTypeId = @"@"; 26 | 27 | #endif -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/MJFoundation.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJFoundation.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 14/7/16. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MJFoundation : NSObject 12 | 13 | + (BOOL)isClassFromFoundation:(Class)c; 14 | + (BOOL)isFromNSObjectProtocolProperty:(NSString *)propertyName; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/MJFoundation.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJFoundation.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 14/7/16. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJFoundation.h" 10 | #import "MJExtensionConst.h" 11 | #import 12 | #import "objc/runtime.h" 13 | 14 | @implementation MJFoundation 15 | 16 | + (BOOL)isClassFromFoundation:(Class)c 17 | { 18 | if (c == [NSObject class] || c == [NSManagedObject class]) return YES; 19 | 20 | static NSSet *foundationClasses; 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | // 集合中没有NSObject,因为几乎所有的类都是继承自NSObject,具体是不是NSObject需要特殊判断 24 | foundationClasses = [NSSet setWithObjects: 25 | [NSURL class], 26 | [NSDate class], 27 | [NSValue class], 28 | [NSData class], 29 | [NSError class], 30 | [NSArray class], 31 | [NSDictionary class], 32 | [NSString class], 33 | [NSAttributedString class], nil]; 34 | }); 35 | 36 | __block BOOL result = NO; 37 | [foundationClasses enumerateObjectsUsingBlock:^(Class foundationClass, BOOL *stop) { 38 | if ([c isSubclassOfClass:foundationClass]) { 39 | result = YES; 40 | *stop = YES; 41 | } 42 | }]; 43 | return result; 44 | } 45 | 46 | + (BOOL)isFromNSObjectProtocolProperty:(NSString *)propertyName 47 | { 48 | if (!propertyName) return NO; 49 | 50 | static NSSet *objectProtocolPropertyNames; 51 | static dispatch_once_t onceToken; 52 | dispatch_once(&onceToken, ^{ 53 | unsigned int count = 0; 54 | objc_property_t *propertyList = protocol_copyPropertyList(@protocol(NSObject), &count); 55 | NSMutableSet *propertyNames = [NSMutableSet setWithCapacity:count]; 56 | for (int i = 0; i < count; i++) { 57 | objc_property_t property = propertyList[i]; 58 | NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding]; 59 | if (propertyName) { 60 | [propertyNames addObject:propertyName]; 61 | } 62 | } 63 | objectProtocolPropertyNames = [propertyNames copy]; 64 | free(propertyList); 65 | }); 66 | 67 | return [objectProtocolPropertyNames containsObject:propertyName]; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/MJProperty.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJProperty.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 包装一个成员属性 8 | 9 | #import 10 | #import 11 | #import "MJPropertyType.h" 12 | #import "MJPropertyKey.h" 13 | 14 | /** 15 | * 包装一个成员 16 | */ 17 | @interface MJProperty : NSObject 18 | /** 成员属性 */ 19 | @property (nonatomic, assign) objc_property_t property; 20 | /** 成员属性的名字 */ 21 | @property (nonatomic, readonly) NSString *name; 22 | 23 | /** 成员属性的类型 */ 24 | @property (nonatomic, readonly) MJPropertyType *type; 25 | /** 成员属性来源于哪个类(可能是父类) */ 26 | @property (nonatomic, assign) Class srcClass; 27 | 28 | /**** 同一个成员属性 - 父类和子类的行为可能不一致(originKey、propertyKeys、objectClassInArray) ****/ 29 | /** 设置最原始的key */ 30 | - (void)setOriginKey:(id)originKey forClass:(Class)c; 31 | /** 对应着字典中的多级key(里面存放的数组,数组里面都是MJPropertyKey对象) */ 32 | - (NSArray *)propertyKeysForClass:(Class)c; 33 | 34 | /** 模型数组中的模型类型 */ 35 | - (void)setObjectClassInArray:(Class)objectClass forClass:(Class)c; 36 | - (Class)objectClassInArrayForClass:(Class)c; 37 | /**** 同一个成员变量 - 父类和子类的行为可能不一致(key、keys、objectClassInArray) ****/ 38 | 39 | /** 40 | * 设置object的成员变量值 41 | */ 42 | - (void)setValue:(id)value forObject:(id)object; 43 | /** 44 | * 得到object的成员属性值 45 | */ 46 | - (id)valueForObject:(id)object; 47 | 48 | /** 49 | * 初始化 50 | */ 51 | + (instancetype)cachedPropertyWithProperty:(objc_property_t)property; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/MJProperty.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJProperty.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJProperty.h" 10 | #import "MJFoundation.h" 11 | #import "MJExtensionConst.h" 12 | #import 13 | 14 | @interface MJProperty() 15 | @property (strong, nonatomic) NSMutableDictionary *propertyKeysDict; 16 | @property (strong, nonatomic) NSMutableDictionary *objectClassInArrayDict; 17 | @property (strong, nonatomic) dispatch_semaphore_t propertyKeysLock; 18 | @property (strong, nonatomic) dispatch_semaphore_t objectClassInArrayLock; 19 | @end 20 | 21 | @implementation MJProperty 22 | 23 | #pragma mark - 初始化 24 | - (instancetype)init 25 | { 26 | if (self = [super init]) { 27 | _propertyKeysDict = [NSMutableDictionary dictionary]; 28 | _objectClassInArrayDict = [NSMutableDictionary dictionary]; 29 | _propertyKeysLock = dispatch_semaphore_create(1); 30 | _objectClassInArrayLock = dispatch_semaphore_create(1); 31 | } 32 | return self; 33 | } 34 | 35 | #pragma mark - 缓存 36 | + (instancetype)cachedPropertyWithProperty:(objc_property_t)property 37 | { 38 | MJProperty *propertyObj = objc_getAssociatedObject(self, property); 39 | if (propertyObj == nil) { 40 | propertyObj = [[self alloc] init]; 41 | propertyObj.property = property; 42 | objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 43 | } 44 | return propertyObj; 45 | } 46 | 47 | #pragma mark - 公共方法 48 | - (void)setProperty:(objc_property_t)property 49 | { 50 | _property = property; 51 | 52 | MJExtensionAssertParamNotNil(property); 53 | 54 | // 1.属性名 55 | _name = @(property_getName(property)); 56 | 57 | // 2.成员类型 58 | NSString *attrs = @(property_getAttributes(property)); 59 | NSUInteger dotLoc = [attrs rangeOfString:@","].location; 60 | NSString *code = nil; 61 | NSUInteger loc = 1; 62 | if (dotLoc == NSNotFound) { // 没有, 63 | code = [attrs substringFromIndex:loc]; 64 | } else { 65 | code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc)]; 66 | } 67 | _type = [MJPropertyType cachedTypeWithCode:code]; 68 | } 69 | 70 | /** 71 | * 获得成员变量的值 72 | */ 73 | - (id)valueForObject:(id)object 74 | { 75 | if (self.type.KVCDisabled) return [NSNull null]; 76 | return [object valueForKey:self.name]; 77 | } 78 | 79 | /** 80 | * 设置成员变量的值 81 | */ 82 | - (void)setValue:(id)value forObject:(id)object 83 | { 84 | if (self.type.KVCDisabled || value == nil) return; 85 | [object setValue:value forKey:self.name]; 86 | } 87 | 88 | /** 89 | * 通过字符串key创建对应的keys 90 | */ 91 | - (NSArray *)propertyKeysWithStringKey:(NSString *)stringKey 92 | { 93 | if (stringKey.length == 0) return nil; 94 | 95 | NSMutableArray *propertyKeys = [NSMutableArray array]; 96 | // 如果有多级映射 97 | NSArray *oldKeys = [stringKey componentsSeparatedByString:@"."]; 98 | 99 | for (NSString *oldKey in oldKeys) { 100 | NSUInteger start = [oldKey rangeOfString:@"["].location; 101 | if (start != NSNotFound) { // 有索引的key 102 | NSString *prefixKey = [oldKey substringToIndex:start]; 103 | NSString *indexKey = prefixKey; 104 | if (prefixKey.length) { 105 | MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init]; 106 | propertyKey.name = prefixKey; 107 | [propertyKeys addObject:propertyKey]; 108 | 109 | indexKey = [oldKey stringByReplacingOccurrencesOfString:prefixKey withString:@""]; 110 | } 111 | 112 | /** 解析索引 **/ 113 | // 元素 114 | NSArray *cmps = [[indexKey stringByReplacingOccurrencesOfString:@"[" withString:@""] componentsSeparatedByString:@"]"]; 115 | for (NSInteger i = 0; i 10 | 11 | typedef enum { 12 | MJPropertyKeyTypeDictionary = 0, // 字典的key 13 | MJPropertyKeyTypeArray // 数组的key 14 | } MJPropertyKeyType; 15 | 16 | /** 17 | * 属性的key 18 | */ 19 | @interface MJPropertyKey : NSObject 20 | /** key的名字 */ 21 | @property (copy, nonatomic) NSString *name; 22 | /** key的种类,可能是@"10",可能是@"age" */ 23 | @property (assign, nonatomic) MJPropertyKeyType type; 24 | 25 | /** 26 | * 根据当前的key,也就是name,从object(字典或者数组)中取值 27 | */ 28 | - (id)valueInObject:(id)object; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/MJPropertyKey.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJPropertyKey.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/8/11. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJPropertyKey.h" 10 | 11 | @implementation MJPropertyKey 12 | 13 | - (id)valueInObject:(id)object 14 | { 15 | if ([object isKindOfClass:[NSDictionary class]] && self.type == MJPropertyKeyTypeDictionary) { 16 | return object[self.name]; 17 | } else if ([object isKindOfClass:[NSArray class]] && self.type == MJPropertyKeyTypeArray) { 18 | NSArray *array = object; 19 | NSUInteger index = self.name.intValue; 20 | if (index < array.count) return array[index]; 21 | return nil; 22 | } 23 | return nil; 24 | } 25 | @end 26 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/MJPropertyType.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJPropertyType.h 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 包装一种类型 8 | 9 | #import 10 | 11 | /** 12 | * 包装一种类型 13 | */ 14 | @interface MJPropertyType : NSObject 15 | /** 类型标识符 */ 16 | @property (nonatomic, copy) NSString *code; 17 | 18 | /** 是否为id类型 */ 19 | @property (nonatomic, readonly, getter=isIdType) BOOL idType; 20 | 21 | /** 是否为基本数字类型:int、float等 */ 22 | @property (nonatomic, readonly, getter=isNumberType) BOOL numberType; 23 | 24 | /** 是否为BOOL类型 */ 25 | @property (nonatomic, readonly, getter=isBoolType) BOOL boolType; 26 | 27 | /** 对象类型(如果是基本数据类型,此值为nil) */ 28 | @property (nonatomic, readonly) Class typeClass; 29 | 30 | /** 类型是否来自于Foundation框架,比如NSString、NSArray */ 31 | @property (nonatomic, readonly, getter = isFromFoundation) BOOL fromFoundation; 32 | /** 类型是否不支持KVC */ 33 | @property (nonatomic, readonly, getter = isKVCDisabled) BOOL KVCDisabled; 34 | 35 | /** 36 | * 获得缓存的类型对象 37 | */ 38 | + (instancetype)cachedTypeWithCode:(NSString *)code; 39 | @end -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/MJPropertyType.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJPropertyType.m 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJPropertyType.h" 10 | #import "MJExtension.h" 11 | #import "MJFoundation.h" 12 | #import "MJExtensionConst.h" 13 | 14 | @implementation MJPropertyType 15 | 16 | + (instancetype)cachedTypeWithCode:(NSString *)code 17 | { 18 | MJExtensionAssertParamNotNil2(code, nil); 19 | 20 | static NSMutableDictionary *types; 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | types = [NSMutableDictionary dictionary]; 24 | }); 25 | 26 | MJPropertyType *type = types[code]; 27 | if (type == nil) { 28 | type = [[self alloc] init]; 29 | type.code = code; 30 | types[code] = type; 31 | } 32 | return type; 33 | } 34 | 35 | #pragma mark - 公共方法 36 | - (void)setCode:(NSString *)code 37 | { 38 | _code = code; 39 | 40 | MJExtensionAssertParamNotNil(code); 41 | 42 | if ([code isEqualToString:MJPropertyTypeId]) { 43 | _idType = YES; 44 | } else if (code.length == 0) { 45 | _KVCDisabled = YES; 46 | } else if (code.length > 3 && [code hasPrefix:@"@\""]) { 47 | // 去掉@"和",截取中间的类型名称 48 | _code = [code substringWithRange:NSMakeRange(2, code.length - 3)]; 49 | _typeClass = NSClassFromString(_code); 50 | _fromFoundation = [MJFoundation isClassFromFoundation:_typeClass]; 51 | _numberType = [_typeClass isSubclassOfClass:[NSNumber class]]; 52 | 53 | } else if ([code isEqualToString:MJPropertyTypeSEL] || 54 | [code isEqualToString:MJPropertyTypeIvar] || 55 | [code isEqualToString:MJPropertyTypeMethod]) { 56 | _KVCDisabled = YES; 57 | } 58 | 59 | // 是否为数字类型 60 | NSString *lowerCode = _code.lowercaseString; 61 | NSArray *numberTypes = @[MJPropertyTypeInt, MJPropertyTypeShort, MJPropertyTypeBOOL1, MJPropertyTypeBOOL2, MJPropertyTypeFloat, MJPropertyTypeDouble, MJPropertyTypeLong, MJPropertyTypeLongLong, MJPropertyTypeChar]; 62 | if ([numberTypes containsObject:lowerCode]) { 63 | _numberType = YES; 64 | 65 | if ([lowerCode isEqualToString:MJPropertyTypeBOOL1] 66 | || [lowerCode isEqualToString:MJPropertyTypeBOOL2]) { 67 | _boolType = YES; 68 | } 69 | } 70 | } 71 | @end 72 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/NSObject+MJClass.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJClass.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/8/11. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * 遍历所有类的block(父类) 13 | */ 14 | typedef void (^MJClassesEnumeration)(Class c, BOOL *stop); 15 | 16 | /** 这个数组中的属性名才会进行字典和模型的转换 */ 17 | typedef NSArray * (^MJAllowedPropertyNames)(void); 18 | /** 这个数组中的属性名才会进行归档 */ 19 | typedef NSArray * (^MJAllowedCodingPropertyNames)(void); 20 | 21 | /** 这个数组中的属性名将会被忽略:不进行字典和模型的转换 */ 22 | typedef NSArray * (^MJIgnoredPropertyNames)(void); 23 | /** 这个数组中的属性名将会被忽略:不进行归档 */ 24 | typedef NSArray * (^MJIgnoredCodingPropertyNames)(void); 25 | 26 | /** 27 | * 类相关的扩展 28 | */ 29 | @interface NSObject (MJClass) 30 | /** 31 | * 遍历所有的类 32 | */ 33 | + (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration; 34 | + (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration; 35 | 36 | #pragma mark - 属性白名单配置 37 | /** 38 | * 这个数组中的属性名才会进行字典和模型的转换 39 | * 40 | * @param allowedPropertyNames 这个数组中的属性名才会进行字典和模型的转换 41 | */ 42 | + (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; 43 | 44 | /** 45 | * 这个数组中的属性名才会进行字典和模型的转换 46 | */ 47 | + (NSMutableArray *)mj_totalAllowedPropertyNames; 48 | 49 | #pragma mark - 属性黑名单配置 50 | /** 51 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 52 | * 53 | * @param ignoredPropertyNames 这个数组中的属性名将会被忽略:不进行字典和模型的转换 54 | */ 55 | + (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames; 56 | 57 | /** 58 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 59 | */ 60 | + (NSMutableArray *)mj_totalIgnoredPropertyNames; 61 | 62 | #pragma mark - 归档属性白名单配置 63 | /** 64 | * 这个数组中的属性名才会进行归档 65 | * 66 | * @param allowedCodingPropertyNames 这个数组中的属性名才会进行归档 67 | */ 68 | + (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames; 69 | 70 | /** 71 | * 这个数组中的属性名才会进行字典和模型的转换 72 | */ 73 | + (NSMutableArray *)mj_totalAllowedCodingPropertyNames; 74 | 75 | #pragma mark - 归档属性黑名单配置 76 | /** 77 | * 这个数组中的属性名将会被忽略:不进行归档 78 | * 79 | * @param ignoredCodingPropertyNames 这个数组中的属性名将会被忽略:不进行归档 80 | */ 81 | + (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames; 82 | 83 | /** 84 | * 这个数组中的属性名将会被忽略:不进行归档 85 | */ 86 | + (NSMutableArray *)mj_totalIgnoredCodingPropertyNames; 87 | 88 | #pragma mark - 内部使用 89 | + (void)mj_setupBlockReturnValue:(id (^)(void))block key:(const char *)key; 90 | @end 91 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/NSObject+MJClass.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJClass.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/8/11. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJClass.h" 10 | #import "NSObject+MJCoding.h" 11 | #import "NSObject+MJKeyValue.h" 12 | #import "MJFoundation.h" 13 | #import 14 | 15 | static const char MJAllowedPropertyNamesKey = '\0'; 16 | static const char MJIgnoredPropertyNamesKey = '\0'; 17 | static const char MJAllowedCodingPropertyNamesKey = '\0'; 18 | static const char MJIgnoredCodingPropertyNamesKey = '\0'; 19 | 20 | @implementation NSObject (MJClass) 21 | 22 | + (NSMutableDictionary *)mj_classDictForKey:(const void *)key 23 | { 24 | static NSMutableDictionary *allowedPropertyNamesDict; 25 | static NSMutableDictionary *ignoredPropertyNamesDict; 26 | static NSMutableDictionary *allowedCodingPropertyNamesDict; 27 | static NSMutableDictionary *ignoredCodingPropertyNamesDict; 28 | 29 | static dispatch_once_t onceToken; 30 | dispatch_once(&onceToken, ^{ 31 | allowedPropertyNamesDict = [NSMutableDictionary dictionary]; 32 | ignoredPropertyNamesDict = [NSMutableDictionary dictionary]; 33 | allowedCodingPropertyNamesDict = [NSMutableDictionary dictionary]; 34 | ignoredCodingPropertyNamesDict = [NSMutableDictionary dictionary]; 35 | }); 36 | 37 | if (key == &MJAllowedPropertyNamesKey) return allowedPropertyNamesDict; 38 | if (key == &MJIgnoredPropertyNamesKey) return ignoredPropertyNamesDict; 39 | if (key == &MJAllowedCodingPropertyNamesKey) return allowedCodingPropertyNamesDict; 40 | if (key == &MJIgnoredCodingPropertyNamesKey) return ignoredCodingPropertyNamesDict; 41 | return nil; 42 | } 43 | 44 | + (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration 45 | { 46 | // 1.没有block就直接返回 47 | if (enumeration == nil) return; 48 | 49 | // 2.停止遍历的标记 50 | BOOL stop = NO; 51 | 52 | // 3.当前正在遍历的类 53 | Class c = self; 54 | 55 | // 4.开始遍历每一个类 56 | while (c && !stop) { 57 | // 4.1.执行操作 58 | enumeration(c, &stop); 59 | 60 | // 4.2.获得父类 61 | c = class_getSuperclass(c); 62 | 63 | if ([MJFoundation isClassFromFoundation:c]) break; 64 | } 65 | } 66 | 67 | + (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration 68 | { 69 | // 1.没有block就直接返回 70 | if (enumeration == nil) return; 71 | 72 | // 2.停止遍历的标记 73 | BOOL stop = NO; 74 | 75 | // 3.当前正在遍历的类 76 | Class c = self; 77 | 78 | // 4.开始遍历每一个类 79 | while (c && !stop) { 80 | // 4.1.执行操作 81 | enumeration(c, &stop); 82 | 83 | // 4.2.获得父类 84 | c = class_getSuperclass(c); 85 | } 86 | } 87 | 88 | #pragma mark - 属性黑名单配置 89 | + (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames 90 | { 91 | [self mj_setupBlockReturnValue:ignoredPropertyNames key:&MJIgnoredPropertyNamesKey]; 92 | } 93 | 94 | + (NSMutableArray *)mj_totalIgnoredPropertyNames 95 | { 96 | return [self mj_totalObjectsWithSelector:@selector(mj_ignoredPropertyNames) key:&MJIgnoredPropertyNamesKey]; 97 | } 98 | 99 | #pragma mark - 归档属性黑名单配置 100 | + (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames 101 | { 102 | [self mj_setupBlockReturnValue:ignoredCodingPropertyNames key:&MJIgnoredCodingPropertyNamesKey]; 103 | } 104 | 105 | + (NSMutableArray *)mj_totalIgnoredCodingPropertyNames 106 | { 107 | return [self mj_totalObjectsWithSelector:@selector(mj_ignoredCodingPropertyNames) key:&MJIgnoredCodingPropertyNamesKey]; 108 | } 109 | 110 | #pragma mark - 属性白名单配置 111 | + (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; 112 | { 113 | [self mj_setupBlockReturnValue:allowedPropertyNames key:&MJAllowedPropertyNamesKey]; 114 | } 115 | 116 | + (NSMutableArray *)mj_totalAllowedPropertyNames 117 | { 118 | return [self mj_totalObjectsWithSelector:@selector(mj_allowedPropertyNames) key:&MJAllowedPropertyNamesKey]; 119 | } 120 | 121 | #pragma mark - 归档属性白名单配置 122 | + (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames 123 | { 124 | [self mj_setupBlockReturnValue:allowedCodingPropertyNames key:&MJAllowedCodingPropertyNamesKey]; 125 | } 126 | 127 | + (NSMutableArray *)mj_totalAllowedCodingPropertyNames 128 | { 129 | return [self mj_totalObjectsWithSelector:@selector(mj_allowedCodingPropertyNames) key:&MJAllowedCodingPropertyNamesKey]; 130 | } 131 | 132 | #pragma mark - block和方法处理:存储block的返回值 133 | + (void)mj_setupBlockReturnValue:(id (^)(void))block key:(const char *)key 134 | { 135 | if (block) { 136 | objc_setAssociatedObject(self, key, block(), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 137 | } else { 138 | objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 139 | } 140 | 141 | // 清空数据 142 | MJExtensionSemaphoreCreate 143 | MJExtensionSemaphoreWait 144 | [[self mj_classDictForKey:key] removeAllObjects]; 145 | MJExtensionSemaphoreSignal 146 | } 147 | 148 | + (NSMutableArray *)mj_totalObjectsWithSelector:(SEL)selector key:(const char *)key 149 | { 150 | MJExtensionSemaphoreCreate 151 | MJExtensionSemaphoreWait 152 | NSMutableArray *array = [self mj_classDictForKey:key][NSStringFromClass(self)]; 153 | if (array == nil) { 154 | // 创建、存储 155 | [self mj_classDictForKey:key][NSStringFromClass(self)] = array = [NSMutableArray array]; 156 | 157 | if ([self respondsToSelector:selector]) { 158 | #pragma clang diagnostic push 159 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 160 | NSArray *subArray = [self performSelector:selector]; 161 | #pragma clang diagnostic pop 162 | if (subArray) { 163 | [array addObjectsFromArray:subArray]; 164 | } 165 | } 166 | 167 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 168 | NSArray *subArray = objc_getAssociatedObject(c, key); 169 | [array addObjectsFromArray:subArray]; 170 | }]; 171 | } 172 | MJExtensionSemaphoreSignal 173 | return array; 174 | } 175 | @end 176 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/NSObject+MJCoding.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJCoding.h 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MJExtensionConst.h" 11 | 12 | /** 13 | * Codeing协议 14 | */ 15 | @protocol MJCoding 16 | @optional 17 | /** 18 | * 这个数组中的属性名才会进行归档 19 | */ 20 | + (NSArray *)mj_allowedCodingPropertyNames; 21 | /** 22 | * 这个数组中的属性名将会被忽略:不进行归档 23 | */ 24 | + (NSArray *)mj_ignoredCodingPropertyNames; 25 | @end 26 | 27 | @interface NSObject (MJCoding) 28 | /** 29 | * 解码(从文件中解析对象) 30 | */ 31 | - (void)mj_decode:(NSCoder *)decoder; 32 | /** 33 | * 编码(将对象写入文件中) 34 | */ 35 | - (void)mj_encode:(NSCoder *)encoder; 36 | @end 37 | 38 | /** 39 | 归档的实现 40 | */ 41 | #define MJCodingImplementation \ 42 | - (id)initWithCoder:(NSCoder *)decoder \ 43 | { \ 44 | if (self = [super init]) { \ 45 | [self mj_decode:decoder]; \ 46 | } \ 47 | return self; \ 48 | } \ 49 | \ 50 | - (void)encodeWithCoder:(NSCoder *)encoder \ 51 | { \ 52 | [self mj_encode:encoder]; \ 53 | } 54 | 55 | #define MJExtensionCodingImplementation MJCodingImplementation -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/NSObject+MJCoding.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJCoding.m 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJCoding.h" 10 | #import "NSObject+MJClass.h" 11 | #import "NSObject+MJProperty.h" 12 | #import "MJProperty.h" 13 | 14 | @implementation NSObject (MJCoding) 15 | 16 | - (void)mj_encode:(NSCoder *)encoder 17 | { 18 | Class clazz = [self class]; 19 | 20 | NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames]; 21 | NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames]; 22 | 23 | [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { 24 | // 检测是否被忽略 25 | if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return; 26 | if ([ignoredCodingPropertyNames containsObject:property.name]) return; 27 | 28 | id value = [property valueForObject:self]; 29 | if (value == nil) return; 30 | [encoder encodeObject:value forKey:property.name]; 31 | }]; 32 | } 33 | 34 | - (void)mj_decode:(NSCoder *)decoder 35 | { 36 | Class clazz = [self class]; 37 | 38 | NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames]; 39 | NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames]; 40 | 41 | [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { 42 | // 检测是否被忽略 43 | if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return; 44 | if ([ignoredCodingPropertyNames containsObject:property.name]) return; 45 | 46 | id value = [decoder decodeObjectForKey:property.name]; 47 | if (value == nil) { // 兼容以前的MJExtension版本 48 | value = [decoder decodeObjectForKey:[@"_" stringByAppendingString:property.name]]; 49 | } 50 | if (value == nil) return; 51 | [property setValue:value forObject:self]; 52 | }]; 53 | } 54 | @end 55 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/NSObject+MJKeyValue.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJKeyValue.h 3 | // MJExtension 4 | // 5 | // Created by mj on 13-8-24. 6 | // Copyright (c) 2013年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MJExtensionConst.h" 11 | #import 12 | #import "MJProperty.h" 13 | 14 | /** 15 | * KeyValue协议 16 | */ 17 | @protocol MJKeyValue 18 | @optional 19 | /** 20 | * 只有这个数组中的属性名才允许进行字典和模型的转换 21 | */ 22 | + (NSArray *)mj_allowedPropertyNames; 23 | 24 | /** 25 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 26 | */ 27 | + (NSArray *)mj_ignoredPropertyNames; 28 | 29 | /** 30 | * 将属性名换为其他key去字典中取值 31 | * 32 | * @return 字典中的key是属性名,value是从字典中取值用的key 33 | */ 34 | + (NSDictionary *)mj_replacedKeyFromPropertyName; 35 | 36 | /** 37 | * 将属性名换为其他key去字典中取值 38 | * 39 | * @return 从字典中取值用的key 40 | */ 41 | + (id)mj_replacedKeyFromPropertyName121:(NSString *)propertyName; 42 | 43 | /** 44 | * 数组中需要转换的模型类 45 | * 46 | * @return 字典中的key是数组属性名,value是数组中存放模型的Class(Class类型或者NSString类型) 47 | */ 48 | + (NSDictionary *)mj_objectClassInArray; 49 | 50 | /** 51 | * 旧值换新值,用于过滤字典中的值 52 | * 53 | * @param oldValue 旧值 54 | * 55 | * @return 新值 56 | */ 57 | - (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property; 58 | 59 | /** 60 | * 当字典转模型完毕时调用 61 | */ 62 | - (void)mj_keyValuesDidFinishConvertingToObject; 63 | - (void)mj_keyValuesDidFinishConvertingToObject:(NSDictionary *)keyValues; 64 | 65 | /** 66 | * 当模型转字典完毕时调用 67 | */ 68 | - (void)mj_objectDidFinishConvertingToKeyValues; 69 | @end 70 | 71 | @interface NSObject (MJKeyValue) 72 | #pragma mark - 类方法 73 | /** 74 | * 字典转模型过程中遇到的错误 75 | */ 76 | + (NSError *)mj_error; 77 | 78 | /** 79 | * 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) 80 | */ 81 | + (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference; 82 | 83 | #pragma mark - 对象方法 84 | /** 85 | * 将字典的键值对转成模型属性 86 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 87 | */ 88 | - (instancetype)mj_setKeyValues:(id)keyValues; 89 | 90 | /** 91 | * 将字典的键值对转成模型属性 92 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 93 | * @param context CoreData上下文 94 | */ 95 | - (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; 96 | 97 | /** 98 | * 将模型转成字典 99 | * @return 字典 100 | */ 101 | - (NSMutableDictionary *)mj_keyValues; 102 | - (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys; 103 | - (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys; 104 | 105 | /** 106 | * 通过模型数组来创建一个字典数组 107 | * @param objectArray 模型数组 108 | * @return 字典数组 109 | */ 110 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray; 111 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys; 112 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys; 113 | 114 | #pragma mark - 字典转模型 115 | /** 116 | * 通过字典来创建一个模型 117 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 118 | * @return 新建的对象 119 | */ 120 | + (instancetype)mj_objectWithKeyValues:(id)keyValues; 121 | 122 | /** 123 | * 通过字典来创建一个CoreData模型 124 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 125 | * @param context CoreData上下文 126 | * @return 新建的对象 127 | */ 128 | + (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; 129 | 130 | /** 131 | * 通过plist来创建一个模型 132 | * @param filename 文件名(仅限于mainBundle中的文件) 133 | * @return 新建的对象 134 | */ 135 | + (instancetype)mj_objectWithFilename:(NSString *)filename; 136 | 137 | /** 138 | * 通过plist来创建一个模型 139 | * @param file 文件全路径 140 | * @return 新建的对象 141 | */ 142 | + (instancetype)mj_objectWithFile:(NSString *)file; 143 | 144 | #pragma mark - 字典数组转模型数组 145 | /** 146 | * 通过字典数组来创建一个模型数组 147 | * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) 148 | * @return 模型数组 149 | */ 150 | + (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray; 151 | 152 | /** 153 | * 通过字典数组来创建一个模型数组 154 | * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) 155 | * @param context CoreData上下文 156 | * @return 模型数组 157 | */ 158 | + (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context; 159 | 160 | /** 161 | * 通过plist来创建一个模型数组 162 | * @param filename 文件名(仅限于mainBundle中的文件) 163 | * @return 模型数组 164 | */ 165 | + (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename; 166 | 167 | /** 168 | * 通过plist来创建一个模型数组 169 | * @param file 文件全路径 170 | * @return 模型数组 171 | */ 172 | + (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file; 173 | 174 | #pragma mark - 转换为JSON 175 | /** 176 | * 转换为JSON Data 177 | */ 178 | - (NSData *)mj_JSONData; 179 | /** 180 | * 转换为字典或者数组 181 | */ 182 | - (id)mj_JSONObject; 183 | /** 184 | * 转换为JSON 字符串 185 | */ 186 | - (NSString *)mj_JSONString; 187 | @end 188 | 189 | @interface NSObject (MJKeyValueDeprecated_v_2_5_16) 190 | - (instancetype)setKeyValues:(id)keyValue MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 191 | - (instancetype)setKeyValues:(id)keyValues error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 192 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 193 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 194 | + (void)referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 195 | - (NSMutableDictionary *)keyValues MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 196 | - (NSMutableDictionary *)keyValuesWithError:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 197 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 198 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 199 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 200 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 201 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 202 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 203 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 204 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 205 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 206 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 207 | + (instancetype)objectWithKeyValues:(id)keyValues MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 208 | + (instancetype)objectWithKeyValues:(id)keyValues error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 209 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 210 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 211 | + (instancetype)objectWithFilename:(NSString *)filename MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 212 | + (instancetype)objectWithFilename:(NSString *)filename error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 213 | + (instancetype)objectWithFile:(NSString *)file MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 214 | + (instancetype)objectWithFile:(NSString *)file error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 215 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 216 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 217 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 218 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 219 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 220 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 221 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 222 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 223 | - (NSData *)JSONData MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 224 | - (id)JSONObject MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 225 | - (NSString *)JSONString MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 226 | @end 227 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/NSObject+MJProperty.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJProperty.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MJExtensionConst.h" 11 | 12 | @class MJProperty; 13 | 14 | /** 15 | * 遍历成员变量用的block 16 | * 17 | * @param property 成员的包装对象 18 | * @param stop YES代表停止遍历,NO代表继续遍历 19 | */ 20 | typedef void (^MJPropertiesEnumeration)(MJProperty *property, BOOL *stop); 21 | 22 | /** 将属性名换为其他key去字典中取值 */ 23 | typedef NSDictionary * (^MJReplacedKeyFromPropertyName)(void); 24 | typedef id (^MJReplacedKeyFromPropertyName121)(NSString *propertyName); 25 | /** 数组中需要转换的模型类 */ 26 | typedef NSDictionary * (^MJObjectClassInArray)(void); 27 | /** 用于过滤字典中的值 */ 28 | typedef id (^MJNewValueFromOldValue)(id object, id oldValue, MJProperty *property); 29 | 30 | /** 31 | * 成员属性相关的扩展 32 | */ 33 | @interface NSObject (MJProperty) 34 | #pragma mark - 遍历 35 | /** 36 | * 遍历所有的成员 37 | */ 38 | + (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration; 39 | 40 | #pragma mark - 新值配置 41 | /** 42 | * 用于过滤字典中的值 43 | * 44 | * @param newValueFormOldValue 用于过滤字典中的值 45 | */ 46 | + (void)mj_setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue; 47 | + (id)mj_getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property; 48 | 49 | #pragma mark - key配置 50 | /** 51 | * 将属性名换为其他key去字典中取值 52 | * 53 | * @param replacedKeyFromPropertyName 将属性名换为其他key去字典中取值 54 | */ 55 | + (void)mj_setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName; 56 | /** 57 | * 将属性名换为其他key去字典中取值 58 | * 59 | * @param replacedKeyFromPropertyName121 将属性名换为其他key去字典中取值 60 | */ 61 | + (void)mj_setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121; 62 | 63 | #pragma mark - array model class配置 64 | /** 65 | * 数组中需要转换的模型类 66 | * 67 | * @param objectClassInArray 数组中需要转换的模型类 68 | */ 69 | + (void)mj_setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray; 70 | @end 71 | 72 | @interface NSObject (MJPropertyDeprecated_v_2_5_16) 73 | + (void)enumerateProperties:(MJPropertiesEnumeration)enumeration MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 74 | + (void)setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 75 | + (id)getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 76 | + (void)setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 77 | + (void)setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121 MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 78 | + (void)setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 79 | @end 80 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/NSObject+MJProperty.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJProperty.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJProperty.h" 10 | #import "NSObject+MJKeyValue.h" 11 | #import "NSObject+MJCoding.h" 12 | #import "NSObject+MJClass.h" 13 | #import "MJProperty.h" 14 | #import "MJFoundation.h" 15 | #import 16 | 17 | #pragma clang diagnostic push 18 | #pragma clang diagnostic ignored "-Wundeclared-selector" 19 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 20 | 21 | static const char MJReplacedKeyFromPropertyNameKey = '\0'; 22 | static const char MJReplacedKeyFromPropertyName121Key = '\0'; 23 | static const char MJNewValueFromOldValueKey = '\0'; 24 | static const char MJObjectClassInArrayKey = '\0'; 25 | 26 | static const char MJCachedPropertiesKey = '\0'; 27 | 28 | @implementation NSObject (Property) 29 | 30 | + (NSMutableDictionary *)mj_propertyDictForKey:(const void *)key 31 | { 32 | static NSMutableDictionary *replacedKeyFromPropertyNameDict; 33 | static NSMutableDictionary *replacedKeyFromPropertyName121Dict; 34 | static NSMutableDictionary *newValueFromOldValueDict; 35 | static NSMutableDictionary *objectClassInArrayDict; 36 | static NSMutableDictionary *cachedPropertiesDict; 37 | 38 | static dispatch_once_t onceToken; 39 | dispatch_once(&onceToken, ^{ 40 | replacedKeyFromPropertyNameDict = [NSMutableDictionary dictionary]; 41 | replacedKeyFromPropertyName121Dict = [NSMutableDictionary dictionary]; 42 | newValueFromOldValueDict = [NSMutableDictionary dictionary]; 43 | objectClassInArrayDict = [NSMutableDictionary dictionary]; 44 | cachedPropertiesDict = [NSMutableDictionary dictionary]; 45 | }); 46 | 47 | if (key == &MJReplacedKeyFromPropertyNameKey) return replacedKeyFromPropertyNameDict; 48 | if (key == &MJReplacedKeyFromPropertyName121Key) return replacedKeyFromPropertyName121Dict; 49 | if (key == &MJNewValueFromOldValueKey) return newValueFromOldValueDict; 50 | if (key == &MJObjectClassInArrayKey) return objectClassInArrayDict; 51 | if (key == &MJCachedPropertiesKey) return cachedPropertiesDict; 52 | return nil; 53 | } 54 | 55 | #pragma mark - --私有方法-- 56 | + (id)mj_propertyKey:(NSString *)propertyName 57 | { 58 | MJExtensionAssertParamNotNil2(propertyName, nil); 59 | 60 | __block id key = nil; 61 | // 查看有没有需要替换的key 62 | if ([self respondsToSelector:@selector(mj_replacedKeyFromPropertyName121:)]) { 63 | key = [self mj_replacedKeyFromPropertyName121:propertyName]; 64 | } 65 | // 兼容旧版本 66 | if ([self respondsToSelector:@selector(replacedKeyFromPropertyName121:)]) { 67 | key = [self performSelector:@selector(replacedKeyFromPropertyName121:) withObject:propertyName]; 68 | } 69 | 70 | // 调用block 71 | if (!key) { 72 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 73 | MJReplacedKeyFromPropertyName121 block = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyName121Key); 74 | if (block) { 75 | key = block(propertyName); 76 | } 77 | if (key) *stop = YES; 78 | }]; 79 | } 80 | 81 | // 查看有没有需要替换的key 82 | if ((!key || [key isEqual:propertyName]) && [self respondsToSelector:@selector(mj_replacedKeyFromPropertyName)]) { 83 | key = [self mj_replacedKeyFromPropertyName][propertyName]; 84 | } 85 | // 兼容旧版本 86 | if ((!key || [key isEqual:propertyName]) && [self respondsToSelector:@selector(replacedKeyFromPropertyName)]) { 87 | key = [self performSelector:@selector(replacedKeyFromPropertyName)][propertyName]; 88 | } 89 | 90 | if (!key || [key isEqual:propertyName]) { 91 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 92 | NSDictionary *dict = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyNameKey); 93 | if (dict) { 94 | key = dict[propertyName]; 95 | } 96 | if (key && ![key isEqual:propertyName]) *stop = YES; 97 | }]; 98 | } 99 | 100 | // 2.用属性名作为key 101 | if (!key) key = propertyName; 102 | 103 | return key; 104 | } 105 | 106 | + (Class)mj_propertyObjectClassInArray:(NSString *)propertyName 107 | { 108 | __block id clazz = nil; 109 | if ([self respondsToSelector:@selector(mj_objectClassInArray)]) { 110 | clazz = [self mj_objectClassInArray][propertyName]; 111 | } 112 | // 兼容旧版本 113 | if ([self respondsToSelector:@selector(objectClassInArray)]) { 114 | clazz = [self performSelector:@selector(objectClassInArray)][propertyName]; 115 | } 116 | 117 | if (!clazz) { 118 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 119 | NSDictionary *dict = objc_getAssociatedObject(c, &MJObjectClassInArrayKey); 120 | if (dict) { 121 | clazz = dict[propertyName]; 122 | } 123 | if (clazz) *stop = YES; 124 | }]; 125 | } 126 | 127 | // 如果是NSString类型 128 | if ([clazz isKindOfClass:[NSString class]]) { 129 | clazz = NSClassFromString(clazz); 130 | } 131 | return clazz; 132 | } 133 | 134 | #pragma mark - --公共方法-- 135 | + (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration 136 | { 137 | // 获得成员变量 138 | MJExtensionSemaphoreCreate 139 | MJExtensionSemaphoreWait 140 | NSArray *cachedProperties = [self mj_properties]; 141 | MJExtensionSemaphoreSignal 142 | // 遍历成员变量 143 | BOOL stop = NO; 144 | for (MJProperty *property in cachedProperties) { 145 | enumeration(property, &stop); 146 | if (stop) break; 147 | } 148 | } 149 | 150 | #pragma mark - 公共方法 151 | + (NSMutableArray *)mj_properties 152 | { 153 | NSMutableArray *cachedProperties = [self mj_propertyDictForKey:&MJCachedPropertiesKey][NSStringFromClass(self)]; 154 | if (cachedProperties == nil) { 155 | 156 | if (cachedProperties == nil) { 157 | cachedProperties = [NSMutableArray array]; 158 | 159 | [self mj_enumerateClasses:^(__unsafe_unretained Class c, BOOL *stop) { 160 | // 1.获得所有的成员变量 161 | unsigned int outCount = 0; 162 | objc_property_t *properties = class_copyPropertyList(c, &outCount); 163 | 164 | // 2.遍历每一个成员变量 165 | for (unsigned int i = 0; i 10 | #import "MJExtensionConst.h" 11 | 12 | @interface NSString (MJExtension) 13 | /** 14 | * 驼峰转下划线(loveYou -> love_you) 15 | */ 16 | - (NSString *)mj_underlineFromCamel; 17 | /** 18 | * 下划线转驼峰(love_you -> loveYou) 19 | */ 20 | - (NSString *)mj_camelFromUnderline; 21 | /** 22 | * 首字母变大写 23 | */ 24 | - (NSString *)mj_firstCharUpper; 25 | /** 26 | * 首字母变小写 27 | */ 28 | - (NSString *)mj_firstCharLower; 29 | 30 | - (BOOL)mj_isPureInt; 31 | 32 | - (NSURL *)mj_url; 33 | @end 34 | 35 | @interface NSString (MJExtensionDeprecated_v_2_5_16) 36 | - (NSString *)underlineFromCamel MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 37 | - (NSString *)camelFromUnderline MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 38 | - (NSString *)firstCharUpper MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 39 | - (NSString *)firstCharLower MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 40 | - (BOOL)isPureInt MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 41 | - (NSURL *)url MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 42 | @end 43 | -------------------------------------------------------------------------------- /Pods/MJExtension/MJExtension/NSString+MJExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+MJExtension.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/6/7. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSString+MJExtension.h" 10 | 11 | @implementation NSString (MJExtension) 12 | - (NSString *)mj_underlineFromCamel 13 | { 14 | if (self.length == 0) return self; 15 | NSMutableString *string = [NSMutableString string]; 16 | for (NSUInteger i = 0; i= 2) [string appendString:[cmp substringFromIndex:1]]; 40 | } else { 41 | [string appendString:cmp]; 42 | } 43 | } 44 | return string; 45 | } 46 | 47 | - (NSString *)mj_firstCharLower 48 | { 49 | if (self.length == 0) return self; 50 | NSMutableString *string = [NSMutableString string]; 51 | [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].lowercaseString]; 52 | if (self.length >= 2) [string appendString:[self substringFromIndex:1]]; 53 | return string; 54 | } 55 | 56 | - (NSString *)mj_firstCharUpper 57 | { 58 | if (self.length == 0) return self; 59 | NSMutableString *string = [NSMutableString string]; 60 | [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].uppercaseString]; 61 | if (self.length >= 2) [string appendString:[self substringFromIndex:1]]; 62 | return string; 63 | } 64 | 65 | - (BOOL)mj_isPureInt 66 | { 67 | NSScanner *scan = [NSScanner scannerWithString:self]; 68 | int val; 69 | return [scan scanInt:&val] && [scan isAtEnd]; 70 | } 71 | 72 | - (NSURL *)mj_url 73 | { 74 | // [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"!$&'()*+,-./:;=?@_~%#[]"]]; 75 | #pragma clang diagnostic push 76 | #pragma clang diagnostic ignored"-Wdeprecated-declarations" 77 | return [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))]; 78 | #pragma clang diagnostic pop 79 | } 80 | @end 81 | 82 | @implementation NSString (MJExtensionDeprecated_v_2_5_16) 83 | - (NSString *)underlineFromCamel 84 | { 85 | return self.mj_underlineFromCamel; 86 | } 87 | 88 | - (NSString *)camelFromUnderline 89 | { 90 | return self.mj_camelFromUnderline; 91 | } 92 | 93 | - (NSString *)firstCharLower 94 | { 95 | return self.mj_firstCharLower; 96 | } 97 | 98 | - (NSString *)firstCharUpper 99 | { 100 | return self.mj_firstCharUpper; 101 | } 102 | 103 | - (BOOL)isPureInt 104 | { 105 | return self.mj_isPureInt; 106 | } 107 | 108 | - (NSURL *)url 109 | { 110 | return self.mj_url; 111 | } 112 | @end 113 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - MJExtension (3.0.17) 3 | 4 | DEPENDENCIES: 5 | - MJExtension (~> 3.0.10) 6 | 7 | SPEC CHECKSUMS: 8 | MJExtension: 74ec83124a68891619fb7ba9c5c811bbf1691076 9 | 10 | PODFILE CHECKSUM: 5dff578281463d3f8c3795963278b45fcbb4771e 11 | 12 | COCOAPODS: 1.4.0 13 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/zhouyn.xcuserdatad/xcschemes/MJExtension.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/zhouyn.xcuserdatad/xcschemes/Pods-ZHFilterMenuView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/zhouyn.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MJExtension.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 0 13 | 14 | Pods-ZHFilterMenuView.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 1 20 | 21 | 22 | SuppressBuildableAutocreation 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MJExtension/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 | FMWK 17 | CFBundleShortVersionString 18 | 3.0.17 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MJExtension/MJExtension-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_MJExtension : NSObject 3 | @end 4 | @implementation PodsDummy_MJExtension 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MJExtension/MJExtension-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MJExtension/MJExtension-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "MJExtension.h" 14 | #import "MJExtensionConst.h" 15 | #import "MJFoundation.h" 16 | #import "MJProperty.h" 17 | #import "MJPropertyKey.h" 18 | #import "MJPropertyType.h" 19 | #import "NSObject+MJClass.h" 20 | #import "NSObject+MJCoding.h" 21 | #import "NSObject+MJKeyValue.h" 22 | #import "NSObject+MJProperty.h" 23 | #import "NSString+MJExtension.h" 24 | 25 | FOUNDATION_EXPORT double MJExtensionVersionNumber; 26 | FOUNDATION_EXPORT const unsigned char MJExtensionVersionString[]; 27 | 28 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MJExtension/MJExtension.modulemap: -------------------------------------------------------------------------------- 1 | framework module MJExtension { 2 | umbrella header "MJExtension-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MJExtension/MJExtension.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MJExtension 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/MJExtension 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ZHFilterMenuView/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ZHFilterMenuView/Pods-ZHFilterMenuView-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## MJExtension 5 | 6 | Copyright (c) 2013-2019 MJExtension (https://github.com/CoderMJLee/MJExtension) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - https://cocoapods.org 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ZHFilterMenuView/Pods-ZHFilterMenuView-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2013-2019 MJExtension (https://github.com/CoderMJLee/MJExtension) 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | MJExtension 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Generated by CocoaPods - https://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ZHFilterMenuView/Pods-ZHFilterMenuView-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_ZHFilterMenuView : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_ZHFilterMenuView 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ZHFilterMenuView/Pods-ZHFilterMenuView-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | # Used as a return value for each invocation of `strip_invalid_archs` function. 10 | STRIP_BINARY_RETVAL=0 11 | 12 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 13 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 14 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 15 | 16 | # Copies and strips a vendored framework 17 | install_framework() 18 | { 19 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 20 | local source="${BUILT_PRODUCTS_DIR}/$1" 21 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 22 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 23 | elif [ -r "$1" ]; then 24 | local source="$1" 25 | fi 26 | 27 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 28 | 29 | if [ -L "${source}" ]; then 30 | echo "Symlinked..." 31 | source="$(readlink "${source}")" 32 | fi 33 | 34 | # Use filter instead of exclude so missing patterns don't throw errors. 35 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 36 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 37 | 38 | local basename 39 | basename="$(basename -s .framework "$1")" 40 | binary="${destination}/${basename}.framework/${basename}" 41 | if ! [ -r "$binary" ]; then 42 | binary="${destination}/${basename}" 43 | fi 44 | 45 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 46 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 47 | strip_invalid_archs "$binary" 48 | fi 49 | 50 | # Resign the code if required by the build settings to avoid unstable apps 51 | code_sign_if_enabled "${destination}/$(basename "$1")" 52 | 53 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 54 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 55 | local swift_runtime_libs 56 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 57 | for lib in $swift_runtime_libs; do 58 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 59 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 60 | code_sign_if_enabled "${destination}/${lib}" 61 | done 62 | fi 63 | } 64 | 65 | # Copies and strips a vendored dSYM 66 | install_dsym() { 67 | local source="$1" 68 | if [ -r "$source" ]; then 69 | # Copy the dSYM into a the targets temp dir. 70 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 71 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 72 | 73 | local basename 74 | basename="$(basename -s .framework.dSYM "$source")" 75 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 76 | 77 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 78 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 79 | strip_invalid_archs "$binary" 80 | fi 81 | 82 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 83 | # Move the stripped file into its final destination. 84 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 85 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 86 | else 87 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 88 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 89 | fi 90 | fi 91 | } 92 | 93 | # Signs a framework with the provided identity 94 | code_sign_if_enabled() { 95 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 96 | # Use the current code_sign_identitiy 97 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 98 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 99 | 100 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 101 | code_sign_cmd="$code_sign_cmd &" 102 | fi 103 | echo "$code_sign_cmd" 104 | eval "$code_sign_cmd" 105 | fi 106 | } 107 | 108 | # Strip invalid architectures 109 | strip_invalid_archs() { 110 | binary="$1" 111 | # Get architectures for current target binary 112 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 113 | # Intersect them with the architectures we are building for 114 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 115 | # If there are no archs supported by this binary then warn the user 116 | if [[ -z "$intersected_archs" ]]; then 117 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 118 | STRIP_BINARY_RETVAL=0 119 | return 120 | fi 121 | stripped="" 122 | for arch in $binary_archs; do 123 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 124 | # Strip non-valid architectures in-place 125 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 126 | stripped="$stripped $arch" 127 | fi 128 | done 129 | if [[ "$stripped" ]]; then 130 | echo "Stripped $binary of architectures:$stripped" 131 | fi 132 | STRIP_BINARY_RETVAL=1 133 | } 134 | 135 | 136 | if [[ "$CONFIGURATION" == "Debug" ]]; then 137 | install_framework "${BUILT_PRODUCTS_DIR}/MJExtension/MJExtension.framework" 138 | fi 139 | if [[ "$CONFIGURATION" == "Release" ]]; then 140 | install_framework "${BUILT_PRODUCTS_DIR}/MJExtension/MJExtension.framework" 141 | fi 142 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 143 | wait 144 | fi 145 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ZHFilterMenuView/Pods-ZHFilterMenuView-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 14 | 15 | case "${TARGETED_DEVICE_FAMILY}" in 16 | 1,2) 17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 18 | ;; 19 | 1) 20 | TARGET_DEVICE_ARGS="--target-device iphone" 21 | ;; 22 | 2) 23 | TARGET_DEVICE_ARGS="--target-device ipad" 24 | ;; 25 | 3) 26 | TARGET_DEVICE_ARGS="--target-device tv" 27 | ;; 28 | 4) 29 | TARGET_DEVICE_ARGS="--target-device watch" 30 | ;; 31 | *) 32 | TARGET_DEVICE_ARGS="--target-device mac" 33 | ;; 34 | esac 35 | 36 | install_resource() 37 | { 38 | if [[ "$1" = /* ]] ; then 39 | RESOURCE_PATH="$1" 40 | else 41 | RESOURCE_PATH="${PODS_ROOT}/$1" 42 | fi 43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 44 | cat << EOM 45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 46 | EOM 47 | exit 1 48 | fi 49 | case $RESOURCE_PATH in 50 | *.storyboard) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.xib) 55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 57 | ;; 58 | *.framework) 59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 63 | ;; 64 | *.xcdatamodel) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 67 | ;; 68 | *.xcdatamodeld) 69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 71 | ;; 72 | *.xcmappingmodel) 73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 75 | ;; 76 | *.xcassets) 77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 79 | ;; 80 | *) 81 | echo "$RESOURCE_PATH" || true 82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 83 | ;; 84 | esac 85 | } 86 | 87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 92 | fi 93 | rm -f "$RESOURCES_TO_COPY" 94 | 95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 96 | then 97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 99 | while read line; do 100 | if [[ $line != "${PODS_ROOT}*" ]]; then 101 | XCASSET_FILES+=("$line") 102 | fi 103 | done <<<"$OTHER_XCASSETS" 104 | 105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 106 | fi 107 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ZHFilterMenuView/Pods-ZHFilterMenuView-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_ZHFilterMenuViewVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_ZHFilterMenuViewVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ZHFilterMenuView/Pods-ZHFilterMenuView.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension/MJExtension.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "MJExtension" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ZHFilterMenuView/Pods-ZHFilterMenuView.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_ZHFilterMenuView { 2 | umbrella header "Pods-ZHFilterMenuView-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-ZHFilterMenuView/Pods-ZHFilterMenuView.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension/MJExtension.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "MJExtension" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZHFilterMenuView 2 | 一款类似贝壳找房的通用筛选控件,当然此控件不局限用于房屋筛选,也可用于其他类型的筛选,如果有其它项目或功能需求筛选,修改展示数据源即可! 3 | * 提供新房、二手房、租房的完整筛选功能实现 4 | * 支持固定和列表sectionHead悬停下拉筛选两种模式 5 | * 支持单列表、双列表、滑动列表、切换列表、列表输入等多种下拉筛选样式 6 | * 展示样式支持自定义调节 7 | * 展示数据支持自定义设置 8 | * 双列表模式支持快速选择 9 | * 新增支持单双列表切换筛选 10 | ## 预览 11 | #### 顶部固定模式 12 | ![ZHFilterMenuView](/Image/filter.gif) 13 | 14 | ![ZHFilterMenuView](/Image/IMG_4274.PNG) 15 | ![ZHFilterMenuView](/Image/IMG_4276.PNG) 16 | #### 列表悬停下拉筛选模式(新增) 17 | ![ZHFilterMenuView](/Image/filter2.gif) 18 | #### 单双列表切换筛选模式(新增) 19 | ![ZHFilterMenuView](/Image/filter3.gif) 20 | 21 | ## 简书文章 22 | 更多了解可以查看[详细文章](https://www.jianshu.com/p/0f0638eef65f) 23 | ## 调用示例 24 | ``` 25 | FilterDataUtil *dataUtil = [[FilterDataUtil alloc] init]; 26 | self.menuView.filterDataArr = [dataUtil getTabDataByType:self.filterType]; 27 | //开始显示 28 | [self.menuView beginShowMenuView]; 29 | ``` 30 | ``` 31 | /** 确定回调 */ 32 | - (void)menuView:(ZHFilterMenuView *)menuView didSelectConfirmAtSelectedModelArr:(NSArray *)selectedModelArr 33 | { 34 | NSArray *dictArr = [ZHFilterItemModel mj_keyValuesArrayWithObjectArray:selectedModelArr]; 35 | NSLog(@"结果回调:%@",dictArr.mj_JSONString); 36 | } 37 | 38 | /** 警告回调(用于错误提示) */ 39 | - (void)menuView:(ZHFilterMenuView *)menuView wangType:(ZHFilterMenuViewWangType)wangType 40 | { 41 | if (wangType == ZHFilterMenuViewWangTypeInput) { 42 | NSLog(@"请输入正确的价格区间!"); 43 | } 44 | } 45 | 46 | /** 返回每个 tabIndex 下的确定类型 */ 47 | - (ZHFilterMenuConfirmType)menuView:(ZHFilterMenuView *)menuView confirmTypeInTabIndex:(NSInteger)tabIndex 48 | { 49 | if (tabIndex == 4) { 50 | return ZHFilterMenuConfirmTypeSpeedConfirm; 51 | } 52 | return ZHFilterMenuConfirmTypeBottomConfirm; 53 | } 54 | 55 | /** 返回每个 tabIndex 下的下拉展示类型 */ 56 | - (ZHFilterMenuDownType)menuView:(ZHFilterMenuView *)menuView downTypeInTabIndex:(NSInteger)tabIndex 57 | { 58 | if (tabIndex == 0) { 59 | return ZHFilterMenuDownTypeTwoLists; 60 | } else if (tabIndex == 1) { 61 | if (self.filterType == FilterTypeISRent) { 62 | return ZHFilterMenuDownTypeOnlyItem; 63 | } else { 64 | return ZHFilterMenuDownTypeItemInput; 65 | } 66 | } else if (tabIndex == 2) { 67 | if (self.filterType == FilterTypeISRent) { 68 | return ZHFilterMenuDownTypeItemInput; 69 | } else { 70 | return ZHFilterMenuDownTypeOnlyItem; 71 | } 72 | } else if (tabIndex == 3) { 73 | return ZHFilterMenuDownTypeOnlyItem; 74 | } else if (tabIndex == 4) { 75 | return ZHFilterMenuDownTypeOnlyList; 76 | } 77 | return ZHFilterMenuDownTypeOnlyList; 78 | } 79 | 80 | 81 | - (ZHFilterMenuView *)menuView 82 | { 83 | if (!_menuView) { 84 | _menuView = [[ZHFilterMenuView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 45) maxHeight:CGRectGetHeight(self.view.frame) - 45]; 85 | _menuView.zh_delegate = self; 86 | _menuView.zh_dataSource = self; 87 | if (self.filterType == FilterTypeIsNewHouse) { 88 | _menuView.titleArr = @[@"区域",@"价格",@"户型",@"更多",@"排序"]; 89 | _menuView.imageNameArr = @[@"x_arrow",@"x_arrow",@"x_arrow",@"x_arrow",@"x_arrow"]; 90 | } else if (self.filterType == FilterTypeSecondHandHouse) { 91 | _menuView.titleArr = @[@"区域",@"价格",@"房型",@"更多",@"排序"]; 92 | _menuView.imageNameArr = @[@"x_arrow",@"x_arrow",@"x_arrow",@"x_arrow",@"x_arrow"]; 93 | } else if (self.filterType == FilterTypeISRent) { 94 | _menuView.titleArr = @[@"位置",@"方式",@"租金",@"更多",@""]; 95 | _menuView.imageNameArr = @[@"x_arrow",@"x_arrow",@"x_arrow",@"x_arrow",@"x_px"]; 96 | } 97 | [self.view addSubview:_menuView]; 98 | } 99 | return _menuView; 100 | } 101 | ``` 102 | ## 意见建议 103 | 如果感觉此项目对你有帮助,欢迎Star!如果使用过程中遇到问题或者有更好的建议,欢迎在Issues提出! 104 | ## 更新记录 105 | * 2020-4-26 新增支持单双列表切换筛选 106 | * 2020-3-12 双列表模式新增支持快速选择 107 | * 2020-3-10 修复悬停筛选展开后页面退出列表未消失的问题 108 | * 2020-2-20 新增列表悬停下拉筛选解决方案 109 | * 2019-12-25 修复双列表点重置出错的问题,增加预览GIF图,完善菜单图片展示判断 110 | -------------------------------------------------------------------------------- /ZHFilterMenuView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ZHFilterMenuView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ZHFilterMenuView.xcodeproj/xcuserdata/zhouyn.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ZHFilterMenuView.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 2 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ZHFilterMenuView.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ZHFilterMenuView.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ZHFilterMenuView.xcworkspace/xcuserdata/zhouyn.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /ZHFilterMenuView/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/8/21. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /ZHFilterMenuView/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/8/21. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 22 | self.window.backgroundColor = [UIColor whiteColor]; 23 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[ViewController new]]; 24 | self.window.rootViewController = nav; 25 | [self.window makeKeyAndVisible]; 26 | 27 | return YES; 28 | } 29 | 30 | - (void)applicationWillResignActive:(UIApplication *)application { 31 | // 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. 32 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 33 | } 34 | 35 | 36 | - (void)applicationDidEnterBackground:(UIApplication *)application { 37 | // 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. 38 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 39 | } 40 | 41 | 42 | - (void)applicationWillEnterForeground:(UIApplication *)application { 43 | // 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. 44 | } 45 | 46 | 47 | - (void)applicationDidBecomeActive:(UIApplication *)application { 48 | // 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. 49 | } 50 | 51 | 52 | - (void)applicationWillTerminate:(UIApplication *)application { 53 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 54 | } 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "icon-20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "icon-20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "icon-29.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "icon-29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "icon-29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "icon-40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "icon-40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "57x57", 47 | "idiom" : "iphone", 48 | "filename" : "icon-57.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "57x57", 53 | "idiom" : "iphone", 54 | "filename" : "icon-57@2x.png", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "size" : "60x60", 59 | "idiom" : "iphone", 60 | "filename" : "icon-60@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "60x60", 65 | "idiom" : "iphone", 66 | "filename" : "icon-60@3x.png", 67 | "scale" : "3x" 68 | }, 69 | { 70 | "size" : "20x20", 71 | "idiom" : "ipad", 72 | "filename" : "icon-20-ipad.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "20x20", 77 | "idiom" : "ipad", 78 | "filename" : "icon-20@2x-ipad.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "29x29", 83 | "idiom" : "ipad", 84 | "filename" : "icon-29-ipad.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "29x29", 89 | "idiom" : "ipad", 90 | "filename" : "icon-29@2x-ipad.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "40x40", 95 | "idiom" : "ipad", 96 | "filename" : "icon-40.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "40x40", 101 | "idiom" : "ipad", 102 | "filename" : "icon-40@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "50x50", 107 | "idiom" : "ipad", 108 | "filename" : "icon-50.png", 109 | "scale" : "1x" 110 | }, 111 | { 112 | "size" : "50x50", 113 | "idiom" : "ipad", 114 | "filename" : "icon-50@2x.png", 115 | "scale" : "2x" 116 | }, 117 | { 118 | "size" : "72x72", 119 | "idiom" : "ipad", 120 | "filename" : "icon-72.png", 121 | "scale" : "1x" 122 | }, 123 | { 124 | "size" : "72x72", 125 | "idiom" : "ipad", 126 | "filename" : "icon-72@2x.png", 127 | "scale" : "2x" 128 | }, 129 | { 130 | "size" : "76x76", 131 | "idiom" : "ipad", 132 | "filename" : "icon-76.png", 133 | "scale" : "1x" 134 | }, 135 | { 136 | "size" : "76x76", 137 | "idiom" : "ipad", 138 | "filename" : "icon-76@2x.png", 139 | "scale" : "2x" 140 | }, 141 | { 142 | "size" : "83.5x83.5", 143 | "idiom" : "ipad", 144 | "filename" : "icon-83.5@2x.png", 145 | "scale" : "2x" 146 | }, 147 | { 148 | "size" : "1024x1024", 149 | "idiom" : "ios-marketing", 150 | "filename" : "icon-1024.png", 151 | "scale" : "1x" 152 | } 153 | ], 154 | "info" : { 155 | "version" : 1, 156 | "author" : "xcode" 157 | } 158 | } -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-1024.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-29-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-29-ipad.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-29.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-40.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-50.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-50@2x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-57.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-57@2x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-72.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-76.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/x_arrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "x_arrow@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "x_arrow@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/x_arrow.imageset/x_arrow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/x_arrow.imageset/x_arrow@2x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/x_arrow.imageset/x_arrow@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/x_arrow.imageset/x_arrow@3x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/x_px.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "x_px@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "x_px@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/x_px.imageset/x_px@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/x_px.imageset/x_px@2x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/x_px.imageset/x_px@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/x_px.imageset/x_px@3x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/x_syt.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "x_syt@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "x_syt@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/x_syt.imageset/x_syt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/x_syt.imageset/x_syt@2x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/x_syt.imageset/x_syt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/x_syt.imageset/x_syt@3x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/xq_ban.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "xq_ban@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "xq_ban@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/xq_ban.imageset/xq_ban@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/xq_ban.imageset/xq_ban@2x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/Assets.xcassets/xq_ban.imageset/xq_ban@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hi-zhouyn/ZHFilterMenuView/35fb359c6a03c85fa07bf2836b739e68e87df743/ZHFilterMenuView/Assets.xcassets/xq_ban.imageset/xq_ban@3x.png -------------------------------------------------------------------------------- /ZHFilterMenuView/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 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /ZHFilterMenuView/Base.lproj/Main.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 | -------------------------------------------------------------------------------- /ZHFilterMenuView/ChangeViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChangeViewController.h 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2020/4/24. 6 | // Copyright © 2020 Zhou. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface ChangeViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /ZHFilterMenuView/ChangeViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChangeViewController.m 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2020/4/24. 6 | // Copyright © 2020 Zhou. All rights reserved. 7 | // 8 | 9 | #import "ChangeViewController.h" 10 | #import "ZHFilterMenuView.h" 11 | #import "FilterDataUtil.h" 12 | #import "HouseTableViewCell.h" 13 | 14 | @interface ChangeViewController () 15 | @property (nonatomic, strong) ZHFilterMenuView *menuView; 16 | @end 17 | 18 | @implementation ChangeViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | 24 | self.view.backgroundColor = [UIColor whiteColor]; 25 | 26 | //便于演示房源展示数据源暂时是写在本地,可根据自身情况,如果需从接口请求可自行做下调整 27 | FilterDataUtil *dataUtil = [[FilterDataUtil alloc] init]; 28 | self.menuView.filterDataArr = [dataUtil getTabDataByType:FilterTypeISQuery]; 29 | //开始显示 30 | [self.menuView beginShowMenuView]; 31 | } 32 | 33 | 34 | /** 确定回调 */ 35 | - (void)menuView:(ZHFilterMenuView *)menuView didSelectConfirmAtSelectedModelArr:(NSArray *)selectedModelArr 36 | { 37 | NSArray *dictArr = [ZHFilterItemModel mj_keyValuesArrayWithObjectArray:selectedModelArr]; 38 | NSLog(@"结果回调:%@",dictArr.mj_JSONString); 39 | } 40 | 41 | 42 | /** 返回每个 tabIndex 下的确定类型 */ 43 | - (ZHFilterMenuConfirmType)menuView:(ZHFilterMenuView *)menuView confirmTypeInTabIndex:(NSInteger)tabIndex 44 | { 45 | if (tabIndex == 2) { 46 | return ZHFilterMenuConfirmTypeSpeedConfirm; 47 | } 48 | return ZHFilterMenuConfirmTypeBottomConfirm; 49 | } 50 | 51 | /** 返回每个 tabIndex 下的下拉展示类型 */ 52 | - (ZHFilterMenuDownType)menuView:(ZHFilterMenuView *)menuView downTypeInTabIndex:(NSInteger)tabIndex 53 | { 54 | if (tabIndex == 2) { 55 | return ZHFilterMenuDownTypeOnlyList; 56 | } 57 | return ZHFilterMenuDownTypeTwoLists; 58 | } 59 | 60 | 61 | - (ZHFilterMenuView *)menuView 62 | { 63 | if (!_menuView) { 64 | _menuView = [[ZHFilterMenuView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 45) maxHeight:CGRectGetHeight(self.view.frame) - 45]; 65 | _menuView.zh_delegate = self; 66 | _menuView.zh_dataSource = self; 67 | _menuView.titleLeft = YES; 68 | _menuView.lastTitleRight = YES; 69 | _menuView.twoListToOneList = YES; 70 | _menuView.titleArr = @[@"类型",@"状态",@""]; 71 | _menuView.imageNameArr = @[@"x_arrow",@"x_arrow",@"x_px"]; 72 | [self.view addSubview:_menuView]; 73 | } 74 | return _menuView; 75 | } 76 | 77 | 78 | /* 79 | #pragma mark - Navigation 80 | 81 | // In a storyboard-based application, you will often want to do a little preparation before navigation 82 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 83 | // Get the new view controller using [segue destinationViewController]. 84 | // Pass the selected object to the new view controller. 85 | } 86 | */ 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /ZHFilterMenuView/FilterDataUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // FilterDataUtil.h 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/12/19. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MJExtension.h" 11 | 12 | /** 筛选类型 */ 13 | typedef NS_ENUM(NSUInteger, FilterType) { 14 | FilterTypeIsNewHouse = 1, //新房 15 | FilterTypeSecondHandHouse, //二手房 16 | FilterTypeISRent, //租房 17 | FilterTypeISQuery, //合同查询 18 | }; 19 | 20 | typedef NS_ENUM(NSUInteger, FilterDataType) { 21 | FilterDataType_GG_QY, //公共-区域-001 22 | FilterDataType_GG_FJ, //公共-附近-002 23 | FilterDataType_GG_ZJ, //新房-总价-003 24 | FilterDataType_XF_DJ, //新房-单价-004 25 | FilterDataType_GG_HX, //公共-户型-005 26 | FilterDataType_XF_MJ, //新房-面积-006 27 | FilterDataType_GG_LX, //公共-类型-007 28 | FilterDataType_GG_ZT, //公共-状态-008 29 | FilterDataType_XF_SJ, //新房-时间-009 30 | FilterDataType_XF_PX, //新房-排序-010 31 | FilterDataType_ESF_MJ, //二手房-面积-011 32 | FilterDataType_GG_LC, //公共-楼层-012 33 | FilterDataType_ESF_LL, //二手房-楼龄-013 34 | FilterDataType_GG_ZX, //公共-装修-014 35 | FilterDataType_GG_DT, //公共-电梯-015 36 | FilterDataType_GG_YT, //公共-用途-016 37 | FilterDataType_GG_QS, //公共-权属-017 38 | FilterDataType_ESF_PX, //二手房-排序-018 39 | FilterDataType_ZF_ZJ, //租房-租金-019 40 | FilterDataType_ZF_ZZ, //租房-整租-020 41 | FilterDataType_ZF_HZ, //租房-合租-021 42 | FilterDataType_ZF_ZQ, //租房-租期-022 43 | FilterDataType_ZF_PX, //租房-排序-023 44 | FilterDataType_GG_CX, //公共-朝向-024 45 | FilterDataType_CX_LX, //查询-类型-025 46 | FilterDataType_ZL_LX, //租赁-类型-026 47 | FilterDataType_CX_PX, //查询-排序-027 48 | FilterDataType_ZT_LX, //状态-类型-028 49 | FilterDataType_ZT_HT, //状态-合同-028002 50 | FilterDataType_ZT_XY, //状态-协议-028003 51 | FilterDataType_ZT_BA, //状态-备案-028004 52 | FilterDataType_ZT_YD, //状态-异动-028005 53 | }; 54 | //extern NSMutableArray *FilterDataTypeArr(FilterDataType type); 55 | 56 | NS_ASSUME_NONNULL_BEGIN 57 | 58 | @interface FilterDataUtil : NSObject 59 | 60 | /** 根据FilterType类型获取数据 */ 61 | - (NSMutableArray *)getTabDataByType:(FilterType)type; 62 | 63 | @end 64 | 65 | NS_ASSUME_NONNULL_END 66 | -------------------------------------------------------------------------------- /ZHFilterMenuView/FilterDataUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // FilterDataUtil.m 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/12/19. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import "FilterDataUtil.h" 10 | #import "ZHFilterModel.h" 11 | 12 | @interface FilterDataUtil () 13 | @property (nonatomic, strong) NSArray *dictArr; 14 | @end 15 | 16 | @implementation FilterDataUtil 17 | 18 | - (NSMutableArray *)getTabDataByType:(FilterType)type 19 | { 20 | NSMutableArray *dataArr = [NSMutableArray array]; 21 | if (type == FilterTypeIsNewHouse) { 22 | NSMutableArray *areaArr = [NSMutableArray array]; 23 | NSMutableArray *priceArr = [NSMutableArray array]; 24 | NSMutableArray *roomTypeArr = [NSMutableArray array]; 25 | NSMutableArray *moreArr = [NSMutableArray array]; 26 | NSMutableArray *sortArr = [NSMutableArray array]; 27 | ZHFilterModel *areaModel = [ZHFilterModel createFilterModelWithHeadTitle:@"城区" modelArr:[self getDataByType:FilterDataType_GG_QY] selectFirst:YES multiple:NO]; 28 | areaModel.selected = YES; 29 | [areaArr addObject:areaModel]; 30 | [areaArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"附近" modelArr:[self getDataByType:FilterDataType_GG_FJ] selectFirst:YES multiple:NO]]; 31 | 32 | [priceArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"总价(万/套)" modelArr:[self getDataByType:FilterDataType_GG_ZJ] selectFirst:NO multiple:YES]]; 33 | [priceArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"单价(万/㎡)" modelArr:[self getDataByType:FilterDataType_XF_DJ] selectFirst:NO multiple:YES]]; 34 | 35 | [roomTypeArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"户型区间" modelArr:[self getDataByType:FilterDataType_GG_HX] selectFirst:NO multiple:YES]]; 36 | 37 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"面积(㎡)" modelArr:[self getDataByType:FilterDataType_XF_MJ] selectFirst:NO multiple:YES]]; 38 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"类型" modelArr:[self getDataByType:FilterDataType_GG_LX] selectFirst:NO multiple:YES]]; 39 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"售卖状态" modelArr:[self getDataByType:FilterDataType_GG_ZT] selectFirst:NO multiple:YES]]; 40 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"开盘时间" modelArr:[self getDataByType:FilterDataType_XF_SJ] selectFirst:NO multiple:YES]]; 41 | 42 | [sortArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"" modelArr:[self getDataByType:FilterDataType_XF_PX] selectFirst:YES multiple:NO]]; 43 | 44 | [dataArr addObject:areaArr]; 45 | [dataArr addObject:priceArr]; 46 | [dataArr addObject:roomTypeArr]; 47 | [dataArr addObject:moreArr]; 48 | [dataArr addObject:sortArr]; 49 | 50 | } else if (type == FilterTypeSecondHandHouse) { 51 | NSMutableArray *areaArr = [NSMutableArray array]; 52 | NSMutableArray *priceArr = [NSMutableArray array]; 53 | NSMutableArray *roomTypeArr = [NSMutableArray array]; 54 | NSMutableArray *moreArr = [NSMutableArray array]; 55 | NSMutableArray *sortArr = [NSMutableArray array]; 56 | 57 | ZHFilterModel *areaModel = [ZHFilterModel createFilterModelWithHeadTitle:@"城区" modelArr:[self getDataByType:FilterDataType_GG_QY] selectFirst:YES multiple:NO]; 58 | areaModel.selected = YES; 59 | [areaArr addObject:areaModel]; 60 | [areaArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"附近" modelArr:[self getDataByType:FilterDataType_GG_FJ] selectFirst:YES multiple:NO]]; 61 | 62 | [priceArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"价格区间(万)" modelArr:[self getDataByType:FilterDataType_GG_ZJ] selectFirst:NO multiple:YES]]; 63 | 64 | [roomTypeArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"房型选择" modelArr:[self getDataByType:FilterDataType_GG_HX] selectFirst:NO multiple:YES]]; 65 | 66 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"面积(㎡)" modelArr:[self getDataByType:FilterDataType_ESF_MJ] selectFirst:NO multiple:YES]]; 67 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"朝向" modelArr:[self getDataByType:FilterDataType_GG_CX] selectFirst:NO multiple:YES]]; 68 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"楼层" modelArr:[self getDataByType:FilterDataType_GG_LC] selectFirst:NO multiple:YES]]; 69 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"楼龄" modelArr:[self getDataByType:FilterDataType_ESF_LL] selectFirst:NO multiple:YES]]; 70 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"装修" modelArr:[self getDataByType:FilterDataType_GG_ZX] selectFirst:NO multiple:YES]]; 71 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"电梯" modelArr:[self getDataByType:FilterDataType_GG_DT] selectFirst:NO multiple:YES]]; 72 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"用途" modelArr:[self getDataByType:FilterDataType_GG_YT] selectFirst:NO multiple:YES]]; 73 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"权属" modelArr:[self getDataByType:FilterDataType_GG_QS] selectFirst:NO multiple:YES]]; 74 | 75 | [sortArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"" modelArr:[self getDataByType:FilterDataType_ESF_PX] selectFirst:YES multiple:NO]]; 76 | 77 | [dataArr addObject:areaArr]; 78 | [dataArr addObject:priceArr]; 79 | [dataArr addObject:roomTypeArr]; 80 | [dataArr addObject:moreArr]; 81 | [dataArr addObject:sortArr]; 82 | 83 | } else if (type == FilterTypeISRent) { 84 | NSMutableArray *areaArr = [NSMutableArray array]; 85 | NSMutableArray *roomTypeArr = [NSMutableArray array]; 86 | NSMutableArray *priceArr = [NSMutableArray array]; 87 | NSMutableArray *moreArr = [NSMutableArray array]; 88 | NSMutableArray *sortArr = [NSMutableArray array]; 89 | 90 | ZHFilterModel *areaModel = [ZHFilterModel createFilterModelWithHeadTitle:@"城区" modelArr:[self getDataByType:FilterDataType_GG_QY] selectFirst:YES multiple:NO]; 91 | areaModel.selected = YES; 92 | [areaArr addObject:areaModel]; 93 | [areaArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"附近" modelArr:[self getDataByType:FilterDataType_GG_FJ] selectFirst:YES multiple:NO]]; 94 | 95 | [roomTypeArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"整租" modelArr:[self getDataByType:FilterDataType_ZF_ZZ] selectFirst:NO multiple:YES]]; 96 | [roomTypeArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"合租" modelArr:[self getDataByType:FilterDataType_ZF_HZ] selectFirst:NO multiple:YES]]; 97 | 98 | [priceArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"租金" modelArr:[self getDataByType:FilterDataType_ZF_ZJ] selectFirst:NO multiple:NO]]; 99 | 100 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"朝向" modelArr:[self getDataByType:FilterDataType_GG_CX] selectFirst:NO multiple:YES]]; 101 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"租期" modelArr:[self getDataByType:FilterDataType_ZF_ZQ] selectFirst:NO multiple:YES]]; 102 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"电梯" modelArr:[self getDataByType:FilterDataType_GG_DT] selectFirst:NO multiple:YES]]; 103 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"楼层" modelArr:[self getDataByType:FilterDataType_GG_LC] selectFirst:NO multiple:YES]]; 104 | [moreArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"装修" modelArr:[self getDataByType:FilterDataType_GG_ZX] selectFirst:NO multiple:YES]]; 105 | 106 | [sortArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"" modelArr:[self getDataByType:FilterDataType_ZF_PX] selectFirst:YES multiple:NO]]; 107 | 108 | [dataArr addObject:areaArr]; 109 | [dataArr addObject:roomTypeArr]; 110 | [dataArr addObject:priceArr]; 111 | [dataArr addObject:moreArr]; 112 | [dataArr addObject:sortArr]; 113 | } else if (type == FilterTypeISQuery) { 114 | NSMutableArray *typeArr = [NSMutableArray array]; 115 | NSMutableArray *statusArr = [NSMutableArray array]; 116 | NSMutableArray *sortArr = [NSMutableArray array]; 117 | 118 | NSArray *itemArr = [self getDataByType:FilterDataType_CX_LX]; 119 | for (int i = 0; i < itemArr.count; i ++) { 120 | ZHFilterItemModel *model = itemArr[i]; 121 | if ([model.name containsString:@"不限"]) { 122 | ZHFilterModel *typeModel = [ZHFilterModel createFilterModelWithHeadTitle:model.name code:model.code modelArr:@[] selectFirst:YES multiple:NO]; 123 | typeModel.selected = YES; 124 | [typeArr addObject:typeModel]; 125 | } else if ([model.name containsString:@"住房租赁"]) { 126 | [typeArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:model.name code:model.code modelArr:[self getDataByType:FilterDataType_ZL_LX] selectFirst:YES multiple:NO]]; 127 | } else { 128 | [typeArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:model.name code:model.code modelArr:@[] selectFirst:NO multiple:NO]]; 129 | } 130 | } 131 | 132 | NSArray *staIitemArr = [self getDataByType:FilterDataType_ZT_LX]; 133 | for (int i = 0; i < staIitemArr.count; i ++) { 134 | ZHFilterItemModel *model = staIitemArr[i]; 135 | if ([model.name containsString:@"不限"]) { 136 | ZHFilterModel *typeModel = [ZHFilterModel createFilterModelWithHeadTitle:model.name code:model.code modelArr:@[] selectFirst:YES multiple:NO]; 137 | typeModel.selected = YES; 138 | [statusArr addObject:typeModel]; 139 | } else if ([model.name containsString:@"合同状态"]) { 140 | [statusArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:model.name code:model.code modelArr:[self getDataByType:FilterDataType_ZT_HT] selectFirst:YES multiple:NO]]; 141 | } else if ([model.name containsString:@"协议状态"]) { 142 | [statusArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:model.name code:model.code modelArr:[self getDataByType:FilterDataType_ZT_XY] selectFirst:YES multiple:NO]]; 143 | } else if ([model.name containsString:@"备案状态"]) { 144 | [statusArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:model.name code:model.code modelArr:[self getDataByType:FilterDataType_ZT_BA] selectFirst:YES multiple:NO]]; 145 | } else if ([model.name containsString:@"异动状态"]) { 146 | [statusArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:model.name code:model.code modelArr:[self getDataByType:FilterDataType_ZT_YD] selectFirst:YES multiple:NO]]; 147 | } 148 | } 149 | 150 | [sortArr addObject:[ZHFilterModel createFilterModelWithHeadTitle:@"" modelArr:[self getDataByType:FilterDataType_CX_PX] selectFirst:YES multiple:NO]]; 151 | 152 | [dataArr addObject:typeArr]; 153 | [dataArr addObject:statusArr]; 154 | [dataArr addObject:sortArr]; 155 | } 156 | 157 | return dataArr; 158 | } 159 | 160 | 161 | - (NSArray *)getDataByType:(FilterDataType)type 162 | { 163 | NSMutableArray *infoArr = [NSMutableArray array]; 164 | NSString *key = [NSString string]; 165 | if (type == FilterDataType_GG_QY) { 166 | key = @"001"; 167 | }else if (type == FilterDataType_GG_FJ){ 168 | key = @"002"; 169 | }else if (type == FilterDataType_GG_ZJ){ 170 | key = @"003"; 171 | }else if (type == FilterDataType_XF_DJ){ 172 | key = @"004"; 173 | }else if (type == FilterDataType_GG_HX){ 174 | key = @"005"; 175 | }else if (type == FilterDataType_XF_MJ){ 176 | key = @"006"; 177 | }else if (type == FilterDataType_GG_LX){ 178 | key = @"007"; 179 | }else if (type == FilterDataType_GG_ZT){ 180 | key = @"008"; 181 | }else if (type == FilterDataType_XF_SJ){ 182 | key = @"009"; 183 | }else if (type == FilterDataType_XF_PX){ 184 | key = @"010"; 185 | }else if (type == FilterDataType_ESF_MJ){ 186 | key = @"011"; 187 | }else if (type == FilterDataType_GG_LC){ 188 | key = @"012"; 189 | }else if (type == FilterDataType_ESF_LL){ 190 | key = @"013"; 191 | }else if (type == FilterDataType_GG_ZX){ 192 | key = @"014"; 193 | }else if (type == FilterDataType_GG_DT){ 194 | key = @"015"; 195 | }else if (type == FilterDataType_GG_YT){ 196 | key = @"016"; 197 | }else if (type == FilterDataType_GG_QS){ 198 | key = @"017"; 199 | }else if (type == FilterDataType_ESF_PX){ 200 | key = @"018"; 201 | }else if (type == FilterDataType_ZF_ZJ){ 202 | key = @"019"; 203 | }else if (type == FilterDataType_ZF_ZZ){ 204 | key = @"020"; 205 | }else if (type == FilterDataType_ZF_HZ){ 206 | key = @"021"; 207 | }else if (type == FilterDataType_ZF_ZQ){ 208 | key = @"022"; 209 | }else if (type == FilterDataType_ZF_PX){ 210 | key = @"023"; 211 | }else if (type == FilterDataType_GG_CX){ 212 | key = @"024"; 213 | }else if (type == FilterDataType_CX_LX){ 214 | key = @"025"; 215 | }else if (type == FilterDataType_ZL_LX){ 216 | key = @"026"; 217 | }else if (type == FilterDataType_CX_PX){ 218 | key = @"027"; 219 | }else if (type == FilterDataType_ZT_LX){ 220 | key = @"028"; 221 | }else if (type == FilterDataType_ZT_HT){ 222 | key = @"028002"; 223 | }else if (type == FilterDataType_ZT_XY){ 224 | key = @"028003"; 225 | }else if (type == FilterDataType_ZT_BA){ 226 | key = @"028004"; 227 | }else if (type == FilterDataType_ZT_YD){ 228 | key = @"028005"; 229 | } 230 | for (ZHFilterItemModel *model in self.dictArr) { 231 | if ([model.parentCode isEqualToString:key]) { 232 | [infoArr addObject:model]; 233 | } 234 | } 235 | return infoArr; 236 | } 237 | 238 | 239 | - (NSArray *)dictArr 240 | { 241 | if (!_dictArr) { 242 | NSString *strPath = [[NSBundle mainBundle] pathForResource:@"FilterData" ofType:@"geojson"]; 243 | NSString *dataJson = [[NSString alloc] initWithContentsOfFile:strPath encoding:NSUTF8StringEncoding error:nil]; 244 | _dictArr = [ZHFilterItemModel mj_objectArrayWithKeyValuesArray:dataJson]; 245 | } 246 | return _dictArr; 247 | } 248 | 249 | 250 | @end 251 | -------------------------------------------------------------------------------- /ZHFilterMenuView/FilterViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FilterViewController.h 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/12/11. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "FilterDataUtil.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface FilterViewController : UIViewController 15 | @property (nonatomic, assign) FilterType filterType; 16 | @property (nonatomic, assign) BOOL twoListCanSpeedSelect; 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /ZHFilterMenuView/FilterViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // FilterViewController.m 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/12/11. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import "FilterViewController.h" 10 | #import "ZHFilterMenuView.h" 11 | #import "FilterDataUtil.h" 12 | 13 | @interface FilterViewController () 14 | @property (nonatomic, strong) NSMutableArray *dataArr; 15 | @property (nonatomic, strong) ZHFilterMenuView *menuView; 16 | @end 17 | 18 | @implementation FilterViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | self.view.backgroundColor = [UIColor whiteColor]; 24 | 25 | } 26 | 27 | - (void)viewWillAppear:(BOOL)animated 28 | { 29 | [super viewWillAppear:animated]; 30 | //便于演示房源展示数据源暂时是写在本地,可根据自身情况,如果需从接口请求可自行做下调整 31 | FilterDataUtil *dataUtil = [[FilterDataUtil alloc] init]; 32 | self.menuView.filterDataArr = [dataUtil getTabDataByType:self.filterType]; 33 | //开始显示 34 | [self.menuView beginShowMenuView]; 35 | } 36 | 37 | /** 确定回调 */ 38 | - (void)menuView:(ZHFilterMenuView *)menuView didSelectConfirmAtSelectedModelArr:(NSArray *)selectedModelArr 39 | { 40 | NSArray *dictArr = [ZHFilterItemModel mj_keyValuesArrayWithObjectArray:selectedModelArr]; 41 | NSLog(@"结果回调:%@",dictArr.mj_JSONString); 42 | } 43 | 44 | /** 警告回调(用于错误提示) */ 45 | - (void)menuView:(ZHFilterMenuView *)menuView wangType:(ZHFilterMenuViewWangType)wangType 46 | { 47 | if (wangType == ZHFilterMenuViewWangTypeInput) { 48 | NSLog(@"请输入正确的价格区间!"); 49 | } 50 | } 51 | 52 | /** 返回每个 tabIndex 下的确定类型 */ 53 | - (ZHFilterMenuConfirmType)menuView:(ZHFilterMenuView *)menuView confirmTypeInTabIndex:(NSInteger)tabIndex 54 | { 55 | if (tabIndex == 4) { 56 | return ZHFilterMenuConfirmTypeSpeedConfirm; 57 | } 58 | if (tabIndex == 0 && self.twoListCanSpeedSelect) { 59 | return ZHFilterMenuConfirmTypeSpeedConfirm; 60 | } 61 | return ZHFilterMenuConfirmTypeBottomConfirm; 62 | } 63 | 64 | /** 返回每个 tabIndex 下的下拉展示类型 */ 65 | - (ZHFilterMenuDownType)menuView:(ZHFilterMenuView *)menuView downTypeInTabIndex:(NSInteger)tabIndex 66 | { 67 | if (tabIndex == 0) { 68 | return ZHFilterMenuDownTypeTwoLists; 69 | } else if (tabIndex == 1) { 70 | if (self.filterType == FilterTypeISRent) { 71 | return ZHFilterMenuDownTypeOnlyItem; 72 | } else { 73 | return ZHFilterMenuDownTypeItemInput; 74 | } 75 | } else if (tabIndex == 2) { 76 | if (self.filterType == FilterTypeISRent) { 77 | return ZHFilterMenuDownTypeItemInput; 78 | } else { 79 | return ZHFilterMenuDownTypeOnlyItem; 80 | } 81 | } else if (tabIndex == 3) { 82 | return ZHFilterMenuDownTypeOnlyItem; 83 | } else if (tabIndex == 4) { 84 | return ZHFilterMenuDownTypeOnlyList; 85 | } 86 | return ZHFilterMenuDownTypeOnlyList; 87 | } 88 | 89 | 90 | - (ZHFilterMenuView *)menuView 91 | { 92 | if (!_menuView) { 93 | _menuView = [[ZHFilterMenuView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 45) maxHeight:CGRectGetHeight(self.view.frame) - 45 - 64]; 94 | _menuView.zh_delegate = self; 95 | _menuView.zh_dataSource = self; 96 | if (self.filterType == FilterTypeIsNewHouse) { 97 | _menuView.titleArr = @[@"区域",@"价格",@"户型",@"更多",@"排序"]; 98 | _menuView.imageNameArr = @[@"x_arrow",@"x_arrow",@"x_arrow",@"x_arrow",@"x_arrow"]; 99 | } else if (self.filterType == FilterTypeSecondHandHouse) { 100 | _menuView.titleArr = @[@"区域",@"价格",@"房型",@"更多",@"排序"]; 101 | _menuView.imageNameArr = @[@"x_arrow",@"x_arrow",@"x_arrow",@"x_arrow",@"x_arrow"]; 102 | } else if (self.filterType == FilterTypeISRent) { 103 | _menuView.titleArr = @[@"位置",@"方式",@"租金",@"更多",@""]; 104 | _menuView.imageNameArr = @[@"x_arrow",@"x_arrow",@"x_arrow",@"x_arrow",@"x_px"]; 105 | } 106 | [self.view addSubview:_menuView]; 107 | } 108 | return _menuView; 109 | } 110 | 111 | /* 112 | #pragma mark - Navigation 113 | 114 | // In a storyboard-based application, you will often want to do a little preparation before navigation 115 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 116 | // Get the new view controller using [segue destinationViewController]. 117 | // Pass the selected object to the new view controller. 118 | } 119 | */ 120 | 121 | @end 122 | -------------------------------------------------------------------------------- /ZHFilterMenuView/HeadFilterViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HeadFilterViewController.h 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2020/2/11. 6 | // Copyright © 2020 Zhou. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface HeadFilterViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /ZHFilterMenuView/HeadFilterViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HeadFilterViewController.m 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2020/2/11. 6 | // Copyright © 2020 Zhou. All rights reserved. 7 | // 8 | 9 | #import "HeadFilterViewController.h" 10 | #import "ZHFilterMenuView.h" 11 | #import "FilterDataUtil.h" 12 | #import "HouseTableViewCell.h" 13 | 14 | #define KStatusBarHeight [UIApplication sharedApplication].statusBarFrame.size.height 15 | #define KNavBarHeight self.navigationController.navigationBar.frame.size.height 16 | #define KNavbarAndStatusHieght (KStatusBarHeight+KNavBarHeight) 17 | #define KHeadImageViewHeight 250 18 | #define KMenuViewHeight 45 19 | 20 | @interface HeadFilterViewController () 21 | 26 | @property (nonatomic, strong) NSMutableArray *dataArr; 27 | @property (nonatomic, strong) ZHFilterMenuView *menuView; 28 | @property (nonatomic, strong) UITableView *tableView; 29 | @property (nonatomic, strong) UIImageView *headImageView; 30 | @end 31 | 32 | @implementation HeadFilterViewController 33 | 34 | - (void)viewDidLoad { 35 | [super viewDidLoad]; 36 | // Do any additional setup after loading the view. 37 | 38 | self.view.backgroundColor = [UIColor whiteColor]; 39 | [self tableView]; 40 | //便于演示房源展示数据源暂时是写在本地,可根据自身情况,如果需从接口请求可自行做下调整 41 | FilterDataUtil *dataUtil = [[FilterDataUtil alloc] init]; 42 | self.menuView.filterDataArr = [dataUtil getTabDataByType:FilterTypeIsNewHouse]; 43 | //开始显示 44 | [self.menuView beginShowMenuView]; 45 | } 46 | 47 | //视图将要消失时需要手动移除视图 48 | - (void)viewWillDisappear:(BOOL)animated 49 | { 50 | [super viewWillDisappear:animated]; 51 | [self.menuView removeMenuList]; 52 | } 53 | 54 | //下拉菜单展示时禁止点击状态栏回到顶部,避免滑动后下拉框未消失的情况(贝壳找房是存在这样的问题) 55 | - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView 56 | { 57 | return !self.menuView.isOpen; 58 | } 59 | 60 | /** 确定回调 */ 61 | - (void)menuView:(ZHFilterMenuView *)menuView didSelectConfirmAtSelectedModelArr:(NSArray *)selectedModelArr 62 | { 63 | NSArray *dictArr = [ZHFilterItemModel mj_keyValuesArrayWithObjectArray:selectedModelArr]; 64 | NSLog(@"结果回调:%@",dictArr.mj_JSONString); 65 | } 66 | 67 | /** 警告回调(用于错误提示) */ 68 | - (void)menuView:(ZHFilterMenuView *)menuView wangType:(ZHFilterMenuViewWangType)wangType 69 | { 70 | if (wangType == ZHFilterMenuViewWangTypeInput) { 71 | NSLog(@"请输入正确的价格区间!"); 72 | } 73 | } 74 | 75 | /** 点击菜单回调 */ 76 | - (void)menuView:(ZHFilterMenuView *)menuView selectMenuAtTabIndex:(NSInteger)tabIndex 77 | { 78 | if (self.tableView.contentOffset.y < KHeadImageViewHeight) { 79 | //设置等待时间,等区头悬停后再允许下拉框展示 80 | menuView.waitTime = 0.2f; 81 | [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:YES scrollPosition:(UITableViewScrollPositionTop)]; 82 | } else { 83 | menuView.waitTime = 0.f; 84 | } 85 | } 86 | 87 | /** 返回每个 tabIndex 下的确定类型 */ 88 | - (ZHFilterMenuConfirmType)menuView:(ZHFilterMenuView *)menuView confirmTypeInTabIndex:(NSInteger)tabIndex 89 | { 90 | if (tabIndex == 4) { 91 | return ZHFilterMenuConfirmTypeSpeedConfirm; 92 | } 93 | return ZHFilterMenuConfirmTypeBottomConfirm; 94 | } 95 | 96 | /** 返回每个 tabIndex 下的下拉展示类型 */ 97 | - (ZHFilterMenuDownType)menuView:(ZHFilterMenuView *)menuView downTypeInTabIndex:(NSInteger)tabIndex 98 | { 99 | if (tabIndex == 0) { 100 | return ZHFilterMenuDownTypeTwoLists; 101 | } else if (tabIndex == 1) { 102 | return ZHFilterMenuDownTypeItemInput; 103 | } else if (tabIndex == 2) { 104 | return ZHFilterMenuDownTypeOnlyItem; 105 | } else if (tabIndex == 3) { 106 | return ZHFilterMenuDownTypeOnlyItem; 107 | } else if (tabIndex == 4) { 108 | return ZHFilterMenuDownTypeOnlyList; 109 | } 110 | return ZHFilterMenuDownTypeOnlyList; 111 | } 112 | 113 | 114 | #pragma mark - tableview 115 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 116 | { 117 | return 1; 118 | } 119 | 120 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 121 | { 122 | return 25; 123 | } 124 | 125 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 126 | { 127 | HouseTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([HouseTableViewCell class])]; 128 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 129 | return cell; 130 | } 131 | 132 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section 133 | { 134 | return self.menuView; 135 | } 136 | 137 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 138 | { 139 | return KMenuViewHeight; 140 | } 141 | 142 | - (ZHFilterMenuView *)menuView 143 | { 144 | if (!_menuView) { 145 | _menuView = [[ZHFilterMenuView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), KMenuViewHeight) maxHeight:CGRectGetHeight(self.view.frame) - KMenuViewHeight - KNavbarAndStatusHieght]; 146 | _menuView.zh_delegate = self; 147 | _menuView.zh_dataSource = self; 148 | //下拉列表展示在window上,以应对列表视图展示的问题 149 | _menuView.showInWindow = YES; 150 | //移动后的menuView坐标转换在window上的minY值,showInWindow为YES时有效 151 | _menuView.inWindowMinY = KNavbarAndStatusHieght; 152 | _menuView.titleArr = @[@"区域",@"价格",@"户型",@"更多",@"排序"]; 153 | _menuView.imageNameArr = @[@"x_arrow",@"x_arrow",@"x_arrow",@"x_arrow",@"x_arrow"]; 154 | } 155 | return _menuView; 156 | } 157 | 158 | - (UITableView *)tableView 159 | { 160 | if (!_tableView) { 161 | _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) - KNavbarAndStatusHieght) style:UITableViewStylePlain]; 162 | _tableView.rowHeight = 115; 163 | _tableView.delegate = self; 164 | _tableView.dataSource = self; 165 | _headImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"xq_ban"]]; 166 | _headImageView.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.frame), KHeadImageViewHeight); 167 | _tableView.tableHeaderView = _headImageView; 168 | [_tableView registerNib:[UINib nibWithNibName:NSStringFromClass([HouseTableViewCell class]) bundle:nil] forCellReuseIdentifier:NSStringFromClass([HouseTableViewCell class])]; 169 | [self.view addSubview:_tableView]; 170 | } 171 | return _tableView; 172 | } 173 | 174 | /* 175 | #pragma mark - Navigation 176 | 177 | // In a storyboard-based application, you will often want to do a little preparation before navigation 178 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 179 | // Get the new view controller using [segue destinationViewController]. 180 | // Pass the selected object to the new view controller. 181 | } 182 | */ 183 | 184 | @end 185 | -------------------------------------------------------------------------------- /ZHFilterMenuView/HouseTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // HouseTableViewCell.h 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2020/2/20. 6 | // Copyright © 2020 Zhou. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface HouseTableViewCell : UITableViewCell 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /ZHFilterMenuView/HouseTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // HouseTableViewCell.m 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2020/2/20. 6 | // Copyright © 2020 Zhou. All rights reserved. 7 | // 8 | 9 | #import "HouseTableViewCell.h" 10 | 11 | @implementation HouseTableViewCell 12 | 13 | - (void)awakeFromNib { 14 | [super awakeFromNib]; 15 | // Initialization code 16 | } 17 | 18 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 19 | [super setSelected:selected animated:animated]; 20 | 21 | // Configure the view for the selected state 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ZHFilterMenuView/HouseTableViewCell.xib: -------------------------------------------------------------------------------- 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 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 57 | 63 | 69 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /ZHFilterMenuView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | ZHFilterMenuView 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 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UIUserInterfaceStyle 47 | Light 48 | 49 | 50 | -------------------------------------------------------------------------------- /ZHFilterMenuView/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/8/21. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ZHFilterMenuView/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/8/21. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "ZHFilterTitleTableViewCell.h" 11 | #import "FilterViewController.h" 12 | #import "HeadFilterViewController.h" 13 | #import "ChangeViewController.h" 14 | 15 | @interface ViewController () 16 | @property (nonatomic, strong) UITableView *tableView; 17 | @property (nonatomic, strong) NSArray *titleArr; 18 | @end 19 | 20 | @implementation ViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | // Do any additional setup after loading the view. 25 | self.navigationItem.title = @"ZHFilterMenuView"; 26 | self.titleArr = @[@"新房筛选",@"二手房筛选",@"租房筛选",@"悬停下拉筛选",@"双列表快速选择筛选",@"单双列表切换筛选"]; 27 | [self.tableView reloadData]; 28 | } 29 | 30 | - (void)viewWillAppear:(BOOL)animated 31 | { 32 | [super viewWillAppear:animated]; 33 | [self.navigationController.navigationBar setTranslucent:NO]; 34 | } 35 | 36 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 37 | { 38 | return 1; 39 | } 40 | 41 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 42 | { 43 | return self.titleArr.count; 44 | } 45 | 46 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 47 | { 48 | ZHFilterTitleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([ZHFilterTitleTableViewCell class])]; 49 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 50 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 51 | cell.titleLabel.text = self.titleArr[indexPath.row]; 52 | return cell; 53 | } 54 | 55 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 56 | { 57 | if (indexPath.row == self.titleArr.count - 2) { 58 | FilterViewController *filterVC = [[FilterViewController alloc] init]; 59 | filterVC.filterType = FilterTypeISRent; 60 | filterVC.twoListCanSpeedSelect = YES; 61 | [self.navigationController pushViewController:filterVC animated:YES]; 62 | }else if (indexPath.row == self.titleArr.count - 1) { 63 | ChangeViewController *filterVC = [[ChangeViewController alloc] init]; 64 | [self.navigationController pushViewController:filterVC animated:YES]; 65 | } else if (indexPath.row == self.titleArr.count - 3) { 66 | HeadFilterViewController *headVC = [[HeadFilterViewController alloc] init]; 67 | [self.navigationController pushViewController:headVC animated:YES]; 68 | } else { 69 | FilterViewController *filterVC = [[FilterViewController alloc] init]; 70 | filterVC.filterType = indexPath.row + 1; 71 | [self.navigationController pushViewController:filterVC animated:YES]; 72 | } 73 | } 74 | 75 | - (UITableView *)tableView{ 76 | if (!_tableView) { 77 | _tableView = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain]; 78 | _tableView.delegate = self; 79 | _tableView.dataSource = self; 80 | _tableView.rowHeight = 45; 81 | _tableView.backgroundColor = [UIColor whiteColor]; 82 | [_tableView registerClass:[ZHFilterTitleTableViewCell class] forCellReuseIdentifier:NSStringFromClass([ZHFilterTitleTableViewCell class])]; 83 | _tableView.tableFooterView = [UIView new]; 84 | [self.view addSubview:_tableView]; 85 | } 86 | return _tableView; 87 | } 88 | 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /ZHFilterMenuView/ZHFilterMenuView/ZHFilterItemModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZHFilterItemModel.h 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/12/6. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface ZHFilterItemModel : NSObject 15 | 16 | @property (nonatomic, assign) BOOL selected; //标记选择状态 17 | @property (nonatomic, copy) NSString *name; //名称 18 | @property (nonatomic, copy) NSString *code; //code 19 | @property (nonatomic, copy) NSString *parentCode; //父类code 20 | @property (nonatomic, copy) NSString *minPrice; //输入框最低值 21 | @property (nonatomic, copy) NSString *maxPrice; //输入框最大值 22 | 23 | @property (nonatomic, strong) NSMutableArray *itemArr; //item数据(暂时无用,后续拓展多层级展示时可使用) 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /ZHFilterMenuView/ZHFilterMenuView/ZHFilterItemModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZHFilterItemModel.m 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/12/6. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import "ZHFilterItemModel.h" 10 | 11 | @implementation ZHFilterItemModel 12 | 13 | //归档 14 | - (void)encodeWithCoder:(NSCoder *)aCoder{ 15 | unsigned int count; 16 | Ivar *ivar = class_copyIvarList([self class], &count); 17 | for (int i=0; i 10 | #import "ZHFilterModel.h" 11 | 12 | typedef NS_ENUM(NSUInteger, ZHFilterItemType) { 13 | ZHFilterItemTypeOnlyItem, //可点item 14 | ZHFilterItemTypeItemInput, //可点item加输入框 15 | }; 16 | 17 | @interface ZHFilterItemManger : NSObject 18 | @property (nonatomic, strong) UIColor * _Nullable titleColor; 19 | @property (nonatomic, strong) UIColor * _Nullable titleSelectedColor; 20 | @property (nonatomic, strong) UIColor * _Nullable itemBGColor; 21 | @property (nonatomic, strong) UIColor * _Nullable itemBGSelectedColor; 22 | @property (nonatomic, assign) CGFloat itemTitleFontSize; 23 | @property (nonatomic, assign) CGFloat width; 24 | @property (nonatomic, assign) CGFloat space;//item间隔(默认15) 25 | @property (nonatomic, assign) CGFloat itemHeight;//item高(默认30) 26 | @property (nonatomic, assign) NSInteger lineNum;//一行展示数量(默认4,当内容字符数大于7时lineNum = 2) 27 | @property (nonatomic, assign) NSInteger maxLength;//输入框最大文本数量(默认7) 28 | @end 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | @interface ZHFilterItemTableViewCell : UITableViewCell 33 | @property (nonatomic, assign) ZHFilterItemType itemType; 34 | @property (nonatomic, strong) ZHFilterItemManger *itemManager; 35 | @property (nonatomic, strong) NSArray *modelArr; 36 | @property (nonatomic, strong) ZHFilterModel *filterModel; 37 | 38 | @end 39 | 40 | NS_ASSUME_NONNULL_END 41 | -------------------------------------------------------------------------------- /ZHFilterMenuView/ZHFilterMenuView/ZHFilterItemTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZHFilterItemTableViewCell.m 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/12/10. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import "ZHFilterItemTableViewCell.h" 10 | 11 | @implementation ZHFilterItemManger 12 | 13 | 14 | @end 15 | 16 | @interface ZHFilterItemTableViewCell () 17 | @property (strong, nonatomic) IBOutlet UILabel *titleLabel; 18 | @property (strong, nonatomic) IBOutlet UIButton *leftButton; 19 | @property (strong, nonatomic) IBOutlet UIButton *rightButton; 20 | @property (strong, nonatomic) IBOutlet UIView *itemView; 21 | @property (strong, nonatomic) IBOutlet UITextField *minTextField; 22 | @property (strong, nonatomic) IBOutlet UITextField *maxTextField; 23 | @property (strong, nonatomic) IBOutlet UIView *bottomView; 24 | @property (strong, nonatomic) IBOutlet NSLayoutConstraint *bottomViewBottom; 25 | 26 | @property (nonatomic, strong) NSMutableArray *buttonArr; 27 | @property (nonatomic, assign) NSInteger maxCount; 28 | @end 29 | 30 | @implementation ZHFilterItemTableViewCell 31 | 32 | - (void)awakeFromNib { 33 | [super awakeFromNib]; 34 | // Initialization code 35 | self.buttonArr = [NSMutableArray array]; 36 | self.selectionStyle = UITableViewCellSelectionStyleNone; 37 | [self.minTextField addTarget:self action:@selector(textFieldChanged:) forControlEvents:UIControlEventEditingChanged]; 38 | [self.maxTextField addTarget:self action:@selector(textFieldChanged:) forControlEvents:UIControlEventEditingChanged]; 39 | } 40 | 41 | - (void)setItemType:(ZHFilterItemType)itemType 42 | { 43 | _itemType = itemType; 44 | if (itemType == ZHFilterItemTypeOnlyItem) { 45 | self.titleLabel.hidden = NO; 46 | self.leftButton.hidden = YES; 47 | self.rightButton.hidden = YES; 48 | self.bottomView.hidden = YES; 49 | self.bottomViewBottom.constant = 0; 50 | } else if (itemType == ZHFilterItemTypeItemInput) { 51 | self.titleLabel.hidden = YES; 52 | self.leftButton.hidden = NO; 53 | self.rightButton.hidden = NO; 54 | self.bottomView.hidden = NO; 55 | self.bottomViewBottom.constant = 90; 56 | } 57 | } 58 | 59 | - (void)setItemManager:(ZHFilterItemManger *)itemManager 60 | { 61 | _itemManager = itemManager; 62 | [self.leftButton setTitleColor:[itemManager.titleColor colorWithAlphaComponent:0.6] forState:UIControlStateNormal]; 63 | [self.leftButton setTitleColor:itemManager.titleSelectedColor forState:UIControlStateSelected]; 64 | [self.rightButton setTitleColor:[itemManager.titleColor colorWithAlphaComponent:0.6] forState:UIControlStateNormal]; 65 | [self.rightButton setTitleColor:itemManager.titleSelectedColor forState:UIControlStateSelected]; 66 | } 67 | 68 | - (void)setModelArr:(NSArray *)modelArr 69 | { 70 | _modelArr = modelArr; 71 | for (ZHFilterModel *filterModel in modelArr) { 72 | if (filterModel.itemArr.count > self.maxCount) { 73 | self.maxCount = filterModel.itemArr.count; 74 | } 75 | } 76 | if (self.itemType == ZHFilterItemTypeItemInput) { 77 | if (modelArr.count > 1) { 78 | [self.leftButton setTitle:[[modelArr firstObject] title] forState:UIControlStateNormal]; 79 | [self.rightButton setTitle:[[modelArr lastObject] title] forState:UIControlStateNormal]; 80 | if (!self.leftButton.selected && !self.rightButton.selected) { 81 | self.leftButton.selected = YES; 82 | self.filterModel = [modelArr firstObject]; 83 | self.filterModel.selected = YES; 84 | } 85 | } else { 86 | self.titleLabel.hidden = NO; 87 | self.leftButton.hidden = YES; 88 | self.rightButton.hidden = YES; 89 | } 90 | } 91 | } 92 | 93 | - (void)setFilterModel:(ZHFilterModel *)filterModel 94 | { 95 | _filterModel = filterModel; 96 | if (self.itemType == ZHFilterItemTypeItemInput) { 97 | self.minTextField.text = filterModel.minPrice; 98 | self.maxTextField.text = filterModel.maxPrice; 99 | } 100 | self.titleLabel.text = filterModel.title; 101 | [self createButtonItem]; 102 | } 103 | 104 | - (void)textFieldChanged:(UITextField *)textField 105 | { 106 | [self strLengthLimitWithTextField:textField maxLength:self.itemManager.maxLength]; 107 | if (textField.tag == 101) { 108 | self.filterModel.minPrice = textField.text; 109 | } else if (textField.tag == 102) { 110 | self.filterModel.maxPrice = textField.text; 111 | } 112 | } 113 | 114 | - (IBAction)leftButtonClick:(id)sender { 115 | if (self.leftButton.selected) { 116 | return; 117 | } 118 | for (UIButton *button in self.itemView.subviews) { 119 | [button removeFromSuperview]; 120 | } 121 | [self.buttonArr removeAllObjects]; 122 | self.leftButton.selected = YES; 123 | self.rightButton.selected = NO; 124 | ZHFilterModel *filterModel = [self.modelArr firstObject]; 125 | filterModel.selected = YES; 126 | [[self.modelArr lastObject] setSelected:NO]; 127 | [filterModel setModelItemSelectesNO]; 128 | self.filterModel = filterModel; 129 | } 130 | 131 | - (IBAction)rightButtonClick:(id)sender { 132 | if (self.rightButton.selected) { 133 | return; 134 | } 135 | for (UIButton *button in self.itemView.subviews) { 136 | [button removeFromSuperview]; 137 | } 138 | [self.buttonArr removeAllObjects]; 139 | self.leftButton.selected = NO; 140 | self.rightButton.selected = YES; 141 | ZHFilterModel *filterModel = [self.modelArr lastObject]; 142 | filterModel.selected = YES; 143 | [[self.modelArr firstObject] setSelected:NO]; 144 | [filterModel setModelItemSelectesNO]; 145 | self.filterModel = filterModel; 146 | } 147 | 148 | 149 | - (void)itemButtonClick:(UIButton *)sender 150 | { 151 | self.minTextField.text = nil; 152 | self.maxTextField.text = nil; 153 | self.filterModel.minPrice = @""; 154 | self.filterModel.maxPrice = @""; 155 | if (self.filterModel.multiple) { 156 | sender.selected = !sender.selected; 157 | ZHFilterItemModel *itemModel = self.filterModel.itemArr[sender.tag]; 158 | itemModel.selected = sender.selected; 159 | NSMutableArray *selectArr = [NSMutableArray array]; 160 | for (ZHFilterItemModel *itemModel in self.filterModel.itemArr) { 161 | if (itemModel.selected) { 162 | [selectArr addObject:itemModel]; 163 | } 164 | } 165 | self.filterModel.selectArr = selectArr; 166 | } else { 167 | for (UIButton *button in self.buttonArr) { 168 | if (button == sender) { 169 | button.selected = YES; 170 | }else{ 171 | button.selected = NO; 172 | } 173 | } 174 | self.filterModel.selectArr = [NSMutableArray array];; 175 | for (int i = 0; i < self.filterModel.itemArr.count; i ++) { 176 | ZHFilterItemModel *itemModel = self.filterModel.itemArr[i]; 177 | if (i == sender.tag) { 178 | itemModel.selected = YES; 179 | NSMutableArray *selectArr = [NSMutableArray arrayWithObjects:itemModel, nil]; 180 | self.filterModel.selectArr = selectArr; 181 | }else{ 182 | itemModel.selected = NO; 183 | } 184 | } 185 | } 186 | } 187 | 188 | - (void)createButtonItem 189 | { 190 | for (UIButton *button in self.itemView.subviews) { 191 | [button removeFromSuperview]; 192 | } 193 | [self.buttonArr removeAllObjects]; 194 | UIButton *tempButton = [UIButton buttonWithType:UIButtonTypeCustom]; 195 | for (int i = 0; i < self.filterModel.itemArr.count; i ++) { 196 | ZHFilterItemModel *model = self.filterModel.itemArr[i]; 197 | UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; 198 | button.layer.masksToBounds = YES; 199 | button.layer.cornerRadius = 2; 200 | button.tintColor = [UIColor clearColor]; 201 | [button setTitle:model.name forState:UIControlStateNormal]; 202 | [button setTitleColor:self.itemManager.titleColor forState:UIControlStateNormal]; 203 | [button setTitleColor:self.itemManager.titleSelectedColor forState:UIControlStateSelected]; 204 | [button setBackgroundImage:[self imageWithColor:self.itemManager.itemBGColor] forState:UIControlStateNormal]; 205 | [button setBackgroundImage:[self imageWithColor:self.itemManager.itemBGSelectedColor] forState:UIControlStateSelected]; 206 | button.titleLabel.font = [UIFont systemFontOfSize:self.itemManager.itemTitleFontSize]; 207 | button.tag = i; 208 | [button addTarget:self action:@selector(itemButtonClick:) forControlEvents:UIControlEventTouchUpInside]; 209 | [self.itemView addSubview:button]; 210 | CGFloat itemWidth = (self.itemManager.width - self.itemManager.space * (self.itemManager.lineNum + 1)) / self.itemManager.lineNum; 211 | CGFloat offictX = (i % self.itemManager.lineNum + 1) * (self.itemManager.space + itemWidth) - itemWidth; 212 | CGFloat offictY = (i / self.itemManager.lineNum) * (self.itemManager.space + self.itemManager.itemHeight) + self.itemManager.space; 213 | button.frame = CGRectMake(offictX, offictY, itemWidth, self.itemManager.itemHeight); 214 | tempButton = button; 215 | [self.buttonArr addObject:button]; 216 | button.selected = model.selected; 217 | } 218 | } 219 | 220 | - (UIImage *)imageWithColor:(UIColor *)color 221 | { 222 | CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f); 223 | UIGraphicsBeginImageContext(rect.size); 224 | CGContextRef context = UIGraphicsGetCurrentContext(); 225 | 226 | CGContextSetFillColorWithColor(context, [color CGColor]); 227 | CGContextFillRect(context, rect); 228 | 229 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 230 | UIGraphicsEndImageContext(); 231 | 232 | return image; 233 | } 234 | 235 | /** 236 | 输入框字符数限制 并且未确定文字不做截取处理 237 | */ 238 | - (void)strLengthLimitWithTextField:(UITextField *)textField maxLength:(NSInteger)maxLength 239 | { 240 | NSString *lang = [[UIApplication sharedApplication]textInputMode].primaryLanguage; // 键盘输入模式 241 | if ([lang isEqualToString:@"zh-Hans"]) { // 简体中文输入,包括简体拼音,健体五笔,简体手写 242 | UITextRange *selectedRange = [textField markedTextRange]; 243 | //获取高亮部分 244 | UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0]; 245 | // 没有高亮选择的字,则对已输入的文字进行字数统计和限制 246 | if (!position) { 247 | if (textField.text.length > maxLength) { 248 | textField.text = [textField.text substringToIndex:maxLength]; 249 | } 250 | }else{ 251 | // 有高亮选择的字符串,则暂不对文字进行统计和限制 252 | } 253 | }else{ 254 | // 中文输入法以外的直接对其统计限制即可,不考虑其他语种情况 255 | if (textField.text.length > maxLength) { 256 | textField.text = [textField.text substringToIndex:maxLength]; 257 | } 258 | } 259 | } 260 | 261 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 262 | [super setSelected:selected animated:animated]; 263 | 264 | // Configure the view for the selected state 265 | } 266 | 267 | @end 268 | -------------------------------------------------------------------------------- /ZHFilterMenuView/ZHFilterMenuView/ZHFilterMenuView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZHFilterMenuView.m 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/12/5. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ZHFilterModel.h" 11 | @class ZHFilterMenuView; 12 | 13 | #define WS(weakSelf) __weak __typeof(&*self)weakSelf = self 14 | #define SS(strongSelf) __strong __typeof(&*self)strongSelf = weakSelf 15 | 16 | #define KKeyWindow [UIApplication sharedApplication].keyWindow 17 | #define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width) 18 | #define SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height) 19 | #define KTableViewCellHeight 44 20 | #define KBottomViewHeight 80 21 | 22 | #define kBaseSetHEXColor(rgbValue,al) ([UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:(al)]) 23 | 24 | #define kSetHEXColor(rgbValue) kBaseSetHEXColor(rgbValue,1) 25 | 26 | #define KTitleColor kSetHEXColor(0x333333) 27 | #define KTitleSelectedColor kSetHEXColor(0x3072F5) 28 | #define KLineColor kSetHEXColor(0xe8e8e8)//分割线 29 | #define KItemBGColor kSetHEXColor(0xf5f5f5) 30 | #define KItemBGSelectedColor kSetHEXColor(0xeef6ff) 31 | 32 | 33 | @interface ZHFilterBottomView : UIView 34 | 35 | @property (nonatomic, strong) UIButton * _Nonnull resetButton; 36 | @property (nonatomic, strong) UIButton * _Nonnull confirmButton; 37 | @property (nonatomic, strong) UIView * _Nonnull lineView; 38 | 39 | /** 快速初始化 */ 40 | - (instancetype _Nonnull )initBottomViewWithTarget:(id _Nonnull )target 41 | resetAction:(SEL _Nonnull )resetAction 42 | confirmAction:(SEL _Nonnull )confirmAction; 43 | 44 | @end 45 | 46 | 47 | 48 | /** 确定类型 */ 49 | typedef NS_ENUM(NSUInteger, ZHFilterMenuConfirmType) { 50 | ZHFilterMenuConfirmTypeSpeedConfirm, //列表快速点击选择 51 | ZHFilterMenuConfirmTypeBottomConfirm, //底部确定按钮选择 52 | }; 53 | 54 | /** 下拉展示类型 */ 55 | typedef NS_ENUM(NSUInteger, ZHFilterMenuDownType) { 56 | ZHFilterMenuDownTypeTwoLists, //双列表 57 | ZHFilterMenuDownTypeOnlyItem, //可点item 58 | ZHFilterMenuDownTypeOnlyList, //单列表 59 | ZHFilterMenuDownTypeItemInput, //可点item加输入框 60 | }; 61 | 62 | /** 警告类型(方便后续拓展) */ 63 | typedef NS_ENUM(NSUInteger, ZHFilterMenuViewWangType) { 64 | ZHFilterMenuViewWangTypeInput, //输入框价格区间不正确 65 | }; 66 | 67 | NS_ASSUME_NONNULL_BEGIN 68 | 69 | @protocol ZHFilterMenuViewDelegate 70 | 71 | /** 确定回调 */ 72 | - (void)menuView:(ZHFilterMenuView *)menuView didSelectConfirmAtSelectedModelArr:(NSArray *)selectedModelArr; 73 | 74 | @optional 75 | 76 | /** 警告回调(用于错误提示) */ 77 | - (void)menuView:(ZHFilterMenuView *)menuView wangType:(ZHFilterMenuViewWangType)wangType; 78 | 79 | /** 消失回调 */ 80 | - (void)menuView:(ZHFilterMenuView *)menuView didHideAtSelectedModelArr:(NSArray *)selectedModelArr; 81 | 82 | /** 列表将要显示回调 */ 83 | - (void)menuView:(ZHFilterMenuView *)menuView willShowAtTabIndex:(NSInteger)tabIndex; 84 | 85 | /** 点击菜单回调 */ 86 | - (void)menuView:(ZHFilterMenuView *)menuView selectMenuAtTabIndex:(NSInteger)tabIndex; 87 | 88 | @end 89 | 90 | 91 | @protocol ZHFilterMenuViewDetaSource 92 | 93 | /** 返回每个 tabIndex 下的确定类型 */ 94 | - (ZHFilterMenuConfirmType)menuView:(ZHFilterMenuView *)menuView confirmTypeInTabIndex:(NSInteger)tabIndex; 95 | 96 | /** 返回每个 tabIndex 下的下拉展示类型 */ 97 | - (ZHFilterMenuDownType)menuView:(ZHFilterMenuView *)menuView downTypeInTabIndex:(NSInteger)tabIndex; 98 | 99 | 100 | @end 101 | 102 | @interface ZHFilterMenuView : UIView 103 | 104 | @property (nonatomic, weak) id zh_delegate; 105 | @property (nonatomic, weak) id zh_dataSource; 106 | 107 | @property (nonatomic, strong) NSMutableArray *filterDataArr; //传入数据源(必传) 108 | @property (nonatomic, strong) NSArray *titleArr; //传入标题数据源(必传) 109 | @property (nonatomic, strong) NSArray *imageNameArr; //传入折叠图片数据源(不传不展示图片) 110 | @property (nonatomic, strong) NSArray *selectImageNameArr;//传入选择状态下的折叠图片数据源(不传默认取imageNameArr里的图片) 111 | 112 | @property (nonatomic, strong) UIColor *titleColor; //菜单标题文本颜色(默认333333) 113 | @property (nonatomic, strong) UIColor *titleSelectedColor; //菜单标题选择状态下的颜色(默认3072F5) 114 | @property (nonatomic, strong) UIColor *lineColor; //菜单标题底部分割线颜色(默认e8e8e8) 115 | @property (nonatomic, assign) CGFloat titleFontSize; //菜单标题字号(默认15) 116 | 117 | @property (nonatomic, assign) BOOL twoListToOneList; //是否支持双列表与单列表转换(默认NO) 118 | @property (nonatomic, assign) BOOL showLine; //菜单标题底部分割线是否显示(默认YES) 119 | @property (nonatomic, assign) BOOL titleLeft; //文字标题是否居左 不平分(默认NO) 120 | @property (nonatomic, assign) BOOL lastTitleRight; //最后一个文字标题是否固定居右(默认NO,为YES的情况下tab标题宽度固定为60) 121 | @property (nonatomic, assign) BOOL showInWindow; //下拉列表展示在window上,以应对列表视图展示的问题(默认NO) 122 | @property (nonatomic, assign) CGFloat inWindowMinY; //移动后的menuView坐标转换在window上的minY值,showInWindow为YES时有效(默认0) 123 | @property (nonatomic, assign) CGFloat waitTime; //下拉框展示等待时间,showInWindow为YES时有效(默认0) 124 | @property (nonatomic, assign) CGFloat listHeight; //选择列表的高度(默认44) 125 | @property (nonatomic, assign) CGFloat bottomHeight; //列表底部的高度(默认80) 126 | 127 | @property (nonatomic, assign) CGFloat itemTitleFontSize; //item标题字号大小(默认12) 128 | @property (nonatomic, strong) UIColor *itemBGColor; //item背景颜色(默认f5f5f5) 129 | @property (nonatomic, strong) UIColor *itemBGSelectedColor; //item选择时背景颜色(默认eef6ff) 130 | @property (nonatomic, assign) CGFloat space; //item间隔(默认15) 131 | @property (nonatomic, assign) CGFloat itemHeight; //item高(默认30) 132 | @property (nonatomic, assign) NSInteger lineNum; //一行展示数量(默认4,当内容字符数大于7时lineNum = 2) 133 | @property (nonatomic, assign) NSInteger maxLength; //输入框最大文本数量(默认7位) 134 | 135 | @property (nonatomic, strong) NSMutableArray *buttonArr; //菜单tab标题button数据(可供外部特殊需求时使用) 136 | @property (nonatomic, assign) BOOL isOpen; //展开状态(供外部取值使用) 137 | 138 | /** 快速初始化 139 | * maxHeight:下拉列表最大展示高度 140 | */ 141 | - (instancetype)initWithFrame:(CGRect)frame maxHeight:(CGFloat)maxHeight; 142 | 143 | /** 参数传完后开始调用以显示 */ 144 | - (void)beginShowMenuView; 145 | 146 | /** 外部快捷调用展开菜单列表 */ 147 | - (void)menuTappedWithIndex:(NSInteger)tapIndex; 148 | 149 | /** 筛选菜单列表关闭消失 */ 150 | - (void)hideMenuList; 151 | 152 | /** 153 | 列表移除 154 | 仅在showInWindow=YES时需要在viewWillDisappear中手动调用移除 155 | */ 156 | - (void)removeMenuList; 157 | 158 | @end 159 | 160 | NS_ASSUME_NONNULL_END 161 | -------------------------------------------------------------------------------- /ZHFilterMenuView/ZHFilterMenuView/ZHFilterModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZHFilterModel.h 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/12/6. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ZHFilterItemModel.h" 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface ZHFilterModel : NSObject 16 | 17 | @property (nonatomic, copy) NSString *title; //标题 18 | @property (nonatomic, copy) NSString *code; //code 19 | @property (nonatomic, strong) NSMutableArray *itemArr; //item数据[] 20 | @property (nonatomic, strong) NSMutableArray *selectArr; //选择的数据[] 21 | @property (nonatomic, assign) BOOL multiple; //是否多选 22 | @property (nonatomic, assign) BOOL selected; //是否已选 23 | @property (nonatomic, assign) BOOL selectFirst; //是否选择第一个 24 | @property (nonatomic, copy) NSString *minPrice; //输入框最低值 25 | @property (nonatomic, copy) NSString *maxPrice; //输入框最大值 26 | 27 | @property (nonatomic, assign) NSInteger listHeight; //列表高度(用于内部列表展示时使用) 28 | 29 | /** 快速构造ZHFilterModel */ 30 | + (ZHFilterModel *)createFilterModelWithHeadTitle:(NSString *)title modelArr:(NSArray *)modelArr selectFirst:(BOOL)selectFirst multiple:(BOOL)multiple; 31 | + (ZHFilterModel *)createFilterModelWithHeadTitle:(NSString *)title code:(NSString *)code modelArr:(NSArray *)modelArr selectFirst:(BOOL)selectFirst multiple:(BOOL)multiple; 32 | 33 | /** 所有置位未选中 */ 34 | - (void)setModelItemSelectesNO; 35 | 36 | /** 首位选中 */ 37 | - (void)setModelItemSelecteFirst; 38 | 39 | /** 快速获取选中ZHFilterItemModel */ 40 | - (ZHFilterItemModel *)getSelectedItemModel; 41 | 42 | - (NSString *)getSelectedItemModelCode; 43 | 44 | - (NSMutableArray *)getSelectedItemModelArr; 45 | 46 | - (NSMutableArray *)getSelectedItemModelCodeArr; 47 | 48 | @end 49 | 50 | NS_ASSUME_NONNULL_END 51 | -------------------------------------------------------------------------------- /ZHFilterMenuView/ZHFilterMenuView/ZHFilterModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZHFilterModel.m 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/12/6. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import "ZHFilterModel.h" 10 | 11 | @implementation ZHFilterModel 12 | 13 | + (ZHFilterModel *)createFilterModelWithHeadTitle:(NSString *)title modelArr:(NSArray *)modelArr selectFirst:(BOOL)selectFirst multiple:(BOOL)multiple 14 | { 15 | return [self createFilterModelWithHeadTitle:title code:@"" modelArr:modelArr selectFirst:selectFirst multiple:multiple]; 16 | } 17 | 18 | + (ZHFilterModel *)createFilterModelWithHeadTitle:(NSString *)title code:(NSString *)code modelArr:(NSArray *)modelArr selectFirst:(BOOL)selectFirst multiple:(BOOL)multiple 19 | { 20 | ZHFilterModel *model = [[ZHFilterModel alloc] init]; 21 | ZHFilterItemModel *infoModel = [modelArr firstObject]; 22 | infoModel.selected = selectFirst; 23 | model.selectFirst = selectFirst; 24 | model.title = title; 25 | model.code = code; 26 | model.itemArr = [NSMutableArray arrayWithArray:modelArr]; 27 | model.multiple = multiple; 28 | return model; 29 | } 30 | 31 | //归档 32 | - (void)encodeWithCoder:(NSCoder *)aCoder{ 33 | unsigned int count; 34 | Ivar *ivar = class_copyIvarList([self class], &count); 35 | for (int i=0; i 10 | #import "ZHFilterModel.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface ZHFilterTitleTableViewCell : UITableViewCell 15 | @property (nonatomic, strong) UILabel *titleLabel; 16 | 17 | @property (nonatomic, strong) ZHFilterModel *filterModel; 18 | @property (nonatomic, strong) ZHFilterItemModel *itemModel; 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /ZHFilterMenuView/ZHFilterMenuView/ZHFilterTitleTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZHFilterTitleTableViewCell.m 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/12/10. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import "ZHFilterTitleTableViewCell.h" 10 | 11 | @implementation ZHFilterTitleTableViewCell 12 | 13 | - (void)awakeFromNib { 14 | [super awakeFromNib]; 15 | // Initialization code 16 | self.selectionStyle = UITableViewCellSelectionStyleNone; 17 | [self titleLabel]; 18 | } 19 | 20 | - (void)setFilterModel:(ZHFilterModel *)filterModel 21 | { 22 | _filterModel = filterModel; 23 | self.titleLabel.text = filterModel.title; 24 | } 25 | 26 | - (void)setItemModel:(ZHFilterItemModel *)itemModel 27 | { 28 | _itemModel = itemModel; 29 | self.titleLabel.text = itemModel.name; 30 | } 31 | 32 | - (UILabel *)titleLabel 33 | { 34 | if (!_titleLabel) { 35 | _titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 0, self.contentView.frame.size.width - 30, self.contentView.frame.size.height)]; 36 | _titleLabel.font = [UIFont systemFontOfSize:15]; 37 | _titleLabel.textColor = [UIColor blackColor]; 38 | _titleLabel.textAlignment = NSTextAlignmentLeft; 39 | [self addSubview:_titleLabel]; 40 | } 41 | return _titleLabel; 42 | } 43 | 44 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 45 | [super setSelected:selected animated:animated]; 46 | 47 | // Configure the view for the selected state 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /ZHFilterMenuView/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ZHFilterMenuView 4 | // 5 | // Created by 周亚楠 on 2019/8/21. 6 | // Copyright © 2019 Zhou. 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 | -------------------------------------------------------------------------------- /ZHFilterMenuViewTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ZHFilterMenuViewTests/ZHFilterMenuViewTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZHFilterMenuViewTests.m 3 | // ZHFilterMenuViewTests 4 | // 5 | // Created by 周亚楠 on 2019/8/21. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ZHFilterMenuViewTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ZHFilterMenuViewTests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | } 20 | 21 | - (void)tearDown { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | - (void)testExample { 26 | // This is an example of a functional test case. 27 | // Use XCTAssert and related functions to verify your tests produce the correct results. 28 | } 29 | 30 | - (void)testPerformanceExample { 31 | // This is an example of a performance test case. 32 | [self measureBlock:^{ 33 | // Put the code you want to measure the time of here. 34 | }]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /ZHFilterMenuViewUITests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ZHFilterMenuViewUITests/ZHFilterMenuViewUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZHFilterMenuViewUITests.m 3 | // ZHFilterMenuViewUITests 4 | // 5 | // Created by 周亚楠 on 2019/8/21. 6 | // Copyright © 2019 Zhou. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ZHFilterMenuViewUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ZHFilterMenuViewUITests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | 20 | // In UI tests it is usually best to stop immediately when a failure occurs. 21 | self.continueAfterFailure = NO; 22 | 23 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 24 | [[[XCUIApplication alloc] init] launch]; 25 | 26 | // 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. 27 | } 28 | 29 | - (void)tearDown { 30 | // Put teardown code here. This method is called after the invocation of each test method in the class. 31 | } 32 | 33 | - (void)testExample { 34 | // Use recording to get started writing UI tests. 35 | // Use XCTAssert and related functions to verify your tests produce the correct results. 36 | } 37 | 38 | @end 39 | --------------------------------------------------------------------------------