Przeglądaj źródła

注册页面完成-wm

wangmeng 2 lat temu
rodzic
commit
fe98bd3e6e
100 zmienionych plików z 52751 dodań i 26890 usunięć
  1. 28 2
      Asteria.xcodeproj/project.pbxproj
  2. 6 3
      Asteria/Fuction/AMoule/AViewController.m
  3. 23 0
      Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido.imageset/Contents.json
  4. BIN
      Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido.imageset/组 8031.png
  5. BIN
      Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido.imageset/组 8031@2x.png
  6. BIN
      Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido.imageset/组 8031@3x.png
  7. 23 0
      Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido_select.imageset/Contents.json
  8. BIN
      Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido_select.imageset/组 6866.png
  9. BIN
      Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido_select.imageset/组 6866@2x.png
  10. BIN
      Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido_select.imageset/组 6866@3x.png
  11. 23 0
      Asteria/Fuction/Login/Assets/Login.xcassets/login_signup.imageset/Contents.json
  12. BIN
      Asteria/Fuction/Login/Assets/Login.xcassets/login_signup.imageset/组 10988.png
  13. BIN
      Asteria/Fuction/Login/Assets/Login.xcassets/login_signup.imageset/组 10988@2x.png
  14. BIN
      Asteria/Fuction/Login/Assets/Login.xcassets/login_signup.imageset/组 10988@3x.png
  15. 3 1
      Asteria/Fuction/Login/BaseView/PassWordSecureBtnV.m
  16. 16 0
      Asteria/Fuction/Login/V/LoginSignUpV.h
  17. 316 0
      Asteria/Fuction/Login/V/LoginSignUpV.m
  18. 3 1
      Asteria/Fuction/Login/V/LoginThirdAuthV.m
  19. 0 0
      Asteria/Fuction/Login/VC/AS_LoginC.h
  20. 64 4
      Asteria/Fuction/Login/AS_LoginC.m
  21. 16 0
      Asteria/Fuction/Login/VC/AS_SignUpC.h
  22. 97 0
      Asteria/Fuction/Login/VC/AS_SignUpC.m
  23. 1 0
      Asteria/PreFixHeader.h
  24. 16 0
      Asteria/ThirdPartService.h
  25. 84 0
      Asteria/ThirdPartService.m
  26. 20 1
      Podfile.lock
  27. 51 0
      Pods/BRPickerView/BRPickerView/AddressPickerView/BRAddressModel.h
  28. 24 0
      Pods/BRPickerView/BRPickerView/AddressPickerView/BRAddressModel.m
  29. 13634 0
      Pods/BRPickerView/BRPickerView/AddressPickerView/BRAddressPickerView.bundle/BRCity.json
  30. 127 0
      Pods/BRPickerView/BRPickerView/AddressPickerView/BRAddressPickerView.h
  31. 562 0
      Pods/BRPickerView/BRPickerView/AddressPickerView/BRAddressPickerView.m
  32. 17 0
      Pods/BRPickerView/BRPickerView/BRPickerView.h
  33. 76 0
      Pods/BRPickerView/BRPickerView/Base/BRBaseView.h
  34. 398 0
      Pods/BRPickerView/BRPickerView/Base/BRBaseView.m
  35. 251 0
      Pods/BRPickerView/BRPickerView/Base/BRPickerStyle.h
  36. 494 0
      Pods/BRPickerView/BRPickerView/Base/BRPickerStyle.m
  37. 35 0
      Pods/BRPickerView/BRPickerView/Base/BRPickerView.bundle/en.lproj/Localizable.strings
  38. 35 0
      Pods/BRPickerView/BRPickerView/Base/BRPickerView.bundle/zh-Hans.lproj/Localizable.strings
  39. 35 0
      Pods/BRPickerView/BRPickerView/Base/BRPickerView.bundle/zh-Hant.lproj/Localizable.strings
  40. 87 0
      Pods/BRPickerView/BRPickerView/Base/BRPickerViewMacro.h
  41. 26 0
      Pods/BRPickerView/BRPickerView/Base/NSBundle+BRPickerView.h
  42. 68 0
      Pods/BRPickerView/BRPickerView/Base/NSBundle+BRPickerView.m
  43. 111 0
      Pods/BRPickerView/BRPickerView/DatePickerView/BRDatePickerView+BR.h
  44. 755 0
      Pods/BRPickerView/BRPickerView/DatePickerView/BRDatePickerView+BR.m
  45. 274 0
      Pods/BRPickerView/BRPickerView/DatePickerView/BRDatePickerView.h
  46. 1965 0
      Pods/BRPickerView/BRPickerView/DatePickerView/BRDatePickerView.m
  47. 126 0
      Pods/BRPickerView/BRPickerView/DatePickerView/NSDate+BRPickerView.h
  48. 400 0
      Pods/BRPickerView/BRPickerView/DatePickerView/NSDate+BRPickerView.m
  49. 37 0
      Pods/BRPickerView/BRPickerView/StringPickerView/BRResultModel.h
  50. 41 0
      Pods/BRPickerView/BRPickerView/StringPickerView/BRResultModel.m
  51. 191 0
      Pods/BRPickerView/BRPickerView/StringPickerView/BRStringPickerView.h
  52. 571 0
      Pods/BRPickerView/BRPickerView/StringPickerView/BRStringPickerView.m
  53. 21 0
      Pods/BRPickerView/LICENSE
  54. 530 0
      Pods/BRPickerView/README.md
  55. 6 0
      Pods/Local Podspecs/WMBase.podspec.json
  56. 20 1
      Pods/Manifest.lock
  57. 27676 26861
      Pods/Pods.xcodeproj/project.pbxproj
  58. 26 0
      Pods/Target Support Files/BRPickerView/BRPickerView-Info.plist
  59. 5 0
      Pods/Target Support Files/BRPickerView/BRPickerView-dummy.m
  60. 12 0
      Pods/Target Support Files/BRPickerView/BRPickerView-prefix.pch
  61. 28 0
      Pods/Target Support Files/BRPickerView/BRPickerView-umbrella.h
  62. 13 0
      Pods/Target Support Files/BRPickerView/BRPickerView.debug.xcconfig
  63. 6 0
      Pods/Target Support Files/BRPickerView/BRPickerView.modulemap
  64. 13 0
      Pods/Target Support Files/BRPickerView/BRPickerView.release.xcconfig
  65. 51 0
      Pods/Target Support Files/Pods-Asteria-NotificationServiceExtension/Pods-Asteria-NotificationServiceExtension-acknowledgements.markdown
  66. 63 0
      Pods/Target Support Files/Pods-Asteria-NotificationServiceExtension/Pods-Asteria-NotificationServiceExtension-acknowledgements.plist
  67. 3 3
      Pods/Target Support Files/Pods-Asteria-NotificationServiceExtension/Pods-Asteria-NotificationServiceExtension.debug.xcconfig
  68. 3 3
      Pods/Target Support Files/Pods-Asteria-NotificationServiceExtension/Pods-Asteria-NotificationServiceExtension.release.xcconfig
  69. 51 0
      Pods/Target Support Files/Pods-Asteria/Pods-Asteria-acknowledgements.markdown
  70. 63 0
      Pods/Target Support Files/Pods-Asteria/Pods-Asteria-acknowledgements.plist
  71. 2 0
      Pods/Target Support Files/Pods-Asteria/Pods-Asteria-frameworks-Debug-input-files.xcfilelist
  72. 2 0
      Pods/Target Support Files/Pods-Asteria/Pods-Asteria-frameworks-Debug-output-files.xcfilelist
  73. 2 0
      Pods/Target Support Files/Pods-Asteria/Pods-Asteria-frameworks-Release-input-files.xcfilelist
  74. 2 0
      Pods/Target Support Files/Pods-Asteria/Pods-Asteria-frameworks-Release-output-files.xcfilelist
  75. 4 0
      Pods/Target Support Files/Pods-Asteria/Pods-Asteria-frameworks.sh
  76. 3 3
      Pods/Target Support Files/Pods-Asteria/Pods-Asteria.debug.xcconfig
  77. 3 3
      Pods/Target Support Files/Pods-Asteria/Pods-Asteria.release.xcconfig
  78. 1 0
      Pods/Target Support Files/WMBase/WMBase-umbrella.h
  79. 2 2
      Pods/Target Support Files/WMBase/WMBase.debug.xcconfig
  80. 2 2
      Pods/Target Support Files/WMBase/WMBase.release.xcconfig
  81. 26 0
      Pods/Target Support Files/YYText/YYText-Info.plist
  82. 5 0
      Pods/Target Support Files/YYText/YYText-dummy.m
  83. 12 0
      Pods/Target Support Files/YYText/YYText-prefix.pch
  84. 41 0
      Pods/Target Support Files/YYText/YYText-umbrella.h
  85. 14 0
      Pods/Target Support Files/YYText/YYText.debug.xcconfig
  86. 6 0
      Pods/Target Support Files/YYText/YYText.modulemap
  87. 14 0
      Pods/Target Support Files/YYText/YYText.release.xcconfig
  88. 22 0
      Pods/YYText/LICENSE
  89. 1095 0
      Pods/YYText/README.md
  90. 55 0
      Pods/YYText/YYText/Component/YYTextContainerView.h
  91. 144 0
      Pods/YYText/YYText/Component/YYTextContainerView.m
  92. 95 0
      Pods/YYText/YYText/Component/YYTextDebugOption.h
  93. 140 0
      Pods/YYText/YYText/Component/YYTextDebugOption.m
  94. 52 0
      Pods/YYText/YYText/Component/YYTextEffectWindow.h
  95. 429 0
      Pods/YYText/YYText/Component/YYTextEffectWindow.m
  96. 87 0
      Pods/YYText/YYText/Component/YYTextInput.h
  97. 152 0
      Pods/YYText/YYText/Component/YYTextInput.m
  98. 98 0
      Pods/YYText/YYText/Component/YYTextKeyboardManager.h
  99. 521 0
      Pods/YYText/YYText/Component/YYTextKeyboardManager.m
  100. 0 0
      Pods/YYText/YYText/Component/YYTextLayout.h

+ 28 - 2
Asteria.xcodeproj/project.pbxproj

@@ -57,6 +57,9 @@
 		9A8DD8C52A0B8F1700573324 /* As_GoodsSizeC.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A8DD8C42A0B8F1700573324 /* As_GoodsSizeC.m */; };
 		9A8DD8C82A0B93F700573324 /* GoodsDetailsVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A8DD8C72A0B93F600573324 /* GoodsDetailsVM.m */; };
 		9ACBEC212A14585300A8F97A /* CTMediator+ASTargerts.m in Sources */ = {isa = PBXBuildFile; fileRef = 9ACBEC202A14585300A8F97A /* CTMediator+ASTargerts.m */; };
+		9ACBEC252A14707400A8F97A /* AS_SignUpC.m in Sources */ = {isa = PBXBuildFile; fileRef = 9ACBEC242A14707400A8F97A /* AS_SignUpC.m */; };
+		9ACBEC282A1472AF00A8F97A /* LoginSignUpV.m in Sources */ = {isa = PBXBuildFile; fileRef = 9ACBEC272A1472AF00A8F97A /* LoginSignUpV.m */; };
+		9ACBEC2B2A14CCA300A8F97A /* ThirdPartService.m in Sources */ = {isa = PBXBuildFile; fileRef = 9ACBEC292A14CCA200A8F97A /* ThirdPartService.m */; };
 		9AD3459E2A08D545005CA070 /* GoodsDetailSrcView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD3459D2A08D545005CA070 /* GoodsDetailSrcView.m */; };
 		9AD345A72A08D571005CA070 /* TYPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD345A32A08D571005CA070 /* TYPageControl.m */; };
 		9AD345A82A08D571005CA070 /* TYCyclePagerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD345A42A08D571005CA070 /* TYCyclePagerView.m */; };
@@ -217,6 +220,12 @@
 		9A8DD8C72A0B93F600573324 /* GoodsDetailsVM.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoodsDetailsVM.m; sourceTree = "<group>"; };
 		9ACBEC1F2A14585300A8F97A /* CTMediator+ASTargerts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CTMediator+ASTargerts.h"; sourceTree = "<group>"; };
 		9ACBEC202A14585300A8F97A /* CTMediator+ASTargerts.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CTMediator+ASTargerts.m"; sourceTree = "<group>"; };
+		9ACBEC232A14707400A8F97A /* AS_SignUpC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AS_SignUpC.h; sourceTree = "<group>"; };
+		9ACBEC242A14707400A8F97A /* AS_SignUpC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AS_SignUpC.m; sourceTree = "<group>"; };
+		9ACBEC262A1472AF00A8F97A /* LoginSignUpV.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginSignUpV.h; sourceTree = "<group>"; };
+		9ACBEC272A1472AF00A8F97A /* LoginSignUpV.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoginSignUpV.m; sourceTree = "<group>"; };
+		9ACBEC292A14CCA200A8F97A /* ThirdPartService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThirdPartService.m; sourceTree = "<group>"; };
+		9ACBEC2A2A14CCA200A8F97A /* ThirdPartService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThirdPartService.h; sourceTree = "<group>"; };
 		9AD3459C2A08D545005CA070 /* GoodsDetailSrcView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoodsDetailSrcView.h; sourceTree = "<group>"; };
 		9AD3459D2A08D545005CA070 /* GoodsDetailSrcView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoodsDetailSrcView.m; sourceTree = "<group>"; };
 		9AD345A12A08D571005CA070 /* TYCyclePagerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TYCyclePagerView.h; sourceTree = "<group>"; };
@@ -441,6 +450,8 @@
 				81C3B44B29F6691300D79294 /* Extensions */,
 				8172449D29F3B2ED005FA9C9 /* AppDelegate.h */,
 				8172449E29F3B2ED005FA9C9 /* AppDelegate.m */,
+				9ACBEC2A2A14CCA200A8F97A /* ThirdPartService.h */,
+				9ACBEC292A14CCA200A8F97A /* ThirdPartService.m */,
 				814F5CF12A11B337003847A9 /* ASUI */,
 				817244A929F3B2EE005FA9C9 /* Assets.xcassets */,
 				817244AB29F3B2EE005FA9C9 /* LaunchScreen.storyboard */,
@@ -598,12 +609,11 @@
 		9A5C64522A11E58F00CBB185 /* Login */ = {
 			isa = PBXGroup;
 			children = (
+				9ACBEC222A1464CD00A8F97A /* VC */,
 				9A65DE4E2A132BDF00BB1269 /* V */,
 				9AD6A5462A123799001DE3D9 /* Target */,
 				9AD6A53A2A120C56001DE3D9 /* Assets */,
 				9AD6A53F2A1218D0001DE3D9 /* BaseView */,
-				9A5C64562A12064300CBB185 /* AS_LoginC.h */,
-				9A5C64572A12064300CBB185 /* AS_LoginC.m */,
 			);
 			path = Login;
 			sourceTree = "<group>";
@@ -613,6 +623,8 @@
 			children = (
 				9A65DE4F2A132FB700BB1269 /* LoginThirdAuthV.h */,
 				9A65DE502A132FB700BB1269 /* LoginThirdAuthV.m */,
+				9ACBEC262A1472AF00A8F97A /* LoginSignUpV.h */,
+				9ACBEC272A1472AF00A8F97A /* LoginSignUpV.m */,
 			);
 			path = V;
 			sourceTree = "<group>";
@@ -644,6 +656,17 @@
 			path = CTMediatoaTargets;
 			sourceTree = "<group>";
 		};
+		9ACBEC222A1464CD00A8F97A /* VC */ = {
+			isa = PBXGroup;
+			children = (
+				9A5C64562A12064300CBB185 /* AS_LoginC.h */,
+				9A5C64572A12064300CBB185 /* AS_LoginC.m */,
+				9ACBEC232A14707400A8F97A /* AS_SignUpC.h */,
+				9ACBEC242A14707400A8F97A /* AS_SignUpC.m */,
+			);
+			path = VC;
+			sourceTree = "<group>";
+		};
 		9AD3459F2A08D55D005CA070 /* Banner */ = {
 			isa = PBXGroup;
 			children = (
@@ -1154,6 +1177,7 @@
 				81C3B44A29F6661500D79294 /* ASBaseNavController.m in Sources */,
 				9AD3460C2A08D60F005CA070 /* ZFVolumeBrightnessView.m in Sources */,
 				9AD346072A08D60F005CA070 /* UIViewController+ZFPlayerRotation.m in Sources */,
+				9ACBEC2B2A14CCA300A8F97A /* ThirdPartService.m in Sources */,
 				9AD346142A08D60F005CA070 /* ZFSpeedLoadingView.m in Sources */,
 				9AD346062A08D60F005CA070 /* ZFReachabilityManager.m in Sources */,
 				9AD345FE2A08D60F005CA070 /* UIScrollView+ZFPlayer.m in Sources */,
@@ -1183,9 +1207,11 @@
 				9A5C64582A12064300CBB185 /* AS_LoginC.m in Sources */,
 				9AD345FD2A08D60F005CA070 /* ZFPersentInteractiveTransition.m in Sources */,
 				9AD345FC2A08D60F005CA070 /* ZFPlayerLogManager.m in Sources */,
+				9ACBEC252A14707400A8F97A /* AS_SignUpC.m in Sources */,
 				9AD346162A08D60F005CA070 /* UIImageView+ZFCache.m in Sources */,
 				9AD345A92A08D571005CA070 /* TYCyclePagerTransformLayout.m in Sources */,
 				81932E2B29F7539B007C37AF /* UIColor+AS.m in Sources */,
+				9ACBEC282A1472AF00A8F97A /* LoginSignUpV.m in Sources */,
 				9AD346152A08D60F005CA070 /* ZFPortraitControlView.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;

+ 6 - 3
Asteria/Fuction/AMoule/AViewController.m

@@ -47,9 +47,12 @@
     
 }
 -(void)buttonTap:(UIButton *)btn{
-    UIViewController *login = [[CTMediator sharedInstance] Login_LoginC:@{}];
-    login.modalPresentationStyle  =  UIModalPresentationFullScreen;
-    [self.navigationController presentViewController:login animated:YES completion:nil];
+    UIViewController *loginC = [[CTMediator sharedInstance] Login_LoginC:@{}];
+    
+    QMUINavigationController *uikitNavController = [[QMUINavigationController alloc] initWithRootViewController:loginC];
+    uikitNavController.navigationBar.hidden = YES;
+    uikitNavController.modalPresentationStyle =UIModalPresentationFullScreen;
+    [self.navigationController presentViewController:uikitNavController animated:YES completion:nil];
 }
 #pragma mark - getters and setters
 - (UIButton *)pushBViewControllerButton

+ 23 - 0
Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido.imageset/Contents.json

@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "filename" : "组 8031.png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "组 8031@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "组 8031@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido.imageset/组 8031.png


BIN
Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido.imageset/组 8031@2x.png


BIN
Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido.imageset/组 8031@3x.png


+ 23 - 0
Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido_select.imageset/Contents.json

@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "filename" : "组 6866.png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "组 6866@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "组 6866@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido_select.imageset/组 6866.png


BIN
Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido_select.imageset/组 6866@2x.png


BIN
Asteria/Fuction/Login/Assets/Login.xcassets/login_selectRido_select.imageset/组 6866@3x.png


+ 23 - 0
Asteria/Fuction/Login/Assets/Login.xcassets/login_signup.imageset/Contents.json

@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "filename" : "组 10988.png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "组 10988@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "组 10988@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
Asteria/Fuction/Login/Assets/Login.xcassets/login_signup.imageset/组 10988.png


BIN
Asteria/Fuction/Login/Assets/Login.xcassets/login_signup.imageset/组 10988@2x.png


BIN
Asteria/Fuction/Login/Assets/Login.xcassets/login_signup.imageset/组 10988@3x.png


+ 3 - 1
Asteria/Fuction/Login/BaseView/PassWordSecureBtnV.m

@@ -71,9 +71,11 @@ if (textField == self.xxx_passwordTF && self.xxx_passwordTF.secureTextEntry) {
     if (!_xxx_secureBtn) {
         _xxx_secureBtn = [UIButton buttonWithType:UIButtonTypeCustom];
         _xxx_secureBtn.frame = CGRectMake(KScreenWidth-40-50, 0,50 , 50);
-        [_xxx_secureBtn setTitle:@"VIEW" forState:UIControlStateNormal];
+        [_xxx_secureBtn setAttributedTitle:[[NSMutableAttributedString alloc] initWithString:@"VIEW"] forState:UIControlStateNormal];
         NSMutableAttributedString *attrSleStr = [[NSMutableAttributedString alloc] initWithString:@"VIEW"                                                                                    attributes:@{NSStrikethroughStyleAttributeName : @(NSUnderlineStyleSingle)}];
         [_xxx_secureBtn setAttributedTitle:attrSleStr forState:UIControlStateSelected];
+        [_xxx_secureBtn setTitleColor:[UIColor qmui_colorWithHexString:@"#000000"] forState:UIControlStateNormal];
+        _xxx_secureBtn.titleLabel.font = [UIFont fontWithName:Rob_Bold size:14];
         [_xxx_secureBtn addTarget:self action:@selector(xxx_changeSecure:) forControlEvents:UIControlEventTouchUpInside];
         _xxx_secureBtn.selected = NO;
         _xxx_secureBtn.backgroundColor = [UIColor clearColor];

+ 16 - 0
Asteria/Fuction/Login/V/LoginSignUpV.h

@@ -0,0 +1,16 @@
+//
+//  LoginSignUpV.h
+//  Asteria
+//
+//  Created by 王猛 on 2023/5/17.
+//
+
+#import <WMBase/WMBase.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface LoginSignUpV : TT_BaseV
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 316 - 0
Asteria/Fuction/Login/V/LoginSignUpV.m

@@ -0,0 +1,316 @@
+//
+//  LoginSignUpV.m
+//  Asteria
+//
+//  Created by 王猛 on 2023/5/17.
+//
+
+#import "LoginSignUpV.h"
+#import "PassWordSecureBtnV.h"
+#import "EamilTFmatchV.h"
+#import <BRPickerView/BRPickerView.h>
+
+@interface LoginSignUpV ()
+@property (nonatomic, strong) TT_CustonTF *xxx_firstName;
+@property (nonatomic, strong) TT_CustonTF *xxx_lasetName;
+@property (nonatomic, strong) EamilTFmatchV *xxx_regMmailV;
+
+
+@property (nonatomic, strong) PassWordSecureBtnV *xxx_regPassWord;
+@property (nonatomic, strong) UILabel *xxx_dateLab;
+@property (nonatomic, strong) UIButton *xxx_creatBtn;
+
+
+@property (nonatomic, strong) UIButton *xxx_subscribedBtn;
+@property (nonatomic, strong) QMUILabel *xxx_subscribedLab;
+
+@property (nonatomic, strong) YYLabel *xxx_ClickLab;
+@property (nonatomic, strong) UIButton *xxx_selectBtn;
+
+@property (nonatomic, strong) NSDate *birthdaySelectDate;
+
+
+@end
+
+@implementation LoginSignUpV
+
+- (void)tt_setupViews{
+    [self addSubview:self.xxx_firstName];
+    [self addSubview:self.xxx_lasetName];
+    [self addSubview:self.xxx_regMmailV];
+    [self addSubview:self.xxx_regPassWord];
+    [self addSubview:self.xxx_dateLab];
+    [self addSubview:self.xxx_creatBtn];
+    [self addSubview:self.xxx_subscribedBtn];
+    [self addSubview:self.xxx_subscribedLab];
+    [self addSubview:self.xxx_selectBtn];
+    [self addSubview:self.xxx_ClickLab];
+    
+    [self bringSubviewToFront:self.xxx_regMmailV];
+    
+    self.birthdaySelectDate = [NSDate date];
+}
+#pragma mark - **************** action ****************
+-(void)action_dateOfBirth{
+    UIViewController *topVC =  topViewController();
+    [topVC.view endEditing:YES];
+    BRDatePickerView *datePickerView = [[BRDatePickerView alloc]init];
+    datePickerView.pickerMode = BRDatePickerModeDate;
+    datePickerView.title = @"Please Select The Date Of Birth";
+    datePickerView.selectDate = self.birthdaySelectDate;
+    datePickerView.minDate = [NSDate br_setYear:1900 month:1 day:1];
+    datePickerView.maxDate = [NSDate date];
+    datePickerView.isAutoSelect = YES;
+    datePickerView.resultBlock = ^(NSDate *selectDate, NSString *selectValue) {
+        self.birthdaySelectDate = selectDate;
+        NSDateFormatter *formartter= [[NSDateFormatter alloc]init];
+        [formartter setDateFormat:@"MM/dd/yyyy"];
+        NSString *currentDateStr = [formartter stringFromDate:selectDate];
+        self.xxx_dateLab.text = currentDateStr;
+        self.xxx_dateLab.textColor = [UIColor qmui_colorWithHexString:@"#000000"];
+        NSLog(@"selectValue=%@", selectValue);
+        NSLog(@"selectDate=%@", selectDate);
+        NSLog(@"---------------------------------");
+        
+    };
+    [datePickerView show];
+}
+-(void)action_creatClick{
+    if (self.xxx_selectBtn.selected == NO) {
+        [self makeToast:@"Please select 'I Agree' to continue." duration:2 position:CSToastPositionCenter];
+        return;
+    }else if (self.xxx_firstName.text.length == 0) {
+        [self makeToast:@"Please Input FirstName" duration:2 position:CSToastPositionCenter];
+        return;
+    }else if (self.xxx_lasetName.text.length == 0){
+        [self makeToast:@"Please Input LastName" duration:2 position:CSToastPositionCenter];
+        return;
+    }else if(self.xxx_regMmailV.xxx_emailTF.text.length == 0 || ![Current_normalTool xxx_isValidateEmail:self.xxx_regMmailV.xxx_emailTF.text]){
+        [self makeToast:@"Please prvide an email address." duration:2 position:CSToastPositionCenter];
+        return;
+    }else if(self.xxx_regPassWord.xxx_passwordTF.text.length<6){
+        [self makeToast:@"Please set the correct password." duration:2 position:CSToastPositionCenter];
+        return;
+    }
+    
+    NSMutableDictionary *tempDic = [NSMutableDictionary dictionary];
+    [tempDic setObject:self.xxx_firstName.text forKey:@"firstName"];
+    [tempDic setObject:self.xxx_lasetName.text forKey:@"lastName"];
+    [tempDic setObject:self.xxx_regMmailV.xxx_emailTF.text forKey:@"email"];
+    [tempDic setObject:self.xxx_regPassWord.xxx_passwordTF.text forKey:@"password"];
+    BOOL isSubscribedSelect = self.xxx_subscribedBtn.selected;
+    [tempDic setObject:[NSString stringWithFormat:@"%d",isSubscribedSelect] forKey:@"is_subscribed"];
+    if ([self.xxx_dateLab.text isEqualToString:@"Date Of Birth"]) {
+        [tempDic setObject:@"" forKey:@"birthday"];
+    }else{
+        [tempDic setObject:self.xxx_dateLab.text forKey:@"birthday"];
+    }
+    [self generaltriggermethodType:1 data:tempDic];
+}
+-(void)action_xxx_subscribedBtnClick:(UIButton *)btn{
+    btn.selected = !btn.selected;
+}
+-(void)action_selectClick:(UIButton *)btn{
+    btn.selected = !btn.selected;
+}
+
+-(TT_CustonTF *)xxx_firstName{
+    if (!_xxx_firstName) {
+        _xxx_firstName = [TT_ControlTool FTT_ControlToolUITextFieldFrame:CGRectMake(20, 0, (KScreenWidth-60)/2, 50)
+                                                             PlaceHolder:@"* FIRST NAME"
+                                                             andLifImage:nil
+                                                           AndRightImage:nil
+                                                          LiftImageFrame:CGRectZero
+                                                         RightImageFrame:CGRectZero
+                                                                  AndTag:0
+                                                         AndKeyboardType:UIKeyboardTypeDefault
+                                                         clearButtonMode:UITextFieldViewModeAlways
+                                                        AndReturnKeyType:UIReturnKeyDone
+                                                           masksToBounds:YES
+                                                            conrenRadius:4
+                                                             BorderColor:[UIColor colorWithHexString:@"#000000"]
+                                                             BorderWidth:1];
+        _xxx_firstName.font = [UIFont fontWithName:Rob_Regular size:14];
+        _xxx_firstName.backgroundColor = [UIColor colorWithHexString:@"#FFFFFF"];
+    }
+    return _xxx_firstName;
+}
+
+-(TT_CustonTF *)xxx_lasetName{
+    if (!_xxx_lasetName) {
+        _xxx_lasetName = [TT_ControlTool FTT_ControlToolUITextFieldFrame:CGRectMake(CGRectGetMaxX(self.xxx_firstName.frame)+20, 0, (KScreenWidth-60)/2, 50)
+                                                             PlaceHolder:@"* LAST NAME"
+                                                             andLifImage:nil
+                                                           AndRightImage:nil
+                                                          LiftImageFrame:CGRectZero
+                                                         RightImageFrame:CGRectZero
+                                                                  AndTag:0
+                                                         AndKeyboardType:UIKeyboardTypeDefault
+                                                         clearButtonMode:UITextFieldViewModeAlways
+                                                        AndReturnKeyType:UIReturnKeyDone
+                                                           masksToBounds:YES
+                                                            conrenRadius:4
+                                                             BorderColor:[UIColor colorWithHexString:@"#000000"]
+                                                             BorderWidth:1];
+        _xxx_lasetName.font = [UIFont fontWithName:Rob_Regular size:14];
+        _xxx_lasetName.backgroundColor = [UIColor colorWithHexString:@"#FFFFFF"];
+    
+    }
+    return _xxx_lasetName;
+}
+-(EamilTFmatchV *)xxx_regMmailV{
+    if (!_xxx_regMmailV) {
+        _xxx_regMmailV = [[EamilTFmatchV alloc]initWithFrame:CGRectMake(0,CGRectGetMaxY(self.xxx_firstName.frame)+30 , KScreenWidth, 50)];
+    }
+    return _xxx_regMmailV;
+}
+-(PassWordSecureBtnV *)xxx_regPassWord{
+    if (!_xxx_regPassWord) {
+        _xxx_regPassWord = [[PassWordSecureBtnV alloc]initWithFrame:CGRectMake(20, CGRectGetMaxY(self.xxx_regMmailV.frame)+30, KScreenWidth-40, 50+20)];
+    }
+    return _xxx_regPassWord;
+}
+
+-(UILabel *)xxx_dateLab{
+    if (!_xxx_dateLab) {
+        _xxx_dateLab = [TT_ControlTool uc_ControlToolUILabelFrame:CGRectMake(20,CGRectGetMaxY(self.xxx_regPassWord.frame)+10, KScreenWidth-40, 50)
+                                                          AndTitle:@"DATE OF BIRTH"
+                                                       AndFontSize:14
+                                                     AndTitleColor:[UIColor colorWithHexString:@"#999999"]
+                                                     Numberoflines:1
+                                                     TextAlignment:NSTextAlignmentCenter
+                                          adjustesFontSizesTowidth:NO
+                                                     masksToBounds:YES
+                                                     conrenrRadius:4
+                                            userInteractionEnabled:YES
+                                                     tap_selector:@selector(action_dateOfBirth)
+                                                           object:self];
+        _xxx_dateLab.layer.borderColor = [UIColor colorWithHexString:@"#000000"].CGColor;
+        _xxx_dateLab.layer.borderWidth = 1;
+        _xxx_dateLab.text = @"DATE OF BIRTH";
+        
+    }
+    return _xxx_dateLab;
+}
+
+- (UIButton *)xxx_creatBtn {
+    if (!_xxx_creatBtn) {
+        _xxx_creatBtn = [TT_ControlTool FTT_ControlToolUIButtonFrame:CGRectMake(20,CGRectGetMaxY(self.xxx_dateLab.frame)+30 ,KScreenWidth-40,50)
+                                                                 taeget:self
+                                                                    sel:@selector(action_creatClick)
+                                                                    tag:0
+                                                               AntTitle:@"CREATE ACCOUNT"
+                                                              titleFont:17
+                                                             titleColor:[UIColor whiteColor]
+                                                               andImage:nil
+                                                           AndBackColor:nil
+                                                adjustsFontSizesTowidth:NO
+                                                          masksToBounds:YES
+                                                           conrenRadius:5
+                                                            BorderColor:nil
+                                                            BorderWidth:0
+                                              ContentHorizontalAligment:0];
+        _xxx_creatBtn.titleLabel.font = [UIFont fontWithName:Rob_Bold size:16];
+        _xxx_creatBtn.backgroundColor = ThemeColor;
+    }
+    return _xxx_creatBtn;
+}
+
+-(UIButton *)xxx_subscribedBtn{
+    if (!_xxx_subscribedBtn) {
+        _xxx_subscribedBtn = [TT_ControlTool FTT_ControlToolUIButtonFrame:CGRectMake(20,CGRectGetMaxY(self.xxx_creatBtn.frame)+20, 40, 40)
+                                                               taeget:self
+                                                                  sel:@selector(action_xxx_subscribedBtnClick:)
+                                                                  tag:0
+                                                             AntTitle:nil
+                                                            titleFont:0
+                                                           titleColor:nil
+                                                             andImage:@"login_selectRido"
+                                                         AndBackColor:[UIColor clearColor]
+                                              adjustsFontSizesTowidth:NO
+                                                        masksToBounds:NO
+                                                         conrenRadius:0
+                                                          BorderColor:nil
+                                                          BorderWidth:0
+                                            ContentHorizontalAligment:0];
+        [_xxx_subscribedBtn setImage:[UIImage imageNamed:@"login_selectRido_select"] forState:UIControlStateSelected];
+        _xxx_subscribedBtn.selected = YES;
+    }
+    return _xxx_subscribedBtn;
+}
+
+-(QMUILabel *)xxx_subscribedLab{
+    if (!_xxx_subscribedLab) {
+        _xxx_subscribedLab = [[QMUILabel alloc]init];
+        _xxx_subscribedLab.frame = CGRectMake(60,  CGRectGetMaxY(self.xxx_creatBtn.frame)+20, KScreenWidth - 20-20-40, 40);
+        NSString *string = @"I’d Like To Receive Emails Or Sms With Exclusive Discounts And News From Asteriahair";
+        _xxx_subscribedLab.text = string;
+        _xxx_subscribedLab.font = [UIFont fontWithName:Rob_Regular size:12];
+        _xxx_subscribedLab.textColor = [UIColor colorWithHexString:@"#000000"];
+    }
+    return _xxx_subscribedLab;
+}
+
+
+-(UIButton *)xxx_selectBtn{
+    if (!_xxx_selectBtn) {
+        _xxx_selectBtn = [TT_ControlTool FTT_ControlToolUIButtonFrame:CGRectMake(20,CGRectGetMaxY(self.xxx_subscribedBtn.frame)+10, 40, 40)
+                                                               taeget:self
+                                                                  sel:@selector(action_selectClick:)
+                                                                  tag:1
+                                                             AntTitle:nil
+                                                            titleFont:0
+                                                           titleColor:nil
+                                                             andImage:@"login_selectRido"
+                                                         AndBackColor:[UIColor clearColor]
+                                              adjustsFontSizesTowidth:NO
+                                                        masksToBounds:NO
+                                                         conrenRadius:0
+                                                          BorderColor:nil
+                                                          BorderWidth:0
+                                            ContentHorizontalAligment:0];
+        [_xxx_selectBtn setImage:[UIImage imageNamed:@"login_selectRido_select"] forState:UIControlStateSelected];
+    }
+    return _xxx_selectBtn;
+}
+-(YYLabel *)xxx_ClickLab{
+    if (!_xxx_ClickLab) {
+        _xxx_ClickLab = [YYLabel new];
+        _xxx_ClickLab.numberOfLines          = 0;
+        _xxx_ClickLab.textVerticalAlignment  = YYTextVerticalAlignmentCenter;
+        _xxx_ClickLab.frame = CGRectMake(60,  CGRectGetMaxY(self.xxx_subscribedBtn.frame)+10, KScreenWidth - 20-20-40, 40);
+        NSString *string = @"I Agree To Asteriahair Terms & Conditions And Privacy Policy.";
+        NSMutableAttributedString *attributed = [[NSMutableAttributedString alloc] initWithString:string];
+
+        YYTextHighlight *protocolHightlight = [[YYTextHighlight alloc] init];
+        [attributed yy_setTextHighlight:protocolHightlight range:[string rangeOfString:@"Terms & Conditions"]];
+        [attributed yy_setTextUnderline:[YYTextDecoration decorationWithStyle:YYTextLineStyleSingle] range:[string rangeOfString:@"Terms & Conditions"]];
+            
+        YYTextHighlight *layHightlight = [[YYTextHighlight alloc] init];
+        [attributed yy_setTextHighlight:layHightlight range:[string rangeOfString:@"Privacy Policy"]];
+        [attributed yy_setTextUnderline:[YYTextDecoration decorationWithStyle:YYTextLineStyleSingle] range:[string rangeOfString:@"Privacy Policy"]];
+
+        _xxx_ClickLab.attributedText = attributed;
+        _xxx_ClickLab.font = [UIFont systemFontOfSize:12];
+        _xxx_ClickLab.textColor = [UIColor colorWithHexString:@"#000000"];
+        @weakify(self)
+        _xxx_ClickLab.highlightTapAction = ^(UIView * _Nonnull containerView, NSAttributedString * _Nonnull text, NSRange range, CGRect rect) {
+            @strongify(self)
+            //wm_todo 确认跳转的网页
+            NSString *string = [text.string substringWithRange:range];
+            if ([string isEqualToString:@"Terms & Conditions"]) {
+//                [Fuction_Tool push_BaseWebUrl:@"https://www.westkiss.com/terms_conditions" webTitle:@"Terms & Conditions"];
+                
+            }else if([string isEqualToString:@"Privacy Policy"]){
+//                [self generaltriggermethodType:100 data:@{@"privacy":@"https://www.alipearlhair.com/privacy_policy"}];
+//                [Fuction_Tool push_BaseWebUrl:@"https://www.westkiss.com/privacy_policy" webTitle:@"Privacy Policy"];
+
+            }
+
+        };
+    }
+    return _xxx_ClickLab;
+}
+
+
+@end

+ 3 - 1
Asteria/Fuction/Login/V/LoginThirdAuthV.m

@@ -63,7 +63,9 @@
     CGSize imageSize = authBtn.imageView.frame.size;
     CGSize titleSize = authBtn.titleLabel.frame.size;
     
-    authBtn.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 132-imageSize.width-titleSize.width-20);
+    authBtn.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 132-imageSize.width-titleSize.width-20-10);
+    authBtn.imageEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
+
     return authBtn;
 }
 

Asteria/Fuction/Login/AS_LoginC.h → Asteria/Fuction/Login/VC/AS_LoginC.h


+ 64 - 4
Asteria/Fuction/Login/AS_LoginC.m

@@ -11,6 +11,7 @@
 #import "LoginThirdAuthV.h"
 
 #import <AFNetworking/AFNetworking.h>
+#import "AS_SignUpC.h"
 
 
 
@@ -23,7 +24,6 @@
 
 
 
-
 @end
 
 @implementation AS_LoginC
@@ -43,7 +43,7 @@
     UIImageView *Hi_imgV = [[UIImageView alloc]init];
     UIImage *hiImg = [UIImage imageNamed:@"login_HI"];
     Hi_imgV.image = hiImg;
-    Hi_imgV.frame = CGRectMake(20, CGRectGetMaxY(logNav.frame)+60, KScreenWidth-40, hiImg.size.height/hiImg.size.width*(KScreenWidth-40));
+    Hi_imgV.frame = CGRectMake(20, CGRectGetMaxY(logNav.frame)+30, KScreenWidth-40, hiImg.size.height/hiImg.size.width*(KScreenWidth-40));
     [self.scrollview addSubview:Hi_imgV];
     
     [self.scrollview addSubview:self.xxx_emailTFV];
@@ -51,15 +51,21 @@
     [self.scrollview bringSubviewToFront:self.xxx_emailTFV];
     [self.scrollview addSubview:self.xxx_logInBtn];
     [self.scrollview addSubview:self.xxx_authV];
+    UIView *bottomV = [self load_bottom_v];
+    [self.scrollview addSubview:bottomV];
+    
+    
     
     self.xxx_emailTFV.mj_y = CGRectGetMaxY(Hi_imgV.frame)+35;
     self.xxx_passwordV.frame = CGRectFlatMake(20, CGRectGetMaxY(self.xxx_emailTFV.frame)+30, KScreenWidth-40, 50+20);
     self.xxx_logInBtn.frame = CGRectMake(20, CGRectGetMaxY(self.xxx_passwordV.frame)+10, KScreenWidth-40, 45);
     self.xxx_authV.mj_y = CGRectGetMaxY(self.xxx_logInBtn.frame)+30;
+    bottomV.mj_y = CGRectGetMaxY(self.xxx_authV.frame)+30;
 }
 
+
 -(void)handle_closeEvent:(UIButton *)btn{
-    [self dismissViewControllerAnimated:YES completion:nil];
+    [self.navigationController dismissViewControllerAnimated:YES completion:nil];
 }
 -(void)handle_xxx_logInBtnEvent:(UIButton *)btn{//进行登录操作
     ///登录的跳转
@@ -99,8 +105,14 @@
     } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
          
     }];
- 
+}
+//注册页面
+-(void)handle_signUpEvent:(UIButton *)btn{
+    AS_SignUpC *upVC = [[AS_SignUpC alloc]init];
+    [self.navigationController pushViewController:upVC animated:YES];
     
+}
+-(void)handle_forgetBtnEvent:(UIButton *)btn{
     
 }
 
@@ -118,6 +130,8 @@
     headImg.image = bannerImg ;
     headImg.frame = CGRectMake((KScreenWidth- bannerImg.size.width)/2, (IPHONEX ? 44 : 20)+20 , bannerImg.size.width, bannerImg.size.height);
     [topView addSubview:headImg];
+    closeBtn.centerY = topView.centerY;
+    headImg.centerY = topView.centerY;
     return topView;
 }
 - (UIScrollView *)scrollview {
@@ -164,4 +178,50 @@
     return _xxx_authV;
 }
 
+
+-(UIView *)load_bottom_v{
+    UIView *bottomV = [[UIView alloc]initWithFrame:CGRectMake(20, 0, KScreenWidth-40, 32)];
+    QMUILabel *disLab = [[QMUILabel alloc]init];
+    disLab.text = @"Don't Have An Account?";
+    disLab.font = [UIFont fontWithName:Rob_Regular size:12];
+    [bottomV addSubview:disLab];
+    
+    UIButton *signUpBtn = [UIButton buttonWithType:UIButtonTypeCustom];
+    signUpBtn.backgroundColor = [UIColor clearColor];
+    NSMutableAttributedString *sinAtr = [[NSMutableAttributedString alloc]initWithString:@"Sign Up" attributes:@{NSUnderlineStyleAttributeName: [NSNumber numberWithInteger:NSUnderlineStyleSingle]}];
+    [signUpBtn setAttributedTitle:sinAtr forState:UIControlStateNormal];
+    [signUpBtn addTarget:self action:@selector(handle_signUpEvent:) forControlEvents:UIControlEventTouchUpInside];
+    [signUpBtn setTitleColor:ThemeColor forState:UIControlStateNormal];
+    signUpBtn.titleLabel.font = [UIFont fontWithName:Rob_Regular size:12];
+    [bottomV addSubview:signUpBtn];
+    
+    UIButton *forgetBtn = [UIButton buttonWithType:UIButtonTypeCustom];
+    forgetBtn.backgroundColor = [UIColor clearColor];
+    NSMutableAttributedString *forgotAtr = [[NSMutableAttributedString alloc]initWithString:@"Forgot Your Password" attributes:@{NSUnderlineStyleAttributeName: [NSNumber numberWithInteger:NSUnderlineStyleSingle]}];
+    [forgetBtn setAttributedTitle:forgotAtr forState:UIControlStateNormal];
+    [forgetBtn setTitleColor:ThemeColor forState:UIControlStateNormal];
+    forgetBtn.titleLabel.font = [UIFont fontWithName:Rob_Regular size:12];
+    [forgetBtn addTarget:self action:@selector(handle_forgetBtnEvent:) forControlEvents:UIControlEventTouchUpInside];
+    [bottomV addSubview:forgetBtn];
+    
+    [disLab mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.top.left.mas_equalTo(0);
+        make.height.mas_equalTo(16);
+    }];
+    [signUpBtn mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.centerY.equalTo(disLab);
+        make.left.equalTo(disLab.mas_right).offset(3);
+        make.height.mas_equalTo(20);
+        make.width.mas_equalTo(45);
+    }];
+    [forgetBtn mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.mas_equalTo(0);
+        make.top.equalTo(disLab.mas_bottom).offset(10);
+        make.height.mas_equalTo(15);
+        make.width.mas_equalTo(120);
+    }];
+    
+    return bottomV;
+}
+
 @end

+ 16 - 0
Asteria/Fuction/Login/VC/AS_SignUpC.h

@@ -0,0 +1,16 @@
+//
+//  AS_SignUpC.h
+//  Asteria
+//
+//  Created by 王猛 on 2023/5/17.
+//
+
+#import <WMBase/WMBase.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface AS_SignUpC : UCMBaseC
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 97 - 0
Asteria/Fuction/Login/VC/AS_SignUpC.m

@@ -0,0 +1,97 @@
+//
+//  AS_SignUpC.m
+//  Asteria
+//
+//  Created by 王猛 on 2023/5/17.
+//
+
+#import "AS_SignUpC.h"
+#import "LoginSignUpV.h"
+
+
+@interface AS_SignUpC () <UIScrollViewDelegate>
+@property (nonatomic, strong) UIScrollView *scrollview;
+@property (nonatomic, strong) LoginSignUpV *xxx_signUpV;
+@end
+
+@implementation AS_SignUpC
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    // Do any additional setup after loading the view.
+    self.customNavBar.hidden = YES;
+    self.navigationController.navigationBar.hidden = YES;
+    [self ucm_veiwTapBlock];
+}
+
+- (void)initSubviews{
+    [super initSubviews];
+    [self.view addSubview:self.scrollview];
+    UIView *logNav = [self logNavView];
+    [self.scrollview addSubview:logNav];
+    UIImageView *Hi_imgV = [[UIImageView alloc]init];
+    UIImage *hiImg = [UIImage imageNamed:@"login_signup"];
+    Hi_imgV.image = hiImg;
+    [self.scrollview addSubview:Hi_imgV];
+    [self.scrollview addSubview:self.xxx_signUpV];
+    Hi_imgV.frame = CGRectMake(20, CGRectGetMaxY(logNav.frame)+30, KScreenWidth-40, hiImg.size.height/hiImg.size.width*(KScreenWidth-40));
+    self.xxx_signUpV.frame = CGRectMake(0, CGRectGetMaxY(Hi_imgV.frame)+30, KScreenWidth, 500);
+    [self.xxx_signUpV sizeToFit];
+    NSLog(@"------%@",self.xxx_signUpV);
+}
+- (void)ucm_veiwTapBlock{
+    @weakify(self)
+    self.xxx_signUpV.ViewtapClose = ^(NSInteger num, id  _Nonnull data) {
+        @strongify(self)
+        [self reqNet_singup:(NSDictionary *)data];
+    };
+}
+//wm_todo 注册请求
+-(void)reqNet_singup:(NSDictionary *)dic{
+    
+}
+
+
+
+-(void)handle_closeEvent:(UIButton *)btn{
+    [self.navigationController popViewControllerAnimated:YES];
+}
+
+
+- (UIScrollView *)scrollview {
+    if (!_scrollview) {
+        _scrollview = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, KScreenWidth, KScreenHeight)];
+    }
+    return _scrollview;
+}
+
+
+
+-(UIView *)logNavView{
+    IPhoneXHeigh
+    UIView *topView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, KScreenWidth, securitytop_Y)];
+    UIButton *closeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
+    [closeBtn setImage:[UIImage imageNamed:@"common_close"] forState:UIControlStateNormal];
+    [closeBtn addTarget:self action:@selector(handle_closeEvent:) forControlEvents:UIControlEventTouchUpInside];
+    closeBtn.frame = CGRectMake(20, 44, 44, 44);
+    [topView addSubview:closeBtn];
+    UIImageView *headImg = [[UIImageView alloc]init];
+    UIImage *bannerImg = [UIImage imageNamed:@"common_headLogo"];
+    headImg.image = bannerImg ;
+    headImg.frame = CGRectMake((KScreenWidth- bannerImg.size.width)/2, 20 , bannerImg.size.width, bannerImg.size.height);
+    [topView addSubview:headImg];
+    
+    closeBtn.centerY = topView.centerY;
+    headImg.centerY = topView.centerY;
+    
+    return topView;
+}
+
+- (LoginSignUpV *)xxx_signUpV {
+    if (!_xxx_signUpV) {
+        _xxx_signUpV = [[LoginSignUpV alloc] init];
+    }
+    return _xxx_signUpV;
+}
+
+@end

+ 1 - 0
Asteria/PreFixHeader.h

@@ -22,6 +22,7 @@
 #import <SDWebImage/SDWebImage.h>
 #import <Masonry/Masonry.h>
 #import <Toast/Toast.h>
+#import <YYText/YYText.h>
 
 //#import <CTMediator/CTMediator.h>
 

+ 16 - 0
Asteria/ThirdPartService.h

@@ -0,0 +1,16 @@
+//
+//  ThirdPartService.h
+//  westkissMob
+//
+//  Created by 王猛 on 2022/9/5.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface ThirdPartService : NSObject
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 84 - 0
Asteria/ThirdPartService.m

@@ -0,0 +1,84 @@
+//
+//  ThirdPartService.m
+//  westkissMob
+//
+//  Created by 王猛 on 2022/9/5.
+//
+
+#import "ThirdPartService.h"
+#import <IQKeyboardManager/IQKeyboardManager.h>
+// 检测网络状态
+#import <Reachability/Reachability.h>
+@interface ThirdPartService()
+@property (strong, nonatomic) Reachability *internetReachability;
+@end
+
+@implementation ThirdPartService
++ (instancetype)sharedInstance {
+    static id sharedInstance = nil;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        sharedInstance = [[self alloc] init];
+    });
+    return sharedInstance;
+}
++ (void)load{
+    [[ThirdPartService sharedInstance] setupKeyboardManager];
+    [[ThirdPartService sharedInstance] setupReachability];
+
+}
+#pragma mark - 设置全局键盘
+- (void)setupKeyboardManager {
+    // 设置键盘监听管理
+    IQKeyboardManager *manager = [IQKeyboardManager sharedManager];
+    manager.toolbarDoneBarButtonItemText =@"Done";
+    manager.enable=YES;
+    manager.toolbarManageBehaviour =IQAutoToolbarByTag;
+    manager.shouldResignOnTouchOutside=NO;
+    manager.shouldToolbarUsesTextFieldTintColor=NO;
+    manager.enableAutoToolbar=YES;
+    manager.shouldShowToolbarPlaceholder = NO;
+    manager.shouldResignOnTouchOutside = YES; // 控制点击背景是否收起键盘
+}
+
+
+#pragma mark - 检测网络状态
+- (void)setupReachability {
+    //添加一个系统通知
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil];
+    //初始化
+    self.internetReachability=[Reachability reachabilityForInternetConnection];
+    //通知添加到Run Loop
+    [self.internetReachability startNotifier];
+    [self updateInterfaceWithReachability:_internetReachability];
+}
+- (void)reachabilityChanged:(NSNotification *)note {
+    Reachability* curReach = [note object];
+    if ([curReach isKindOfClass:[Reachability class]]) {
+        NSParameterAssert([curReach isKindOfClass: [Reachability class]]);
+        [self updateInterfaceWithReachability: curReach];
+    } else {
+        UIViewController *topVc = topViewController();
+        [topVc.view makeToast:@"Network status has been switched."];
+    }
+}
+
+- (void)updateInterfaceWithReachability:(Reachability *)reachability {
+    NetworkStatus netStatus = [reachability currentReachabilityStatus];
+    UIViewController *topVc = topViewController();
+    switch (netStatus) {
+        case NotReachable:
+            NSLog(@"====当前网络状态不可达=======");
+            [topVc.view makeToast:@"Current network status is unavailable"];
+            break;
+        case ReachableViaWiFi:
+            NSLog(@"====当前网络状态为Wifi=======");
+            [topVc.view makeToast:@"The current network status is WiFi."];
+            break;
+        case ReachableViaWWAN:
+            NSLog(@"====当前网络状态为蜂窝网络=======");
+            [topVc.view makeToast:@"The current status is cellular network."];
+            break;
+    }
+}
+@end

+ 20 - 1
Podfile.lock

@@ -623,6 +623,18 @@ PODS:
   - BoringSSL-GRPC/Implementation (0.0.24):
     - BoringSSL-GRPC/Interface (= 0.0.24)
   - BoringSSL-GRPC/Interface (0.0.24)
+  - BRPickerView (2.8.1):
+    - BRPickerView/AddressPickerView (= 2.8.1)
+    - BRPickerView/Base (= 2.8.1)
+    - BRPickerView/DatePickerView (= 2.8.1)
+    - BRPickerView/StringPickerView (= 2.8.1)
+  - BRPickerView/AddressPickerView (2.8.1):
+    - BRPickerView/Base
+  - BRPickerView/Base (2.8.1)
+  - BRPickerView/DatePickerView (2.8.1):
+    - BRPickerView/Base
+  - BRPickerView/StringPickerView (2.8.1):
+    - BRPickerView/Base
   - Bugly (2.5.93)
   - CTMediator (48)
   - FBAEMKit (16.0.0):
@@ -1113,6 +1125,7 @@ PODS:
   - Toast (4.0.0)
   - WMBase (1.0.9):
     - AFNetworking
+    - BRPickerView
     - CTMediator
     - Masonry
     - MBProgressHUD
@@ -1123,9 +1136,11 @@ PODS:
     - Toast
     - YTKNetwork
     - YYCache
+    - YYText
   - YTKNetwork (3.0.6):
     - AFNetworking/NSURLSession (~> 4.0)
   - YYCache (1.0.4)
+  - YYText (1.0.7)
 
 DEPENDENCIES:
   - AFNetworking
@@ -1157,6 +1172,7 @@ SPEC REPOS:
     - AFNetworking
     - AppAuth
     - BoringSSL-GRPC
+    - BRPickerView
     - Bugly
     - CTMediator
     - FBAEMKit
@@ -1195,6 +1211,7 @@ SPEC REPOS:
     - Toast
     - YTKNetwork
     - YYCache
+    - YYText
 
 EXTERNAL SOURCES:
   WMBase:
@@ -1205,6 +1222,7 @@ SPEC CHECKSUMS:
   AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58
   AppAuth: 8fca6b5563a5baef2c04bee27538025e4ceb2add
   BoringSSL-GRPC: 3175b25143e648463a56daeaaa499c6cb86dad33
+  BRPickerView: 2531a2d4d0fea0b57a1c738de215af0f88863a2f
   Bugly: b8715e6ec4004b7f7fbffab0643ba80545aee3da
   CTMediator: 3f3578b525b3a46a7a2f92a99922ec40363f4269
   FBAEMKit: 7fb5a0b5caf2ed2900e29c3a17de92ea7193a247
@@ -1241,9 +1259,10 @@ SPEC CHECKSUMS:
   Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
   SDWebImage: 302d4e14efff86b36b5f36d1bf891b635436d071
   Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
-  WMBase: eb93e38dff7d9be4edb267acef5b3bdc1bc4d3a8
+  WMBase: a0c4b37b0af79598c62864d8580991b7d4f651f2
   YTKNetwork: c16be90b06be003de9e9cd0d3b187cc8eaf35c04
   YYCache: 8105b6638f5e849296c71f331ff83891a4942952
+  YYText: 5c461d709e24d55a182d1441c41dc639a18a4849
 
 PODFILE CHECKSUM: 9e16269a21fd5c9c1688e530bba69cffe563a650
 

+ 51 - 0
Pods/BRPickerView/BRPickerView/AddressPickerView/BRAddressModel.h

@@ -0,0 +1,51 @@
+//
+//  BRAddressModel.h
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2017/8/11.
+//  Copyright © 2017 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// 省
+@interface BRProvinceModel : NSObject
+/** 省对应的code或id */
+@property (nullable, nonatomic, copy) NSString *code;
+/** 省的名称 */
+@property (nullable, nonatomic, copy) NSString *name;
+/** 城市数组 */
+@property (nullable, nonatomic, copy) NSArray *citylist;
+/** 记录省选择的索引位置 */
+@property (nonatomic, assign) NSInteger index;
+
+@end
+
+/// 市
+@interface BRCityModel : NSObject
+/** 市对应的code或id */
+@property (nullable, nonatomic, copy) NSString *code;
+/** 市的名称 */
+@property (nullable, nonatomic, copy) NSString *name;
+/** 地区数组 */
+@property (nullable, nonatomic, copy) NSArray *arealist;
+/** 记录市选择的索引位置 */
+@property (nonatomic, assign) NSInteger index;
+
+@end
+
+/// 区
+@interface BRAreaModel : NSObject
+/** 区对应的code或id */
+@property (nullable, nonatomic, copy) NSString *code;
+/** 区的名称 */
+@property (nullable, nonatomic, copy) NSString *name;
+/** 记录区选择的索引位置 */
+@property (nonatomic, assign) NSInteger index;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 24 - 0
Pods/BRPickerView/BRPickerView/AddressPickerView/BRAddressModel.m

@@ -0,0 +1,24 @@
+//
+//  BRAddressModel.m
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2017/8/11.
+//  Copyright © 2017 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import "BRAddressModel.h"
+
+@implementation BRProvinceModel
+
+@end
+
+
+@implementation BRCityModel
+
+@end
+
+
+@implementation BRAreaModel
+
+@end

Plik diff jest za duży
+ 13634 - 0
Pods/BRPickerView/BRPickerView/AddressPickerView/BRAddressPickerView.bundle/BRCity.json


+ 127 - 0
Pods/BRPickerView/BRPickerView/AddressPickerView/BRAddressPickerView.h

@@ -0,0 +1,127 @@
+//
+//  BRAddressPickerView.h
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2017/8/11.
+//  Copyright © 2017 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import "BRBaseView.h"
+#import "BRAddressModel.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// 地址选择器类型
+typedef NS_ENUM(NSInteger, BRAddressPickerMode) {
+    /** 显示【省市区】(默认) */
+    BRAddressPickerModeArea,
+    /** 显示【省市】 */
+    BRAddressPickerModeCity,
+    /** 显示【省】 */
+    BRAddressPickerModeProvince
+};
+
+typedef void(^BRAddressResultBlock)(BRProvinceModel * _Nullable province, BRCityModel * _Nullable city, BRAreaModel * _Nullable area);
+
+@interface BRAddressPickerView : BRBaseView
+
+/**
+ //////////////////////////////////////////////////////////////////////////
+ ///
+ ///   【用法一】
+ ///    特点:灵活,扩展性强(推荐使用!)
+ ///
+ ////////////////////////////////////////////////////////////////////////*/
+
+/** 地址选择器显示类型 */
+@property (nonatomic, assign) BRAddressPickerMode pickerMode;
+
+/** 默认选中的位置(1.传索引数组,如:@[@10, @0, @4]) */
+@property (nullable, nonatomic, copy) NSArray <NSNumber *> *selectIndexs;
+/** 默认选中的位置(2.传值数组,如:@[@"浙江省", @"杭州市", @"西湖区"]) */
+@property (nullable, nonatomic, copy) NSArray <NSString *> *selectValues;
+
+/** 选择结果的回调 */
+@property (nullable, nonatomic, copy) BRAddressResultBlock resultBlock;
+
+/** 滚动选择时触发的回调 */
+@property (nullable, nonatomic, copy) BRAddressResultBlock changeBlock;
+
+/**
+ *  地区数据源(不传或为nil,默认就获取本地 BRCity.json 文件的数据)
+ *  1.可以传 JSON数组,要注意 层级结构 和 key 要与 BRCity.json 保持一致
+ *  2.可以传 模型数组(NSArray <BRProvinceModel *> * 类型),自己解析数据源 只需要注意层级结构就行
+ */
+@property (nullable, nonatomic, copy) NSArray *dataSourceArr;
+
+/// 初始化地址选择器
+/// @param pickerMode 地址选择器显示类型
+- (instancetype)initWithPickerMode:(BRAddressPickerMode)pickerMode;
+
+/// 弹出选择器视图
+- (void)show;
+
+/// 关闭选择器视图
+- (void)dismiss;
+
+
+
+
+//================================================= 华丽的分割线 =================================================
+
+
+
+
+/**
+ //////////////////////////////////////////////////////////////////////////
+ ///
+ ///   【用法二】:快捷使用,直接选择下面其中的一个方法进行使用
+ ///    特点:快捷,方便
+ ///
+ ////////////////////////////////////////////////////////////////////////*/
+
+/**
+ *  1.显示地址选择器
+ *
+ *  @param selectIndexs             默认选中的值(传索引数组,如:@[@10, @0, @4])
+ *  @param resultBlock              选择后的回调
+ *
+ */
++ (void)showAddressPickerWithSelectIndexs:(nullable NSArray <NSNumber *> *)selectIndexs
+                              resultBlock:(nullable BRAddressResultBlock)resultBlock;
+
+/**
+ *  2.显示地址选择器
+ *
+ *  @param mode                     地址选择器显示类型
+ *  @param selectIndexs             默认选中的值(传索引数组,如:@[@10, @0, @4])
+ *  @param isAutoSelect             是否自动选择,即滚动选择器后就执行结果回调,默认为 NO
+ *  @param resultBlock              选择后的回调
+ *
+ */
++ (void)showAddressPickerWithMode:(BRAddressPickerMode)mode
+                     selectIndexs:(nullable NSArray <NSNumber *> *)selectIndexs
+                     isAutoSelect:(BOOL)isAutoSelect
+                      resultBlock:(nullable BRAddressResultBlock)resultBlock;
+
+/**
+ *  3.显示地址选择器
+ *
+ *  @param mode                     地址选择器显示类型
+ *  @param dataSource               地区数据源
+ *  @param selectIndexs             默认选中的值(传索引数组,如:@[@10, @0, @4])
+ *  @param isAutoSelect             是否自动选择,即滚动选择器后就执行结果回调,默认为 NO
+ *  @param resultBlock              选择后的回调
+ *
+ */
++ (void)showAddressPickerWithMode:(BRAddressPickerMode)mode
+                       dataSource:(nullable NSArray *)dataSource
+                     selectIndexs:(nullable NSArray <NSNumber *> *)selectIndexs
+                     isAutoSelect:(BOOL)isAutoSelect
+                      resultBlock:(nullable BRAddressResultBlock)resultBlock;
+
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 562 - 0
Pods/BRPickerView/BRPickerView/AddressPickerView/BRAddressPickerView.m

@@ -0,0 +1,562 @@
+//
+//  BRAddressPickerView.m
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2017/8/11.
+//  Copyright © 2017 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import "BRAddressPickerView.h"
+#import "NSBundle+BRPickerView.h"
+
+@interface BRAddressPickerView ()<UIPickerViewDataSource, UIPickerViewDelegate>
+// 地址选择器
+@property (nonatomic, strong) UIPickerView *pickerView;
+// 省模型数组
+@property(nonatomic, copy) NSArray *provinceModelArr;
+// 市模型数组
+@property(nonatomic, copy) NSArray *cityModelArr;
+// 区模型数组
+@property(nonatomic, copy) NSArray *areaModelArr;
+// 选中的省
+@property(nonatomic, strong) BRProvinceModel *selectProvinceModel;
+// 选中的市
+@property(nonatomic, strong) BRCityModel *selectCityModel;
+// 选中的区
+@property(nonatomic, strong) BRAreaModel *selectAreaModel;
+// 记录省选中的位置
+@property(nonatomic, assign) NSInteger provinceIndex;
+// 记录市选中的位置
+@property(nonatomic, assign) NSInteger cityIndex;
+// 记录区选中的位置
+@property(nonatomic, assign) NSInteger areaIndex;
+
+@property (nonatomic, copy) NSArray <NSString *>* mSelectValues;
+
+@end
+
+@implementation BRAddressPickerView
+
+#pragma mark - 1.显示地址选择器
++ (void)showAddressPickerWithSelectIndexs:(NSArray <NSNumber *>*)selectIndexs
+                              resultBlock:(BRAddressResultBlock)resultBlock {
+    [self showAddressPickerWithMode:BRAddressPickerModeArea dataSource:nil selectIndexs:selectIndexs isAutoSelect:NO resultBlock:resultBlock];
+}
+
+#pragma mark - 2.显示地址选择器
++ (void)showAddressPickerWithMode:(BRAddressPickerMode)mode
+                     selectIndexs:(NSArray <NSNumber *>*)selectIndexs
+                     isAutoSelect:(BOOL)isAutoSelect
+                      resultBlock:(BRAddressResultBlock)resultBlock {
+    [self showAddressPickerWithMode:mode dataSource:nil selectIndexs:selectIndexs isAutoSelect:isAutoSelect resultBlock:resultBlock];
+}
+
+
+#pragma mark - 3.显示地址选择器
++ (void)showAddressPickerWithMode:(BRAddressPickerMode)mode
+                       dataSource:(NSArray *)dataSource
+                     selectIndexs:(NSArray <NSNumber *>*)selectIndexs
+                     isAutoSelect:(BOOL)isAutoSelect
+                      resultBlock:(BRAddressResultBlock)resultBlock {
+    // 创建地址选择器
+    BRAddressPickerView *addressPickerView = [[BRAddressPickerView alloc] initWithPickerMode:mode];
+    addressPickerView.dataSourceArr = dataSource;
+    addressPickerView.selectIndexs = selectIndexs;
+    addressPickerView.isAutoSelect = isAutoSelect;
+    addressPickerView.resultBlock = resultBlock;
+    // 显示
+    [addressPickerView show];
+}
+
+#pragma mark - 初始化地址选择器
+- (instancetype)initWithPickerMode:(BRAddressPickerMode)pickerMode {
+    if (self = [super init]) {
+        self.pickerMode = pickerMode;
+    }
+    return self;
+}
+
+#pragma mark - 处理选择器数据
+- (void)handlerPickerData {
+    if (self.dataSourceArr && self.dataSourceArr.count > 0) {
+        id item = [self.dataSourceArr firstObject];
+        // 如果传的值是解析好的模型数组
+        if ([item isKindOfClass:[BRProvinceModel class]]) {
+            self.provinceModelArr = self.dataSourceArr;
+        } else {
+            self.provinceModelArr = [self getProvinceModelArr:self.dataSourceArr];
+        }
+    } else {
+        // 如果外部没有传入地区数据源,就使用本地的数据源
+        NSArray *dataSource = [self br_addressJsonArray];
+        
+        if (!dataSource || dataSource.count == 0) {
+            return;
+        }
+        self.dataSourceArr = dataSource;
+        self.provinceModelArr = [self getProvinceModelArr:self.dataSourceArr];
+    }
+    
+    // 设置默认值
+    [self handlerDefaultSelectValue];
+}
+
+#pragma mark - 获取城市JSON数据
+- (NSArray *)br_addressJsonArray {
+    static NSArray *cityArray = nil;
+    if (!cityArray) {
+        // 获取 BRAddressPickerView.bundle
+        NSBundle *containnerBundle = [NSBundle bundleForClass:[BRAddressPickerView class]];
+        NSString *bundlePath = [containnerBundle pathForResource:@"BRAddressPickerView" ofType:@"bundle"];
+        NSBundle *addressPickerBundle = [NSBundle bundleWithPath:bundlePath];
+        
+        // 获取bundle中的JSON文件
+        NSString *filePath = [addressPickerBundle pathForResource:@"BRCity" ofType:@"json"];
+        NSData *data = [NSData dataWithContentsOfFile:filePath];
+        cityArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
+    }
+    return cityArray;
+}
+
+#pragma mark - 获取模型数组
+- (NSArray <BRProvinceModel *>*)getProvinceModelArr:(NSArray *)dataSourceArr {
+    NSMutableArray *tempArr1 = [NSMutableArray array];
+    for (NSDictionary *proviceDic in dataSourceArr) {
+        BRProvinceModel *proviceModel = [[BRProvinceModel alloc]init];
+        proviceModel.code = [proviceDic objectForKey:@"code"];
+        proviceModel.name = [proviceDic objectForKey:@"name"];
+        proviceModel.index = [dataSourceArr indexOfObject:proviceDic];
+        NSArray *cityList = [proviceDic.allKeys containsObject:@"cityList"] ? [proviceDic objectForKey:@"cityList"] : [proviceDic objectForKey:@"citylist"];
+        NSMutableArray *tempArr2 = [NSMutableArray array];
+        for (NSDictionary *cityDic in cityList) {
+            BRCityModel *cityModel = [[BRCityModel alloc]init];
+            cityModel.code = [cityDic objectForKey:@"code"];
+            cityModel.name = [cityDic objectForKey:@"name"];
+            cityModel.index = [cityList indexOfObject:cityDic];
+            NSArray *areaList = [cityDic.allKeys containsObject:@"areaList"] ? [cityDic objectForKey:@"areaList"] : [cityDic objectForKey:@"arealist"];
+            NSMutableArray *tempArr3 = [NSMutableArray array];
+            for (NSDictionary *areaDic in areaList) {
+                BRAreaModel *areaModel = [[BRAreaModel alloc]init];
+                areaModel.code = [areaDic objectForKey:@"code"];
+                areaModel.name = [areaDic objectForKey:@"name"];
+                areaModel.index = [areaList indexOfObject:areaDic];
+                [tempArr3 addObject:areaModel];
+            }
+            cityModel.arealist = [tempArr3 copy];
+            [tempArr2 addObject:cityModel];
+        }
+        proviceModel.citylist = [tempArr2 copy];
+        [tempArr1 addObject:proviceModel];
+    }
+    return [tempArr1 copy];
+}
+
+#pragma mark - 设置默认选择的值
+- (void)handlerDefaultSelectValue {
+    __block NSString *selectProvinceName = nil;
+    __block NSString *selectCityName = nil;
+    __block NSString *selectAreaName = nil;
+    
+    if (self.mSelectValues.count > 0) {
+        selectProvinceName = self.mSelectValues.count > 0 ? self.mSelectValues[0] : nil;
+        selectCityName = self.mSelectValues.count > 1 ? self.mSelectValues[1] : nil;
+        selectAreaName = self.mSelectValues.count > 2 ? self.mSelectValues[2] : nil;
+    }
+    
+    __weak typeof(self) weakSelf = self;
+    
+    if (self.pickerMode == BRAddressPickerModeProvince || self.pickerMode == BRAddressPickerModeCity || self.pickerMode == BRAddressPickerModeArea) {
+        if (self.selectIndexs.count > 0) {
+            NSInteger provinceIndex = [self.selectIndexs[0] integerValue];
+            self.provinceIndex = (provinceIndex > 0 && provinceIndex < self.provinceModelArr.count) ? provinceIndex : 0;
+            self.selectProvinceModel = self.provinceModelArr.count > self.provinceIndex ? self.provinceModelArr[self.provinceIndex] : nil;
+        } else {
+            self.provinceIndex = 0;
+            self.selectProvinceModel = self.provinceModelArr.count > 0 ? self.provinceModelArr[0] : nil;
+            [self.provinceModelArr enumerateObjectsUsingBlock:^(BRProvinceModel *  _Nonnull model, NSUInteger idx, BOOL * _Nonnull stop) {
+                if (selectProvinceName && [model.name isEqualToString:selectProvinceName]) {
+                    weakSelf.provinceIndex = idx;
+                    weakSelf.selectProvinceModel = model;
+                    *stop = YES;
+                }
+            }];
+        }
+    }
+    
+    if (self.pickerMode == BRAddressPickerModeCity || self.pickerMode == BRAddressPickerModeArea) {
+        self.cityModelArr = [self getCityModelArray:self.provinceIndex];
+        if (self.selectIndexs.count > 0) {
+            NSInteger cityIndex = self.selectIndexs.count > 1 ? [self.selectIndexs[1] integerValue] : 0;
+            self.cityIndex = (cityIndex > 0 && cityIndex < self.cityModelArr.count) ? cityIndex : 0;
+            self.selectCityModel = self.cityModelArr.count > self.cityIndex ? self.cityModelArr[self.cityIndex] : nil;
+        } else {
+            self.cityIndex = 0;
+            self.selectCityModel = self.cityModelArr.count > 0 ? self.cityModelArr[0] : nil;
+            [self.cityModelArr enumerateObjectsUsingBlock:^(BRCityModel *  _Nonnull model, NSUInteger idx, BOOL * _Nonnull stop) {
+                if (selectCityName && [model.name isEqualToString:selectCityName]) {
+                    weakSelf.cityIndex = idx;
+                    weakSelf.selectCityModel = model;
+                    *stop = YES;
+                }
+            }];
+        }
+    }
+    
+    if (self.pickerMode == BRAddressPickerModeArea) {
+        self.areaModelArr = [self getAreaModelArray:self.provinceIndex cityIndex:self.cityIndex];
+        if (self.selectIndexs.count > 0) {
+            NSInteger areaIndex = self.selectIndexs.count > 2 ? [self.selectIndexs[2] integerValue] : 0;
+            self.areaIndex = (areaIndex > 0 && areaIndex < self.areaModelArr.count) ? areaIndex : 0;
+            self.selectAreaModel = self.areaModelArr.count > self.areaIndex ? self.areaModelArr[self.areaIndex] : nil;
+        } else {
+            self.areaIndex = 0;
+            self.selectAreaModel = self.areaModelArr.count > 0 ? self.areaModelArr[0] : nil;
+            [self.areaModelArr enumerateObjectsUsingBlock:^(BRAreaModel *  _Nonnull model, NSUInteger idx, BOOL * _Nonnull stop) {
+                if (selectAreaName && [model.name isEqualToString:selectAreaName]) {
+                    weakSelf.areaIndex = idx;
+                    weakSelf.selectAreaModel = model;
+                    *stop = YES;
+                }
+            }];
+        }
+    }
+}
+
+// 根据 省索引 获取 城市模型数组
+- (NSArray *)getCityModelArray:(NSInteger)provinceIndex {
+    BRProvinceModel *provinceModel = self.provinceModelArr[provinceIndex];
+    // 返回城市模型数组
+    return provinceModel.citylist;
+}
+
+// 根据 省索引和城市索引 获取 区域模型数组
+- (NSArray *)getAreaModelArray:(NSInteger)provinceIndex cityIndex:(NSInteger)cityIndex {
+    BRProvinceModel *provinceModel = self.provinceModelArr[provinceIndex];
+    if (provinceModel.citylist && provinceModel.citylist.count > 0) {
+        BRCityModel *cityModel = provinceModel.citylist[cityIndex];
+        // 返回地区模型数组
+        return cityModel.arealist;
+    } else {
+        return nil;
+    }
+}
+
+#pragma mark - 地址选择器
+- (UIPickerView *)pickerView {
+    if (!_pickerView) {
+        CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
+        _pickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, self.pickerStyle.titleBarHeight + pickerHeaderViewHeight, self.keyView.bounds.size.width, self.pickerStyle.pickerHeight)];
+        _pickerView.backgroundColor = self.pickerStyle.pickerColor;
+        _pickerView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
+        _pickerView.dataSource = self;
+        _pickerView.delegate = self;
+    }
+    return _pickerView;
+}
+
+#pragma mark - UIPickerViewDataSource
+// 1.设置 pickerView 的列数
+- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
+    switch (self.pickerMode) {
+        case BRAddressPickerModeProvince:
+            return 1;
+            break;
+        case BRAddressPickerModeCity:
+            return 2;
+            break;
+        case BRAddressPickerModeArea:
+            return 3;
+            break;
+            
+        default:
+            break;
+    }
+}
+
+// 2.设置 pickerView 每列的行数
+- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
+    if (component == 0) {
+        // 返回省个数
+        return self.provinceModelArr.count;
+    }
+    if (component == 1) {
+        // 返回市个数
+        return self.cityModelArr.count;
+    }
+    if (component == 2) {
+        // 返回区个数
+        return self.areaModelArr.count;
+    }
+    return 0;
+}
+
+#pragma mark - UIPickerViewDelegate
+// 3.设置 pickerView 的显示内容
+- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(nullable UIView *)view {
+    // 1.自定义 row 的内容视图
+    UILabel *label = (UILabel *)view;
+    if (!label) {
+        label = [[UILabel alloc]init];
+        label.backgroundColor = [UIColor clearColor];
+        label.textAlignment = NSTextAlignmentCenter;
+        label.font = self.pickerStyle.pickerTextFont;
+        label.textColor = self.pickerStyle.pickerTextColor;
+        // 字体自适应属性
+        label.adjustsFontSizeToFitWidth = YES;
+        // 自适应最小字体缩放比例
+        label.minimumScaleFactor = 0.5f;
+    }
+    if (component == 0) {
+        BRProvinceModel *model = self.provinceModelArr[row];
+        label.text = model.name;
+    } else if (component == 1) {
+        BRCityModel *model = self.cityModelArr[row];
+        label.text = model.name;
+    } else if (component == 2) {
+        BRAreaModel *model = self.areaModelArr[row];
+        label.text = model.name;
+    }
+    
+    // 2.设置选择器中间选中行的样式
+    [self.pickerStyle setupPickerSelectRowStyle:pickerView titleForRow:row forComponent:component];
+    
+    return label;
+}
+
+// 4.滚动 pickerView 执行的回调方法
+- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
+    if (component == 0) { // 选择省
+        // 保存选择的省份的索引
+        self.provinceIndex = row;
+        switch (self.pickerMode) {
+            case BRAddressPickerModeProvince:
+            {
+                self.selectProvinceModel = self.provinceModelArr.count > self.provinceIndex ? self.provinceModelArr[self.provinceIndex] : nil;
+                self.selectCityModel = nil;
+                self.selectAreaModel = nil;
+            }
+                break;
+            case BRAddressPickerModeCity:
+            {
+                self.cityModelArr = [self getCityModelArray:self.provinceIndex];
+                [self.pickerView reloadComponent:1];
+                [self.pickerView selectRow:0 inComponent:1 animated:YES];
+                self.selectProvinceModel = self.provinceModelArr.count > self.provinceIndex ? self.provinceModelArr[self.provinceIndex] : nil;
+                self.selectCityModel = self.cityModelArr.count > 0 ? self.cityModelArr[0] : nil;
+                self.selectAreaModel = nil;
+            }
+                break;
+            case BRAddressPickerModeArea:
+            {
+                self.cityModelArr = [self getCityModelArray:self.provinceIndex];
+                self.areaModelArr = [self getAreaModelArray:self.provinceIndex cityIndex:0];
+                [self.pickerView reloadComponent:1];
+                [self.pickerView selectRow:0 inComponent:1 animated:YES];
+                [self.pickerView reloadComponent:2];
+                [self.pickerView selectRow:0 inComponent:2 animated:YES];
+                self.selectProvinceModel = self.provinceModelArr.count > self.provinceIndex ? self.provinceModelArr[self.provinceIndex] : nil;
+                self.selectCityModel = self.cityModelArr.count > 0 ? self.cityModelArr[0] : nil;
+                self.selectAreaModel = self.areaModelArr.count > 0 ? self.areaModelArr[0] : nil;
+            }
+                break;
+            default:
+                break;
+        }
+    }
+    if (component == 1) { // 选择市
+        // 保存选择的城市的索引
+        self.cityIndex = row;
+        switch (self.pickerMode) {
+            case BRAddressPickerModeCity:
+            {
+                self.selectCityModel = self.cityModelArr.count > self.cityIndex ? self.cityModelArr[self.cityIndex] : nil;
+                self.selectAreaModel = nil;
+            }
+                break;
+            case BRAddressPickerModeArea:
+            {
+                self.areaModelArr = [self getAreaModelArray:self.provinceIndex cityIndex:self.cityIndex];
+                [self.pickerView reloadComponent:2];
+                [self.pickerView selectRow:0 inComponent:2 animated:YES];
+                self.selectCityModel = self.cityModelArr.count > self.cityIndex ? self.cityModelArr[self.cityIndex] : nil;
+                self.selectAreaModel = self.areaModelArr.count > 0 ? self.areaModelArr[0] : nil;
+            }
+                break;
+            default:
+                break;
+        }
+    }
+    if (component == 2) { // 选择区
+        // 保存选择的地区的索引
+        self.areaIndex = row;
+        if (self.pickerMode == BRAddressPickerModeArea) {
+            self.selectAreaModel = self.areaModelArr.count > self.areaIndex ? self.areaModelArr[self.areaIndex] : nil;
+        }
+    }
+    
+    // 滚动选择时执行 changeBlock
+    if (self.changeBlock) {
+        self.changeBlock(self.selectProvinceModel, self.selectCityModel, self.selectAreaModel);
+    }
+    
+    // 设置自动选择时,滚动选择时就执行 resultBlock
+    if (self.isAutoSelect) {
+        if (self.resultBlock) {
+            self.resultBlock(self.selectProvinceModel, self.selectCityModel, self.selectAreaModel);
+        }
+    }
+}
+
+// 设置行高
+- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component {
+    return self.pickerStyle.rowHeight;
+}
+
+#pragma mark - 重写父类方法
+- (void)reloadData {
+    // 1.处理数据源
+    [self handlerPickerData];
+    // 2.刷新选择器
+    [self.pickerView reloadAllComponents];
+    // 3.滚动到选择的地区
+    if (self.pickerMode == BRAddressPickerModeProvince) {
+        [self.pickerView selectRow:self.provinceIndex inComponent:0 animated:YES];
+    } else if (self.pickerMode == BRAddressPickerModeCity) {
+        [self.pickerView selectRow:self.provinceIndex inComponent:0 animated:YES];
+        [self.pickerView selectRow:self.cityIndex inComponent:1 animated:YES];
+    } else if (self.pickerMode == BRAddressPickerModeArea) {
+        [self.pickerView selectRow:self.provinceIndex inComponent:0 animated:YES];
+        [self.pickerView selectRow:self.cityIndex inComponent:1 animated:YES];
+        [self.pickerView selectRow:self.areaIndex inComponent:2 animated:YES];
+    }
+}
+
+- (void)addPickerToView:(UIView *)view {
+    // 1.添加地址选择器
+    if (view) {
+        // 立即刷新容器视图 view 的布局(防止 view 使用自动布局时,选择器视图无法正常显示)
+        [view setNeedsLayout];
+        [view layoutIfNeeded];
+        
+        self.frame = view.bounds;
+        CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
+        CGFloat pickerFooterViewHeight = self.pickerFooterView ? self.pickerFooterView.bounds.size.height : 0;
+        self.pickerView.frame = CGRectMake(0, pickerHeaderViewHeight, view.bounds.size.width, view.bounds.size.height - pickerHeaderViewHeight - pickerFooterViewHeight);
+        [self addSubview:self.pickerView];
+    } else {
+        // iOS16:重新设置 pickerView 高度(解决懒加载设置frame不生效问题)
+        CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
+        self.pickerView.frame = CGRectMake(0, self.pickerStyle.titleBarHeight + pickerHeaderViewHeight, self.keyView.bounds.size.width, self.pickerStyle.pickerHeight);
+        
+        [self.alertView addSubview:self.pickerView];
+    }
+    
+    // ③添加中间选择行的两条分割线
+    if (self.pickerStyle.clearPickerNewStyle) {
+        [self.pickerStyle addSeparatorLineView:self.pickerView];
+    }
+    
+    // 2.绑定数据
+    [self reloadData];
+    
+    __weak typeof(self) weakSelf = self;
+    self.doneBlock = ^{
+        // 点击确定按钮后,执行block回调
+        if (weakSelf.resultBlock) {
+            weakSelf.resultBlock(weakSelf.selectProvinceModel, weakSelf.selectCityModel, weakSelf.selectAreaModel);
+        }
+    };
+    
+    [super addPickerToView:view];
+}
+
+#pragma mark - 重写父类方法
+- (void)addSubViewToPicker:(UIView *)customView {
+    [self.pickerView addSubview:customView];
+}
+
+#pragma mark - 弹出选择器视图
+- (void)show {
+    [self addPickerToView:nil];
+}
+
+#pragma mark - 关闭选择器视图
+- (void)dismiss {
+    [self removePickerFromView:nil];
+}
+
+#pragma mark - setter方法
+- (void)setSelectValues:(NSArray<NSString *> *)selectValues {
+    self.mSelectValues = selectValues;
+}
+
+#pragma mark - getter方法
+- (NSArray *)provinceModelArr {
+    if (!_provinceModelArr) {
+        _provinceModelArr = [NSArray array];
+    }
+    return _provinceModelArr;
+}
+
+- (NSArray *)cityModelArr {
+    if (!_cityModelArr) {
+        _cityModelArr = [NSArray array];
+    }
+    return _cityModelArr;
+}
+
+- (NSArray *)areaModelArr {
+    if (!_areaModelArr) {
+        _areaModelArr = [NSArray array];
+    }
+    return _areaModelArr;
+}
+
+- (BRProvinceModel *)selectProvinceModel {
+    if (!_selectProvinceModel) {
+        _selectProvinceModel = [[BRProvinceModel alloc]init];
+    }
+    return _selectProvinceModel;
+}
+
+- (BRCityModel *)selectCityModel {
+    if (!_selectCityModel) {
+        _selectCityModel = [[BRCityModel alloc]init];
+        _selectCityModel.code = @"";
+        _selectCityModel.name = @"";
+    }
+    return _selectCityModel;
+}
+
+- (BRAreaModel *)selectAreaModel {
+    if (!_selectAreaModel) {
+        _selectAreaModel = [[BRAreaModel alloc]init];
+        _selectAreaModel.code = @"";
+        _selectAreaModel.name = @"";
+    }
+    return _selectAreaModel;
+}
+
+- (NSArray *)dataSourceArr {
+    if (!_dataSourceArr) {
+        _dataSourceArr = [NSArray array];
+    }
+    return _dataSourceArr;
+}
+
+- (NSArray<NSString *> *)mSelectValues {
+    if (!_mSelectValues) {
+        _mSelectValues = [NSArray array];
+    }
+    return _mSelectValues;
+}
+
+- (NSArray<NSNumber *> *)selectIndexs {
+    if (!_selectIndexs) {
+        _selectIndexs = [NSArray array];
+    }
+    return _selectIndexs;
+}
+
+@end

+ 17 - 0
Pods/BRPickerView/BRPickerView/BRPickerView.h

@@ -0,0 +1,17 @@
+//
+//  BRPickerView.h
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2017/8/11.
+//  Copyright © 2017 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#ifndef BRPickerView_h
+#define BRPickerView_h
+
+#import "BRDatePickerView.h"
+#import "BRAddressPickerView.h"
+#import "BRStringPickerView.h"
+
+#endif /* BRPickerView_h */

+ 76 - 0
Pods/BRPickerView/BRPickerView/Base/BRBaseView.h

@@ -0,0 +1,76 @@
+//
+//  BaseView.h
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2017/8/11.
+//  Copyright © 2017 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import <UIKit/UIKit.h>
+#import "BRPickerStyle.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef void(^BRCancelBlock)(void);
+typedef void(^BRDoneClickBlock)(void);
+
+@interface BRBaseView : UIView
+
+/** 选择器标题 */
+@property (nullable, nonatomic, copy) NSString *title;
+
+/** 是否自动选择,即滚动选择器后就执行结果回调,默认为 NO */
+@property (nonatomic, assign) BOOL isAutoSelect;
+
+/** 自定义UI样式(不传或为nil时,是默认样式) */
+@property (nullable, nonatomic, strong) BRPickerStyle *pickerStyle;
+
+/** 取消选择的回调 */
+@property (nullable, nonatomic, copy) BRCancelBlock cancelBlock;
+
+/** accessory view for above picker view. default is nil */
+@property (nullable, nonatomic, strong) UIView *pickerHeaderView;
+
+/** accessory view below picker view. default is nil */
+@property (nullable, nonatomic, strong) UIView *pickerFooterView;
+
+/// 确定按钮点击事件的回调
+/// 应用场景:如果是自定义确定按钮,需要在该按钮点击事件方法里,执行一下 doneBlock 回调。目的是触发组件内部执行 resultBlock 回调,回调选择的值
+@property (nullable, nonatomic, copy) BRDoneClickBlock doneBlock;
+
+/** 弹框视图(使用场景:可以在 alertView 上添加选择器的自定义背景视图) */
+@property (nullable, nonatomic, strong) UIView *alertView;
+
+/** 组件的父视图:可以传 自己获取的 keyWindow,或页面的 view */
+@property (nullable, nonatomic, strong) UIView *keyView;
+
+
+/// 刷新选择器数据
+/// 应用场景:动态更新数据源、动态更新选择的值、选择器类型切换等
+- (void)reloadData;
+
+/// 扩展一:添加选择器到指定容器视图上
+/// 应用场景:可将中间的滚轮选择器 pickerView 视图(不包含蒙层及标题栏)添加到任何自定义视图上(会自动填满容器视图),也方便自定义更多的弹框样式
+/// 补充说明:如果是自定义确定按钮,需要回调默认选择的值:只需在自定义确定按钮的点击事件方法里执行一下 doneBlock 回调(目的是去触发组件内部执行 resultBlock 回调,进而回调默认选择的值)
+/// @param view 容器视图
+- (void)addPickerToView:(nullable UIView *)view NS_REQUIRES_SUPER;
+
+/// 从指定容器视图上移除选择器
+/// @param view 容器视图
+- (void)removePickerFromView:(nullable UIView *)view;
+
+/// 扩展二:添加自定义视图到选择器(pickerView)上
+/// 应用场景:可以添加一些固定的标题、单位等到选择器中间
+/// @param customView 自定义视图
+- (void)addSubViewToPicker:(UIView *)customView;
+
+/// 扩展三:添加自定义视图到标题栏(titleBarView)上
+/// 应用场景:可以添加一些子控件到标题栏
+/// @param customView 自定义视图
+- (void)addSubViewToTitleBar:(UIView *)customView;
+
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 398 - 0
Pods/BRPickerView/BRPickerView/Base/BRBaseView.m

@@ -0,0 +1,398 @@
+//
+//  BaseView.m
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2017/8/11.
+//  Copyright © 2017 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import "BRBaseView.h"
+
+@interface BRBaseView ()
+// 蒙层视图
+@property (nonatomic, strong) UIView *maskView;
+// 标题栏背景视图
+@property (nonatomic, strong) UIView *titleBarView;
+// 左边取消按钮
+@property (nonatomic, strong) UIButton *cancelBtn;
+// 右边确定按钮
+@property (nonatomic, strong) UIButton *doneBtn;
+// 中间标题
+@property (nonatomic, strong) UILabel *titleLabel;
+
+// 取消按钮离屏幕边缘的距离
+@property (nonatomic, assign) CGFloat cancelBtnMargin;
+// 确定按钮离屏幕边缘的距离
+@property (nonatomic, assign) CGFloat doneBtnMargin;
+
+@end
+
+@implementation BRBaseView
+
+- (void)initUI {
+    self.frame = self.keyView.bounds;
+    // 设置子视图的宽度随着父视图变化
+    self.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+    
+    if (!self.pickerStyle.hiddenMaskView) {
+        [self addSubview:self.maskView];
+    }
+    
+    [self addSubview:self.alertView];
+    
+    // 是否隐藏标题栏
+    if (!self.pickerStyle.hiddenTitleBarView) {
+        [self.alertView addSubview:self.titleBarView];
+        [self.alertView sendSubviewToBack:self.titleBarView];
+
+        if (!self.pickerStyle.hiddenTitleLabel) {
+            [self.titleBarView addSubview:self.titleLabel];
+        }
+        if (!self.pickerStyle.hiddenCancelBtn) {
+            [self.titleBarView addSubview:self.cancelBtn];
+            // 获取边距
+            if (self.pickerStyle.cancelBtnFrame.origin.x < self.bounds.size.width / 2) {
+                self.cancelBtnMargin = self.pickerStyle.cancelBtnFrame.origin.x;
+            } else {
+                self.cancelBtnMargin = self.bounds.size.width - self.pickerStyle.cancelBtnFrame.origin.x - self.pickerStyle.cancelBtnFrame.size.width;
+            }
+        }
+        if (!self.pickerStyle.hiddenDoneBtn) {
+            [self.titleBarView addSubview:self.doneBtn];
+            // 获取边距
+            if (self.pickerStyle.doneBtnFrame.origin.x < self.bounds.size.width / 2) {
+                self.doneBtnMargin = self.pickerStyle.doneBtnFrame.origin.x;
+            } else {
+                self.doneBtnMargin = self.bounds.size.width - self.pickerStyle.doneBtnFrame.origin.x - self.pickerStyle.doneBtnFrame.size.width;
+            }
+        }
+    }
+}
+
+#pragma mark - 适配横屏安全区域,更新子视图布局
+- (void)layoutSubviews {
+    [super layoutSubviews];
+    if (_cancelBtn || _doneBtn) {
+        if (@available(iOS 11.0, *)) {
+            UIEdgeInsets safeInsets = self.safeAreaInsets;
+            if (_cancelBtn) {
+                CGRect cancelBtnFrame = self.pickerStyle.cancelBtnFrame;
+                if (cancelBtnFrame.origin.x < MIN(self.bounds.size.width / 2, self.bounds.size.height / 2)) {
+                    cancelBtnFrame.origin.x += safeInsets.left;
+                } else {
+                    cancelBtnFrame.origin.x = self.bounds.size.width - cancelBtnFrame.size.width - safeInsets.right - self.cancelBtnMargin;
+                }
+                self.cancelBtn.frame = cancelBtnFrame;
+            }
+            if (_doneBtn) {
+                CGRect doneBtnFrame = self.pickerStyle.doneBtnFrame;
+                if (doneBtnFrame.origin.x < MIN(self.bounds.size.width / 2, self.bounds.size.height / 2)) {
+                    doneBtnFrame.origin.x += safeInsets.left;
+                } else {
+                    doneBtnFrame.origin.x = self.bounds.size.width - doneBtnFrame.size.width - safeInsets.right - self.doneBtnMargin;
+                }
+                self.doneBtn.frame = doneBtnFrame;
+            }
+        }
+    }
+    
+    if (_alertView && self.pickerStyle.topCornerRadius > 0) {
+        // 设置顶部圆角
+        [BRPickerStyle br_setView:_alertView roundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight withRadius:self.pickerStyle.topCornerRadius];
+    }
+}
+
+#pragma mark - 蒙层视图
+- (UIView *)maskView {
+    if (!_maskView) {
+        _maskView = [[UIView alloc]initWithFrame:self.keyView.bounds];
+        _maskView.backgroundColor = self.pickerStyle.maskColor;
+        // 设置子视图的大小随着父视图变化
+        _maskView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+        _maskView.userInteractionEnabled = YES;
+        UITapGestureRecognizer *myTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(didTapMaskView:)];
+        [_maskView addGestureRecognizer:myTap];
+    }
+    return _maskView;
+}
+
+#pragma mark - 弹框视图
+- (UIView *)alertView {
+    if (!_alertView) {
+        CGFloat accessoryViewHeight = 0;
+        if (self.pickerHeaderView) {
+            accessoryViewHeight += self.pickerHeaderView.bounds.size.height;
+        }
+        if (self.pickerFooterView) {
+            accessoryViewHeight += self.pickerFooterView.bounds.size.height;
+        }
+        CGFloat height = self.pickerStyle.titleBarHeight + self.pickerStyle.pickerHeight + self.pickerStyle.paddingBottom + accessoryViewHeight;
+        _alertView = [[UIView alloc]initWithFrame:CGRectMake(0, self.keyView.bounds.size.height - height, self.keyView.bounds.size.width, height)];
+        _alertView.backgroundColor = self.pickerStyle.alertViewColor ? self.pickerStyle.alertViewColor : self.pickerStyle.pickerColor;
+        if (!self.pickerStyle.topCornerRadius && !self.pickerStyle.hiddenShadowLine) {
+            // 设置弹框视图顶部边框线
+            UIView *shadowLineView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, _alertView.frame.size.width, self.pickerStyle.shadowLineHeight)];
+            shadowLineView.backgroundColor = self.pickerStyle.shadowLineColor;
+            shadowLineView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+            [_alertView addSubview:shadowLineView];
+        }
+        _alertView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
+    }
+    return _alertView;
+}
+
+#pragma mark - 标题栏视图
+- (UIView *)titleBarView {
+    if (!_titleBarView) {
+        _titleBarView =[[UIView alloc]initWithFrame:CGRectMake(0, 0, self.keyView.bounds.size.width, self.pickerStyle.titleBarHeight)];
+        _titleBarView.backgroundColor = self.pickerStyle.titleBarColor;
+        _titleBarView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+        if (!self.pickerStyle.hiddenTitleLine) {
+            // 设置标题栏底部分割线
+            UIView *titleLineView = [[UIView alloc]initWithFrame:CGRectMake(0, _titleBarView.frame.size.height - 0.5f, _titleBarView.frame.size.width, 0.5f)];
+            titleLineView.backgroundColor = self.pickerStyle.titleLineColor;
+            titleLineView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+            [_titleBarView addSubview:titleLineView];
+        }
+    }
+    return _titleBarView;
+}
+
+#pragma mark - 取消按钮
+- (UIButton *)cancelBtn {
+    if (!_cancelBtn) {
+        _cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
+        _cancelBtn.frame = self.pickerStyle.cancelBtnFrame;
+        _cancelBtn.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin;
+        _cancelBtn.backgroundColor = self.pickerStyle.cancelColor;;
+        _cancelBtn.titleLabel.font = self.pickerStyle.cancelTextFont;
+        [_cancelBtn setTitleColor:self.pickerStyle.cancelTextColor forState:UIControlStateNormal];
+        if (self.pickerStyle.cancelBtnImage) {
+            [_cancelBtn setImage:self.pickerStyle.cancelBtnImage forState:UIControlStateNormal];
+        }
+        if (self.pickerStyle.cancelBtnTitle) {
+            [_cancelBtn setTitle:self.pickerStyle.cancelBtnTitle forState:UIControlStateNormal];
+        }
+        [_cancelBtn addTarget:self action:@selector(clickCancelBtn) forControlEvents:UIControlEventTouchUpInside];
+        // 设置按钮圆角或边框
+        if (self.pickerStyle.cancelBorderStyle == BRBorderStyleSolid) {
+            _cancelBtn.layer.cornerRadius = self.pickerStyle.cancelCornerRadius > 0 ? self.pickerStyle.cancelCornerRadius : 6.0f;
+            _cancelBtn.layer.borderColor = self.pickerStyle.cancelTextColor.CGColor;
+            _cancelBtn.layer.borderWidth = self.pickerStyle.cancelBorderWidth > 0 ? self.pickerStyle.cancelBorderWidth : 1.0f;
+            _cancelBtn.layer.masksToBounds = YES;
+        } else if (self.pickerStyle.cancelBorderStyle == BRBorderStyleFill) {
+            _cancelBtn.layer.cornerRadius = self.pickerStyle.cancelCornerRadius > 0 ? self.pickerStyle.cancelCornerRadius : 6.0f;
+            _cancelBtn.layer.masksToBounds = YES;
+        }
+    }
+    return _cancelBtn;
+}
+
+#pragma mark - 确定按钮
+- (UIButton *)doneBtn {
+    if (!_doneBtn) {
+        _doneBtn = [UIButton buttonWithType:UIButtonTypeCustom];
+        _doneBtn.frame = self.pickerStyle.doneBtnFrame;
+        _doneBtn.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin;
+        _doneBtn.backgroundColor = self.pickerStyle.doneColor;
+        if (self.pickerStyle.doneBtnImage) {
+            [_doneBtn setImage:self.pickerStyle.doneBtnImage forState:UIControlStateNormal];
+        }
+        if (self.pickerStyle.doneBtnTitle) {
+            _doneBtn.titleLabel.font = self.pickerStyle.doneTextFont;
+            [_doneBtn setTitleColor:self.pickerStyle.doneTextColor forState:UIControlStateNormal];
+            [_doneBtn setTitle:self.pickerStyle.doneBtnTitle forState:UIControlStateNormal];
+        }
+        [_doneBtn addTarget:self action:@selector(clickDoneBtn) forControlEvents:UIControlEventTouchUpInside];
+        // 设置按钮圆角或边框
+        if (self.pickerStyle.doneBorderStyle == BRBorderStyleSolid) {
+            _doneBtn.layer.cornerRadius = self.pickerStyle.doneCornerRadius > 0 ? self.pickerStyle.doneCornerRadius : 6.0f;
+            _doneBtn.layer.borderColor = self.pickerStyle.doneTextColor.CGColor;
+            _doneBtn.layer.borderWidth = self.pickerStyle.doneBorderWidth > 0 ? self.pickerStyle.doneBorderWidth : 1.0f;
+            _doneBtn.layer.masksToBounds = YES;
+        } else if (self.pickerStyle.doneBorderStyle == BRBorderStyleFill) {
+            _doneBtn.layer.cornerRadius = self.pickerStyle.doneCornerRadius > 0 ? self.pickerStyle.doneCornerRadius : 6.0f;
+            _doneBtn.layer.masksToBounds = YES;
+        }
+    }
+    return _doneBtn;
+}
+
+#pragma mark - 中间标题label
+- (UILabel *)titleLabel {
+    if (!_titleLabel) {
+        _titleLabel = [[UILabel alloc]initWithFrame:self.pickerStyle.titleLabelFrame];
+        _titleLabel.backgroundColor = self.pickerStyle.titleLabelColor;
+        _titleLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin;
+        _titleLabel.textAlignment = NSTextAlignmentCenter;
+        _titleLabel.font = self.pickerStyle.titleTextFont;
+        _titleLabel.textColor = self.pickerStyle.titleTextColor;
+        _titleLabel.text = self.title;
+    }
+    return _titleLabel;
+}
+
+#pragma mark - 点击蒙层视图事件
+- (void)didTapMaskView:(UITapGestureRecognizer *)sender {
+    [self removePickerFromView:nil];
+    if (self.cancelBlock) {
+        self.cancelBlock();
+    }
+}
+
+#pragma mark - 取消按钮的点击事件
+- (void)clickCancelBtn {
+    [self removePickerFromView:nil];
+    if (self.cancelBlock) {
+        self.cancelBlock();
+    }
+}
+
+#pragma mark - 确定按钮的点击事件
+- (void)clickDoneBtn {
+    [self removePickerFromView:nil];
+    if (self.doneBlock) {
+        self.doneBlock();
+    }
+}
+
+#pragma mark - 添加视图方法
+- (void)addPickerToView:(UIView *)view {
+    if (view) {
+        self.frame = view.bounds;
+        self.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+        
+        CGFloat accessoryViewHeight = 0;
+        if (self.pickerHeaderView) {
+            CGRect rect = self.pickerHeaderView.frame;
+            self.pickerHeaderView.frame = CGRectMake(0, 0, view.bounds.size.width, rect.size.height);
+            self.pickerHeaderView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+            [self addSubview:self.pickerHeaderView];
+            
+            accessoryViewHeight += self.pickerHeaderView.bounds.size.height;
+        }
+        if (self.pickerFooterView) {
+            CGRect rect = self.pickerFooterView.frame;
+            self.pickerFooterView.frame = CGRectMake(0, view.bounds.size.height - rect.size.height, view.bounds.size.width, rect.size.height);
+            self.pickerFooterView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+            [self addSubview:self.pickerFooterView];
+            
+            accessoryViewHeight += self.pickerFooterView.bounds.size.height;
+        }
+        
+        [view addSubview:self];
+    } else {
+        [self initUI];
+        
+        if (self.pickerHeaderView) {
+            CGRect rect = self.pickerHeaderView.frame;
+            CGFloat titleBarHeight = self.pickerStyle.hiddenTitleBarView ? 0 : self.pickerStyle.titleBarHeight;
+            self.pickerHeaderView.frame = CGRectMake(0, titleBarHeight, self.alertView.bounds.size.width, rect.size.height);
+            self.pickerHeaderView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+            [self.alertView addSubview:self.pickerHeaderView];
+        }
+        if (self.pickerFooterView) {
+            CGRect rect = self.pickerFooterView.frame;
+            self.pickerFooterView.frame = CGRectMake(0, self.alertView.bounds.size.height - self.pickerStyle.paddingBottom - rect.size.height, self.alertView.bounds.size.width, rect.size.height);
+            self.pickerFooterView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+            [self.alertView addSubview:self.pickerFooterView];
+        }
+    
+        [self.keyView addSubview:self];
+        
+        // iOS16:重新设置 alertView 高度(解决懒加载设置frame不生效问题)
+        CGFloat accessoryViewHeight = 0;
+        if (self.pickerHeaderView) {
+            accessoryViewHeight += self.pickerHeaderView.bounds.size.height;
+        }
+        if (self.pickerFooterView) {
+            accessoryViewHeight += self.pickerFooterView.bounds.size.height;
+        }
+        CGFloat height = self.pickerStyle.titleBarHeight + self.pickerStyle.pickerHeight + self.pickerStyle.paddingBottom + accessoryViewHeight;
+        self.alertView.frame = CGRectMake(0, self.keyView.bounds.size.height - height, self.keyView.bounds.size.width, height);
+        
+        // 动画前初始位置
+        CGRect rect = self.alertView.frame;
+        rect.origin.y = self.bounds.size.height;
+        self.alertView.frame = rect;
+        // 弹出动画
+        if (!self.pickerStyle.hiddenMaskView) {
+            self.maskView.alpha = 0;
+        }
+        [UIView animateWithDuration:0.3f animations:^{
+            if (!self.pickerStyle.hiddenMaskView) {
+                self.maskView.alpha = 1;
+            }
+            CGFloat alertViewHeight = self.alertView.bounds.size.height;
+            CGRect rect = self.alertView.frame;
+            rect.origin.y -= alertViewHeight;
+            self.alertView.frame = rect;
+        }];
+    }
+}
+
+#pragma mark - 移除视图方法
+- (void)removePickerFromView:(UIView *)view {
+    if (view) {
+        [self removeFromSuperview];
+    } else {
+        // 关闭动画
+        [UIView animateWithDuration:0.2f animations:^{
+            CGFloat alertViewHeight = self.alertView.bounds.size.height;
+            CGRect rect = self.alertView.frame;
+            rect.origin.y += alertViewHeight;
+            self.alertView.frame = rect;
+            if (!self.pickerStyle.hiddenMaskView) {
+                self.maskView.alpha = 0;
+            }
+        } completion:^(BOOL finished) {
+            [self removeFromSuperview];
+        }];
+    }
+}
+
+#pragma mark - 刷新选择器数据
+- (void)reloadData {
+    
+}
+
+#pragma mark - 添加自定义视图到选择器(picker)上
+- (void)addSubViewToPicker:(UIView *)customView {
+    
+}
+
+#pragma mark - 添加自定义视图到标题栏(titleBar)上
+- (void)addSubViewToTitleBar:(UIView *)customView {
+    if (!self.pickerStyle.hiddenTitleBarView) {
+        [self.titleBarView addSubview:customView];
+    }
+}
+
+- (BRPickerStyle *)pickerStyle {
+    if (!_pickerStyle) {
+        _pickerStyle = [[BRPickerStyle alloc]init];
+    }
+    return _pickerStyle;
+}
+
+- (UIView *)keyView {
+    if (!_keyView) {
+        _keyView = BRGetKeyWindow();
+    }
+    return _keyView;
+}
+
+#pragma mark - setter 方法(支持动态设置标题)
+- (void)setTitle:(NSString *)title {
+    _title = title;
+    if (_titleLabel) {
+        _titleLabel.text = title;
+    }
+}
+
+- (void)dealloc {
+    NSLog(@"%@ dealloc", NSStringFromClass([self class]));
+}
+
+@end

+ 251 - 0
Pods/BRPickerView/BRPickerView/Base/BRPickerStyle.h

@@ -0,0 +1,251 @@
+//
+//  BRPickerStyle.h
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2019/10/2.
+//  Copyright © 2019 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+#import "BRPickerViewMacro.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+// 边框样式(左边取消按钮/右边确定按钮)
+typedef NS_ENUM(NSInteger, BRBorderStyle) {
+    /** 无边框(默认) */
+    BRBorderStyleNone = 0,
+    /** 有圆角和边框 */
+    BRBorderStyleSolid,
+    /** 仅有圆角 */
+    BRBorderStyleFill
+};
+
+@interface BRPickerStyle : NSObject
+
+
+/////////////////////////////// 蒙层视图(maskView)///////////////////////////////
+
+/** 设置背景颜色 */
+@property (nullable, nonatomic, strong) UIColor *maskColor;
+
+/** 隐藏 maskView,默认为 NO */
+@property (nonatomic, assign) BOOL hiddenMaskView;
+
+
+////////////////////////////// 弹框视图(alertView)///////////////////////////////
+
+/** 设置 alertView 弹框视图的背景颜色 */
+@property (nullable, nonatomic, strong) UIColor *alertViewColor;
+
+/** 设置 alertView 弹框视图左上和右上的圆角半径  */
+@property (nonatomic, assign) NSInteger topCornerRadius;
+
+/** 设置 alertView 弹框视图顶部边框线颜色  */
+@property (nullable, nonatomic, strong) UIColor *shadowLineColor;
+
+/** 设置 alertView 弹框视图顶部边框线高度  */
+@property (nonatomic, assign) CGFloat shadowLineHeight;
+
+/** 隐藏 alertView 弹框视图顶部边框线,默认为 NO */
+@property (nonatomic, assign) BOOL hiddenShadowLine;
+
+/** 设置 alertView 弹框视图底部内边距,默认为安全区域底部距屏幕底部的高度  */
+@property (nonatomic, assign) CGFloat paddingBottom;
+
+
+//////////////////////////// 标题栏视图(titleBarView) ////////////////////////////
+
+/** 设置 titleBarView 标题栏的背景颜色 */
+@property (nullable, nonatomic, strong) UIColor *titleBarColor;
+
+/** 设置 titleBarView 标题栏的高度 */
+@property (nonatomic, assign) CGFloat titleBarHeight;
+
+/** 设置 titleBarView 标题栏底部分割线颜色 */
+@property (nullable, nonatomic, strong) UIColor *titleLineColor;
+
+/** 隐藏 titleBarView 标题栏底部分割线,默认为 NO  */
+@property (nonatomic, assign) BOOL hiddenTitleLine;
+
+/** 隐藏 titleBarView,默认为 NO */
+@property (nonatomic, assign) BOOL hiddenTitleBarView;
+
+
+////////////////////////// 标题栏中间label(titleLabel)///////////////////////////
+
+/** 设置 titleLabel 的背景颜色 */
+@property (nullable, nonatomic, strong) UIColor *titleLabelColor;
+
+/** 设置 titleLabel 文本颜色 */
+@property (nullable, nonatomic, strong) UIColor *titleTextColor;
+
+/** 设置 titleLabel 字体大小 */
+@property (nullable, nonatomic, strong) UIFont *titleTextFont;
+
+/** 设置 titleLabel 的 frame */
+@property (nonatomic, assign) CGRect titleLabelFrame;
+
+/** 隐藏 titleLabel,默认为 NO */
+@property (nonatomic, assign) BOOL hiddenTitleLabel;
+
+
+/////////////////////////////// 取消按钮(cancelBtn)//////////////////////////////
+
+/** 设置 cancelBtn 的背景颜色 */
+@property (nullable, nonatomic, strong) UIColor *cancelColor;
+
+/** 设置 cancelBtn 标题的颜色 */
+@property (nullable, nonatomic, strong) UIColor *cancelTextColor;
+
+/** 设置 cancelBtn 标题的字体 */
+@property (nullable, nonatomic, strong) UIFont *cancelTextFont;
+
+/** 设置 cancelBtn 的 frame */
+@property (nonatomic, assign) CGRect cancelBtnFrame;
+
+/** 设置 cancelBtn 的边框样式 */
+@property (nonatomic, assign) BRBorderStyle cancelBorderStyle;
+
+/** 设置 cancelBtn 的圆角大小 */
+@property (nonatomic, assign) CGFloat cancelCornerRadius;
+
+/** 设置 cancelBtn 的边框宽度 */
+@property (nonatomic, assign) CGFloat cancelBorderWidth;
+
+/** 设置 cancelBtn 的 image */
+@property (nullable, nonatomic, strong) UIImage *cancelBtnImage;
+
+/** 设置 cancelBtn 的 title */
+@property (nullable, nonatomic, copy) NSString *cancelBtnTitle;
+
+/** 隐藏 cancelBtn,默认为 NO */
+@property (nonatomic, assign) BOOL hiddenCancelBtn;
+
+
+/////////////////////////////// 确定按钮(doneBtn)////////////////////////////////
+
+/** 设置 doneBtn 的背景颜色 */
+@property (nullable, nonatomic, strong) UIColor *doneColor;
+
+/** 设置 doneBtn 标题的颜色 */
+@property (nullable, nonatomic, strong) UIColor *doneTextColor;
+
+/** 设置 doneBtn 标题的字体 */
+@property (nullable, nonatomic, strong) UIFont *doneTextFont;
+
+/** 设置 doneBtn 的 frame */
+@property (nonatomic, assign) CGRect doneBtnFrame;
+
+/** 设置 doneBtn 的边框样式 */
+@property (nonatomic, assign) BRBorderStyle doneBorderStyle;
+
+/** 设置 doneBtn 的圆角大小 */
+@property (nonatomic, assign) CGFloat doneCornerRadius;
+
+/** 设置 doneBtn 的边框宽度 */
+@property (nonatomic, assign) CGFloat doneBorderWidth;
+
+/** 设置 doneBtn 的 image */
+@property (nullable, nonatomic, strong) UIImage *doneBtnImage;
+
+/** 设置 doneBtn 的 title */
+@property (nullable, nonatomic, copy) NSString *doneBtnTitle;
+
+/** 隐藏 doneBtn,默认为 NO */
+@property (nonatomic, assign) BOOL hiddenDoneBtn;
+
+
+/////////////////////////////// 选择器(pickerView)///////////////////////////////
+
+/** 设置 picker 的背景颜色 */
+@property (nullable, nonatomic, strong) UIColor *pickerColor;
+
+/** 设置 picker 中间两条分割线的背景颜色。暂不支持日期选择器前4种类型 */
+@property (nullable, nonatomic, strong) UIColor *separatorColor;
+
+/** 设置 picker 中间两条分割线的高度。暂不支持日期选择器前4种类型 */
+@property (nonatomic, assign) CGFloat separatorHeight;
+
+/** 设置 picker 文本的颜色。暂不支持日期选择器前4种类型 */
+@property (nullable, nonatomic, strong) UIColor *pickerTextColor;
+
+/** 设置 picker 文本的字体。暂不支持日期选择器前4种类型 */
+@property (nullable, nonatomic, strong) UIFont *pickerTextFont;
+
+/** 设置 picker 中间选中行的背景颜色。暂不支持日期选择器前4种类型 */
+@property (nullable, nonatomic, strong) UIColor *selectRowColor;
+
+/** 设置 picker 中间选中行文本的颜色。暂不支持日期选择器前4种类型 */
+@property (nullable, nonatomic, strong) UIColor *selectRowTextColor;
+
+/** 设置 picker 中间选中行文本的字体。暂不支持日期选择器前4种类型 */
+@property (nullable, nonatomic, strong) UIFont *selectRowTextFont;
+
+/** 设置 picker 的高度,系统默认高度为 216 */
+@property (nonatomic, assign) CGFloat pickerHeight;
+
+/** 设置 picker 的行高。暂不支持日期选择器前4种类型 */
+@property (nonatomic, assign) CGFloat rowHeight;
+
+/**
+ *  清除iOS14之后选择器默认自带的新样式。暂不支持日期选择器前4种类型
+ *  主要是:①隐藏中间选择行的背景样式,②清除默认的内边距,③新增中间选择行的两条分割线;与iOS14之前的样式保持一致),默认为 YES
+ */
+@property (nonatomic, assign) BOOL clearPickerNewStyle;
+
+
+/**
+ *  设置语言(不设置或为nil时,将随系统的语言自动改变)
+ *  language: zh-Hans(简体中文)、zh-Hant(繁体中文)、en(英语 )
+ */
+@property(nullable, nonatomic, copy) NSString *language;
+
+
+/////// 日期选择器单位样式(showUnitType == BRShowUnitTypeOnlyCenter 时生效。暂不支持日期选择器前4种类型 )///////
+
+/** 设置日期选择器单位文本的颜色 */
+@property (nullable, nonatomic, strong) UIColor *dateUnitTextColor;
+
+/** 设置日期选择器单位文本的字体 */
+@property (nullable, nonatomic, strong) UIFont *dateUnitTextFont;
+
+/** 设置日期选择器单位 label 的水平方向偏移量 */
+@property (nonatomic, assign) CGFloat dateUnitOffsetX;
+
+/** 设置日期选择器单位 label 的竖直方向偏移量 */
+@property (nonatomic, assign) CGFloat dateUnitOffsetY;
+
+
+//////////////////////////////// 常用的几种模板样式 ////////////////////////////////
+
+/// 弹框模板样式1 - 取消/确定按钮圆角样式
+/// @param themeColor 主题颜色
++ (instancetype)pickerStyleWithThemeColor:(nullable UIColor *)themeColor;
+
+/// 弹框模板样式2 - 顶部圆角样式 + 完成按钮
+/// @param doneTextColor 完成按钮标题的颜色
++ (instancetype)pickerStyleWithDoneTextColor:(nullable UIColor *)doneTextColor;
+
+/// 弹框模板样式3 - 顶部圆角样式 + 图标按钮
+/// @param doneBtnImage 完成按钮的 image
++ (instancetype)pickerStyleWithDoneBtnImage:(nullable UIImage *)doneBtnImage;
+
+
+//////////////////////////////// 以下是组件内部使用的几个封装方法 ////////////////////////////////
+
+/** 设置选择器中间选中行的样式 */
+- (void)setupPickerSelectRowStyle:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component;
+
+/** 添加选择器中间行上下两条分割线(iOS14之后系统默认去掉,需要手动添加)*/
+- (void)addSeparatorLineView:(UIView *)pickerView;
+
+/** 设置 view 的部分圆角 */
+// corners(枚举类型,可组合使用):UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight | UIRectCornerAllCorners
++ (void)br_setView:(UIView *)view roundingCorners:(UIRectCorner)corners withRadius:(CGFloat)radius;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 494 - 0
Pods/BRPickerView/BRPickerView/Base/BRPickerStyle.m

@@ -0,0 +1,494 @@
+//
+//  BRPickerStyle.m
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2019/10/2.
+//  Copyright © 2019 irenb. All dones reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import "BRPickerStyle.h"
+#import "NSBundle+BRPickerView.h"
+
+// 标题颜色
+#define kBRDefaultTextColor BR_RGB_HEX(0x333333, 1.0f)
+
+@implementation BRPickerStyle
+
+- (instancetype)init {
+    if (self = [super init]) {
+        self.clearPickerNewStyle = YES;
+    }
+    return self;
+}
+
+/// 设置默认样式
+
+- (UIColor *)maskColor {
+    if (!_maskColor) {
+        _maskColor = [self br_colorWithLightColor:BR_RGB_HEX(0x000000, 0.3f) darkColor:BR_RGB_HEX(0x666666, 0.3f)];
+    }
+    return _maskColor;
+}
+
+- (UIColor *)shadowLineColor {
+    if (!_shadowLineColor) {
+        if (@available(iOS 13.0, *)) {
+            // 边框线颜色,有透明度
+            _shadowLineColor = [UIColor separatorColor];
+        } else {
+            _shadowLineColor = BR_RGB_HEX(0xc6c6c8, 1.0f);
+        }
+    }
+    return _shadowLineColor;
+}
+
+- (CGFloat)shadowLineHeight {
+    if (_shadowLineHeight <= 0 || _shadowLineHeight > 5.0f) {
+        _shadowLineHeight = 0.5f;
+    }
+    return _shadowLineHeight;
+}
+
+- (CGFloat)paddingBottom {
+    if (_paddingBottom <= 0) {
+        _paddingBottom = BR_BOTTOM_MARGIN;
+    }
+    return _paddingBottom;
+}
+
+- (UIColor *)titleBarColor {
+    if (!_titleBarColor) {
+        if (@available(iOS 13.0, *)) {
+            // #ffffff(正常)、#1c1c1e(深色)
+            _titleBarColor = [UIColor secondarySystemGroupedBackgroundColor];
+        } else {
+            _titleBarColor = [UIColor whiteColor];
+        }
+    }
+    return _titleBarColor;
+}
+
+- (CGFloat)titleBarHeight {
+    if (!self.hiddenTitleBarView) {
+        if (_titleBarHeight < 44.0f && (!self.hiddenCancelBtn || !self.hiddenDoneBtn || !self.hiddenTitleLabel)) {
+            _titleBarHeight = 44.0f;
+        }
+    } else {
+        _titleBarHeight = 0;
+    }
+    return _titleBarHeight;
+}
+
+- (UIColor *)titleLineColor {
+    if (!_titleLineColor) {
+        _titleLineColor = [self br_colorWithLightColor:BR_RGB_HEX(0xededee, 1.0f) darkColor:BR_RGB_HEX(0x18181c, 1.0f)];
+    }
+    return _titleLineColor;
+}
+
+- (UIColor *)cancelColor {
+    if (!_cancelColor) {
+        _cancelColor = [UIColor clearColor];
+    }
+    return _cancelColor;
+}
+
+- (UIColor *)cancelTextColor {
+    if (!_cancelTextColor) {
+        if (@available(iOS 13.0, *)) {
+            _cancelTextColor = [UIColor labelColor];
+        } else {
+            _cancelTextColor = kBRDefaultTextColor;
+        }
+    }
+    return _cancelTextColor;
+}
+
+- (UIFont *)cancelTextFont {
+    if (!_cancelTextFont) {
+        _cancelTextFont = [UIFont systemFontOfSize:16.0f];
+    }
+    return _cancelTextFont;
+}
+
+- (NSString *)cancelBtnTitle {
+    if (!_cancelBtnTitle && !_cancelBtnImage) {
+        _cancelBtnTitle = [NSBundle br_localizedStringForKey:@"取消" language:self.language];
+    }
+    return _cancelBtnTitle;
+}
+
+- (CGRect)cancelBtnFrame {
+    if (CGRectEqualToRect(_cancelBtnFrame, CGRectZero) || _cancelBtnFrame.size.height == 0) {
+        _cancelBtnFrame = CGRectMake(5, 8, 60, 28);
+    }
+    return _cancelBtnFrame;
+}
+
+- (UIColor *)titleLabelColor {
+    if (!_titleLabelColor) {
+        _titleLabelColor = [UIColor clearColor];
+    }
+    return _titleLabelColor;
+}
+
+- (UIColor *)titleTextColor {
+    if (!_titleTextColor) {
+        if (@available(iOS 13.0, *)) {
+            _titleTextColor = [UIColor secondaryLabelColor];
+        } else {
+            _titleTextColor = BR_RGB_HEX(0x999999, 1.0f);
+        }
+    }
+    return _titleTextColor;
+}
+
+- (UIFont *)titleTextFont {
+    if (!_titleTextFont) {
+        _titleTextFont = [UIFont systemFontOfSize:15.0f];
+    }
+    return _titleTextFont;
+}
+
+- (CGRect)titleLabelFrame {
+    if (CGRectEqualToRect(_titleLabelFrame, CGRectZero) || _titleLabelFrame.size.height == 0) {
+        _titleLabelFrame = CGRectMake(5 + 60 + 2, 0, BRGetKeyWindow().bounds.size.width - 2 * (5 + 60 + 2), 44);
+    }
+    return _titleLabelFrame;
+}
+
+- (UIColor *)doneColor {
+    if (!_doneColor) {
+        _doneColor = [UIColor clearColor];
+    }
+    return _doneColor;
+}
+
+- (UIColor *)doneTextColor {
+    if (!_doneTextColor) {
+        if (@available(iOS 13.0, *)) {
+            _doneTextColor = [UIColor labelColor];
+        } else {
+            _doneTextColor = kBRDefaultTextColor;
+        }
+    }
+    return _doneTextColor;
+}
+
+- (UIFont *)doneTextFont {
+    if (!_doneTextFont) {
+        _doneTextFont = [UIFont systemFontOfSize:16.0f];
+    }
+    return _doneTextFont;
+}
+
+- (NSString *)doneBtnTitle {
+    if (!_doneBtnTitle && !_doneBtnImage) {
+        _doneBtnTitle = [NSBundle br_localizedStringForKey:@"确定" language:self.language];
+    }
+    return _doneBtnTitle;
+}
+
+- (CGRect)doneBtnFrame {
+    if (CGRectEqualToRect(_doneBtnFrame, CGRectZero) || _doneBtnFrame.size.height == 0) {
+        _doneBtnFrame = CGRectMake(BRGetKeyWindow().bounds.size.width - 60 - 5, 8, 60, 28);
+    }
+    return _doneBtnFrame;
+}
+
+- (UIColor *)pickerColor {
+    if (!_pickerColor) {
+        if (@available(iOS 13.0, *)) {
+            // #ffffff(正常)、#1c1c1e(深色)
+            _pickerColor = [UIColor secondarySystemGroupedBackgroundColor];
+        } else {
+            _pickerColor = [UIColor whiteColor];
+        }
+    }
+    return _pickerColor;
+}
+
+- (UIColor *)separatorColor {
+    if (!_separatorColor) {
+        if (@available(iOS 13.0, *)) {
+            // 分割线颜色,无透明度
+            _separatorColor = [UIColor opaqueSeparatorColor];
+        } else {
+            _separatorColor = BR_RGB_HEX(0xc6c6c8, 1.0f);
+        }
+    }
+    return _separatorColor;
+}
+
+- (UIColor *)pickerTextColor {
+    if (!_pickerTextColor) {
+        if (@available(iOS 13.0, *)) {
+            _pickerTextColor = [UIColor labelColor];
+        } else {
+            _pickerTextColor = kBRDefaultTextColor;
+        }
+    }
+    return _pickerTextColor;
+}
+
+- (UIFont *)pickerTextFont {
+    if (!_pickerTextFont) {
+        _pickerTextFont = [UIFont systemFontOfSize:18.0f];
+    }
+    return _pickerTextFont;
+}
+
+- (CGFloat)pickerHeight {
+    if (_pickerHeight < 40) {
+        _pickerHeight = 216.0f;
+    }
+    return _pickerHeight;
+}
+
+- (CGFloat)rowHeight {
+    if (_rowHeight < 20) {
+        _rowHeight = 35.0f;
+    }
+    return _rowHeight;
+}
+
+- (NSString *)language {
+    if (!_language) {
+        // 跟随系统的首选语言自动改变
+        // zh-Hans-CN(简体中文)、zh-Hant-CN(繁体中文)、en-CN(美式英语)、en-GB(英式英语)
+        // 其中`CN`是iOS9以后新增的地区代码,如:CN 代表中国,US 代表美国
+        _language = [NSLocale preferredLanguages].firstObject;
+    }
+    return _language;
+}
+
+- (UIColor *)dateUnitTextColor {
+    if (!_dateUnitTextColor) {
+        if (@available(iOS 13.0, *)) {
+            _dateUnitTextColor = [UIColor labelColor];
+        } else {
+            _dateUnitTextColor = kBRDefaultTextColor;
+        }
+    }
+    return _dateUnitTextColor;
+}
+
+- (UIFont *)dateUnitTextFont {
+    if (!_dateUnitTextFont) {
+        _dateUnitTextFont = [UIFont systemFontOfSize:18.0f];
+    }
+    return _dateUnitTextFont;
+}
+
+#pragma mark - 创建自定义动态颜色(适配深色模式)
+- (UIColor *)br_colorWithLightColor:(UIColor *)lightColor darkColor:(UIColor *)darkColor {
+    if (@available(iOS 13.0, *)) {
+        UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
+            if ([traitCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
+                return lightColor;
+            } else {
+                return darkColor;
+            }
+        }];
+        return dyColor;
+    } else {
+        return lightColor;
+    }
+}
+
+#pragma mark - 弹框模板样式1 - 取消/确定按钮圆角样式
++ (instancetype)pickerStyleWithThemeColor:(UIColor *)themeColor {
+    BRPickerStyle *customStyle = [[self alloc]init];
+    if (themeColor) {
+        customStyle.cancelTextColor = themeColor;
+        customStyle.cancelBorderStyle = BRBorderStyleSolid;
+        customStyle.doneColor = themeColor;
+        customStyle.doneTextColor = [UIColor whiteColor];
+        customStyle.doneBorderStyle = BRBorderStyleFill;
+    }
+    return customStyle;
+}
+
+#pragma mark - 弹框模板样式2 - 顶部圆角样式 + 完成按钮
++ (instancetype)pickerStyleWithDoneTextColor:(UIColor *)doneTextColor {
+    BRPickerStyle *customStyle = [[self alloc]init];
+    if (doneTextColor) {
+        customStyle.topCornerRadius = 16.0f;
+        customStyle.hiddenCancelBtn = YES;
+        customStyle.hiddenTitleLine = YES;
+        customStyle.titleLabelFrame = CGRectMake(20, 4, 100, 40);
+        customStyle.doneTextColor = doneTextColor;
+        customStyle.doneTextFont = [UIFont boldSystemFontOfSize:18.0f];
+        customStyle.doneBtnFrame = CGRectMake(BRGetKeyWindow().bounds.size.width - 60, 4, 60, 40);
+        customStyle.doneBtnTitle = [NSBundle br_localizedStringForKey:@"完成" language:customStyle.language];
+        customStyle.selectRowTextColor = doneTextColor;
+        customStyle.selectRowTextFont = [UIFont boldSystemFontOfSize:20.0f];
+    }
+    return customStyle;
+}
+
+#pragma mark - 弹框模板样式3 - 顶部圆角样式 + 图标按钮
++ (instancetype)pickerStyleWithDoneBtnImage:(UIImage *)doneBtnImage {
+    BRPickerStyle *customStyle = [[self alloc]init];
+    if (doneBtnImage) {
+        customStyle.topCornerRadius = 16.0f;
+        customStyle.hiddenTitleLine = YES;
+        customStyle.hiddenCancelBtn = YES;
+        customStyle.titleLabelFrame = CGRectMake(20, 4, 100, 40);
+        customStyle.doneBtnImage = doneBtnImage;
+        customStyle.doneBtnFrame = CGRectMake(BRGetKeyWindow().bounds.size.width - 44, 4, 40, 40);
+    }
+    return customStyle;
+}
+
+
+#pragma mark - 设置选择器中间选中行的样式
+- (void)setupPickerSelectRowStyle:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
+    // 1.设置分割线的颜色
+    NSString *systemVersion = [UIDevice currentDevice].systemVersion;
+    if (systemVersion.doubleValue < 14.0) {
+        for (UIView *subView in pickerView.subviews) {
+            if (subView && [subView isKindOfClass:[UIView class]] && subView.frame.size.height <= 1) {
+                subView.backgroundColor = self.separatorColor;
+                // 设置分割线高度
+                if (self.separatorHeight > 0) {
+                    CGRect rect = subView.frame;
+                    rect.size.height = self.separatorHeight;
+                    subView.frame = rect;
+                }
+            }
+        }
+    }
+    
+    // 2.设置选择器中间选中行的背景颜色
+    UIView *contentView = nil;
+    NSArray *subviews = pickerView.subviews;
+    if (subviews.count > 0) {
+        id firstView = subviews.firstObject;
+        if (firstView && [firstView isKindOfClass:[UIView class]]) {
+            contentView = (UIView *)firstView;
+        }
+    }
+    if (self.selectRowColor) {
+        UIView *columnView = nil;
+        if (contentView) {
+            id obj = [contentView valueForKey:@"subviewCache"];
+            if (obj && [obj isKindOfClass:[NSArray class]]) {
+                NSArray *columnViews = (NSArray *)obj;
+                if (columnViews.count > 0) {
+                    id columnObj = columnViews.firstObject;
+                    if (columnObj && [columnObj isKindOfClass:[UIView class]]) {
+                        columnView = (UIView *)columnObj;
+                    }
+                }
+            }
+        }
+        if (columnView) {
+            id obj = [columnView valueForKey:@"middleContainerView"];
+            if (obj && [obj isKindOfClass:[UIView class]]) {
+                UIView *selectRowView = (UIView *)obj;
+                // 中间选中行的背景颜色
+                selectRowView.backgroundColor = self.selectRowColor;
+            }
+        }
+    }
+    
+    if (contentView && self.clearPickerNewStyle) {
+        if (systemVersion.doubleValue >= 14.0) {
+            // ①隐藏中间选择行的背景样式
+            id lastView = subviews.lastObject;
+            if (lastView && [lastView isKindOfClass:[UIView class]]) {
+                UIView *rectBgView = (UIView *)lastView;
+                rectBgView.hidden = YES;
+            }
+            
+            // ②清除iOS14上选择器默认的内边距
+            if (systemVersion.doubleValue < 15.0f) {
+                [self setPickerAllSubViewsStyle:contentView];
+            }
+        }
+    }
+    
+    // 3.设置选择器中间选中行的字体颜色/字体大小
+    if (self.selectRowTextColor || self.selectRowTextFont) {
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+            // 当前选中的 label
+            UILabel *selectLabel = (UILabel *)[pickerView viewForRow:row forComponent:component];
+            if (selectLabel) {
+                if (self.selectRowTextColor) {
+                    selectLabel.textColor = self.selectRowTextColor;
+                }
+                if (self.selectRowTextFont) {
+                    selectLabel.font = self.selectRowTextFont;
+                }
+            }
+        });
+    }
+}
+
+// 遍历子视图,重新设置 frame(清空在 iOS14 上 UIPickerView 出现的内边距)
+- (void)setPickerAllSubViewsStyle:(UIView *)view {
+    NSArray *subViews = view.subviews;
+    if (subViews.count == 0 || [view isKindOfClass:[UILabel class]]) return;
+    for (UIView *subView in subViews) {
+        NSString *className = NSStringFromClass([subView class]);
+        if ([className isEqualToString:@"UIPickerColumnView"]) {
+            CGRect rect = subView.frame;
+            rect.origin.x = 0;
+            rect.size.width = view.bounds.size.width;
+            subView.frame = rect;
+        }
+        NSString *superClassName = NSStringFromClass([view class]);
+        if ([superClassName isEqualToString:@"UIPickerColumnView"]) {
+            CGRect rect = subView.frame;
+            rect.size.width = view.bounds.size.width;
+            subView.frame = rect;
+        }
+        if ([subView isKindOfClass:[UILabel class]]) {
+            CGRect rect = subView.frame;
+            rect.origin.x = 10;
+            subView.frame = rect;
+        }
+        
+        [self setPickerAllSubViewsStyle:subView];
+    }
+}
+
+#pragma mark - 添加选择器中间行上下两条分割线(iOS14之后系统默认去掉,需要手动添加)
+- (void)addSeparatorLineView:(UIView *)pickerView {
+    if ([UIDevice currentDevice].systemVersion.doubleValue >= 14.0) {
+        UIView *topLineView = [[UIView alloc]initWithFrame:CGRectMake(0, pickerView.bounds.size.height / 2 - self.rowHeight / 2, pickerView.bounds.size.width, 0.5f)];
+        topLineView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
+        topLineView.backgroundColor = self.separatorColor;
+        // 设置分割线高度
+        if (self.separatorHeight > 0) {
+            CGRect topRect = topLineView.frame;
+            topRect.size.height = self.separatorHeight;
+            topLineView.frame = topRect;
+        }
+        [pickerView addSubview:topLineView];
+        
+        UIView *bottomLineView = [[UIView alloc]initWithFrame:CGRectMake(0, pickerView.bounds.size.height / 2 + self.rowHeight / 2, pickerView.bounds.size.width, 0.5f)];
+        bottomLineView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
+        bottomLineView.backgroundColor = self.separatorColor;
+        // 设置分割线高度
+        if (self.separatorHeight > 0) {
+            CGRect bottomRect = bottomLineView.frame;
+            bottomRect.size.height = self.separatorHeight;
+            bottomLineView.frame = bottomRect;
+        }
+        [pickerView addSubview:bottomLineView];
+    }
+}
+
+#pragma mark - 设置 view 的部分圆角
+// corners(枚举类型,可组合使用):UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight | UIRectCornerAllCorners
++ (void)br_setView:(UIView *)view roundingCorners:(UIRectCorner)corners withRadius:(CGFloat)radius {
+    UIBezierPath *rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];
+    CAShapeLayer *shape = [[CAShapeLayer alloc]init];
+    [shape setPath:rounded.CGPath];
+    view.layer.mask = shape;
+}
+
+@end

+ 35 - 0
Pods/BRPickerView/BRPickerView/Base/BRPickerView.bundle/en.lproj/Localizable.strings

@@ -0,0 +1,35 @@
+/* 
+  Localizable.strings
+  BRPickerViewDemo
+
+  Created by renbo on 2019/10/30.
+  Copyright © 2019 irenb. All rights reserved.
+*/
+
+"确定" = "OK";
+"取消" = "Cancel";
+"完成" = "Done";
+
+"年" = " ";
+"月" = " ";
+"日" = " ";
+"时" = " ";
+"分" = " ";
+"秒" = " ";
+
+"周" = " ";
+"季度" = " ";
+
+"上午" = "AM";
+"下午" = "PM";
+
+"至今" = " Now";
+"今天" = " Today";
+
+"周一" = " Mon";
+"周二" = " Tue";
+"周三" = " Wed";
+"周四" = " Thu";
+"周五" = " Fri";
+"周六" = " Sat";
+"周日" = " Sun";

+ 35 - 0
Pods/BRPickerView/BRPickerView/Base/BRPickerView.bundle/zh-Hans.lproj/Localizable.strings

@@ -0,0 +1,35 @@
+/* 
+  Localizable.strings
+  BRPickerViewDemo
+
+  Created by renbo on 2019/10/30.
+  Copyright © 2019 irenb. All rights reserved.
+*/
+
+"确定" = "确定";
+"取消" = "取消";
+"完成" = "完成";
+
+"年" = "年";
+"月" = "月";
+"日" = "日";
+"时" = "时";
+"分" = "分";
+"秒" = "秒";
+
+"周" = "周";
+"季度" = "季度";
+
+"上午" = "上午";
+"下午" = "下午";
+
+"至今" = "至今";
+"今天" = "今天";
+
+"周一" = "周一";
+"周二" = "周二";
+"周三" = "周三";
+"周四" = "周四";
+"周五" = "周五";
+"周六" = "周六";
+"周日" = "周日";

+ 35 - 0
Pods/BRPickerView/BRPickerView/Base/BRPickerView.bundle/zh-Hant.lproj/Localizable.strings

@@ -0,0 +1,35 @@
+/* 
+  Localizable.strings
+  BRPickerViewDemo
+
+  Created by renbo on 2019/10/30.
+  Copyright © 2019 irenb. All rights reserved.
+*/
+
+"确定" = "確定";
+"取消" = "取消";
+"完成" = "完成";
+
+"年" = "年";
+"月" = "月";
+"日" = "日";
+"时" = "時";
+"分" = "分";
+"秒" = "秒";
+
+"周" = "周";
+"季度" = "季度";
+
+"上午" = "上午";
+"下午" = "下午";
+
+"至今" = "至今";
+"今天" = "今天";
+
+"周一" = "周壹";
+"周二" = "周二";
+"周三" = "周三";
+"周四" = "周四";
+"周五" = "周五";
+"周六" = "周六";
+"周日" = "周日";

+ 87 - 0
Pods/BRPickerView/BRPickerView/Base/BRPickerViewMacro.h

@@ -0,0 +1,87 @@
+//
+//  BRPickerViewMacro.h
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2018/4/23.
+//  Copyright © 2018 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#ifndef BRPickerViewMacro_h
+#define BRPickerViewMacro_h
+
+#import <UIKit/UIKit.h>
+
+// 屏幕安全区域下边距
+#define BR_BOTTOM_MARGIN \
+({CGFloat safeBottomHeight = 0;\
+if (@available(iOS 11.0, *)) {\
+safeBottomHeight = BRGetKeyWindow().safeAreaInsets.bottom;\
+}\
+(safeBottomHeight);})
+
+
+// 静态库中编写 Category 时的便利宏,用于解决 Category 方法从静态库中加载需要特别设置的问题
+#ifndef BRSYNTH_DUMMY_CLASS
+
+#define BRSYNTH_DUMMY_CLASS(_name_) \
+@interface BRSYNTH_DUMMY_CLASS_ ## _name_ : NSObject @end \
+@implementation BRSYNTH_DUMMY_CLASS_ ## _name_ @end
+
+#endif
+
+
+// 打印错误日志
+#ifdef DEBUG
+    #define BRErrorLog(...) NSLog(@"reason: %@", [NSString stringWithFormat:__VA_ARGS__])
+#else
+    #define BRErrorLog(...)
+#endif
+
+
+/** RGB颜色(16进制) */
+static inline UIColor *BR_RGB_HEX(uint32_t rgbValue, CGFloat alpha) {
+    return [UIColor colorWithRed:((CGFloat)((rgbValue & 0xFF0000) >> 16)) / 255.0
+                           green:((CGFloat)((rgbValue & 0xFF00) >> 8)) / 255.0
+                            blue:((CGFloat)(rgbValue & 0xFF)) / 255.0
+                           alpha:(alpha)];
+}
+
+
+/** 获取 keyWindow */
+static inline UIWindow *BRGetKeyWindow(void) {
+    UIWindow *keyWindow = nil;
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 // 编译时检查SDK版本(兼容不同版本的Xcode,防止编译报错)
+    if (@available(iOS 13.0, *)) { // 运行时检查系统版本(兼容不同版本的系统,防止运行报错)
+        NSSet<UIScene *> *connectedScenes = [UIApplication sharedApplication].connectedScenes;
+        for (UIScene *scene in connectedScenes) {
+            if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]]) {
+                UIWindowScene *windowScene = (UIWindowScene *)scene;
+                for (UIWindow *window in windowScene.windows) {
+                    if (window.isKeyWindow) {
+                        keyWindow = window;
+                        break;
+                    }
+                }
+            }
+        }
+    }
+#endif
+        
+    if (!keyWindow) {
+        keyWindow = [UIApplication sharedApplication].windows.firstObject;
+        if (!keyWindow.isKeyWindow) {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED < 130000
+            UIWindow *window = [UIApplication sharedApplication].keyWindow;
+            if (CGRectEqualToRect(window.bounds, UIScreen.mainScreen.bounds)) {
+                keyWindow = window;
+            }
+#endif
+        }
+    }
+    
+    return keyWindow;
+}
+
+
+#endif /* BRPickerViewMacro_h */

+ 26 - 0
Pods/BRPickerView/BRPickerView/Base/NSBundle+BRPickerView.h

@@ -0,0 +1,26 @@
+//
+//  NSBundle+BRPickerView.h
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2019/10/30.
+//  Copyright © 2019 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface NSBundle (BRPickerView)
+
+/// 获取 BRPickerView.bundle
++ (instancetype)br_pickerBundle;
+
+/// 获取国际化后的文本
+/// @param key 代表 Localizable.strings 文件中 key-value 中的 key。
+/// @param language 设置语言(可为空,为nil时将随系统的语言自动改变)
++ (NSString *)br_localizedStringForKey:(NSString *)key language:(NSString *)language;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 68 - 0
Pods/BRPickerView/BRPickerView/Base/NSBundle+BRPickerView.m

@@ -0,0 +1,68 @@
+//
+//  NSBundle+BRPickerView.m
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2019/10/30.
+//  Copyright © 2019 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import "NSBundle+BRPickerView.h"
+#import "BRBaseView.h"
+
+BRSYNTH_DUMMY_CLASS(NSBundle_BRPickerView)
+
+@implementation NSBundle (BRPickerView)
+
+#pragma mark - 获取 BRPickerView.bundle
++ (instancetype)br_pickerBundle {
+    static NSBundle *pickerBundle = nil;
+    if (!pickerBundle) {
+        /*
+            先拿到最外面的 bundle。
+            对 framework 链接方式来说就是 framework 的 bundle 根目录,
+            对静态库链接方式来说就是 target client 的 main bundle,
+            然后再去找下面名为 BRPickerView 的 bundle 对象。
+         */
+        NSBundle *bundle = [NSBundle bundleForClass:[BRBaseView class]];
+        NSURL *url = [bundle URLForResource:@"BRPickerView" withExtension:@"bundle"];
+        pickerBundle = [NSBundle bundleWithURL:url];
+    }
+    return pickerBundle;
+}
+
+#pragma mark - 获取国际化后的文本
++ (NSString *)br_localizedStringForKey:(NSString *)key language:(NSString *)language {
+    return [self br_localizedStringForKey:key value:nil language:language];
+}
+
++ (NSString *)br_localizedStringForKey:(NSString *)key value:(NSString *)value language:(NSString *)language {
+    static NSBundle *bundle = nil;
+    if (!bundle) {
+        // 如果没有手动设置语言,将随系统的语言自动改变
+        if (!language) {
+            // 系统首选语言
+            language = [NSLocale preferredLanguages].firstObject;
+        }
+        
+        if ([language hasPrefix:@"en"]) {
+            language = @"en";
+        } else if ([language hasPrefix:@"zh"]) {
+            if ([language rangeOfString:@"Hans"].location != NSNotFound) {
+                language = @"zh-Hans"; // 简体中文
+            } else { // zh-Hant、zh-HK、zh-TW
+                language = @"zh-Hant"; // 繁體中文
+            }
+        } else {
+            language = @"en";
+        }
+        
+        // 从 BRPickerView.bundle 中查找资源
+        bundle = [NSBundle bundleWithPath:[[self br_pickerBundle] pathForResource:language ofType:@"lproj"]];
+    }
+    value = [bundle localizedStringForKey:key value:value table:nil];
+    
+    return [[NSBundle mainBundle] localizedStringForKey:key value:value table:nil];
+}
+
+@end

+ 111 - 0
Pods/BRPickerView/BRPickerView/DatePickerView/BRDatePickerView+BR.h

@@ -0,0 +1,111 @@
+//
+//  BRDatePickerView+BR.h
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2020/6/16.
+//  Copyright © 2020 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import "BRDatePickerView.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface BRDatePickerView (BR)
+
+/** 最小日期 */
+- (NSDate *)handlerMinDate:(nullable NSDate *)minDate;
+
+/** 最大日期 */
+- (NSDate *)handlerMaxDate:(nullable NSDate *)maxDate;
+
+/** 默认选中的日期 */
+- (NSDate *)handlerSelectDate:(nullable NSDate *)selectDate dateFormat:(NSString *)dateFormat;
+
+/** NSDate 转 NSString */
+- (NSString *)br_stringFromDate:(NSDate *)date dateFormat:(NSString *)dateFormat;
+
+/** NSString 转 NSDate */
+- (NSDate *)br_dateFromString:(NSString *)dateString dateFormat:(NSString *)dateFormat;
+
+/** 比较两个日期大小(可以指定比较级数,即按指定格式进行比较) */
+- (NSComparisonResult)br_compareDate:(NSDate *)date targetDate:(NSDate *)targetDate dateFormat:(NSString *)dateFormat;
+
+/** 获取 yearArr 数组 */
+- (NSArray *)getYearArr;
+
+/** 获取 monthArr 数组 */
+- (NSArray *)getMonthArr:(NSInteger)year;
+
+/** 获取 dayArr 数组 */
+- (NSArray *)getDayArr:(NSInteger)year month:(NSInteger)month;
+
+/** 获取 hourArr 数组 */
+- (NSArray *)getHourArr:(NSInteger)year month:(NSInteger)month day:(NSInteger)day;
+
+/** 获取 minuteArr 数组 */
+- (NSArray *)getMinuteArr:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour;
+
+/** 获取 secondArr 数组 */
+- (NSArray *)getSecondArr:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute;
+
+/**  获取 monthWeekArr 数组 */
+- (NSArray *)getMonthWeekArr:(NSInteger)year month:(NSInteger)month;
+
+/**  获取 yearWeekArr 数组 */
+- (NSArray *)getYearWeekArr:(NSInteger)year;
+
+ /**  获取 quarterArr 数组 */
+- (NSArray *)getQuarterArr:(NSInteger)year;
+
+/** 添加 pickerView */
+- (void)setupPickerView:(UIView *)pickerView toView:(UIView *)view;
+
+/** 设置日期单位 */
+- (NSArray *)setupPickerUnitLabel:(UIPickerView *)pickerView unitArr:(NSArray *)unitArr;
+
+- (NSString *)getYearNumber:(NSInteger)year;
+
+- (NSString *)getMDHMSNumber:(NSInteger)number;
+
+- (NSString *)getYearText:(NSArray *)yearArr row:(NSInteger)row;
+
+- (NSString *)getMonthText:(NSArray *)monthArr row:(NSInteger)row;
+
+- (NSString *)getDayText:(NSArray *)dayArr row:(NSInteger)row mSelectDate:(NSDate *)mSelectDate;
+
+- (NSString *)getHourText:(NSArray *)hourArr row:(NSInteger)row;
+
+- (NSString *)getMinuteText:(NSArray *)minuteArr row:(NSInteger)row;
+
+- (NSString *)getSecondText:(NSArray *)secondArr row:(NSInteger)row;
+
+- (NSString *)getWeekText:(NSArray *)weekArr row:(NSInteger)row;
+
+- (NSString *)getQuarterText:(NSArray *)quarterArr row:(NSInteger)row;
+
+- (NSString *)getAMText;
+
+- (NSString *)getPMText;
+
+- (NSString *)getYearUnit;
+
+- (NSString *)getMonthUnit;
+
+- (NSString *)getDayUnit;
+
+- (NSString *)getHourUnit;
+
+- (NSString *)getMinuteUnit;
+
+- (NSString *)getSecondUnit;
+
+- (NSString *)getWeekUnit;
+
+- (NSString *)getQuarterUnit;
+
+- (NSInteger)getIndexWithArray:(NSArray *)array object:(NSString *)obj;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 755 - 0
Pods/BRPickerView/BRPickerView/DatePickerView/BRDatePickerView+BR.m

@@ -0,0 +1,755 @@
+//
+//  BRDatePickerView+BR.m
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2020/6/16.
+//  Copyright © 2020 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import "BRDatePickerView+BR.h"
+#import "NSBundle+BRPickerView.h"
+
+BRSYNTH_DUMMY_CLASS(BRDatePickerView_BR)
+
+//////////////////////////////////////////
+/// 本分类主要是给 BRDatePickerView 文件瘦身
+//////////////////////////////////////////
+
+@implementation BRDatePickerView (BR)
+
+#pragma mark - 最小日期
+- (NSDate *)handlerMinDate:(NSDate *)minDate {
+    if (!minDate) {
+        if (self.pickerMode == BRDatePickerModeMDHM) {
+            minDate = [NSDate br_setMonth:1 day:1 hour:0 minute:0];
+        } else if (self.pickerMode == BRDatePickerModeMD) {
+            minDate = [NSDate br_setMonth:1 day:1];
+        } else if (self.pickerMode == BRDatePickerModeTime || self.pickerMode == BRDatePickerModeCountDownTimer || self.pickerMode == BRDatePickerModeHM) {
+            minDate = [NSDate br_setHour:0 minute:0];
+        } else if (self.pickerMode == BRDatePickerModeHMS) {
+            minDate = [NSDate br_setHour:0 minute:0 second:0];
+        } else if (self.pickerMode == BRDatePickerModeMS) {
+            minDate = [NSDate br_setMinute:0 second:0];
+        } else {
+            minDate = [NSDate distantPast]; // 遥远的过去的一个时间点
+        }
+    }
+    return minDate;
+}
+
+#pragma mark - 最大日期
+- (NSDate *)handlerMaxDate:(NSDate *)maxDate {
+    if (!maxDate) {
+        if (self.pickerMode == BRDatePickerModeMDHM) {
+            maxDate = [NSDate br_setMonth:12 day:31 hour:23 minute:59];
+        } else if (self.pickerMode == BRDatePickerModeMD) {
+            maxDate = [NSDate br_setMonth:12 day:31];
+        } else if (self.pickerMode == BRDatePickerModeTime || self.pickerMode == BRDatePickerModeCountDownTimer || self.pickerMode == BRDatePickerModeHM) {
+            maxDate = [NSDate br_setHour:23 minute:59];
+        } else if (self.pickerMode == BRDatePickerModeHMS) {
+            maxDate = [NSDate br_setHour:23 minute:59 second:59];
+        } else if (self.pickerMode == BRDatePickerModeMS) {
+            maxDate = [NSDate br_setMinute:59 second:59];
+        } else {
+            maxDate = [NSDate distantFuture]; // 遥远的未来的一个时间点
+        }
+    }
+    return maxDate;
+}
+
+#pragma mark - 默认选中的日期
+- (NSDate *)handlerSelectDate:(NSDate *)selectDate dateFormat:(NSString *)dateFormat {
+    // selectDate 优先级高于 selectValue(推荐使用 selectDate 设置默认选中的日期)
+    if (!selectDate) {
+        if (self.selectValue && self.selectValue.length > 0) {
+            if (self.pickerMode == BRDatePickerModeYMDH && self.isShowAMAndPM) {
+                NSString *firstString = [[self.selectValue componentsSeparatedByString:@" "] firstObject];
+                NSString *lastString = [[self.selectValue componentsSeparatedByString:@" "] lastObject];
+                if ([lastString isEqualToString:[self getAMText]]) {
+                    self.selectValue = [NSString stringWithFormat:@"%@ 00", firstString];
+                }
+                if ([lastString isEqualToString:[self getPMText]]) {
+                    self.selectValue = [NSString stringWithFormat:@"%@ 12", firstString];
+                }
+            }
+            
+            NSDate *date = nil;
+            if ([self.selectValue isEqualToString:self.lastRowContent]) {
+                date = self.addToNow ? [NSDate date] : nil;
+            } else if ([self.selectValue isEqualToString:self.firstRowContent]) {
+                date = nil;
+            } else {
+                date = [self br_dateFromString:self.selectValue dateFormat:dateFormat];
+                if (!date) {
+                    BRErrorLog(@"参数异常!字符串 selectValue 的正确格式是:%@", dateFormat);
+                    NSAssert(date, @"参数异常!请检查字符串 selectValue 的格式");
+                    date = [NSDate date]; // 默认值参数格式错误时,重置/忽略默认值,防止在 Release 环境下崩溃!
+                }
+                if (self.pickerMode == BRDatePickerModeMDHM) {
+                    selectDate = [NSDate br_setMonth:date.br_month day:date.br_day hour:date.br_hour minute:date.br_minute];
+                } else if (self.pickerMode == BRDatePickerModeMD) {
+                    selectDate = [NSDate br_setMonth:date.br_month day:date.br_day];
+                } else if (self.pickerMode == BRDatePickerModeTime || self.pickerMode == BRDatePickerModeCountDownTimer || self.pickerMode == BRDatePickerModeHM) {
+                    selectDate = [NSDate br_setHour:date.br_hour minute:date.br_minute];
+                } else if (self.pickerMode == BRDatePickerModeHMS) {
+                    selectDate = [NSDate br_setHour:date.br_hour minute:date.br_minute second:date.br_second];
+                } else if (self.pickerMode == BRDatePickerModeMS) {
+                    selectDate = [NSDate br_setMinute:date.br_minute second:date.br_second];
+                } else {
+                    selectDate = date;
+                }
+            }
+        } else {
+            // 不设置默认日期
+            if (self.pickerMode == BRDatePickerModeTime ||
+                self.pickerMode == BRDatePickerModeCountDownTimer ||
+                self.pickerMode == BRDatePickerModeHM ||
+                self.pickerMode == BRDatePickerModeHMS ||
+                self.pickerMode == BRDatePickerModeMS) {
+                // 默认选中最小日期
+                selectDate = self.minDate;
+            } else {
+                if (self.minuteInterval > 1 || self.secondInterval > 1) {
+                    NSDate *date = [NSDate date];
+                    NSInteger minute = self.minDate.br_minute % self.minuteInterval == 0 ? self.minDate.br_minute : 0;
+                    NSInteger second = self.minDate.br_second % self.secondInterval == 0 ? self.minDate.br_second : 0;
+                    selectDate = [NSDate br_setYear:date.br_year month:date.br_month day:date.br_day hour:date.br_hour minute:minute second:second];
+                } else {
+                    // 默认选中今天的日期
+                    selectDate = [NSDate date];
+                }
+            }
+        }
+    }
+    
+    // 判断日期是否超过边界限制
+    BOOL selectLessThanMin = [self br_compareDate:selectDate targetDate:self.minDate dateFormat:dateFormat] == NSOrderedAscending;
+    BOOL selectMoreThanMax = [self br_compareDate:selectDate targetDate:self.maxDate dateFormat:dateFormat] == NSOrderedDescending;
+    if (selectLessThanMin) {
+        BRErrorLog(@"默认选择的日期不能小于最小日期!");
+        selectDate = self.minDate;
+    }
+    if (selectMoreThanMax) {
+        BRErrorLog(@"默认选择的日期不能大于最大日期!");
+        selectDate = self.maxDate;
+    }
+    
+    return selectDate;
+}
+
+#pragma mark - NSDate 转 NSString
+- (NSString *)br_stringFromDate:(NSDate *)date dateFormat:(NSString *)dateFormat {
+    return [NSDate br_stringFromDate:date dateFormat:dateFormat timeZone:self.timeZone language:self.pickerStyle.language];
+}
+
+#pragma mark - NSString 转 NSDate
+- (NSDate *)br_dateFromString:(NSString *)dateString dateFormat:(NSString *)dateFormat {
+    return [NSDate br_dateFromString:dateString dateFormat:dateFormat timeZone:self.timeZone language:self.pickerStyle.language];
+}
+
+#pragma mark - 比较两个日期大小(可以指定比较级数,即按指定格式进行比较)
+- (NSComparisonResult)br_compareDate:(NSDate *)date targetDate:(NSDate *)targetDate dateFormat:(NSString *)dateFormat {
+    NSString *dateString1 = [self br_stringFromDate:date dateFormat:dateFormat];
+    NSString *dateString2 = [self br_stringFromDate:targetDate dateFormat:dateFormat];
+    NSDate *date1 = [self br_dateFromString:dateString1 dateFormat:dateFormat];
+    NSDate *date2 = [self br_dateFromString:dateString2 dateFormat:dateFormat];
+    if ([date1 compare:date2] == NSOrderedDescending) {
+        return 1; // 大于
+    } else if ([date1 compare:date2] == NSOrderedAscending) {
+        return -1; // 小于
+    } else {
+        return 0; // 等于
+    }
+}
+
+#pragma mark - 获取 yearArr 数组
+- (NSArray *)getYearArr {
+    NSMutableArray *tempArr = [[NSMutableArray alloc]init];
+    for (NSInteger i = self.minDate.br_year; i <= self.maxDate.br_year; i++) {
+        [tempArr addObject:[self getYearNumber:i]];
+    }
+    if (self.isDescending) {
+        NSArray *reversedArr = [[tempArr reverseObjectEnumerator] allObjects];
+        tempArr = [reversedArr mutableCopy];
+    }
+    // 判断是否需要添加【自定义字符串】
+    if (self.lastRowContent || self.firstRowContent) {
+        switch (self.pickerMode) {
+            case BRDatePickerModeYMDHMS:
+            case BRDatePickerModeYMDHM:
+            case BRDatePickerModeYMDH:
+            case BRDatePickerModeYMD:
+            case BRDatePickerModeYM:
+            case BRDatePickerModeY:
+            {
+                if (self.lastRowContent) {
+                    [tempArr addObject:self.lastRowContent];
+                }
+                if (self.firstRowContent) {
+                    [tempArr insertObject:self.firstRowContent atIndex:0];
+                }
+            }
+                break;
+                
+            default:
+                break;
+        }
+    }
+    
+    return [tempArr copy];
+}
+
+#pragma mark - 获取 monthArr 数组
+- (NSArray *)getMonthArr:(NSInteger)year {
+    NSInteger startMonth = 1;
+    NSInteger endMonth = 12;
+    if (year == self.minDate.br_year) {
+        startMonth = self.minDate.br_month;
+    }
+    if (year == self.maxDate.br_year) {
+        endMonth = self.maxDate.br_month;
+    }
+    NSMutableArray *tempArr = [[NSMutableArray alloc]init];
+    for (NSInteger i = startMonth; i <= endMonth; i++) {
+        [tempArr addObject:[self getMDHMSNumber:i]];
+    }
+    if (self.isDescending) {
+        NSArray *reversedArr = [[tempArr reverseObjectEnumerator] allObjects];
+        tempArr = [reversedArr mutableCopy];
+    }
+    // 判断是否需要添加【自定义字符串】
+    if (self.lastRowContent || self.firstRowContent) {
+        switch (self.pickerMode) {
+            case BRDatePickerModeMDHM:
+            case BRDatePickerModeMD:
+            {
+                if (self.lastRowContent) {
+                    [tempArr addObject:self.lastRowContent];
+                }
+                if (self.firstRowContent) {
+                    [tempArr insertObject:self.firstRowContent atIndex:0];
+                }
+            }
+                break;
+                
+            default:
+                break;
+        }
+    }
+    
+    return [tempArr copy];
+}
+
+#pragma mark - 获取 dayArr 数组
+- (NSArray *)getDayArr:(NSInteger)year month:(NSInteger)month {
+    NSInteger startDay = 1;
+    NSInteger endDay = [NSDate br_getDaysInYear:year month:month];
+    if (year == self.minDate.br_year && month == self.minDate.br_month) {
+        startDay = self.minDate.br_day;
+    }
+    if (year == self.maxDate.br_year && month == self.maxDate.br_month) {
+        endDay = self.maxDate.br_day;
+    }
+    NSMutableArray *tempArr = [[NSMutableArray alloc]init];
+    for (NSInteger i = startDay; i <= endDay; i++) {
+        [tempArr addObject:[self getMDHMSNumber:i]];
+    }
+    if (self.isDescending) {
+        return [[tempArr reverseObjectEnumerator] allObjects];
+    }
+    
+    return [tempArr copy];
+}
+
+#pragma mark - 获取 hourArr 数组
+- (NSArray *)getHourArr:(NSInteger)year month:(NSInteger)month day:(NSInteger)day {
+    if (self.pickerMode == BRDatePickerModeYMDH && self.isShowAMAndPM) {
+        return @[[self getAMText], [self getPMText]];
+    }
+    
+    NSInteger startHour = 0;
+    NSInteger endHour = 23;
+    if (year == self.minDate.br_year && month == self.minDate.br_month && day == self.minDate.br_day) {
+        startHour = self.minDate.br_hour;
+    }
+    if (year == self.maxDate.br_year && month == self.maxDate.br_month && day == self.maxDate.br_day) {
+        endHour = self.maxDate.br_hour;
+    }
+    NSMutableArray *tempArr = [[NSMutableArray alloc]init];
+    for (NSInteger i = startHour; i <= endHour; i++) {
+        [tempArr addObject:[self getMDHMSNumber:i]];
+    }
+    if (self.isDescending) {
+        NSArray *reversedArr = [[tempArr reverseObjectEnumerator] allObjects];
+        tempArr = [reversedArr mutableCopy];
+    }
+    // 判断是否需要添加【自定义字符串】
+    if (self.lastRowContent || self.firstRowContent) {
+        switch (self.pickerMode) {
+            case BRDatePickerModeHMS:
+            case BRDatePickerModeHM:
+            {
+                if (self.lastRowContent) {
+                    [tempArr addObject:self.lastRowContent];
+                }
+                if (self.firstRowContent) {
+                    [tempArr insertObject:self.firstRowContent atIndex:0];
+                }
+            }
+                break;
+                
+            default:
+                break;
+        }
+    }
+    
+    return [tempArr copy];
+}
+
+#pragma mark - 获取 minuteArr 数组
+- (NSArray *)getMinuteArr:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour {
+    NSInteger startMinute = 0;
+    NSInteger endMinute = 59;
+    if (year == self.minDate.br_year && month == self.minDate.br_month && day == self.minDate.br_day && hour == self.minDate.br_hour) {
+        startMinute = self.minDate.br_minute;
+    }
+    if (year == self.maxDate.br_year && month == self.maxDate.br_month && day == self.maxDate.br_day && hour == self.maxDate.br_hour) {
+        endMinute = self.maxDate.br_minute;
+    }
+    NSMutableArray *tempArr = [[NSMutableArray alloc]init];
+    for (NSInteger i = startMinute; i <= endMinute; i += self.minuteInterval) {
+        [tempArr addObject:[self getMDHMSNumber:i]];
+    }
+    if (self.isDescending) {
+        NSArray *reversedArr = [[tempArr reverseObjectEnumerator] allObjects];
+        tempArr = [reversedArr mutableCopy];
+    }
+    // 判断是否需要添加【自定义字符串】
+    if (self.lastRowContent || self.firstRowContent) {
+        switch (self.pickerMode) {
+            case BRDatePickerModeMS:
+            {
+                if (self.lastRowContent) {
+                    [tempArr addObject:self.lastRowContent];
+                }
+                if (self.firstRowContent) {
+                    [tempArr insertObject:self.firstRowContent atIndex:0];
+                }
+            }
+                break;
+                
+            default:
+                break;
+        }
+    }
+    
+    return [tempArr copy];
+}
+
+#pragma mark - 获取 secondArr 数组
+- (NSArray *)getSecondArr:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute {
+    NSInteger startSecond = 0;
+    NSInteger endSecond = 59;
+    if (year == self.minDate.br_year && month == self.minDate.br_month && day == self.minDate.br_day && hour == self.minDate.br_hour && minute == self.minDate.br_minute) {
+        startSecond = self.minDate.br_second;
+    }
+    if (year == self.maxDate.br_year && month == self.maxDate.br_month && day == self.maxDate.br_day && hour == self.maxDate.br_hour && minute == self.maxDate.br_minute) {
+        endSecond = self.maxDate.br_second;
+    }
+    NSMutableArray *tempArr = [[NSMutableArray alloc]init];
+    for (NSInteger i = startSecond; i <= endSecond; i += self.secondInterval) {
+        [tempArr addObject:[self getMDHMSNumber:i]];
+    }
+    if (self.isDescending) {
+        return [[tempArr reverseObjectEnumerator] allObjects];
+    }
+    
+    return [tempArr copy];
+}
+
+#pragma mark - 获取 monthWeekArr 数组
+- (NSArray *)getMonthWeekArr:(NSInteger)year month:(NSInteger)month {
+    NSInteger startWeek = 1;
+    NSInteger endWeek = [NSDate br_getWeeksOfMonthInYear:year month:month];
+    if (year == self.minDate.br_year && month == self.minDate.br_month) {
+        startWeek = self.minDate.br_monthWeek;
+    }
+    if (year == self.maxDate.br_year && month == self.maxDate.br_month) {
+        endWeek = self.maxDate.br_monthWeek;
+    }
+    NSMutableArray *tempArr = [[NSMutableArray alloc]init];
+    for (NSInteger i = startWeek; i <= endWeek; i++) {
+        [tempArr addObject:[self getMDHMSNumber:i]];
+    }
+    if (self.isDescending) {
+        return [[tempArr reverseObjectEnumerator] allObjects];
+    }
+    
+    return [tempArr copy];
+}
+
+#pragma mark - 获取 yearWeekArr 数组
+- (NSArray *)getYearWeekArr:(NSInteger)year {
+    NSInteger startWeek = 1;
+    NSInteger endWeek = [NSDate br_getWeeksOfYearInYear:year];
+    if (year == self.minDate.br_year) {
+        startWeek = self.minDate.br_yearWeek;
+    }
+    if (year == self.maxDate.br_year) {
+        endWeek = self.maxDate.br_yearWeek;
+    }
+    NSMutableArray *tempArr = [[NSMutableArray alloc]init];
+    for (NSInteger i = startWeek; i <= endWeek; i++) {
+        [tempArr addObject:[self getMDHMSNumber:i]];
+    }
+    if (self.isDescending) {
+        return [[tempArr reverseObjectEnumerator] allObjects];
+    }
+    
+    return [tempArr copy];
+}
+
+#pragma mark - 获取 quarterArr 数组
+- (NSArray *)getQuarterArr:(NSInteger)year {
+    NSInteger startQuarter = 1;
+    NSInteger endQuarter = [NSDate br_getQuartersInYear:year];
+    if (year == self.minDate.br_year) {
+        startQuarter = self.minDate.br_quarter;
+    }
+    if (year == self.maxDate.br_year) {
+        endQuarter = self.maxDate.br_quarter;
+    }
+    NSMutableArray *tempArr = [[NSMutableArray alloc]init];
+    for (NSInteger i = startQuarter; i <= endQuarter; i++) {
+        [tempArr addObject:[self getMDHMSNumber:i]];
+    }
+    if (self.isDescending) {
+        return [[tempArr reverseObjectEnumerator] allObjects];
+    }
+    
+    return [tempArr copy];
+}
+
+#pragma mark - 添加 pickerView
+- (void)setupPickerView:(UIView *)pickerView toView:(UIView *)view {
+    if (view) {
+        // 立即刷新容器视图 view 的布局(防止 view 使用自动布局时,选择器视图无法正常显示)
+        [view setNeedsLayout];
+        [view layoutIfNeeded];
+        
+        self.frame = view.bounds;
+        CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
+        CGFloat pickerFooterViewHeight = self.pickerFooterView ? self.pickerFooterView.bounds.size.height : 0;
+        pickerView.frame = CGRectMake(0, pickerHeaderViewHeight, view.bounds.size.width, view.bounds.size.height - pickerHeaderViewHeight - pickerFooterViewHeight);
+        [self addSubview:pickerView];
+    } else {
+        // iOS16:重新设置 pickerView 高度(解决懒加载设置frame不生效问题)
+        CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
+        pickerView.frame = CGRectMake(0, self.pickerStyle.titleBarHeight + pickerHeaderViewHeight, self.keyView.bounds.size.width, self.pickerStyle.pickerHeight);
+
+        [self.alertView addSubview:pickerView];
+    }
+}
+
+#pragma mark - 获取日期单位
+- (NSArray *)setupPickerUnitLabel:(UIPickerView *)pickerView unitArr:(NSArray *)unitArr {
+    NSMutableArray *tempArr = [[NSMutableArray alloc]init];
+    for (NSInteger i = 0; i < pickerView.numberOfComponents; i++) {
+        // label宽度
+        CGFloat labelWidth = pickerView.bounds.size.width / pickerView.numberOfComponents;
+        // 根据占位文本长度去计算宽度
+        NSString *tempText = @"00";
+        if (i == 0) {
+            switch (self.pickerMode) {
+                case BRDatePickerModeYMDHMS:
+                case BRDatePickerModeYMDHM:
+                case BRDatePickerModeYMDH:
+                case BRDatePickerModeYMD:
+                case BRDatePickerModeYM:
+                case BRDatePickerModeY:
+                {
+                    tempText = @"0123";
+                }
+                    break;
+                    
+                default:
+                    break;
+            }
+        }
+        // 文本宽度
+        CGFloat labelTextWidth = [tempText boundingRectWithSize:CGSizeMake(MAXFLOAT, self.pickerStyle.rowHeight)
+                                                        options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
+                                                     attributes:@{NSFontAttributeName: self.pickerStyle.pickerTextFont}
+                                                        context:nil].size.width;
+        // 单位label
+        UILabel *unitLabel = [[UILabel alloc]init];
+        unitLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
+        unitLabel.backgroundColor = [UIColor clearColor];
+        if (self.pickerMode != BRDatePickerModeYMDHMS) {
+            unitLabel.textAlignment = NSTextAlignmentCenter;
+        }
+        unitLabel.font = self.pickerStyle.dateUnitTextFont;
+        unitLabel.textColor = self.pickerStyle.dateUnitTextColor;
+        // 字体自适应属性
+        unitLabel.adjustsFontSizeToFitWidth = YES;
+        // 自适应最小字体缩放比例
+        unitLabel.minimumScaleFactor = 0.5f;
+        unitLabel.text = (unitArr.count > 0 && i < unitArr.count) ? unitArr[i] : nil;
+        
+        CGFloat originX = i * labelWidth + labelWidth / 2.0 + labelTextWidth / 2.0 + self.pickerStyle.dateUnitOffsetX;
+        CGFloat originY = (pickerView.frame.size.height - self.pickerStyle.rowHeight) / 2 + self.pickerStyle.dateUnitOffsetY;
+        unitLabel.frame = CGRectMake(originX, originY, MAX(self.pickerStyle.rowHeight, labelTextWidth), self.pickerStyle.rowHeight);
+        
+        [tempArr addObject:unitLabel];
+        
+        [pickerView addSubview:unitLabel];
+    }
+    
+    return [tempArr copy];
+}
+
+- (NSString *)getYearNumber:(NSInteger)year {
+    NSString *yearString = [NSString stringWithFormat:@"%@", @(year)];
+    if (self.isNumberFullName) {
+        yearString = [NSString stringWithFormat:@"%04d", [yearString intValue]];
+    }
+    return yearString;
+}
+
+- (NSString *)getMDHMSNumber:(NSInteger)number {
+    NSString *string = [NSString stringWithFormat:@"%@", @(number)];
+    if (self.isNumberFullName) {
+        string = [NSString stringWithFormat:@"%02d", [string intValue]];
+    }
+    return string;
+}
+
+- (NSString *)getYearText:(NSArray *)yearArr row:(NSInteger)row {
+    NSInteger index = 0;
+    if (row >= 0) {
+        index = MIN(row, yearArr.count - 1);
+    }
+    NSString *yearString = [yearArr objectAtIndex:index];
+    if ((self.lastRowContent && [yearString isEqualToString:self.lastRowContent]) || (self.firstRowContent && [yearString isEqualToString:self.firstRowContent])) {
+        return yearString;
+    }
+    NSString *yearUnit = self.showUnitType == BRShowUnitTypeAll ? [self getYearUnit] : @"";
+    return [NSString stringWithFormat:@"%@%@", yearString, yearUnit];
+}
+
+- (NSString *)getMonthText:(NSArray *)monthArr row:(NSInteger)row {
+    NSInteger index = 0;
+    if (row >= 0) {
+        index = MIN(row, monthArr.count - 1);
+    }
+    NSString *monthString = [monthArr objectAtIndex:index];
+    // 首行/末行是自定义字符串,直接返回
+    if ((self.firstRowContent && [monthString isEqualToString:self.firstRowContent]) || (self.lastRowContent && [monthString isEqualToString:self.lastRowContent])) {
+        return monthString;
+    }
+    
+    // 自定义月份数据源
+    if (self.monthNames && self.monthNames.count > 0) {
+        NSInteger index = [monthString integerValue] - 1;
+        monthString = (index >= 0 && index < self.monthNames.count) ? self.monthNames[index] : @"";
+    } else {
+        if (![self.pickerStyle.language hasPrefix:@"zh"] && (self.pickerMode == BRDatePickerModeYMD || self.pickerMode == BRDatePickerModeYM || self.pickerMode == BRDatePickerModeYMW)) {
+            // 非中文环境:月份使用系统的月份名称
+            NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
+            dateFormatter.locale = [[NSLocale alloc]initWithLocaleIdentifier:self.pickerStyle.language];
+            // monthSymbols: @[@"January", @"February", @"March", @"April", @"May", @"June", @"July", @"August", @"September", @"October", @"November", @"December"];
+            // shortMonthSymbols: @[@"Jan", @"Feb", @"Mar", @"Apr", @"May", @"Jun", @"Jul", @"Aug", @"Sep", @"Oct", @"Nov", @"Dec"];
+            NSArray *monthNames = self.isShortMonthName ? dateFormatter.shortMonthSymbols : dateFormatter.monthSymbols;
+            NSInteger index = [monthString integerValue] - 1;
+            monthString = (index >= 0 && index < monthNames.count) ? monthNames[index] : @"";
+        } else {
+            // 中文环境:月份显示数字
+            NSString *monthUnit = self.showUnitType == BRShowUnitTypeAll ? [self getMonthUnit] : @"";
+            monthString = [NSString stringWithFormat:@"%@%@", monthString, monthUnit];
+        }
+    }
+    
+    return monthString;
+}
+
+- (NSString *)getDayText:(NSArray *)dayArr row:(NSInteger)row mSelectDate:(NSDate *)mSelectDate {
+    NSInteger index = 0;
+    if (row >= 0) {
+        index = MIN(row, dayArr.count - 1);
+    }
+    NSString *dayString = [dayArr objectAtIndex:index];
+    if (self.isShowToday && mSelectDate.br_year == [NSDate date].br_year && mSelectDate.br_month == [NSDate date].br_month && [dayString integerValue] == [NSDate date].br_day) {
+        return [NSBundle br_localizedStringForKey:@"今天" language:self.pickerStyle.language];
+    }
+    NSString *dayUnit = self.showUnitType == BRShowUnitTypeAll ? [self getDayUnit] : @"";
+    dayString = [NSString stringWithFormat:@"%@%@", dayString, dayUnit];
+    if (self.isShowWeek) {
+        NSDate *date = [NSDate br_setYear:mSelectDate.br_year month:mSelectDate.br_month day:[dayString integerValue]];
+        NSString *weekdayString = [NSBundle br_localizedStringForKey:[date br_weekdayString] language:self.pickerStyle.language];
+        dayString = [NSString stringWithFormat:@"%@%@", dayString, weekdayString];
+    }
+    return dayString;
+}
+
+- (NSString *)getHourText:(NSArray *)hourArr row:(NSInteger)row {
+    NSInteger index = 0;
+    if (row >= 0) {
+        index = MIN(row, hourArr.count - 1);
+    }
+    NSString *hourString = [hourArr objectAtIndex:index];
+    if ((self.lastRowContent && [hourString isEqualToString:self.lastRowContent]) || (self.firstRowContent && [hourString isEqualToString:self.firstRowContent])) {
+        return hourString;
+    }
+    NSString *hourUnit = self.showUnitType == BRShowUnitTypeAll ? [self getHourUnit] : @"";
+    return [NSString stringWithFormat:@"%@%@", hourString, hourUnit];
+}
+
+- (NSString *)getMinuteText:(NSArray *)minuteArr row:(NSInteger)row {
+    NSInteger index = 0;
+    if (row >= 0) {
+        index = MIN(row, minuteArr.count - 1);
+    }
+    NSString *minuteString = [minuteArr objectAtIndex:index];
+    NSString *minuteUnit = self.showUnitType == BRShowUnitTypeAll ? [self getMinuteUnit] : @"";
+    return [NSString stringWithFormat:@"%@%@", minuteString, minuteUnit];
+}
+
+- (NSString *)getSecondText:(NSArray *)secondArr row:(NSInteger)row {
+    NSInteger index = 0;
+    if (row >= 0) {
+        index = MIN(row, secondArr.count - 1);
+    }
+    NSString *secondString = [secondArr objectAtIndex:index];
+    NSString *secondUnit = self.showUnitType == BRShowUnitTypeAll ? [self getSecondUnit] : @"";
+    return [NSString stringWithFormat:@"%@%@", secondString, secondUnit];
+}
+
+- (NSString *)getWeekText:(NSArray *)weekArr row:(NSInteger)row {
+    NSInteger index = 0;
+    if (row >= 0) {
+        index = MIN(row, weekArr.count - 1);
+    }
+    NSString *weekString = [weekArr objectAtIndex:index];
+    if ((self.lastRowContent && [weekString isEqualToString:self.lastRowContent]) || (self.firstRowContent && [weekString isEqualToString:self.firstRowContent])) {
+        return weekString;
+    }
+    NSString *weekUnit = self.showUnitType == BRShowUnitTypeAll ? [self getWeekUnit] : @"";
+    return [NSString stringWithFormat:@"%@%@", weekString, weekUnit];
+}
+
+- (NSString *)getQuarterText:(NSArray *)quarterArr row:(NSInteger)row {
+    NSInteger index = 0;
+    if (row >= 0) {
+        index = MIN(row, quarterArr.count - 1);
+    }
+    NSString *quarterString = [quarterArr objectAtIndex:index];
+    if ((self.lastRowContent && [quarterString isEqualToString:self.lastRowContent]) || (self.firstRowContent && [quarterString isEqualToString:self.firstRowContent])) {
+        return quarterString;
+    }
+    NSString *quarterUnit = self.showUnitType == BRShowUnitTypeAll ? [self getQuarterUnit] : @"";
+    return [NSString stringWithFormat:@"%@%@", quarterString, quarterUnit];
+}
+
+- (NSString *)getAMText {
+    return [NSBundle br_localizedStringForKey:@"上午" language:self.pickerStyle.language];
+}
+
+- (NSString *)getPMText {
+    return [NSBundle br_localizedStringForKey:@"下午" language:self.pickerStyle.language];
+}
+
+- (NSString *)getYearUnit {
+    if (self.customUnit) {
+        return self.customUnit[@"year"] ? : @"";
+    }
+    if (![self.pickerStyle.language hasPrefix:@"zh"]) {
+        return @"";
+    }
+    return [NSBundle br_localizedStringForKey:@"年" language:self.pickerStyle.language];
+}
+
+- (NSString *)getMonthUnit {
+    if (self.customUnit) {
+        return self.customUnit[@"month"] ? : @"";
+    }
+    if (![self.pickerStyle.language hasPrefix:@"zh"]) {
+        return @"";
+    }
+    return [NSBundle br_localizedStringForKey:@"月" language:self.pickerStyle.language];
+}
+
+- (NSString *)getDayUnit {
+    if (self.customUnit) {
+        return self.customUnit[@"day"] ? : @"";
+    }
+    if (![self.pickerStyle.language hasPrefix:@"zh"]) {
+        return @"";
+    }
+    return [NSBundle br_localizedStringForKey:@"日" language:self.pickerStyle.language];
+}
+
+- (NSString *)getHourUnit {
+    if (self.pickerMode == BRDatePickerModeYMDH && self.isShowAMAndPM) {
+        return @"";
+    }
+    if (self.customUnit) {
+        return self.customUnit[@"hour"] ? : @"";
+    }
+    if (![self.pickerStyle.language hasPrefix:@"zh"]) {
+        return @"";
+    }
+    return [NSBundle br_localizedStringForKey:@"时" language:self.pickerStyle.language];
+}
+
+- (NSString *)getMinuteUnit {
+    if (self.customUnit) {
+        return self.customUnit[@"minute"] ? : @"";
+    }
+    if (![self.pickerStyle.language hasPrefix:@"zh"]) {
+        return @"";
+    }
+    return [NSBundle br_localizedStringForKey:@"分" language:self.pickerStyle.language];
+}
+
+- (NSString *)getSecondUnit {
+    if (self.customUnit) {
+        return self.customUnit[@"second"] ? : @"";
+    }
+    if (![self.pickerStyle.language hasPrefix:@"zh"]) {
+        return @"";
+    }
+    return [NSBundle br_localizedStringForKey:@"秒" language:self.pickerStyle.language];
+}
+
+- (NSString *)getWeekUnit {
+    if (self.customUnit) {
+        return self.customUnit[@"week"] ? : @"";
+    }
+    if (![self.pickerStyle.language hasPrefix:@"zh"]) {
+        return @"";
+    }
+    return [NSBundle br_localizedStringForKey:@"周" language:self.pickerStyle.language];
+}
+
+- (NSString *)getQuarterUnit {
+    if (self.customUnit) {
+        return self.customUnit[@"quarter"] ? : @"";
+    }
+    if (![self.pickerStyle.language hasPrefix:@"zh"]) {
+        return @"";
+    }
+    return [NSBundle br_localizedStringForKey:@"季度" language:self.pickerStyle.language];
+}
+
+- (NSInteger)getIndexWithArray:(NSArray *)array object:(NSString *)obj {
+    if (!array || array.count == 0 || !obj) {
+        return 0;
+    }
+    if ([array containsObject:obj]) {
+        return [array indexOfObject:obj];
+    }
+    return 0;
+}
+
+@end

+ 274 - 0
Pods/BRPickerView/BRPickerView/DatePickerView/BRDatePickerView.h

@@ -0,0 +1,274 @@
+//
+//  BRDatePickerView.h
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2017/8/11.
+//  Copyright © 2017 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import "BRBaseView.h"
+#import "NSDate+BRPickerView.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// 日期选择器格式
+typedef NS_ENUM(NSInteger, BRDatePickerMode) {
+    // ----- 以下4种是系统样式(兼容国际化日期格式) -----
+    /** 【yyyy-MM-dd】UIDatePickerModeDate(美式日期:MM-dd-yyyy;英式日期:dd-MM-yyyy)*/
+    BRDatePickerModeDate,
+    /** 【yyyy-MM-dd HH:mm】 UIDatePickerModeDateAndTime */
+    BRDatePickerModeDateAndTime,
+    /** 【HH:mm】UIDatePickerModeTime */
+    BRDatePickerModeTime,
+    /** 【HH:mm】UIDatePickerModeCountDownTimer */
+    BRDatePickerModeCountDownTimer,
+    
+    // ----- 以下14种是自定义样式 -----
+    /** 【yyyy-MM-dd HH:mm:ss】年月日时分秒 */
+    BRDatePickerModeYMDHMS,
+    /** 【yyyy-MM-dd HH:mm】年月日时分 */
+    BRDatePickerModeYMDHM,
+    /** 【yyyy-MM-dd HH】年月日时 */
+    BRDatePickerModeYMDH,
+    /** 【MM-dd HH:mm】月日时分 */
+    BRDatePickerModeMDHM,
+    /** 【yyyy-MM-dd】年月日(兼容国际化日期:dd-MM-yyyy)*/
+    BRDatePickerModeYMD,
+    /** 【yyyy-MM】年月(兼容国际化日期:MM-yyyy)*/
+    BRDatePickerModeYM,
+    /** 【yyyy】年 */
+    BRDatePickerModeY,
+    /** 【MM-dd】月日 */
+    BRDatePickerModeMD,
+    /** 【HH:mm:ss】时分秒 */
+    BRDatePickerModeHMS,
+    /** 【HH:mm】时分 */
+    BRDatePickerModeHM,
+    /** 【mm:ss】分秒 */
+    BRDatePickerModeMS,
+    
+    /** 【yyyy-qq】年季度 */
+    BRDatePickerModeYQ,
+    /** 【yyyy-MM-ww】年月周 */
+    BRDatePickerModeYMW,
+    /** 【yyyy-ww】年周 */
+    BRDatePickerModeYW
+};
+
+/// 日期单位显示的位置
+typedef NS_ENUM(NSInteger, BRShowUnitType) {
+    /** 日期单位显示全部行(默认)*/
+    BRShowUnitTypeAll,
+    /** 日期单位仅显示中间行 */
+    BRShowUnitTypeOnlyCenter,
+    /** 日期单位不显示(隐藏日期单位)*/
+    BRShowUnitTypeNone
+};
+
+typedef void (^BRDateResultBlock)(NSDate * _Nullable selectDate, NSString * _Nullable selectValue);
+
+typedef void (^BRDateResultRangeBlock)(NSDate * _Nullable selectStartDate, NSDate * _Nullable selectEndDate, NSString * _Nullable selectValue);
+
+@interface BRDatePickerView : BRBaseView
+
+/**
+ //////////////////////////////////////////////////////////////////////////
+ ///
+ ///   【用法一】
+ ///    特点:灵活,扩展性强(推荐使用!)
+ ///
+ ////////////////////////////////////////////////////////////////////////*/
+
+/** 日期选择器显示类型 */
+@property (nonatomic, assign) BRDatePickerMode pickerMode;
+
+/** 设置选中的日期(推荐使用 selectDate) */
+@property (nullable, nonatomic, strong) NSDate *selectDate;
+@property (nullable, nonatomic, copy) NSString *selectValue;
+
+/** 最小日期(可使用 NSDate+BRPickerView 分类中对应的方法进行创建)*/
+@property (nullable, nonatomic, strong) NSDate *minDate;
+/** 最大日期(可使用 NSDate+BRPickerView 分类中对应的方法进行创建)*/
+@property (nullable, nonatomic, strong) NSDate *maxDate;
+
+/** 选择结果的回调 */
+@property (nullable, nonatomic, copy) BRDateResultBlock resultBlock;
+/** 选择结果范围的回调:for `BRDatePickerModeYQ`、`BRDatePickerModeYMW`、`BRDatePickerModeYW`, ignored otherwise. */
+@property (nullable, nonatomic, copy) BRDateResultRangeBlock resultRangeBlock;
+
+/** 滚动选择时触发的回调 */
+@property (nullable, nonatomic, copy) BRDateResultBlock changeBlock;
+/** 滚动选择范围时触发的回调:for `BRDatePickerModeYQ`、`BRDatePickerModeYMW`、`BRDatePickerModeYW`, ignored otherwise. */
+@property (nullable, nonatomic, copy) BRDateResultRangeBlock changeRangeBlock;
+
+/** 日期单位显示类型 */
+@property (nonatomic, assign) BRShowUnitType showUnitType;
+
+/** 是否显示【星期】,默认为 NO */
+@property (nonatomic, assign, getter=isShowWeek) BOOL showWeek;
+
+/** 是否显示【今天】,默认为 NO */
+@property (nonatomic, assign, getter=isShowToday) BOOL showToday;
+
+/** 是否添加【至今】,默认为 NO */
+@property (nonatomic, assign, getter=isAddToNow) BOOL addToNow;
+
+/** 首行添加【自定义字符串】,配合 selectValue 可设置默认选中 */
+@property (nullable, nonatomic, copy) NSString *firstRowContent;
+
+/** 末行添加【自定义字符串】,配合 selectValue 可设置默认选中 */
+@property (nullable, nonatomic, copy) NSString *lastRowContent;
+
+/** 最后一行,添加【自定义字符串】 */
+@property (nullable, nonatomic, copy) NSString *addCustomString DEPRECATED_MSG_ATTRIBUTE("Use 'lastRowContent' instead");
+
+/** 滚轮上日期数据排序是否降序,默认为 NO(升序)*/
+@property (nonatomic, assign, getter=isDescending) BOOL descending;
+
+/** 选择器上数字是否带有前导零,默认为 NO(如:无前导零:2020-1-1;有前导零:2020-01-01)*/
+@property (nonatomic, assign, getter=isNumberFullName) BOOL numberFullName;
+
+/** 设置分的时间间隔,默认为1(范围:1 ~ 30)*/
+@property (nonatomic, assign) NSInteger minuteInterval;
+
+/** 设置秒的时间间隔,默认为1(范围:1 ~ 30)*/
+@property (nonatomic, assign) NSInteger secondInterval;
+
+/** 设置倒计时的时长,默认为0(范围:0 ~ 24*60*60-1,单位为秒) for `BRDatePickerModeCountDownTimer`, ignored otherwise. */
+@property (nonatomic, assign) NSTimeInterval countDownDuration;
+
+/**
+ *  自定义月份数据源
+ *  如:@[@"1月", @"2月",..., @"12月"]、 @[@"一月", @"二月",..., @"十二月"]、 @[@"Jan", @"Feb",..., @"Dec"] 等
+ */
+@property (nonatomic, copy) NSArray <NSString *> *monthNames;
+
+/**
+ *  设置国际化日期(非中文环境下)月份是否显示简称,默认为 NO。for `BRDatePickerModeYMD` and `BRDatePickerModeYM`, ignored otherwise.
+ *  如:January 的简称为:Jan
+ */
+@property (nonatomic, assign, getter=isShortMonthName) BOOL shortMonthName;
+
+/**
+ *  自定义日期单位
+ *  字典格式:@{@"year": @"年", @"month": @"月", @"day": @"日", @"hour": @"时", @"minute": @"分", @"second": @"秒"}
+ */
+@property (nonatomic, copy) NSDictionary *customUnit;
+
+/** 显示上午和下午,默认为 NO. for `BRDatePickerModeYMDH`, ignored otherwise. */
+@property (nonatomic, assign, getter=isShowAMAndPM) BOOL showAMAndPM;
+
+/** 设置时区,默认为当前时区 */
+@property (nullable, nonatomic, copy) NSTimeZone *timeZone;
+
+/** default is [NSCalendar currentCalendar]. setting nil returns to default. for `UIDatePicker` */
+@property (nonatomic, copy) NSCalendar *calendar;
+
+/** 指定不允许选择的日期 */
+@property (nullable, nonatomic, copy) NSArray <NSDate *> *nonSelectableDates;
+
+/** 不允许选择日期的回调 */
+@property (nullable, nonatomic, copy) BRDateResultBlock nonSelectableBlock;
+
+/// 初始化日期选择器
+/// @param pickerMode  日期选择器显示类型
+- (instancetype)initWithPickerMode:(BRDatePickerMode)pickerMode;
+
+/// 弹出选择器视图
+- (void)show;
+
+/// 关闭选择器视图
+- (void)dismiss;
+
+
+
+
+//================================================= 华丽的分割线 =================================================
+
+
+
+
+/**
+ //////////////////////////////////////////////////////////////////////////
+ ///
+ ///   【用法二】:快捷使用,直接选择下面其中的一个方法进行使用
+ ///    特点:快捷,方便
+ ///
+ ////////////////////////////////////////////////////////////////////////*/
+
+/**
+ *  1.显示日期选择器
+ *
+ *  @param mode             日期显示类型
+ *  @param title            选择器标题
+ *  @param selectValue      默认选中的日期(默认选中当前日期)
+ *  @param resultBlock      选择结果的回调
+ *
+ */
++ (void)showDatePickerWithMode:(BRDatePickerMode)mode
+                         title:(nullable NSString *)title
+                   selectValue:(nullable NSString *)selectValue
+                   resultBlock:(nullable BRDateResultBlock)resultBlock;
+
+/**
+ *  2.显示日期选择器
+ *
+ *  @param mode             日期显示类型
+ *  @param title            选择器标题
+ *  @param selectValue      默认选中的日期(默认选中当前日期)
+ *  @param isAutoSelect     是否自动选择,即滚动选择器后就执行结果回调,默认为 NO
+ *  @param resultBlock      选择结果的回调
+ *
+ */
++ (void)showDatePickerWithMode:(BRDatePickerMode)mode
+                         title:(nullable NSString *)title
+                   selectValue:(nullable NSString *)selectValue
+                  isAutoSelect:(BOOL)isAutoSelect
+                   resultBlock:(nullable BRDateResultBlock)resultBlock;
+
+/**
+ *  3.显示日期选择器
+ *
+ *  @param mode             日期显示类型
+ *  @param title            选择器标题
+ *  @param selectValue      默认选中的日期(默认选中当前日期)
+ *  @param minDate          最小日期(可使用 NSDate+BRPickerView 分类中对应的方法进行创建)
+ *  @param maxDate          最大日期(可使用 NSDate+BRPickerView 分类中对应的方法进行创建)
+ *  @param isAutoSelect     是否自动选择,即滚动选择器后就执行结果回调,默认为 NO
+ *  @param resultBlock      选择结果的回调
+ *
+ */
++ (void)showDatePickerWithMode:(BRDatePickerMode)mode
+                         title:(nullable NSString *)title
+                   selectValue:(nullable NSString *)selectValue
+                       minDate:(nullable NSDate *)minDate
+                       maxDate:(nullable NSDate *)maxDate
+                  isAutoSelect:(BOOL)isAutoSelect
+                   resultBlock:(nullable BRDateResultBlock)resultBlock;
+
+/**
+ * 3.显示日期选择器
+ *
+ * @param mode             日期显示类型
+ * @param title            选择器标题
+ * @param selectValue      默认选中的日期(默认选中当前日期)
+ * @param minDate          最小日期(可使用 NSDate+BRPickerView 分类中对应的方法进行创建)
+ * @param maxDate          最大日期(可使用 NSDate+BRPickerView 分类中对应的方法进行创建)
+ * @param isAutoSelect     是否自动选择,即滚动选择器后就执行结果回调,默认为 NO
+ * @param resultBlock      选择结果的回调
+ * @param resultRangeBlock 范围选择结果的回调
+ *
+ */
++ (void)showDatePickerWithMode:(BRDatePickerMode)mode
+                         title:(nullable NSString *)title
+                   selectValue:(nullable NSString *)selectValue
+                       minDate:(nullable NSDate *)minDate
+                       maxDate:(nullable NSDate *)maxDate
+                  isAutoSelect:(BOOL)isAutoSelect
+                   resultBlock:(nullable BRDateResultBlock)resultBlock
+              resultRangeBlock:(nullable BRDateResultRangeBlock)resultRangeBlock;
+
+@end
+
+NS_ASSUME_NONNULL_END

Plik diff jest za duży
+ 1965 - 0
Pods/BRPickerView/BRPickerView/DatePickerView/BRDatePickerView.m


+ 126 - 0
Pods/BRPickerView/BRPickerView/DatePickerView/NSDate+BRPickerView.h

@@ -0,0 +1,126 @@
+//
+//  NSDate+BRPickerView.h
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2018/3/15.
+//  Copyright © 2018 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface NSDate (BRPickerView)
+/// 获取指定date的详细信息
+@property (readonly) NSInteger br_year;         // 年
+@property (readonly) NSInteger br_month;        // 月
+@property (readonly) NSInteger br_day;          // 日
+@property (readonly) NSInteger br_hour;         // 时
+@property (readonly) NSInteger br_minute;       // 分
+@property (readonly) NSInteger br_second;       // 秒
+@property (readonly) NSInteger br_weekday;      // 星期
+@property (readonly) NSInteger br_monthWeek;    // 月周
+@property (readonly) NSInteger br_yearWeek;     // 年周
+@property (readonly) NSInteger br_quarter;      // 季度
+
+/** 获取中文星期字符串 */
+@property (nullable, nonatomic, readonly, copy) NSString *br_weekdayString;
+
+/** 获取日历单例对象 */
++ (NSCalendar *)br_calendar;
+
+
+/// ---------------- 创建 date ----------------
+/** 通过 NSDateComponents对象 来创建 NSDate对象(可以设置时区) */
++ (nullable NSDate *)br_setDateFromComponents:(NSDateComponents *)components timeZone:(NSTimeZone *)timeZone;
+
+/** yyyy */
++ (nullable NSDate *)br_setYear:(NSInteger)year;
+
+/** yyyy-MM */
++ (nullable NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month;
+
+/** yyyy-MM-dd */
++ (nullable NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day;
+
+/** yyyy-MM-dd HH */
++ (nullable NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour;
+
+/** yyyy-MM-dd HH:mm */
++ (nullable NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute;
+
+/** yyyy-MM-dd HH:mm:ss */
++ (nullable NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second;
+
+/** MM-dd HH:mm */
++ (nullable NSDate *)br_setMonth:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute;
+
+/** MM-dd */
++ (nullable NSDate *)br_setMonth:(NSInteger)month day:(NSInteger)day;
+
+/** HH:mm:ss */
++ (nullable NSDate *)br_setHour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second;
+
+/** HH:mm */
++ (nullable NSDate *)br_setHour:(NSInteger)hour minute:(NSInteger)minute;
+
+/** mm:ss */
++ (nullable NSDate *)br_setMinute:(NSInteger)minute second:(NSInteger)second;
+
+/** yyyy-MM-ww */
++ (nullable NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month weekOfMonth:(NSInteger)weekOfMont;
+
+/** yyyy-ww */
++ (nullable NSDate *)br_setYear:(NSInteger)year weekOfYear:(NSInteger)weekOfYear;
+
+/** yyyy-qq */
++ (nullable NSDate *)br_setYear:(NSInteger)year quarter:(NSInteger)quarter;
+
+
+/** 获取某个月的天数(通过年月求每月天数)*/
++ (NSUInteger)br_getDaysInYear:(NSInteger)year month:(NSInteger)month;
+
+/** 获取某个月的周数(通过年月求该月周数)*/
++ (NSUInteger)br_getWeeksOfMonthInYear:(NSInteger)year month:(NSInteger)month;
+
+/** 获取某一年的周数(通过年求该年周数)*/
++ (NSUInteger)br_getWeeksOfYearInYear:(NSInteger)year;
+
+/** 获取某一年的季度数(通过年求该年季度数)*/
++ (NSUInteger)br_getQuartersInYear:(NSInteger)year;
+
+/**  获取 日期加上/减去某天数后的新日期 */
+- (nullable NSDate *)br_getNewDateToDays:(NSTimeInterval)days;
+
+/**  获取 日期加上/减去某个月数后的新日期 */
+- (nullable NSDate *)br_getNewDateToMonths:(NSTimeInterval)months;
+
+/** NSDate 转 NSString */
++ (nullable NSString *)br_stringFromDate:(NSDate *)date dateFormat:(NSString *)dateFormat;
+/** NSDate 转 NSString */
++ (nullable NSString *)br_stringFromDate:(NSDate *)date
+                     dateFormat:(NSString *)dateFormat
+                       timeZone:(nullable NSTimeZone *)timeZone
+                       language:(nullable NSString *)language;
+
+
+/** NSString 转 NSDate */
++ (nullable NSDate *)br_dateFromString:(NSString *)dateString dateFormat:(NSString *)dateFormat;
+/** NSString 转 NSDate */
++ (nullable NSDate *)br_dateFromString:(NSString *)dateString
+                   dateFormat:(NSString *)dateFormat
+                     timeZone:(nullable NSTimeZone *)timeZone
+                     language:(nullable NSString *)language;
+
+
+/** NSDate 转 NSString(已弃用) */
++ (nullable NSString *)br_getDateString:(NSDate *)date format:(NSString *)format DEPRECATED_MSG_ATTRIBUTE("Use 'br_stringFromDate:dateFormat:' instead");
+
+/** NSString 转 NSDate(已弃用) */
++ (nullable NSDate *)br_getDate:(NSString *)dateString format:(NSString *)format DEPRECATED_MSG_ATTRIBUTE("Use 'br_dateFromString:dateFormat:' instead");
+
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 400 - 0
Pods/BRPickerView/BRPickerView/DatePickerView/NSDate+BRPickerView.m

@@ -0,0 +1,400 @@
+//
+//  NSDate+BRPickerView.m
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2018/3/15.
+//  Copyright © 2018 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import "NSDate+BRPickerView.h"
+#import "BRPickerViewMacro.h"
+
+BRSYNTH_DUMMY_CLASS(NSDate_BRPickerView)
+
+static const NSCalendarUnit unitFlags = (NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekOfYear | NSCalendarUnitWeekOfMonth |  NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond | NSCalendarUnitWeekday | NSCalendarUnitWeekdayOrdinal | NSCalendarUnitQuarter);
+
+@implementation NSDate (BRPickerView)
+
+#pragma mark - 获取日历单例对象
++ (NSCalendar *)br_calendar {
+    static NSCalendar *sharedCalendar = nil;
+    if (!sharedCalendar) {
+        // 创建日历对象,指定日历的算法(公历/阳历)
+        sharedCalendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
+        // NSCalendar 设置时区
+        //sharedCalendar.timeZone = [NSTimeZone timeZoneWithName:@"America/Chicago"];
+    }
+    return sharedCalendar;
+}
+
+#pragma mark - NSDate 转 NSDateComponents
++ (NSDateComponents *)br_componentsFromDate:(NSDate *)date {
+    // 通过日历类 NSCalendar 进行转换
+    NSCalendar *calendar = [self br_calendar];
+    // NSDateComponents 可以获得日期的详细信息,即日期的组成
+    return [calendar components:unitFlags fromDate:date];
+}
+
+#pragma mark - NSDateComponents 转 NSDate
++ (NSDate *)br_dateFromComponents:(NSDateComponents *)components {
+    // 通过日历类 NSCalendar 进行转换
+    NSCalendar *calendar = [self br_calendar];
+    return [calendar dateFromComponents:components];
+}
+
+#pragma mark - 获取指定日期的年份
+- (NSInteger)br_year {
+    return [NSDate br_componentsFromDate:self].year;
+}
+
+#pragma mark - 获取指定日期的月份
+- (NSInteger)br_month {
+    return [NSDate br_componentsFromDate:self].month;
+}
+
+#pragma mark - 获取指定日期的天
+- (NSInteger)br_day {
+    return [NSDate br_componentsFromDate:self].day;
+}
+
+#pragma mark - 获取指定日期的小时
+- (NSInteger)br_hour {
+    return [NSDate br_componentsFromDate:self].hour;
+}
+
+#pragma mark - 获取指定日期的分钟
+- (NSInteger)br_minute {
+    return [NSDate br_componentsFromDate:self].minute;
+}
+
+#pragma mark - 获取指定日期的秒
+- (NSInteger)br_second {
+    return [NSDate br_componentsFromDate:self].second;
+}
+
+#pragma mark - 获取指定日期的星期
+- (NSInteger)br_weekday {
+    return [NSDate br_componentsFromDate:self].weekday;
+}
+
+#pragma mark - 获取指定日期的月周
+- (NSInteger)br_monthWeek {
+    return [NSDate br_componentsFromDate:self].weekOfMonth;
+}
+
+#pragma mark - 获取指定日期的年周
+- (NSInteger)br_yearWeek {
+    return [NSDate br_componentsFromDate:self].weekOfYear;
+}
+
+#pragma mark - 获取指定日期的季度
+- (NSInteger)br_quarter {
+//    [NSDate br_componentsFromDate:self].quarter; // 取到的季度值总是0?
+    NSInteger quarter = 1;
+    NSInteger month = self.br_month;
+    if (month > 3) quarter = 2;
+    if (month > 6) quarter = 3;
+    if (month > 9) quarter = 4;
+    
+    return quarter;
+}
+
+#pragma mark - 获取指定日期的星期
+- (NSString *)br_weekdayString {
+    switch (self.br_weekday - 1) {
+        case 0:
+        {
+            return @"周日";
+        }
+            break;
+        case 1:
+        {
+            return @"周一";
+        }
+            break;
+        case 2:
+        {
+            return @"周二";
+        }
+            break;
+        case 3:
+        {
+            return @"周三";
+        }
+            break;
+        case 4:
+        {
+            return @"周四";
+        }
+            break;
+        case 5:
+        {
+            return @"周五";
+        }
+            break;
+        case 6:
+        {
+            return @"周六";
+        }
+            break;
+            
+        default:
+            break;
+    }
+    
+    return @"";
+}
+
+/// ---------------- 创建 date ----------------
+#pragma mark - 通过 NSDateComponents对象 来创建 NSDate对象(可以设置时区)
++ (nullable NSDate *)br_setDateFromComponents:(NSDateComponents *)components timeZone:(NSTimeZone *)timeZone {
+    // 创建日历对象,指定日历的算法(公历/阳历)
+    NSCalendar *calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
+    if (timeZone) {
+        // NSCalendar 设置时区
+        calendar.timeZone = timeZone;
+    }
+    return [calendar dateFromComponents:components];
+}
+
+#pragma mark - 创建date(通过 NSCalendar 类来创建日期)
++ (NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second {
+    return [self br_setYear:year month:month day:day hour:hour minute:minute second:second weekOfMonth:0 weekOfYear:0 quarter:0];
+}
+
++ (NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day
+                  hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second
+           weekOfMonth:(NSInteger)weekOfMonth weekOfYear:(NSInteger)weekOfYear quarter:(NSInteger)quarter {
+    NSDateComponents *components = [self br_componentsFromDate:[NSDate date]];
+    if (year > 0) {
+        // 初始化日期组件
+        components = [[NSDateComponents alloc]init];
+        components.year = year;
+    }
+    if (month > 0) {
+        components.month = month;
+    }
+    if (day > 0) {
+        components.day = day;
+    }
+    if (hour >= 0) {
+        components.hour = hour;
+    }
+    if (minute >= 0) {
+        components.minute = minute;
+    }
+    if (second >= 0) {
+        components.second = second;
+    }
+    if (weekOfMonth > 0) {
+        components.weekOfMonth = weekOfMonth;
+    }
+    if (weekOfYear > 0) {
+        components.weekOfYear = weekOfYear;
+    }
+    if (quarter > 0) {
+        components.quarter = quarter;
+    }
+    
+    return [self br_dateFromComponents:components];
+}
+
++ (NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute {
+    return [self br_setYear:year month:month day:day hour:hour minute:minute second:0];
+}
+
++ (NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour {
+    return [self br_setYear:year month:month day:day hour:hour minute:0 second:0];
+}
+
++ (NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day {
+    return [self br_setYear:year month:month day:day hour:0 minute:0 second:0];
+}
+
++ (NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month {
+    return [self br_setYear:year month:month day:0 hour:0 minute:0 second:0];
+}
+
++ (NSDate *)br_setYear:(NSInteger)year {
+    return [self br_setYear:year month:0 day:0 hour:0 minute:0 second:0];
+}
+
++ (NSDate *)br_setMonth:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute {
+    return [self br_setYear:0 month:month day:day hour:hour minute:minute second:0];
+}
+
++ (NSDate *)br_setMonth:(NSInteger)month day:(NSInteger)day {
+    return [self br_setYear:0 month:month day:day hour:0 minute:0 second:0];
+}
+
++ (NSDate *)br_setHour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second {
+    return [self br_setYear:0 month:0 day:0 hour:hour minute:minute second:second];
+}
+
++ (NSDate *)br_setHour:(NSInteger)hour minute:(NSInteger)minute {
+    return [self br_setYear:0 month:0 day:0 hour:hour minute:minute second:0];
+}
+
++ (NSDate *)br_setMinute:(NSInteger)minute second:(NSInteger)second {
+    return [self br_setYear:0 month:0 day:0 hour:0 minute:minute second:second];
+}
+
++ (NSDate *)br_setYear:(NSInteger)year month:(NSInteger)month weekOfMonth:(NSInteger)weekOfMonth {
+    return [self br_setYear:year month:month day:0 hour:0 minute:0 second:0 weekOfMonth:weekOfMonth weekOfYear:0 quarter:0];
+}
+
++ (NSDate *)br_setYear:(NSInteger)year weekOfYear:(NSInteger)weekOfYear {
+    return [self br_setYear:year month:0 day:0 hour:0 minute:0 second:0 weekOfMonth:0 weekOfYear:weekOfYear quarter:0];
+}
+
++ (NSDate *)br_setYear:(NSInteger)year quarter:(NSInteger)quarter {
+    return [self br_setYear:year month:0 day:0 hour:0 minute:0 second:0 weekOfMonth:0 weekOfYear:0 quarter:quarter];
+}
+
+#pragma mark - 获取某个月的天数(通过年月求每月天数)
++ (NSUInteger)br_getDaysInYear:(NSInteger)year month:(NSInteger)month {
+    BOOL isLeapYear = year % 4 == 0 ? (year % 100 == 0 ? (year % 400 == 0 ? YES : NO) : YES) : NO;
+    switch (month) {
+        case 1:
+        case 3:
+        case 5:
+        case 7:
+        case 8:
+        case 10:
+        case 12:
+        {
+            return 31;
+        }
+        case 4:
+        case 6:
+        case 9:
+        case 11:
+        {
+            return 30;
+        }
+        case 2:
+        {
+            if (isLeapYear) {
+                return 29;
+            } else {
+                return 28;
+            }
+        }
+        default:
+            break;
+    }
+    
+    return 0;
+}
+
+#pragma mark - 获取某个月的周数(通过年月求该月周数)
++ (NSUInteger)br_getWeeksOfMonthInYear:(NSInteger)year month:(NSInteger)month {
+    NSUInteger lastDayOfMonth = [self br_getDaysInYear:year month:month];
+    NSDate *endDate = [self br_setYear:year month:month day:lastDayOfMonth];
+    return endDate.br_monthWeek;
+}
+
+#pragma mark - 获取某一年的周数(通过年求该年周数)
++ (NSUInteger)br_getWeeksOfYearInYear:(NSInteger)year {
+    NSDate *endDate = [self br_setYear:year month:12 day:31];
+    NSInteger weeks = endDate.br_yearWeek;
+    if (weeks == 1) weeks = 52;
+    return weeks;
+}
+
+#pragma mark - 获取某一年的季度数(通过年求该年季度数)
++ (NSUInteger)br_getQuartersInYear:(NSInteger)year {
+    NSDate *endDate = [self br_setYear:year month:12 day:31];
+    return endDate.br_quarter;
+}
+
+#pragma mark - 获取 日期加上/减去某天数后的新日期
+- (NSDate *)br_getNewDateToDays:(NSTimeInterval)days {
+    // days 为正数时,表示几天之后的日期;负数表示几天之前的日期
+    return [self dateByAddingTimeInterval:60 * 60 * 24 * days];
+}
+
+#pragma mark - 获取 日期加上/减去某个月数后的新日期
+- (nullable NSDate *)br_getNewDateToMonths:(NSTimeInterval)months {
+    // months 为正数时,表示几个月之后的日期;负数表示几个月之前的日期
+    NSDateComponents *components = [[NSDateComponents alloc] init];
+    [components setMonth:months];
+    NSCalendar *calender = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
+    return [calender dateByAddingComponents:components toDate:self options:0];
+}
+
+#pragma mark - NSDate 转 NSString
++ (NSString *)br_stringFromDate:(NSDate *)date dateFormat:(NSString *)dateFormat {
+    return [self br_stringFromDate:date dateFormat:dateFormat timeZone:nil language:nil];
+}
+#pragma mark - NSDate 转 NSString
++ (NSString *)br_stringFromDate:(NSDate *)date
+                     dateFormat:(NSString *)dateFormat
+                       timeZone:(NSTimeZone *)timeZone
+                       language:(NSString *)language {
+    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
+    // 设置日期格式
+    dateFormatter.dateFormat = dateFormat;
+    // NSDateFormatter 设置时区 ,不设置默认为系统时区
+    if (timeZone) {
+        dateFormatter.timeZone = timeZone;
+    }
+    if (!language) {
+        language = [NSLocale preferredLanguages].firstObject;
+    }
+    dateFormatter.locale = [[NSLocale alloc]initWithLocaleIdentifier:language];
+    NSString *dateString = [dateFormatter stringFromDate:date];
+
+    return dateString;
+}
+
+#pragma mark - NSString 转 NSDate
++ (NSDate *)br_dateFromString:(NSString *)dateString dateFormat:(NSString *)dateFormat {
+    return [self br_dateFromString:dateString dateFormat:dateFormat timeZone:nil language:nil];
+}
+#pragma mark - NSString 转 NSDate
++ (NSDate *)br_dateFromString:(NSString *)dateString
+                   dateFormat:(NSString *)dateFormat
+                     timeZone:(NSTimeZone *)timeZone
+                     language:(NSString *)language {
+    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
+    // 设置日期格式
+    dateFormatter.dateFormat = dateFormat;
+    // 设置时区
+    if (!timeZone) {
+        timeZone = [self currentTimeZone];
+    }
+    if (!language) {
+        language = [NSLocale preferredLanguages].firstObject;
+    }
+    dateFormatter.timeZone = timeZone;
+    dateFormatter.locale = [[NSLocale alloc]initWithLocaleIdentifier:language];
+    // 如果当前时间不存在,就获取距离最近的整点时间
+    dateFormatter.lenient = YES;
+    
+    return [dateFormatter dateFromString:dateString];
+}
+
+#pragma mark - 获取当前时区(不使用夏时制)
++ (NSTimeZone *)currentTimeZone {
+    // 当前时区
+    NSTimeZone *localTimeZone = [NSTimeZone localTimeZone];
+    // 当前时区相对于GMT(零时区)的偏移秒数
+    NSInteger interval = [localTimeZone secondsFromGMTForDate:[NSDate date]];
+    // 当前时区(不使用夏时制):由偏移量获得对应的NSTimeZone对象
+    // 注意:一些夏令时时间 NSString 转 NSDate 时,默认会导致 NSDateFormatter 格式化失败,返回 null
+    return [NSTimeZone timeZoneForSecondsFromGMT:interval];
+}
+
+#pragma mark - NSDate 转 NSString(已弃用)
++ (NSString *)br_getDateString:(NSDate *)date format:(NSString *)format {
+    return [self br_stringFromDate:date dateFormat:format];
+}
+
+#pragma mark - NSString 转 NSDate(已弃用)
++ (NSDate *)br_getDate:(NSString *)dateString format:(NSString *)format {
+    return [self br_dateFromString:dateString dateFormat:format];
+}
+
+@end

+ 37 - 0
Pods/BRPickerView/BRPickerView/StringPickerView/BRResultModel.h

@@ -0,0 +1,37 @@
+//
+//  BRResultModel.h
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2019/10/2.
+//  Copyright © 2019 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface BRResultModel : NSObject
+
+/** key */
+@property (nullable, nonatomic, copy) NSString *key;
+/** value */
+@property (nullable, nonatomic, copy) NSString *value;
+/** 父级key(提示:联动时第一级数据,parentKey设置为:@"-1") */
+@property (nullable, nonatomic, copy) NSString *parentKey;
+/** 父级value */
+@property (nullable, nonatomic, copy) NSString *parentValue;
+/** 级别 */
+@property (nullable, nonatomic, copy) NSString *level;
+/** 子级list */
+@property (nullable, nonatomic, copy) NSArray<BRResultModel *> *children;
+/** 记录选择的索引位置 */
+@property (nonatomic, assign) NSInteger index;
+
+/// 其它扩展字段
+@property (nullable, nonatomic, strong) id extras;
+@property (nullable, nonatomic, copy) NSString *remark;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 41 - 0
Pods/BRPickerView/BRPickerView/StringPickerView/BRResultModel.m

@@ -0,0 +1,41 @@
+//
+//  BRResultModel.m
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2019/10/2.
+//  Copyright © 2019 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import "BRResultModel.h"
+
+@implementation BRResultModel
+
+/// 判断两个对象是否相等
+/// @param object 目标对象
+- (BOOL)isEqual:(id)object {
+    // 1.对象的地址相同
+    if (self == object) {
+        return YES;
+    }
+    
+    if (![object isKindOfClass:[BRResultModel class]]) {
+        return NO;
+    }
+    
+    BRResultModel *model = (BRResultModel *)object;
+    if (!model) {
+        return NO;
+    }
+    // 2.对象的类型相同,且对象的各个属性相等
+    BOOL isSameKey = (!self.key && !model.key) || [self.key isEqualToString:model.key];
+    BOOL isSameValue = (!self.value && !model.value) || [self.value isEqualToString:model.value];
+    
+    return isSameKey && isSameValue;
+}
+
+- (NSUInteger)hash {
+    return [self.key hash] ^ [self.value hash];
+}
+
+@end

+ 191 - 0
Pods/BRPickerView/BRPickerView/StringPickerView/BRStringPickerView.h

@@ -0,0 +1,191 @@
+//
+//  BRStringPickerView.h
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2017/8/11.
+//  Copyright © 2017 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import "BRBaseView.h"
+#import "BRResultModel.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// 字符串选择器类型
+typedef NS_ENUM(NSInteger, BRStringPickerMode) {
+    /** 单列选择器 */
+    BRStringPickerComponentSingle,
+    /** 多列选择器 */
+    BRStringPickerComponentMulti,
+    /** 多级联动选择器 */
+    BRStringPickerComponentLinkage
+};
+
+typedef void(^BRStringResultModelBlock)(BRResultModel * _Nullable resultModel);
+
+typedef void(^BRStringResultModelArrayBlock)(NSArray <BRResultModel *> * _Nullable resultModelArr);
+
+@interface BRStringPickerView : BRBaseView
+
+/**
+ //////////////////////////////////////////////////////////////////////////
+ ///
+ ///   【用法一】
+ ///    特点:灵活,扩展性强(推荐使用!)
+ ///
+ ////////////////////////////////////////////////////////////////////////*/
+
+/** 字符串选择器显示类型 */
+@property (nonatomic, assign) BRStringPickerMode pickerMode;
+
+/**
+ *  1.设置数据源
+ *    单列:@[@"男", @"女", @"其他"],或直接传一维模型数组(NSArray <BRResultModel *>*)
+ *    多列:@[@[@"语文", @"数学", @"英语"], @[@"优秀", @"良好"]],或直接传多维模型数组
+ *    联动:直接传一维模型数组(NSArray <BRResultModel *>*),要注意数据源联动格式,可参考Demo
+ */
+@property (nullable, nonatomic, copy) NSArray *dataSourceArr;
+/**
+ *  2.设置数据源
+ *    直接传plist文件名:NSString类型(如:@"test.plist"),要带后缀名
+ *    场景:可以将数据源数据(数组类型)放到plist文件中,直接传plist文件名更加简单
+ */
+@property (nullable, nonatomic, copy) NSString *plistName;
+
+/**
+ *  设置默认选中的位置【单列】
+ *  推荐使用 selectIndex,更加严谨,可以避免使用 selectValue 时,有名称相同的情况
+ */
+@property (nonatomic, assign) NSInteger selectIndex;
+@property (nullable, nonatomic, copy) NSString *selectValue;
+
+/**
+ *  设置默认选中的位置【多列】
+ *  推荐使用 selectIndexs,更加严谨,可以避免使用 selectValues 时,有名称相同的情况
+ */
+@property (nullable, nonatomic, copy) NSArray <NSNumber *> *selectIndexs;
+@property (nullable, nonatomic, copy) NSArray <NSString *> *selectValues;
+
+/** 选择结果的回调【单列】 */
+@property (nullable, nonatomic, copy) BRStringResultModelBlock resultModelBlock;
+/** 选择结果的回调【多列】 */
+@property (nullable, nonatomic, copy) BRStringResultModelArrayBlock resultModelArrayBlock;
+
+/** 滚动选择时触发的回调【单列】 */
+@property (nullable, nonatomic, copy) BRStringResultModelBlock changeModelBlock;
+/** 滚动选择时触发的回调【多列】 */
+@property (nullable, nonatomic, copy) BRStringResultModelArrayBlock changeModelArrayBlock;
+
+/**
+ *  最大层级数(列数) for `BRStringPickerComponentLinkage`, ignored otherwise.
+ *  使用场景:默认可选,当数据源中有 key 等于 parentKey 情况时,必须要设置
+ */
+@property (nonatomic, assign) NSInteger numberOfComponents;
+
+/// 初始化字符串选择器
+/// @param pickerMode 字符串选择器显示类型
+- (instancetype)initWithPickerMode:(BRStringPickerMode)pickerMode;
+
+/// 弹出选择器视图
+- (void)show;
+
+/// 关闭选择器视图
+- (void)dismiss;
+
+
+
+
+//================================================= 华丽的分割线 =================================================
+
+
+
+
+/**
+ //////////////////////////////////////////////////////////////////////////
+ ///
+ ///   【用法二】:快捷使用,直接选择下面其中的一个方法进行使用
+ ///    特点:快捷,方便
+ ///
+ ////////////////////////////////////////////////////////////////////////*/
+
+/**
+ *  1.显示【单列】选择器
+ *
+ *  @param title               选择器标题
+ *  @param dataSourceArr       数据源,格式:@[@"男", @"女", @"其他"],或直接传一维模型数组(NSArray <BRResultModel *>*)
+ *  @param selectIndex         默认选中的位置
+ *  @param resultBlock         选择后的回调
+ *
+ */
++ (void)showPickerWithTitle:(nullable NSString *)title
+              dataSourceArr:(nullable NSArray *)dataSourceArr
+                selectIndex:(NSInteger)selectIndex
+                resultBlock:(nullable BRStringResultModelBlock)resultBlock;
+
+/**
+ *  2.显示【单列】选择器
+ *
+ *  @param title               选择器标题
+ *  @param dataSourceArr       数据源(如:@[@"男", @"女", @"其他"],或直接传模型数组)
+ *  @param selectIndex         默认选中的位置
+ *  @param isAutoSelect        是否自动选择,即滚动选择器后就执行结果回调,默认为 NO
+ *  @param resultBlock         选择后的回调
+ *
+ */
++ (void)showPickerWithTitle:(nullable NSString *)title
+              dataSourceArr:(nullable NSArray *)dataSourceArr
+                selectIndex:(NSInteger)selectIndex
+               isAutoSelect:(BOOL)isAutoSelect
+                resultBlock:(nullable BRStringResultModelBlock)resultBlock;
+
+/**
+ *  3.显示【多列】选择器
+ *
+ *  @param title               选择器标题
+ *  @param dataSourceArr       数据源,格式:@[@[@"语文", @"数学", @"英语"], @[@"优秀", @"良好"]],或直接传多维模型数组
+ *  @param selectIndexs        默认选中的位置(传索引数组,如:@[@2, @1])
+ *  @param resultBlock         选择后的回调
+ *
+ */
++ (void)showMultiPickerWithTitle:(nullable NSString *)title
+                   dataSourceArr:(nullable NSArray *)dataSourceArr
+                    selectIndexs:(nullable NSArray <NSNumber *> *)selectIndexs
+                     resultBlock:(nullable BRStringResultModelArrayBlock)resultBlock;
+
+/**
+ *  4.显示【多列】选择器
+ *
+ *  @param title               选择器标题
+ *  @param dataSourceArr       数据源,格式:@[@[@"语文", @"数学", @"英语"], @[@"优秀", @"良好"]],或直接传多维模型数组
+ *  @param selectIndexs        默认选中的位置(传索引数组,如:@[@2, @1])
+ *  @param isAutoSelect        是否自动选择,即滚动选择器后就执行结果回调,默认为 NO
+ *  @param resultBlock         选择后的回调
+ *
+ */
++ (void)showMultiPickerWithTitle:(nullable NSString *)title
+                   dataSourceArr:(nullable NSArray *)dataSourceArr
+                    selectIndexs:(nullable NSArray <NSNumber *> *)selectIndexs
+                    isAutoSelect:(BOOL)isAutoSelect
+                     resultBlock:(nullable BRStringResultModelArrayBlock)resultBlock;
+
+/**
+ *  5.显示【联动】选择器
+ *
+ *  @param title               选择器标题
+ *  @param dataSourceArr       数据源,格式:直接传一维模型数组(NSArray <BRResultModel *>*)
+ *  @param selectIndexs        默认选中的位置(传索引数组,如:@[@2, @1])
+ *  @param isAutoSelect        是否自动选择,即滚动选择器后就执行结果回调,默认为 NO
+ *  @param resultBlock         选择后的回调
+ *
+ */
++ (void)showLinkagePickerWithTitle:(nullable NSString *)title
+                     dataSourceArr:(nullable NSArray *)dataSourceArr
+                      selectIndexs:(nullable NSArray <NSNumber *> *)selectIndexs
+                      isAutoSelect:(BOOL)isAutoSelect
+                       resultBlock:(nullable BRStringResultModelArrayBlock)resultBlock;
+
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 571 - 0
Pods/BRPickerView/BRPickerView/StringPickerView/BRStringPickerView.m

@@ -0,0 +1,571 @@
+//
+//  BRStringPickerView.m
+//  BRPickerViewDemo
+//
+//  Created by renbo on 2017/8/11.
+//  Copyright © 2017 irenb. All rights reserved.
+//
+//  最新代码下载地址:https://github.com/91renb/BRPickerView
+
+#import "BRStringPickerView.h"
+
+@interface BRStringPickerView ()<UIPickerViewDelegate, UIPickerViewDataSource>
+{
+    BOOL _dataSourceException; // 数据源格式是否有误
+}
+/** 选择器 */
+@property (nonatomic, strong) UIPickerView *pickerView;
+/** 单列选择的值 */
+@property (nonatomic, copy) NSString *mSelectValue;
+/** 多列选择的值 */
+@property (nonatomic, copy) NSArray <NSString *>* mSelectValues;
+
+/** 数据源 */
+@property (nullable, nonatomic, copy) NSArray *mDataSourceArr;
+
+@end
+
+@implementation BRStringPickerView
+
+#pragma mark - 1.显示【单列】选择器
++ (void)showPickerWithTitle:(NSString *)title
+              dataSourceArr:(NSArray *)dataSourceArr
+                selectIndex:(NSInteger)selectIndex
+                resultBlock:(BRStringResultModelBlock)resultBlock {
+    [self showPickerWithTitle:title dataSourceArr:dataSourceArr selectIndex:selectIndex isAutoSelect:NO resultBlock:resultBlock];
+}
+
+#pragma mark - 2.显示【单列】选择器
++ (void)showPickerWithTitle:(NSString *)title
+              dataSourceArr:(NSArray *)dataSourceArr
+                selectIndex:(NSInteger)selectIndex
+               isAutoSelect:(BOOL)isAutoSelect
+                resultBlock:(BRStringResultModelBlock)resultBlock {
+    // 创建选择器
+    BRStringPickerView *strPickerView = [[BRStringPickerView alloc]init];
+    strPickerView.pickerMode = BRStringPickerComponentSingle;
+    strPickerView.title = title;
+    strPickerView.dataSourceArr = dataSourceArr;
+    strPickerView.selectIndex = selectIndex;
+    strPickerView.isAutoSelect = isAutoSelect;
+    strPickerView.resultModelBlock = resultBlock;
+    
+    // 显示
+    [strPickerView show];
+}
+
+#pragma mark - 3.显示【多列】选择器
++ (void)showMultiPickerWithTitle:(NSString *)title
+                   dataSourceArr:(NSArray *)dataSourceArr
+                    selectIndexs:(NSArray <NSNumber *>*)selectIndexs
+                     resultBlock:(BRStringResultModelArrayBlock)resultBlock {
+    [self showMultiPickerWithTitle:title dataSourceArr:dataSourceArr selectIndexs:selectIndexs isAutoSelect:NO resultBlock:resultBlock];
+}
+
+#pragma mark - 4.显示【多列】选择器
++ (void)showMultiPickerWithTitle:(NSString *)title
+                   dataSourceArr:(NSArray *)dataSourceArr
+                    selectIndexs:(NSArray <NSNumber *>*)selectIndexs
+                    isAutoSelect:(BOOL)isAutoSelect
+                     resultBlock:(BRStringResultModelArrayBlock)resultBlock {
+    // 创建选择器
+    BRStringPickerView *strPickerView = [[BRStringPickerView alloc]init];
+    strPickerView.pickerMode = BRStringPickerComponentMulti;
+    strPickerView.title = title;
+    strPickerView.dataSourceArr = dataSourceArr;
+    strPickerView.selectIndexs = selectIndexs;
+    strPickerView.isAutoSelect = isAutoSelect;
+    strPickerView.resultModelArrayBlock = resultBlock;
+    
+    // 显示
+    [strPickerView show];
+}
+
+#pragma mark - 5.显示【联动】选择器
++ (void)showLinkagePickerWithTitle:(nullable NSString *)title
+                     dataSourceArr:(nullable NSArray *)dataSourceArr
+                      selectIndexs:(nullable NSArray <NSNumber *> *)selectIndexs
+                      isAutoSelect:(BOOL)isAutoSelect
+                       resultBlock:(nullable BRStringResultModelArrayBlock)resultBlock {
+    // 创建选择器
+    BRStringPickerView *strPickerView = [[BRStringPickerView alloc]init];
+    strPickerView.pickerMode = BRStringPickerComponentLinkage;
+    strPickerView.title = title;
+    strPickerView.dataSourceArr = dataSourceArr;
+    strPickerView.selectIndexs = selectIndexs;
+    strPickerView.isAutoSelect = isAutoSelect;
+    strPickerView.resultModelArrayBlock = resultBlock;
+    
+    // 显示
+    [strPickerView show];
+}
+
+#pragma mark - 初始化自定义选择器
+- (instancetype)initWithPickerMode:(BRStringPickerMode)pickerMode {
+    if (self = [super init]) {
+        self.pickerMode = pickerMode;
+    }
+    return self;
+}
+
+#pragma mark - 处理选择器数据
+- (void)handlerPickerData {
+    if (self.dataSourceArr.count == 0) {
+        _dataSourceException = YES;
+    }
+    id item = [self.dataSourceArr firstObject];
+    if (self.pickerMode == BRStringPickerComponentSingle) {
+        _dataSourceException = [item isKindOfClass:[NSArray class]];
+    } else if (self.pickerMode == BRStringPickerComponentMulti) {
+        _dataSourceException = [item isKindOfClass:[NSString class]];
+    } else if (self.pickerMode == BRStringPickerComponentLinkage) {
+        _dataSourceException = ![item isKindOfClass:[BRResultModel class]];
+    }
+    if (_dataSourceException) {
+        NSAssert(!_dataSourceException, @"数据源异常!请检查选择器数据源的格式");
+        return;
+    }
+    
+    // 处理选择器当前选择的值
+    if (self.pickerMode == BRStringPickerComponentSingle) {
+        self.mDataSourceArr = self.dataSourceArr;
+        NSInteger selectIndex = 0;
+        if (self.selectIndex > 0 && self.selectIndex < self.mDataSourceArr.count) {
+            selectIndex = self.selectIndex;
+        } else {
+            if (self.mSelectValue) {
+                id item = [self.mDataSourceArr firstObject];
+                if ([item isKindOfClass:[BRResultModel class]]) {
+                    for (NSInteger i = 0; i < self.mDataSourceArr.count; i++) {
+                        BRResultModel *model = self.mDataSourceArr[i];
+                        if ([model.value isEqualToString:self.mSelectValue]) {
+                            selectIndex = i;
+                            break;
+                        }
+                    }
+                } else {
+                    if ([self.mDataSourceArr containsObject:self.mSelectValue]) {
+                        selectIndex = [self.mDataSourceArr indexOfObject:self.mSelectValue];
+                    }
+                }
+            }
+        }
+        self.selectIndex = selectIndex;
+        
+    } else if (self.pickerMode == BRStringPickerComponentMulti) {
+        self.mDataSourceArr = self.dataSourceArr;
+        NSMutableArray *selectIndexs = [[NSMutableArray alloc]init];
+        for (NSInteger i = 0; i < self.mDataSourceArr.count; i++) {
+            NSArray *itemArr = self.mDataSourceArr[i];
+            NSInteger row = 0;
+            if (self.selectIndexs.count > 0) {
+                if (i < self.selectIndexs.count) {
+                    NSInteger index = [self.selectIndexs[i] integerValue];
+                    row = ((index > 0 && index < itemArr.count) ? index : 0);
+                }
+            } else {
+                if (self.mSelectValues.count > 0 && i < self.mSelectValues.count) {
+                    NSString *value = self.mSelectValues[i];
+                    id item = [itemArr firstObject];
+                    if ([item isKindOfClass:[BRResultModel class]]) {
+                        for (NSInteger j = 0; j < itemArr.count; j++) {
+                            BRResultModel *model = itemArr[j];
+                            if ([model.value isEqualToString:value]) {
+                                row = j;
+                                break;
+                            }
+                        }
+                    } else {
+                        if ([itemArr containsObject:value]) {
+                            row = [itemArr indexOfObject:value];
+                        }
+                    }
+                }
+            }
+            [selectIndexs addObject:@(row)];
+        }
+        self.selectIndexs = [selectIndexs copy];
+        
+    } else if (self.pickerMode == BRStringPickerComponentLinkage) {
+        
+        NSMutableArray *selectIndexs = [[NSMutableArray alloc]init];
+        NSMutableArray *mDataSourceArr = [[NSMutableArray alloc]init];
+        
+        BRResultModel *selectModel = nil;
+        BOOL hasNext = YES;
+        NSInteger i = 0;
+
+        NSMutableArray *dataArr = [self.dataSourceArr mutableCopy];
+        
+        do {
+            NSArray *nextArr = [self getNextDataArr:dataArr selectModel:selectModel];
+            // 设置 numberOfComponents,防止 key 等于 parentKey 时进入死循环
+            if (nextArr.count == 0 || i > self.numberOfComponents - 1) {
+                hasNext = NO;
+                break;
+            }
+            
+            NSInteger selectIndex = 0;
+            if (self.selectIndexs.count > i && [self.selectIndexs[i] integerValue] < nextArr.count) {
+                selectIndex = [self.selectIndexs[i] integerValue];
+            }
+            selectModel = nextArr[selectIndex];
+            
+            [selectIndexs addObject:@(selectIndex)];
+            [mDataSourceArr addObject:nextArr];
+
+            i++;
+            
+        } while (hasNext);
+        
+        self.selectIndexs = [selectIndexs copy];
+        self.mDataSourceArr = [mDataSourceArr copy];
+    }
+}
+
+- (NSArray <BRResultModel *>*)getNextDataArr:(NSArray *)dataArr selectModel:(BRResultModel *)selectModel {
+    NSMutableArray *tempArr = [[NSMutableArray alloc]init];
+    // parentKey = @"-1",表示是第一列数据
+    NSString *key = selectModel ? selectModel.key : @"-1";
+    for (BRResultModel *model in dataArr) {
+        if ([model.parentKey isEqualToString:key]) {
+            [tempArr addObject:model];
+        }
+    }
+    return [tempArr copy];
+}
+
+#pragma mark - 选择器
+- (UIPickerView *)pickerView {
+    if (!_pickerView) {
+        CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
+        _pickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, self.pickerStyle.titleBarHeight + pickerHeaderViewHeight, self.keyView.bounds.size.width, self.pickerStyle.pickerHeight)];
+        _pickerView.backgroundColor = self.pickerStyle.pickerColor;
+        _pickerView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
+        _pickerView.dataSource = self;
+        _pickerView.delegate = self;
+    }
+    return _pickerView;
+}
+
+#pragma mark - UIPickerViewDataSource
+// 1.设置 pickerView 的列数
+- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
+    switch (self.pickerMode) {
+        case BRStringPickerComponentSingle:
+            return 1;
+            break;
+        case BRStringPickerComponentMulti:
+        case BRStringPickerComponentLinkage:
+            return self.mDataSourceArr.count;
+            break;
+            
+        default:
+            break;
+    }
+}
+
+// 2.设置 pickerView 每列的行数
+- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
+    switch (self.pickerMode) {
+        case BRStringPickerComponentSingle:
+            return self.mDataSourceArr.count;
+            break;
+        case BRStringPickerComponentMulti:
+        case BRStringPickerComponentLinkage:
+        {
+            NSArray *itemArr = self.mDataSourceArr[component];
+            return itemArr.count;
+        }
+            break;
+            
+        default:
+            break;
+    }
+}
+
+#pragma mark - UIPickerViewDelegate
+// 3.设置 pickerView 的显示内容
+- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(nullable UIView *)view {
+    // 1.自定义 row 的内容视图
+    UILabel *label = (UILabel *)view;
+    if (!label) {
+        label = [[UILabel alloc]init];
+        label.backgroundColor = [UIColor clearColor];
+        label.textAlignment = NSTextAlignmentCenter;
+        label.font = self.pickerStyle.pickerTextFont;
+        label.textColor = self.pickerStyle.pickerTextColor;
+        // 字体自适应属性
+        label.adjustsFontSizeToFitWidth = YES;
+        // 自适应最小字体缩放比例
+        label.minimumScaleFactor = 0.5f;
+    }
+    if (self.pickerMode == BRStringPickerComponentSingle) {
+        id item = self.mDataSourceArr[row];
+        if ([item isKindOfClass:[BRResultModel class]]) {
+            BRResultModel *model = (BRResultModel *)item;
+            label.text = model.value;
+        } else {
+            label.text = item;
+        }
+    } else if (self.pickerMode == BRStringPickerComponentMulti || self.pickerMode == BRStringPickerComponentLinkage) {
+        NSArray *itemArr = self.mDataSourceArr[component];
+        id item = [itemArr objectAtIndex:row];
+        if ([item isKindOfClass:[BRResultModel class]]) {
+            BRResultModel *model = (BRResultModel *)item;
+            label.text = model.value;
+        } else {
+            label.text = item;
+        }
+    }
+    
+    // 2.设置选择器中间选中行的样式
+    [self.pickerStyle setupPickerSelectRowStyle:pickerView titleForRow:row forComponent:component];
+    
+    return label;
+}
+
+// 4.滚动 pickerView 执行的回调方法
+- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
+    switch (self.pickerMode) {
+        case BRStringPickerComponentSingle:
+        {
+            self.selectIndex = row;
+            
+            // 滚动选择时执行 changeModelBlock
+            if (self.changeModelBlock) {
+                self.changeModelBlock([self getResultModel]);
+            }
+            
+            // 设置自动选择时,滚动选择时就执行 resultModelBlock
+            if (self.isAutoSelect) {
+                if (self.resultModelBlock) {
+                    self.resultModelBlock([self getResultModel]);
+                }
+            }
+        }
+            break;
+        case BRStringPickerComponentMulti:
+        {
+            if (component < self.selectIndexs.count) {
+                NSMutableArray *mutableArr = [self.selectIndexs mutableCopy];
+                [mutableArr replaceObjectAtIndex:component withObject:@(row)];
+                self.selectIndexs = [mutableArr copy];
+            }
+            
+            // 滚动选择时执行 changeModelArrayBlock
+            if (self.changeModelArrayBlock) {
+                self.changeModelArrayBlock([self getResultModelArr]);
+            }
+            
+            // 设置自动选择时,滚动选择时就执行 resultModelArrayBlock
+            if (self.isAutoSelect) {
+                if (self.resultModelArrayBlock) {
+                    self.resultModelArrayBlock([self getResultModelArr]);
+                }
+            }
+        }
+            break;
+        case BRStringPickerComponentLinkage:
+        {
+            if (component < self.selectIndexs.count) {
+                NSMutableArray *selectIndexs = [[NSMutableArray alloc]init];
+                for (NSInteger i = 0; i < self.selectIndexs.count; i++) {
+                    if (i < component) {
+                        [selectIndexs addObject:self.selectIndexs[i]];
+                    } else if (i == component) {
+                        [selectIndexs addObject:@(row)];
+                    } else {
+                        [selectIndexs addObject:@(0)];
+                    }
+                }
+                self.selectIndexs = [selectIndexs copy];
+            }
+            
+            // 刷新选择器数据
+            [self reloadData];
+            
+            // 滚动选择时执行 changeModelArrayBlock
+            if (self.changeModelArrayBlock) {
+                self.changeModelArrayBlock([self getResultModelArr]);
+            }
+            
+            // 设置自动选择时,滚动选择时就执行 resultModelArrayBlock
+            if (self.isAutoSelect) {
+                if (self.resultModelArrayBlock) {
+                    self.resultModelArrayBlock([self getResultModelArr]);
+                }
+            }
+        }
+            break;
+            
+        default:
+            break;
+    }
+}
+
+#pragma mark - 获取【单列】选择器选择的值
+- (BRResultModel *)getResultModel {
+    id item = self.selectIndex < self.mDataSourceArr.count ? self.mDataSourceArr[self.selectIndex] : nil;
+    if ([item isKindOfClass:[BRResultModel class]]) {
+        BRResultModel *model = (BRResultModel *)item;
+        model.index = self.selectIndex;
+        return model;
+    } else {
+        BRResultModel *model = [[BRResultModel alloc]init];
+        model.index = self.selectIndex;
+        model.value = item;
+        return model;
+    }
+}
+
+#pragma mark - 获取【多列】选择器选择的值
+- (NSArray *)getResultModelArr {
+    NSMutableArray *resultModelArr = [[NSMutableArray alloc]init];
+    for (NSInteger i = 0; i < self.mDataSourceArr.count; i++) {
+        NSInteger index = [self.selectIndexs[i] integerValue];
+        NSArray *dataArr = self.mDataSourceArr[i];
+        
+        id item = index < dataArr.count ? dataArr[index] : nil;
+        if ([item isKindOfClass:[BRResultModel class]]) {
+            BRResultModel *model = (BRResultModel *)item;
+            model.index = index;
+            [resultModelArr addObject:model];
+        } else {
+            BRResultModel *model = [[BRResultModel alloc]init];
+            model.index = index;
+            model.value = item;
+            [resultModelArr addObject:model];
+        }
+    }
+    return [resultModelArr copy];
+}
+
+// 设置行高
+- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component {
+    return self.pickerStyle.rowHeight;
+}
+
+#pragma mark - 重写父类方法
+- (void)reloadData {
+    // 1.处理数据源
+    [self handlerPickerData];
+    // 2.刷新选择器
+    [self.pickerView reloadAllComponents];
+    // 3.滚动到选择的值
+    if (self.pickerMode == BRStringPickerComponentSingle) {
+        [self.pickerView selectRow:self.selectIndex inComponent:0 animated:NO];
+    } else if (self.pickerMode == BRStringPickerComponentMulti || self.pickerMode == BRStringPickerComponentLinkage) {
+        for (NSInteger i = 0; i < self.selectIndexs.count; i++) {
+            NSNumber *index = [self.selectIndexs objectAtIndex:i];
+            [self.pickerView selectRow:[index integerValue] inComponent:i animated:NO];
+        }
+    }
+}
+
+- (void)addPickerToView:(UIView *)view {
+    // 1.添加选择器
+    if (view) {
+        // 立即刷新容器视图 view 的布局(防止 view 使用自动布局时,选择器视图无法正常显示)
+        [view setNeedsLayout];
+        [view layoutIfNeeded];
+        
+        self.frame = view.bounds;
+        CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
+        CGFloat pickerFooterViewHeight = self.pickerFooterView ? self.pickerFooterView.bounds.size.height : 0;
+        self.pickerView.frame = CGRectMake(0, pickerHeaderViewHeight, view.bounds.size.width, view.bounds.size.height - pickerHeaderViewHeight - pickerFooterViewHeight);
+        [self addSubview:self.pickerView];
+    } else {
+        // iOS16:重新设置 pickerView 高度(解决懒加载设置frame不生效问题)
+        CGFloat pickerHeaderViewHeight = self.pickerHeaderView ? self.pickerHeaderView.bounds.size.height : 0;
+        self.pickerView.frame = CGRectMake(0, self.pickerStyle.titleBarHeight + pickerHeaderViewHeight, self.keyView.bounds.size.width, self.pickerStyle.pickerHeight);
+        
+        [self.alertView addSubview:self.pickerView];
+    }
+    
+    // ③添加中间选择行的两条分割线
+    if (self.pickerStyle.clearPickerNewStyle) {
+        [self.pickerStyle addSeparatorLineView:self.pickerView];
+    }
+    
+    // 2.绑定数据
+    [self reloadData];
+    
+    __weak typeof(self) weakSelf = self;
+    self.doneBlock = ^{
+        // 点击确定按钮后,执行block回调
+        if (weakSelf.pickerMode == BRStringPickerComponentSingle) {
+            if (weakSelf.resultModelBlock) {
+                weakSelf.resultModelBlock([weakSelf getResultModel]);
+            }
+        } else if (weakSelf.pickerMode == BRStringPickerComponentMulti || weakSelf.pickerMode == BRStringPickerComponentLinkage) {
+            if (weakSelf.resultModelArrayBlock) {
+                weakSelf.resultModelArrayBlock([weakSelf getResultModelArr]);
+            }
+        }
+    };
+    
+    [super addPickerToView:view];
+}
+
+#pragma mark - 重写父类方法
+- (void)addSubViewToPicker:(UIView *)customView {
+    [self.pickerView addSubview:customView];
+}
+
+#pragma mark - 弹出选择器视图
+- (void)show {
+    [self addPickerToView:nil];
+}
+
+#pragma mark - 关闭选择器视图
+- (void)dismiss {
+    [self removePickerFromView:nil];
+}
+
+#pragma mark - setter 方法
+- (void)setPlistName:(NSString *)plistName {
+    NSString *path = [[NSBundle mainBundle] pathForResource:plistName ofType:nil];
+    if (path && path.length > 0) {
+        self.dataSourceArr = [[NSArray alloc] initWithContentsOfFile:path];
+    }
+}
+
+- (void)setSelectValue:(NSString *)selectValue {
+    self.mSelectValue = selectValue;
+}
+
+- (void)setSelectValues:(NSArray<NSString *> *)selectValues {
+    self.mSelectValues = selectValues;
+}
+
+#pragma mark - getter 方法
+- (NSArray *)mDataSourceArr {
+    if (!_mDataSourceArr) {
+        _mDataSourceArr = [NSArray array];
+    }
+    return _mDataSourceArr;
+}
+
+- (NSArray<NSNumber *> *)selectIndexs {
+    if (!_selectIndexs) {
+        _selectIndexs = [NSArray array];
+    }
+    return _selectIndexs;
+}
+
+- (NSArray<NSString *> *)mSelectValues {
+    if (!_mSelectValues) {
+        _mSelectValues = [NSArray array];
+    }
+    return _mSelectValues;
+}
+
+- (NSInteger)numberOfComponents {
+    if (_numberOfComponents <= 0) {
+        _numberOfComponents = 3;
+    }
+    return _numberOfComponents;
+}
+
+@end

+ 21 - 0
Pods/BRPickerView/LICENSE

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

+ 530 - 0
Pods/BRPickerView/README.md

@@ -0,0 +1,530 @@
+# BRPickerView
+
+BRPickerView 封装的是iOS中常用的选择器组件,主要包括:日期选择器(支持年月日、年月等15种日期样式选择,支持设置星期、至今等)、地址选择器(支持省市区、省市、省三种地区选择)、自定义字符串选择器(支持单列、多列、二级联动、三级联动选择)。支持自定义主题样式,适配深色模式,支持将选择器组件添加到指定容器视图。
+
+【说明】
+
+- 当前最新版本为: `2.8.1` 。
+- 如果不能找到最新版本,请先执行一下 `pod repo update` 更新本地仓库,待更新完成后;再执行 `pod search BRPickerView` 进行搜索,就会看到最新版本。
+
+# 效果演示
+
+查看并运行 `BRPickerViewDemo.xcodeproj`
+
+| ![效果图1](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/a.gif?raw=true) | ![效果图2](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/b.gif?raw=true) |
+| :----------------------------------------------------------: | :----------------------------------------------------------: |
+|                     框架Demo运行效果图1                      |                     框架Demo运行效果图2                      |
+
+# 安装
+
+#### 1. CocoaPods
+
+1. 在 Podfile 中添加 `pod 'BRPickerView'`。
+
+2. 执行 `pod install` 或 `pod update` 。
+
+3. 导入头文件 ` #import <BRPickerView.h>`。
+
+
+#### 2. 手动导入
+
+1. 将与 `README.md` 同级目录下的 `BRPickerView` 文件夹拽入项目中
+
+2. 导入头文件 ` #import "BRPickerView.h"`。
+
+
+# 系统要求
+
+- iOS 8.0+
+- ARC
+
+# 使用
+
+#### 1. 时间选择器:`BRDatePickerView`
+
+​	查看 BRDatePickerView.h 头文件,里面提供了两种使用方式,参见源码。
+
+```objective-c
+/// 日期选择器格式
+typedef NS_ENUM(NSInteger, BRDatePickerMode) {
+    // ----- 以下4种是系统样式(兼容国际化日期格式) -----
+    /** 【yyyy-MM-dd】UIDatePickerModeDate(美式日期:MM-dd-yyyy;英式日期:dd-MM-yyyy)*/
+    BRDatePickerModeDate,
+    /** 【yyyy-MM-dd HH:mm】 UIDatePickerModeDateAndTime */
+    BRDatePickerModeDateAndTime,
+    /** 【HH:mm】UIDatePickerModeTime */
+    BRDatePickerModeTime,
+    /** 【HH:mm】UIDatePickerModeCountDownTimer */
+    BRDatePickerModeCountDownTimer,
+    
+    // ----- 以下14种是自定义样式 -----
+    /** 【yyyy-MM-dd HH:mm:ss】年月日时分秒 */
+    BRDatePickerModeYMDHMS,
+    /** 【yyyy-MM-dd HH:mm】年月日时分 */
+    BRDatePickerModeYMDHM,
+    /** 【yyyy-MM-dd HH】年月日时 */
+    BRDatePickerModeYMDH,
+    /** 【MM-dd HH:mm】月日时分 */
+    BRDatePickerModeMDHM,
+    /** 【yyyy-MM-dd】年月日(兼容国际化日期:dd-MM-yyyy)*/
+    BRDatePickerModeYMD,
+    /** 【yyyy-MM】年月(兼容国际化日期:MM-yyyy)*/
+    BRDatePickerModeYM,
+    /** 【yyyy】年 */
+    BRDatePickerModeY,
+    /** 【MM-dd】月日 */
+    BRDatePickerModeMD,
+    /** 【HH:mm:ss】时分秒 */
+    BRDatePickerModeHMS,
+    /** 【HH:mm】时分 */
+    BRDatePickerModeHM,
+    /** 【mm:ss】分秒 */
+    BRDatePickerModeMS,
+    
+    /** 【yyyy-qq】年季度 */
+    BRDatePickerModeYQ,
+    /** 【yyyy-MM-ww】年月周 */
+    BRDatePickerModeYMW,
+    /** 【yyyy-ww】年周 */
+    BRDatePickerModeYW
+};
+```
+
+- 使用示例(参考Demo):
+
+```objective-c
+// 1.创建日期选择器
+BRDatePickerView *datePickerView = [[BRDatePickerView alloc]init];
+// 2.设置属性
+datePickerView.pickerMode = BRDatePickerModeYMD;
+datePickerView.title = @"选择年月日";
+// datePickerView.selectValue = @"2019-10-30";
+datePickerView.selectDate = [NSDate br_setYear:2019 month:10 day:30];
+datePickerView.minDate = [NSDate br_setYear:1949 month:3 day:12];
+datePickerView.maxDate = [NSDate date];
+datePickerView.isAutoSelect = YES;
+datePickerView.resultBlock = ^(NSDate *selectDate, NSString *selectValue) {
+    NSLog(@"选择的值:%@", selectValue);
+};
+// 设置自定义样式
+BRPickerStyle *customStyle = [[BRPickerStyle alloc]init];
+customStyle.pickerColor = BR_RGB_HEX(0xd9dbdf, 1.0f);
+customStyle.pickerTextColor = [UIColor redColor];
+customStyle.separatorColor = [UIColor redColor];
+datePickerView.pickerStyle = customStyle;
+
+// 3.显示
+[datePickerView show];
+```
+
+**时间选择器显示类型的效果图(默认样式):**
+
+- 以下4种样式是使用 UIDatePicker 类 进行封装的,支持循环滚动
+
+| ![样式1:BRDatePickerModeTime](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type1.png?raw=true) | ![样式2:BRDatePickerModeDate](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type2.png?raw=true) |
+| :----------------------------------------------------------: | :----------------------------------------------------------: |
+|                 样式1:BRDatePickerModeDate                  |              样式2:BRDatePickerModeDateAndTime              |
+|                                                              |                                                              |
+| ![样式3:BRDatePickerModeDateAndTime](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type3.png?raw=true) | ![样式4:BRDatePickerModeCountDownTimer](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type4.png?raw=true) |
+|                 样式3:BRDatePickerModeTime                  |            样式4:BRDatePickerModeCountDownTimer             |
+
+- 以下11种样式是使用 UIPickerView 类进行封装的。
+
+| ![样式5:BRDatePickerModeYMDHMS](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type5.png?raw=true) | ![样式6:BRDatePickerModeYMDHM](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type6.png?raw=true) |
+| :----------------------------------------------------------: | :----------------------------------------------------------: |
+|                样式5:BRDatePickerModeYMDHMS                 |                 样式6:BRDatePickerModeYMDHM                 |
+|                                                              |                                                              |
+| ![样式7:BRDatePickerModeYMDH](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type7.png?raw=true) | ![样式8:BRDatePickerModeMDHM](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type8.png?raw=true) |
+|                 样式7:BRDatePickerModeYMDH                  |                 样式8:BRDatePickerModeMDHM                  |
+|                                                              |                                                              |
+| ![样式9:BRDatePickerModeYMDE](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type9.png?raw=true) | ![样式10:BRDatePickerModeYMD](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type10.png?raw=true) |
+|                  样式9:BRDatePickerModeYMD                  |                  样式10:BRDatePickerModeYM                  |
+|                                                              |                                                              |
+| ![样式11:BRDatePickerModeYM](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type11.png?raw=true) | ![样式12:BRDatePickerModeY](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type12.png?raw=true) |
+|                  样式11:BRDatePickerModeY                   |                  样式12:BRDatePickerModeMD                  |
+|                                                              |                                                              |
+| ![样式13:BRDatePickerModeMD](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type13.png?raw=true) | ![样式14:BRDatePickerModeHMS](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type14.png?raw=true) |
+|                 样式13:BRDatePickerModeHMS                  |                  样式14:BRDatePickerModeHM                  |
+|                                                              |                                                              |
+| ![样式15:BRDatePickerModeHM](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type15.png?raw=true) |                                                              |
+|                  样式15:BRDatePickerModeMS                  |                                                              |
+
+- 其它日期样式
+
+| ![设置显示星期](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_week1.png?raw=true) | ![设置显示星期](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_week2.png?raw=true) |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| 设置显示星期:datePickerView.showWeek = YES;                 | 设置显示星期:datePickerView.showWeek = YES;                 |
+|                                                              |                                                              |
+| ![设置添加至今](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_now.png?raw=true) | ![设置显示今天](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_today.png?raw=true) |
+| 设置添加至今:datePickerView.addToNow = YES;                 | 设置显示今天:datePickerView.showToday = YES;                |
+|                                                              |                                                              |
+| ![日期单位单行显示样式](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_unit.png?raw=true) | ![自定义选择器选中行颜色](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_row.png?raw=true) |
+| 日期单位显示样式:datePickerView.showUnitType = BRShowUnitTypeOnlyCenter; | 设置选择器中间选中行的背景颜色:selectRowColor               |
+
+```objective-c
+// 设置选择器中间选中行的样式
+BRPickerStyle *customStyle = [[BRPickerStyle alloc]init];
+customStyle.selectRowColor = [UIColor blueColor];
+customStyle.selectRowTextFont = [UIFont boldSystemFontOfSize:20.0f];
+customStyle.selectRowTextColor = [UIColor redColor];
+datePickerView.pickerStyle = customStyle;
+```
+
+| ![英式日期年月日](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_en1.png?raw=true) | ![英式日期年月](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_en2.png?raw=true) |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| 样式:BRDatePickerModeYMD (默认非中文环境显示英式日期)     | 样式:BRDatePickerModeYM (默认非中文环境显示英式日期)      |
+
+- 几种常见的弹框样式模板
+
+| ![模板样式1](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/template_style1.png?raw=true) | ![模板样式2](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/template_style2.png?raw=true) |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| 弹框样式模板1:datePickerView.pickerStyle = [BRPickerStyle pickerStyleWithThemeColor:[UIColor blueColor]]; | 弹框样式模板2:datePickerView.pickerStyle = [BRPickerStyle pickerStyleWithDoneTextColor:[UIColor blueColor]]; |
+|                                                              |                                                              |
+| ![模板样式3](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/template_style3.png?raw=true) | ![添加选择器的头视图](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/date_type_top.png?raw=true) |
+| 弹框样式模板3:datePickerView.pickerStyle = [BRPickerStyle pickerStyleWithDoneBtnImage:[UIImage imageNamed:@"icon_close"]]; | 添加选择器的头视图:pickerHeaderView                         |
+
+```objective-c
+// 添加选择器头视图(pickerHeaderView)
+UIView *headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 36)];
+headerView.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.1f];
+NSArray *unitArr = @[@"年", @"月", @"日"];
+for (NSInteger i = 0; i < unitArr.count; i++) {
+    CGFloat width = SCREEN_WIDTH / unitArr.count;
+    CGFloat orginX = i * (SCREEN_WIDTH / unitArr.count);
+    UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(orginX, 0, width, 36)];
+    label.backgroundColor = [UIColor clearColor];
+    label.textAlignment = NSTextAlignmentCenter;
+    label.font = [UIFont systemFontOfSize:16.0f];
+    label.textColor = [UIColor darkGrayColor];
+    label.text = unitArr[i];
+    [headerView addSubview:label];
+}
+datePickerView.pickerHeaderView = headerView;
+```
+
+#### 2. 地址选择器:`BRAddressPickerView`
+
+​	查看 BRAddressPickerView.h 头文件,里面提供了两种使用方式,参见源码。
+
+- 使用示例(参考Demo):
+
+```objective-c
+/// 地址选择器
+BRAddressPickerView *addressPickerView = [[BRAddressPickerView alloc]init];
+addressPickerView.pickerMode = BRAddressPickerModeArea;
+addressPickerView.title = @"请选择地区";
+//addressPickerView.selectValues = @[@"浙江省", @"杭州市", @"西湖区"];
+addressPickerView.selectIndexs = @[@10, @0, @4];
+addressPickerView.isAutoSelect = YES;
+addressPickerView.resultBlock = ^(BRProvinceModel *province, BRCityModel *city, BRAreaModel *area) {
+    NSLog(@"选择的值:%@", [NSString stringWithFormat:@"%@ %@ %@", province.name, city.name, area.name]);
+};
+
+[addressPickerView show];
+```
+
+- 地址选择器的3种显示类型(showType 的3个枚举值):
+
+| ![省份](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/BRAddressPickerModeProvince.png?raw=true) | ![城市](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/BRAddressPickerModeCity.png?raw=true) |
+| :----------------------------------------------------------: | :----------------------------------------------------------: |
+|              样式1:BRAddressPickerModeProvince              |                样式2:BRAddressPickerModeCity                |
+|                                                              |                                                              |
+| ![地区](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/BRAddressPickerModeArea.png?raw=true) |                                                              |
+|                样式3:BRAddressPickerModeArea                |                                                              |
+
+#### 3.  自定义字符串选择器:`BRStringPickerView`
+
+​	查看 BRStringPickerView.h 头文件,里面提供了两种使用方式,参见源码。
+
+- 使用示例(参考Demo):
+
+```objective-c
+/// 1.单列字符串选择器(传字符串数组)
+BRStringPickerView *stringPickerView = [[BRStringPickerView alloc]init];
+stringPickerView.pickerMode = BRStringPickerComponentSingle;
+stringPickerView.title = @"学历";
+stringPickerView.dataSourceArr = @[@"大专以下", @"大专", @"本科", @"硕士", @"博士", @"博士后"];
+stringPickerView.selectIndex = 2;
+stringPickerView.resultModelBlock = ^(BRResultModel *resultModel) {
+    NSLog(@"选择的值:%@", resultModel.value);
+};
+
+[stringPickerView show];
+
+
+/// 2.单列字符串选择器(可以传模型数组)
+NSArray *infoArr = @[@{@"key": @"1001", @"value": @"无融资", @"remark": @""},
+                     @{@"key": @"2001", @"value": @"天使轮", @"remark": @""},
+                     @{@"key": @"3001", @"value": @"A轮", @"remark": @""},
+                     @{@"key": @"4001", @"value": @"B轮", @"remark": @""},
+                     @{@"key": @"5001", @"value": @"C轮以后", @"remark": @""},
+                     @{@"key": @"6001", @"value": @"已上市", @"remark": @""}];
+NSMutableArray *modelArr = [[NSMutableArray alloc]init];
+for (NSDictionary *dic in infoArr) {
+    BRResultModel *model = [[BRResultModel alloc]init];
+    model.key = dic[@"key"];
+    model.value = dic[@"value"];
+    model.remark = dic[@"remark"];
+    [modelArr addObject:model];
+}
+BRStringPickerView *stringPickerView = [[BRStringPickerView alloc]init];
+stringPickerView.pickerMode = BRStringPickerComponentSingle;
+stringPickerView.title = @"融资情况";
+stringPickerView.dataSourceArr = [modelArr copy];
+stringPickerView.selectIndex = 2;
+stringPickerView.resultModelBlock = ^(BRResultModel *resultModel) {
+    NSLog(@"选择的索引:%@", @(resultModel.index));
+    NSLog(@"选择的值:%@", resultModel.value);
+};
+
+[stringPickerView show];
+
+
+/// 3.多列字符串选择器
+BRStringPickerView *stringPickerView = [[BRStringPickerView alloc]init];
+stringPickerView.pickerMode = BRStringPickerComponentMulti;
+stringPickerView.title = @"自定义多列字符串";
+stringPickerView.dataSourceArr = @[@[@"语文", @"数学", @"英语", @"物理", @"化学", @"生物"], @[@"优秀", @"良好", @"及格", @"不及格"]];
+stringPickerView.selectIndexs = @[@2, @1];
+stringPickerView.resultModelArrayBlock = ^(NSArray<BRResultModel *> *resultModelArr) {
+    NSLog(@"选择的值:%@", [NSString stringWithFormat:@"%@,%@", resultModelArr[0].value, resultModelArr[1].value]);
+};
+
+// 设置选择器中间选中行的样式
+BRPickerStyle *customStyle = [[BRPickerStyle alloc]init];
+customStyle.selectRowTextFont = [UIFont boldSystemFontOfSize:20.0f];
+customStyle.selectRowTextColor = [UIColor blueColor];
+stringPickerView.pickerStyle = customStyle;
+
+[stringPickerView show];
+```
+
+- 字符串选择器效果图:
+
+| ![自定义单列字符串](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/string_single.png?raw=true) | ![融资情况](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/string_rongzi.png?raw=true) |
+| :----------------------------------------------------------: | :----------------------------------------------------------: |
+|                       单列字符串选择器                       |                       单列字符串选择器                       |
+|                                                              |                                                              |
+| ![多列字符串选择器](https://github.com/91renb/BRPickerView/blob/master/BRPickerViewDemo/images/string_more.png?raw=true) |                                                              |
+|                       多列字符串选择器                       |                                                              |
+
+# 更新记录
+
+#### 2022-07-08(V2.8.0)
+
+- 优化代码。
+
+#### 2022-06-16(V2.7.8)
+
+- 优化代码。
+
+#### 2022-03-30(V2.7.7)
+
+- 优化代码。
+
+#### 2021-10-09(V2.7.6)
+
+- 适配iOS15
+
+#### 2021-05-28(V2.7.5)
+
+- 日期选择器新增属性:`monthNames` 和 `customUnit`
+
+- 解决已知问题:[#232](https://github.com/91renb/BRPickerView/issues/232) 、[#231](https://github.com/91renb/BRPickerView/issues/231)  、[#230](https://github.com/91renb/BRPickerView/issues/230)  、[#227](https://github.com/91renb/BRPickerView/issues/227)  、[#225](https://github.com/91renb/BRPickerView/issues/225) 、[#219](https://github.com/91renb/BRPickerView/issues/219) 、[#206](https://github.com/91renb/BRPickerView/issues/206) 
+
+#### 2020-09-25(V2.7.3)
+
+- 适配选择器iOS14的样式:[#189](https://github.com/91renb/BRPickerView/issues/189) 、[#191](https://github.com/91renb/BRPickerView/issues/191)
+
+#### 2020-09-23(V2.7.2)
+
+- 日期选择器新增添加自定义字符串属性:`firstRowContent` 和 `lastRowContent`
+- 解决日期选择器设置最小日期时,存在的联动不正确的问题:[#184](https://github.com/91renb/BRPickerView/issues/184) 
+
+#### 2020-08-28(V2.7.0)
+
+- 日期选择器添加 `nonSelectableDates` 属性:[#178](https://github.com/91renb/BRPickerView/issues/178) 
+- 优化选中行文本显示:[#177](https://github.com/91renb/BRPickerView/issues/177) 
+
+#### 2020-08-16(V2.6.8)
+
+- 优化代码,适配 iPad 分屏显示
+- 新增 `keyView` 属性(即组件的父视图:可以将组件添加到 自己获取的 keyWindow 上,或页面的 view 上)
+
+#### 2020-08-09(V2.6.7)
+
+- 适配 iOS14
+
+#### 2020-08-06(V2.6.6)
+
+- 修复 [#163](https://github.com/91renb/BRPickerView/issues/163) 和  [#170](https://github.com/91renb/BRPickerView/issues/170) 
+
+#### 2020-07-18(V2.6.5)
+
+- 字符串选择器新增支持多级联动选择
+
+#### 2020-06-24(V2.6.3)
+
+- 日期选择器新增属性:`timeZone` 和 `addCustomString`
+
+#### 2020-05-12(V2.6.2)
+
+- 实现 [#145](#145) 和  [#146](#146) 需求
+
+#### 2020-04-30(V2.6.0)
+
+- 新增样式属性:`selectRowTextColor` 和 `selectRowTextFont`
+- 日期选择器新增数字显示属性:`numberFullName`
+- 优化代码,添加 `BRDatePickerModeYMD` 支持国际化英式日期
+
+- 修复 [#143](#143)
+
+#### 2020-04-27(V2.5.8)
+
+- 修复 [#138](https://github.com/91renb/BRPickerView/issues/138) 和 [#142](https://github.com/91renb/BRPickerView/issues/142)
+- 日期选择器新增 `descending` 属性,支持降序的时间列表
+- 更新地址选择器地区数据源
+
+#### 2020-03-31(V2.5.7)
+
+- 优化代码,解决已知问题
+
+#### 2020-02-26(V2.5.6)
+
+- 优化代码,兼容部分国际化日期样式
+
+#### 2020-02-24(V2.5.5)
+
+- 添加设置选择器选中行背景颜色的功能,新增属性 `selectRowColor`
+
+#### 2020-01-31(V2.5.3)
+
+- 新增属性:`pickerHeaderView`、`pickerFooterView`
+- 新增刷新选择器数据方法:`reloadData`
+
+#### 2020-01-05(V2.5.1)
+
+- 优化代码,添加 `BRDatePickerModeYM` 支持国际化英式日期
+
+#### 2020-01-02(V2.5.0)
+
+- 日期选择器新增属性:`showUnitType`(日期单位显示样式)、`minuteInterval`、`secondInterval`
+- 封装了常用的几种模板样式,使用更加简单便捷
+- 框架内默认适配深色模式显示
+
+#### 2019-12-26(V2.4.6)
+
+- 添加支持动态更新属性 `title` 、 `selectDate`、`pickerMode` 的值
+- 日期选择器添加 `showWeek` 属性,及新增 `BRDatePickerModeMS` 日期类型
+- 优化选择器【用法二】的使用,新增选择器滚动选择时回调的属性
+
+#### 2019-11-28(V2.4.5)
+
+- 日期选择器新增选择 ”至今“ 和 显示 ”今天“ 的功能,见以下两个属性:
+
+  `showToday` :控制是否显示 “今天” ,默认为 NO
+
+  `addToNow`:控制是否添加选择 “至今”,默认为 NO
+
+#### 2019-11-26(V2.4.3)
+
+- 日期选择器新增以下三种选择类型:
+
+  `BRDatePickerModeYMDHMS`(年月日时分秒)、`BRDatePickerModeYMDE`(年月日星期)、`BRDatePickerModeHMS`(时分秒)
+
+- 更新地址选择器地区数据源
+
+#### 2019-11-07(V2.4.2)
+
+- 日期选择器添加:BRDatePickerModeYMDH(yyyy-MM-dd HH)类型
+- 地址选择器添加:selectIndexs 属性,可根据索引去设置默认选择
+- 适配横屏及刘海屏安全区域显示效果
+
+#### 2019-11-04(V2.4.0)
+
+- 优化选择器子目录管理,方便轻量级、模块化集成
+
+  `pod 'BRPickerView'`	// 集成全部的功能
+
+  `pod 'BRPickerView/DatePickerView'`	// 仅集成日期选择器的功能
+
+  `pod 'BRPickerView/AddressPickerView'`	// 仅集成地址选择器的功能
+
+  `pod 'BRPickerView/StringPickerView'`	// 仅集成字符串选择器的功能
+
+#### 2019-11-01(V2.3.8)
+
+- 优化代码,添加更多的自定义样式属性
+
+#### 2019-10-30(V2.3.6)
+
+- 优化代码,添加国际化支持
+
+#### 2019-10-26(V2.3.5)
+
+- 添加传统的创建对象设置属性的使用方式
+- 开放设置选择器颜色及样式,适配深色模式
+- 更新省市区数据源,数据与政府官网最新公布的一致(参见:[行政区划代码](http://www.mca.gov.cn/article/sj/xzqh/2019/))
+- 支持将选择器添加到指定容器视图上(见BaseView.h文件,扩展一方法)
+- 支持将子视图添加到选择器上(见BaseView.h文件,扩展二方法)
+- 优化代码,配置Pod库的层级目录
+
+#### 2018-04-27(V2.2.1):
+
+- 修复bug,适配iPad和横屏显示。
+- 优化代码,提高框架适应性,降低内存消耗。
+
+#### 2018-04-03(V2.2.0)
+
+- 时间选择器新添加了7种显示类型(BRDatePickerMode),可根据自己项目的需求选择性使用。
+- 适配横屏,及 iPhoneX 底部安全区域。
+- 修改了最小时间和最大时间的参数名称(以前版本是传 NSString 类型, 现在传 NSDate 类型)
+- 修复比较时间大小时出现的bug。
+
+#### 2018-03-19(V2.1.3)
+
+- 修改地址选择器确认选择后的回调参数。
+- 现修改如下:可通过省市区的模型获取省市区的 name(名称)、code(id)、index(索引)`resultBlock:^(BRProvinceModel *province, BRCityModel *city, BRAreaModel *area) {}`
+- 去掉第三方依赖库 `MJExtension` ,修改为手动解析地址数据源。
+
+#### 2018-03-11(V2.1.2)
+
+- 重命名了Github用户名,更新项目相关的路径。(提示:pod之前的版本不受影响)
+
+#### 2018-02-28(V2.1.1)
+
+- 修复某些情况下无法用bundle加载本地数据源(BRCity.plist)bug。
+
+#### 2018-01-26(V2.1.0)
+
+- 给地址选择器添加了一个方法(见方法4),提供数据源参数,支持外部传入地区数据源。
+- 提示:要注意数据源格式,参考 BRCity.json。可以把 BRCity.json 文件的内容放到后台去维护,通过后台接口获取地区数据源(即 BRCity.json 文件的内容)。
+
+#### 2018-01-25(V2.0.0)
+
+- 更新了地址数据源(BRCity.plist),地区信息是2018年最新最全的,与微信的地区信息完全一致。
+- 支持自定义默认选择地址(格式:@[@"浙江省", @"杭州市", @"西湖区"]),支持下次点击进入地址选择器时,默认地址为上次选择的结果。
+- 修改了日期选择器、地址选择器、字符串选择器的接口方法(删除了之前的方法2)。
+- 添加了地址选择器显示类型,支持3种显示:只显示省份、显示省份和城市、显示省市区。
+
+#### 2018-01-05(V1.3.0)
+
+- 添加取消选择的回调方法(点击背景或取消按钮会执行 `cancelBlock` )
+- 合并了字符串选择器 数组数据源和plist数据源对应的方法,`dataSource` 参数支持两种类型:
+
+#### 2018-01-02(V1.2.0)
+
+- 添加支持自定义主题颜色的方法。
+
+#### 2017-11-26(V1.1.0)
+
+- 更换第三方依赖库。
+- 用MJExtension 替换了 原来的YYModel,以前没有注意导入YYModel,同时又导入YYKit会导致重复导入而冲突(另外使用YYModel时,手动导入和pod导入 其中的头文件和方法名也不一样,所以很容易出错)。
+
+#### 2017-11-16(V1.0.0)
+
+- 初始版本!
+
+# 许可证
+
+BRPickerView 使用 MIT 许可证,详情见 LICENSE 文件。

+ 6 - 0
Pods/Local Podspecs/WMBase.podspec.json

@@ -36,6 +36,9 @@
     ],
     "YYCache": [
 
+    ],
+    "YYText": [
+
     ],
     "MBProgressHUD": [
 
@@ -54,6 +57,9 @@
     ],
     "CTMediator": [
 
+    ],
+    "BRPickerView": [
+
     ]
   }
 }

+ 20 - 1
Pods/Manifest.lock

@@ -623,6 +623,18 @@ PODS:
   - BoringSSL-GRPC/Implementation (0.0.24):
     - BoringSSL-GRPC/Interface (= 0.0.24)
   - BoringSSL-GRPC/Interface (0.0.24)
+  - BRPickerView (2.8.1):
+    - BRPickerView/AddressPickerView (= 2.8.1)
+    - BRPickerView/Base (= 2.8.1)
+    - BRPickerView/DatePickerView (= 2.8.1)
+    - BRPickerView/StringPickerView (= 2.8.1)
+  - BRPickerView/AddressPickerView (2.8.1):
+    - BRPickerView/Base
+  - BRPickerView/Base (2.8.1)
+  - BRPickerView/DatePickerView (2.8.1):
+    - BRPickerView/Base
+  - BRPickerView/StringPickerView (2.8.1):
+    - BRPickerView/Base
   - Bugly (2.5.93)
   - CTMediator (48)
   - FBAEMKit (16.0.0):
@@ -1113,6 +1125,7 @@ PODS:
   - Toast (4.0.0)
   - WMBase (1.0.9):
     - AFNetworking
+    - BRPickerView
     - CTMediator
     - Masonry
     - MBProgressHUD
@@ -1123,9 +1136,11 @@ PODS:
     - Toast
     - YTKNetwork
     - YYCache
+    - YYText
   - YTKNetwork (3.0.6):
     - AFNetworking/NSURLSession (~> 4.0)
   - YYCache (1.0.4)
+  - YYText (1.0.7)
 
 DEPENDENCIES:
   - AFNetworking
@@ -1157,6 +1172,7 @@ SPEC REPOS:
     - AFNetworking
     - AppAuth
     - BoringSSL-GRPC
+    - BRPickerView
     - Bugly
     - CTMediator
     - FBAEMKit
@@ -1195,6 +1211,7 @@ SPEC REPOS:
     - Toast
     - YTKNetwork
     - YYCache
+    - YYText
 
 EXTERNAL SOURCES:
   WMBase:
@@ -1205,6 +1222,7 @@ SPEC CHECKSUMS:
   AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58
   AppAuth: 8fca6b5563a5baef2c04bee27538025e4ceb2add
   BoringSSL-GRPC: 3175b25143e648463a56daeaaa499c6cb86dad33
+  BRPickerView: 2531a2d4d0fea0b57a1c738de215af0f88863a2f
   Bugly: b8715e6ec4004b7f7fbffab0643ba80545aee3da
   CTMediator: 3f3578b525b3a46a7a2f92a99922ec40363f4269
   FBAEMKit: 7fb5a0b5caf2ed2900e29c3a17de92ea7193a247
@@ -1241,9 +1259,10 @@ SPEC CHECKSUMS:
   Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
   SDWebImage: 302d4e14efff86b36b5f36d1bf891b635436d071
   Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
-  WMBase: eb93e38dff7d9be4edb267acef5b3bdc1bc4d3a8
+  WMBase: a0c4b37b0af79598c62864d8580991b7d4f651f2
   YTKNetwork: c16be90b06be003de9e9cd0d3b187cc8eaf35c04
   YYCache: 8105b6638f5e849296c71f331ff83891a4942952
+  YYText: 5c461d709e24d55a182d1441c41dc639a18a4849
 
 PODFILE CHECKSUM: 9e16269a21fd5c9c1688e530bba69cffe563a650
 

Plik diff jest za duży
+ 27676 - 26861
Pods/Pods.xcodeproj/project.pbxproj


+ 26 - 0
Pods/Target Support Files/BRPickerView/BRPickerView-Info.plist

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>${PODS_DEVELOPMENT_LANGUAGE}</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>2.8.1</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>

+ 5 - 0
Pods/Target Support Files/BRPickerView/BRPickerView-dummy.m

@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_BRPickerView : NSObject
+@end
+@implementation PodsDummy_BRPickerView
+@end

+ 12 - 0
Pods/Target Support Files/BRPickerView/BRPickerView-prefix.pch

@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+

+ 28 - 0
Pods/Target Support Files/BRPickerView/BRPickerView-umbrella.h

@@ -0,0 +1,28 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "BRPickerView.h"
+#import "BRAddressModel.h"
+#import "BRAddressPickerView.h"
+#import "BRBaseView.h"
+#import "BRPickerStyle.h"
+#import "BRPickerViewMacro.h"
+#import "NSBundle+BRPickerView.h"
+#import "BRDatePickerView+BR.h"
+#import "BRDatePickerView.h"
+#import "NSDate+BRPickerView.h"
+#import "BRResultModel.h"
+#import "BRStringPickerView.h"
+
+FOUNDATION_EXPORT double BRPickerViewVersionNumber;
+FOUNDATION_EXPORT const unsigned char BRPickerViewVersionString[];
+

+ 13 - 0
Pods/Target Support Files/BRPickerView/BRPickerView.debug.xcconfig

@@ -0,0 +1,13 @@
+APPLICATION_EXTENSION_API_ONLY = YES
+CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/BRPickerView
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/BRPickerView
+PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 6 - 0
Pods/Target Support Files/BRPickerView/BRPickerView.modulemap

@@ -0,0 +1,6 @@
+framework module BRPickerView {
+  umbrella header "BRPickerView-umbrella.h"
+
+  export *
+  module * { export * }
+}

+ 13 - 0
Pods/Target Support Files/BRPickerView/BRPickerView.release.xcconfig

@@ -0,0 +1,13 @@
+APPLICATION_EXTENSION_API_ONLY = YES
+CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/BRPickerView
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/BRPickerView
+PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 51 - 0
Pods/Target Support Files/Pods-Asteria-NotificationServiceExtension/Pods-Asteria-NotificationServiceExtension-acknowledgements.markdown

@@ -230,6 +230,31 @@ THE SOFTWARE.
    limitations under the License.
 
 
+## BRPickerView
+
+MIT License
+
+Copyright (c) 2019 91renb
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
 ## BoringSSL-GRPC
 
 BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL
@@ -3191,6 +3216,32 @@ SOFTWARE.
 
 
 
+## YYText
+
+The MIT License (MIT)
+
+Copyright (c) 2015 ibireme <ibireme@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+
 ## abseil
 
 

+ 63 - 0
Pods/Target Support Files/Pods-Asteria-NotificationServiceExtension/Pods-Asteria-NotificationServiceExtension-acknowledgements.plist

@@ -253,6 +253,37 @@ THE SOFTWARE.
 			<key>Type</key>
 			<string>PSGroupSpecifier</string>
 		</dict>
+		<dict>
+			<key>FooterText</key>
+			<string>MIT License
+
+Copyright (c) 2019 91renb
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+</string>
+			<key>License</key>
+			<string>MIT</string>
+			<key>Title</key>
+			<string>BRPickerView</string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
 		<dict>
 			<key>FooterText</key>
 			<string>BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL
@@ -3430,6 +3461,38 @@ SOFTWARE.
 			<key>Type</key>
 			<string>PSGroupSpecifier</string>
 		</dict>
+		<dict>
+			<key>FooterText</key>
+			<string>The MIT License (MIT)
+
+Copyright (c) 2015 ibireme &lt;ibireme@gmail.com&gt;
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+</string>
+			<key>License</key>
+			<string>MIT</string>
+			<key>Title</key>
+			<string>YYText</string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
 		<dict>
 			<key>FooterText</key>
 			<string>

Plik diff jest za duży
+ 3 - 3
Pods/Target Support Files/Pods-Asteria-NotificationServiceExtension/Pods-Asteria-NotificationServiceExtension.debug.xcconfig


Plik diff jest za duży
+ 3 - 3
Pods/Target Support Files/Pods-Asteria-NotificationServiceExtension/Pods-Asteria-NotificationServiceExtension.release.xcconfig


+ 51 - 0
Pods/Target Support Files/Pods-Asteria/Pods-Asteria-acknowledgements.markdown

@@ -230,6 +230,31 @@ THE SOFTWARE.
    limitations under the License.
 
 
+## BRPickerView
+
+MIT License
+
+Copyright (c) 2019 91renb
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
 ## BoringSSL-GRPC
 
 BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL
@@ -3191,6 +3216,32 @@ SOFTWARE.
 
 
 
+## YYText
+
+The MIT License (MIT)
+
+Copyright (c) 2015 ibireme <ibireme@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+
 ## abseil
 
 

+ 63 - 0
Pods/Target Support Files/Pods-Asteria/Pods-Asteria-acknowledgements.plist

@@ -253,6 +253,37 @@ THE SOFTWARE.
 			<key>Type</key>
 			<string>PSGroupSpecifier</string>
 		</dict>
+		<dict>
+			<key>FooterText</key>
+			<string>MIT License
+
+Copyright (c) 2019 91renb
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+</string>
+			<key>License</key>
+			<string>MIT</string>
+			<key>Title</key>
+			<string>BRPickerView</string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
 		<dict>
 			<key>FooterText</key>
 			<string>BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL
@@ -3430,6 +3461,38 @@ SOFTWARE.
 			<key>Type</key>
 			<string>PSGroupSpecifier</string>
 		</dict>
+		<dict>
+			<key>FooterText</key>
+			<string>The MIT License (MIT)
+
+Copyright (c) 2015 ibireme &lt;ibireme@gmail.com&gt;
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+</string>
+			<key>License</key>
+			<string>MIT</string>
+			<key>Title</key>
+			<string>YYText</string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
 		<dict>
 			<key>FooterText</key>
 			<string>

+ 2 - 0
Pods/Target Support Files/Pods-Asteria/Pods-Asteria-frameworks-Debug-input-files.xcfilelist

@@ -1,6 +1,7 @@
 ${PODS_ROOT}/Target Support Files/Pods-Asteria/Pods-Asteria-frameworks.sh
 ${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework
 ${BUILT_PRODUCTS_DIR}/AppAuth/AppAuth.framework
+${BUILT_PRODUCTS_DIR}/BRPickerView/BRPickerView.framework
 ${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC/openssl_grpc.framework
 ${BUILT_PRODUCTS_DIR}/CTMediator/CTMediator.framework
 ${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework
@@ -28,6 +29,7 @@ ${BUILT_PRODUCTS_DIR}/Toast/Toast.framework
 ${BUILT_PRODUCTS_DIR}/WMBase/WMBase.framework
 ${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework
 ${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework
+${BUILT_PRODUCTS_DIR}/YYText/YYText.framework
 ${BUILT_PRODUCTS_DIR}/abseil/absl.framework
 ${BUILT_PRODUCTS_DIR}/gRPC-C++/grpcpp.framework
 ${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework

+ 2 - 0
Pods/Target Support Files/Pods-Asteria/Pods-Asteria-frameworks-Debug-output-files.xcfilelist

@@ -1,5 +1,6 @@
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AFNetworking.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppAuth.framework
+${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BRPickerView.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl_grpc.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CTMediator.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework
@@ -27,6 +28,7 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Toast.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WMBase.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YTKNetwork.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYCache.framework
+${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYText.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/absl.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpcpp.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpc.framework

+ 2 - 0
Pods/Target Support Files/Pods-Asteria/Pods-Asteria-frameworks-Release-input-files.xcfilelist

@@ -1,6 +1,7 @@
 ${PODS_ROOT}/Target Support Files/Pods-Asteria/Pods-Asteria-frameworks.sh
 ${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework
 ${BUILT_PRODUCTS_DIR}/AppAuth/AppAuth.framework
+${BUILT_PRODUCTS_DIR}/BRPickerView/BRPickerView.framework
 ${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC/openssl_grpc.framework
 ${BUILT_PRODUCTS_DIR}/CTMediator/CTMediator.framework
 ${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework
@@ -28,6 +29,7 @@ ${BUILT_PRODUCTS_DIR}/Toast/Toast.framework
 ${BUILT_PRODUCTS_DIR}/WMBase/WMBase.framework
 ${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework
 ${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework
+${BUILT_PRODUCTS_DIR}/YYText/YYText.framework
 ${BUILT_PRODUCTS_DIR}/abseil/absl.framework
 ${BUILT_PRODUCTS_DIR}/gRPC-C++/grpcpp.framework
 ${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework

+ 2 - 0
Pods/Target Support Files/Pods-Asteria/Pods-Asteria-frameworks-Release-output-files.xcfilelist

@@ -1,5 +1,6 @@
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AFNetworking.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppAuth.framework
+${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BRPickerView.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl_grpc.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CTMediator.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework
@@ -27,6 +28,7 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Toast.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WMBase.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YTKNetwork.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYCache.framework
+${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYText.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/absl.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpcpp.framework
 ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpc.framework

+ 4 - 0
Pods/Target Support Files/Pods-Asteria/Pods-Asteria-frameworks.sh

@@ -178,6 +178,7 @@ code_sign_if_enabled() {
 if [[ "$CONFIGURATION" == "Debug" ]]; then
   install_framework "${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/AppAuth/AppAuth.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/BRPickerView/BRPickerView.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC/openssl_grpc.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/CTMediator/CTMediator.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework"
@@ -205,6 +206,7 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then
   install_framework "${BUILT_PRODUCTS_DIR}/WMBase/WMBase.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/YYText/YYText.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/abseil/absl.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/gRPC-C++/grpcpp.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework"
@@ -222,6 +224,7 @@ fi
 if [[ "$CONFIGURATION" == "Release" ]]; then
   install_framework "${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/AppAuth/AppAuth.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/BRPickerView/BRPickerView.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC/openssl_grpc.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/CTMediator/CTMediator.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework"
@@ -249,6 +252,7 @@ if [[ "$CONFIGURATION" == "Release" ]]; then
   install_framework "${BUILT_PRODUCTS_DIR}/WMBase/WMBase.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/YYText/YYText.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/abseil/absl.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/gRPC-C++/grpcpp.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework"

Plik diff jest za duży
+ 3 - 3
Pods/Target Support Files/Pods-Asteria/Pods-Asteria.debug.xcconfig


Plik diff jest za duży
+ 3 - 3
Pods/Target Support Files/Pods-Asteria/Pods-Asteria.release.xcconfig


+ 1 - 0
Pods/Target Support Files/WMBase/WMBase-umbrella.h

@@ -23,6 +23,7 @@
 #import "UC_CommonmoduleCat.h"
 #import "UIColor+Categpry.h"
 #import "UIImage+Rotate.h"
+#import "UIView+Frame.h"
 #import "UIViewController+HUD.h"
 #import "Msg_BackNormalFooter.h"
 #import "TT_BaceProtocol.h"

Plik diff jest za duży
+ 2 - 2
Pods/Target Support Files/WMBase/WMBase.debug.xcconfig


Plik diff jest za duży
+ 2 - 2
Pods/Target Support Files/WMBase/WMBase.release.xcconfig


+ 26 - 0
Pods/Target Support Files/YYText/YYText-Info.plist

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>${PODS_DEVELOPMENT_LANGUAGE}</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.0.7</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>

+ 5 - 0
Pods/Target Support Files/YYText/YYText-dummy.m

@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_YYText : NSObject
+@end
+@implementation PodsDummy_YYText
+@end

+ 12 - 0
Pods/Target Support Files/YYText/YYText-prefix.pch

@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+

+ 41 - 0
Pods/Target Support Files/YYText/YYText-umbrella.h

@@ -0,0 +1,41 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "YYTextContainerView.h"
+#import "YYTextDebugOption.h"
+#import "YYTextEffectWindow.h"
+#import "YYTextInput.h"
+#import "YYTextKeyboardManager.h"
+#import "YYTextLayout.h"
+#import "YYTextLine.h"
+#import "YYTextMagnifier.h"
+#import "YYTextSelectionView.h"
+#import "YYTextArchiver.h"
+#import "YYTextAttribute.h"
+#import "YYTextParser.h"
+#import "YYTextRubyAnnotation.h"
+#import "YYTextRunDelegate.h"
+#import "NSAttributedString+YYText.h"
+#import "NSParagraphStyle+YYText.h"
+#import "UIPasteboard+YYText.h"
+#import "UIView+YYText.h"
+#import "YYTextAsyncLayer.h"
+#import "YYTextTransaction.h"
+#import "YYTextUtilities.h"
+#import "YYTextWeakProxy.h"
+#import "YYLabel.h"
+#import "YYText.h"
+#import "YYTextView.h"
+
+FOUNDATION_EXPORT double YYTextVersionNumber;
+FOUNDATION_EXPORT const unsigned char YYTextVersionString[];
+

+ 14 - 0
Pods/Target Support Files/YYText/YYText.debug.xcconfig

@@ -0,0 +1,14 @@
+APPLICATION_EXTENSION_API_ONLY = YES
+CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YYText
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "CoreFoundation" -framework "CoreText" -framework "MobileCoreServices" -framework "QuartzCore" -framework "UIKit"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/YYText
+PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 6 - 0
Pods/Target Support Files/YYText/YYText.modulemap

@@ -0,0 +1,6 @@
+framework module YYText {
+  umbrella header "YYText-umbrella.h"
+
+  export *
+  module * { export * }
+}

+ 14 - 0
Pods/Target Support Files/YYText/YYText.release.xcconfig

@@ -0,0 +1,14 @@
+APPLICATION_EXTENSION_API_ONLY = YES
+CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YYText
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "CoreFoundation" -framework "CoreText" -framework "MobileCoreServices" -framework "QuartzCore" -framework "UIKit"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/YYText
+PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 22 - 0
Pods/YYText/LICENSE

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

Plik diff jest za duży
+ 1095 - 0
Pods/YYText/README.md


+ 55 - 0
Pods/YYText/YYText/Component/YYTextContainerView.h

@@ -0,0 +1,55 @@
+//
+//  YYTextContainerView.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/21.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+#if __has_include(<YYText/YYText.h>)
+#import <YYText/YYTextLayout.h>
+#else
+#import "YYTextLayout.h"
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A simple view to diaplay `YYTextLayout`.
+
+ @discussion This view can become first responder. If this view is first responder,
+ all the action (such as UIMenu's action) would forward to the `hostView` property.
+ Typically, you should not use this class directly.
+ 
+ @warning All the methods in this class should be called on main thread.
+ */
+@interface YYTextContainerView : UIView
+
+/// First responder's aciton will forward to this view.
+@property (nullable, nonatomic, weak) UIView *hostView;
+
+/// Debug option for layout debug. Set this property will let the view redraw it's contents.
+@property (nullable, nonatomic, copy) YYTextDebugOption *debugOption;
+
+/// Text vertical alignment.
+@property (nonatomic) YYTextVerticalAlignment textVerticalAlignment;
+
+/// Text layout. Set this property will let the view redraw it's contents.
+@property (nullable, nonatomic, strong) YYTextLayout *layout;
+
+/// The contents fade animation duration when the layout's contents changed. Default is 0 (no animation).
+@property (nonatomic) NSTimeInterval contentsFadeDuration;
+
+/// Convenience method to set `layout` and `contentsFadeDuration`.
+/// @param layout  Same as `layout` property.
+/// @param fadeDuration  Same as `contentsFadeDuration` property.
+- (void)setLayout:(nullable YYTextLayout *)layout withFadeDuration:(NSTimeInterval)fadeDuration;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 144 - 0
Pods/YYText/YYText/Component/YYTextContainerView.m

@@ -0,0 +1,144 @@
+//
+//  YYTextContainerView.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/21.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextContainerView.h"
+
+@implementation YYTextContainerView {
+    BOOL _attachmentChanged;
+    NSMutableArray *_attachmentViews;
+    NSMutableArray *_attachmentLayers;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    self = [super initWithFrame:frame];
+    if (!self) return nil;
+    self.backgroundColor = [UIColor clearColor];
+    _attachmentViews = [NSMutableArray array];
+    _attachmentLayers = [NSMutableArray array];
+    return self;
+}
+
+- (void)setDebugOption:(YYTextDebugOption *)debugOption {
+    BOOL needDraw = _debugOption.needDrawDebug;
+    _debugOption = debugOption.copy;
+    if (_debugOption.needDrawDebug != needDraw) {
+        [self setNeedsDisplay];
+    }
+}
+
+- (void)setTextVerticalAlignment:(YYTextVerticalAlignment)textVerticalAlignment {
+    if (_textVerticalAlignment == textVerticalAlignment) return;
+    _textVerticalAlignment = textVerticalAlignment;
+    [self setNeedsDisplay];
+}
+
+- (void)setContentsFadeDuration:(NSTimeInterval)contentsFadeDuration {
+    if (_contentsFadeDuration == contentsFadeDuration) return;
+    _contentsFadeDuration = contentsFadeDuration;
+    if (contentsFadeDuration <= 0) {
+        [self.layer removeAnimationForKey:@"contents"];
+    }
+}
+
+- (void)setLayout:(YYTextLayout *)layout {
+    if (_layout == layout) return;
+    _layout = layout;
+    _attachmentChanged = YES;
+    [self setNeedsDisplay];
+}
+
+- (void)setLayout:(YYTextLayout *)layout withFadeDuration:(NSTimeInterval)fadeDuration {
+    self.contentsFadeDuration = fadeDuration;
+    self.layout = layout;
+}
+
+- (void)drawRect:(CGRect)rect {
+    // fade content
+    [self.layer removeAnimationForKey:@"contents"];
+    if (_contentsFadeDuration > 0) {
+        CATransition *transition = [CATransition animation];
+        transition.duration = _contentsFadeDuration;
+        transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
+        transition.type = kCATransitionFade;
+        [self.layer addAnimation:transition forKey:@"contents"];
+    }
+    
+    // update attachment
+    if (_attachmentChanged) {
+        for (UIView *view in _attachmentViews) {
+            if (view.superview == self) [view removeFromSuperview];
+        }
+        for (CALayer *layer in _attachmentLayers) {
+            if (layer.superlayer == self.layer) [layer removeFromSuperlayer];
+        }
+        [_attachmentViews removeAllObjects];
+        [_attachmentLayers removeAllObjects];
+    }
+    
+    // draw layout
+    CGSize boundingSize = _layout.textBoundingSize;
+    CGPoint point = CGPointZero;
+    if (_textVerticalAlignment == YYTextVerticalAlignmentCenter) {
+        if (_layout.container.isVerticalForm) {
+            point.x = -(self.bounds.size.width - boundingSize.width) * 0.5;
+        } else {
+            point.y = (self.bounds.size.height - boundingSize.height) * 0.5;
+        }
+    } else if (_textVerticalAlignment == YYTextVerticalAlignmentBottom) {
+        if (_layout.container.isVerticalForm) {
+            point.x = -(self.bounds.size.width - boundingSize.width);
+        } else {
+            point.y = (self.bounds.size.height - boundingSize.height);
+        }
+    }
+    [_layout drawInContext:UIGraphicsGetCurrentContext() size:self.bounds.size point:point view:self layer:self.layer debug:_debugOption cancel:nil];
+    
+    // update attachment
+    if (_attachmentChanged) {
+        _attachmentChanged = NO;
+        for (YYTextAttachment *a in _layout.attachments) {
+            if ([a.content isKindOfClass:[UIView class]]) [_attachmentViews addObject:a.content];
+            if ([a.content isKindOfClass:[CALayer class]]) [_attachmentLayers addObject:a.content];
+        }
+    }
+}
+
+- (void)setFrame:(CGRect)frame {
+    CGSize oldSize = self.bounds.size;
+    [super setFrame:frame];
+    if (!CGSizeEqualToSize(oldSize, self.bounds.size)) {
+        [self setNeedsLayout];
+    }
+}
+
+- (void)setBounds:(CGRect)bounds {
+    CGSize oldSize = self.bounds.size;
+    [super setBounds:bounds];
+    if (!CGSizeEqualToSize(oldSize, self.bounds.size)) {
+        [self setNeedsLayout];
+    }
+}
+
+#pragma mark - UIResponder forward
+
+- (BOOL)canBecomeFirstResponder {
+    return YES;
+}
+
+- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
+    return [self.hostView canPerformAction:action withSender:sender];
+}
+
+- (id)forwardingTargetForSelector:(SEL)aSelector {
+    return self.hostView;
+}
+
+@end

+ 95 - 0
Pods/YYText/YYText/Component/YYTextDebugOption.h

@@ -0,0 +1,95 @@
+//
+//  YYTextDebugOption.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/8.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+@class YYTextDebugOption;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The YYTextDebugTarget protocol defines the method a debug target should implement.
+ A debug target can be add to the global container to receive the shared debug
+ option changed notification.
+ */
+@protocol YYTextDebugTarget <NSObject>
+
+@required
+/**
+ When the shared debug option changed, this method would be called on main thread.
+ It should return as quickly as possible. The option's property should not be changed
+ in this method.
+ 
+ @param option  The shared debug option.
+ */
+- (void)setDebugOption:(nullable YYTextDebugOption *)option;
+@end
+
+
+
+/**
+ The debug option for YYText.
+ */
+@interface YYTextDebugOption : NSObject <NSCopying>
+@property (nullable, nonatomic, strong) UIColor *baselineColor;      ///< baseline color
+@property (nullable, nonatomic, strong) UIColor *CTFrameBorderColor; ///< CTFrame path border color
+@property (nullable, nonatomic, strong) UIColor *CTFrameFillColor;   ///< CTFrame path fill color
+@property (nullable, nonatomic, strong) UIColor *CTLineBorderColor;  ///< CTLine bounds border color
+@property (nullable, nonatomic, strong) UIColor *CTLineFillColor;    ///< CTLine bounds fill color
+@property (nullable, nonatomic, strong) UIColor *CTLineNumberColor;  ///< CTLine line number color
+@property (nullable, nonatomic, strong) UIColor *CTRunBorderColor;   ///< CTRun bounds border color
+@property (nullable, nonatomic, strong) UIColor *CTRunFillColor;     ///< CTRun bounds fill color
+@property (nullable, nonatomic, strong) UIColor *CTRunNumberColor;   ///< CTRun number color
+@property (nullable, nonatomic, strong) UIColor *CGGlyphBorderColor; ///< CGGlyph bounds border color
+@property (nullable, nonatomic, strong) UIColor *CGGlyphFillColor;   ///< CGGlyph bounds fill color
+
+- (BOOL)needDrawDebug; ///< `YES`: at least one debug color is visible. `NO`: all debug color is invisible/nil.
+- (void)clear; ///< Set all debug color to nil.
+
+/**
+ Add a debug target.
+ 
+ @discussion When `setSharedDebugOption:` is called, all added debug target will 
+ receive `setDebugOption:` in main thread. It maintains an unsafe_unretained
+ reference to this target. The target must to removed before dealloc.
+ 
+ @param target A debug target.
+ */
++ (void)addDebugTarget:(id<YYTextDebugTarget>)target;
+
+/**
+ Remove a debug target which is added by `addDebugTarget:`.
+ 
+ @param target A debug target.
+ */
++ (void)removeDebugTarget:(id<YYTextDebugTarget>)target;
+
+/**
+ Returns the shared debug option.
+ 
+ @return The shared debug option, default is nil.
+ */
++ (nullable YYTextDebugOption *)sharedDebugOption;
+
+/**
+ Set a debug option as shared debug option.
+ This method must be called on main thread.
+ 
+ @discussion When call this method, the new option will set to all debug target
+ which is added by `addDebugTarget:`.
+ 
+ @param option  A new debug option (nil is valid).
+ */
++ (void)setSharedDebugOption:(nullable YYTextDebugOption *)option;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 140 - 0
Pods/YYText/YYText/Component/YYTextDebugOption.m

@@ -0,0 +1,140 @@
+//
+//  YYTextDebugOption.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/8.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextDebugOption.h"
+#import "YYTextWeakProxy.h"
+#import <libkern/OSAtomic.h>
+#import <pthread.h>
+
+static pthread_mutex_t _sharedDebugLock;
+static CFMutableSetRef _sharedDebugTargets = nil;
+static YYTextDebugOption *_sharedDebugOption = nil;
+
+static const void* _sharedDebugSetRetain(CFAllocatorRef allocator, const void *value) {
+    return value;
+}
+
+static void _sharedDebugSetRelease(CFAllocatorRef allocator, const void *value) {
+}
+
+void _sharedDebugSetFunction(const void *value, void *context) {
+    id<YYTextDebugTarget> target = (__bridge id<YYTextDebugTarget>)(value);
+    [target setDebugOption:_sharedDebugOption];
+}
+
+static void _initSharedDebug() {
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        pthread_mutex_init(&_sharedDebugLock, NULL);
+        CFSetCallBacks callbacks = kCFTypeSetCallBacks;
+        callbacks.retain = _sharedDebugSetRetain;
+        callbacks.release = _sharedDebugSetRelease;
+        _sharedDebugTargets = CFSetCreateMutable(CFAllocatorGetDefault(), 0, &callbacks);
+    });
+}
+
+static void _setSharedDebugOption(YYTextDebugOption *option) {
+    _initSharedDebug();
+    pthread_mutex_lock(&_sharedDebugLock);
+    _sharedDebugOption = option.copy;
+    CFSetApplyFunction(_sharedDebugTargets, _sharedDebugSetFunction, NULL);
+    pthread_mutex_unlock(&_sharedDebugLock);
+}
+
+static YYTextDebugOption *_getSharedDebugOption() {
+    _initSharedDebug();
+    pthread_mutex_lock(&_sharedDebugLock);
+    YYTextDebugOption *op = _sharedDebugOption;
+    pthread_mutex_unlock(&_sharedDebugLock);
+    return op;
+}
+
+static void _addDebugTarget(id<YYTextDebugTarget> target) {
+    _initSharedDebug();
+    pthread_mutex_lock(&_sharedDebugLock);
+    CFSetAddValue(_sharedDebugTargets, (__bridge const void *)(target));
+    pthread_mutex_unlock(&_sharedDebugLock);
+}
+
+static void _removeDebugTarget(id<YYTextDebugTarget> target) {
+    _initSharedDebug();
+    pthread_mutex_lock(&_sharedDebugLock);
+    CFSetRemoveValue(_sharedDebugTargets, (__bridge const void *)(target));
+    pthread_mutex_unlock(&_sharedDebugLock);
+}
+
+
+@implementation YYTextDebugOption
+
+- (id)copyWithZone:(NSZone *)zone {
+    YYTextDebugOption *op = [self.class new];
+    op.baselineColor = self.baselineColor;
+    op.CTFrameBorderColor = self.CTFrameBorderColor;
+    op.CTFrameFillColor = self.CTFrameFillColor;
+    op.CTLineBorderColor = self.CTLineBorderColor;
+    op.CTLineFillColor = self.CTLineFillColor;
+    op.CTLineNumberColor = self.CTLineNumberColor;
+    op.CTRunBorderColor = self.CTRunBorderColor;
+    op.CTRunFillColor = self.CTRunFillColor;
+    op.CTRunNumberColor = self.CTRunNumberColor;
+    op.CGGlyphBorderColor = self.CGGlyphBorderColor;
+    op.CGGlyphFillColor = self.CGGlyphFillColor;
+    return op;
+}
+
+- (BOOL)needDrawDebug {
+    if (self.baselineColor ||
+        self.CTFrameBorderColor ||
+        self.CTFrameFillColor ||
+        self.CTLineBorderColor ||
+        self.CTLineFillColor ||
+        self.CTLineNumberColor ||
+        self.CTRunBorderColor ||
+        self.CTRunFillColor ||
+        self.CTRunNumberColor ||
+        self.CGGlyphBorderColor ||
+        self.CGGlyphFillColor) return YES;
+    return NO;
+}
+
+- (void)clear {
+    self.baselineColor = nil;
+    self.CTFrameBorderColor = nil;
+    self.CTFrameFillColor = nil;
+    self.CTLineBorderColor = nil;
+    self.CTLineFillColor = nil;
+    self.CTLineNumberColor = nil;
+    self.CTRunBorderColor = nil;
+    self.CTRunFillColor = nil;
+    self.CTRunNumberColor = nil;
+    self.CGGlyphBorderColor = nil;
+    self.CGGlyphFillColor = nil;
+}
+
++ (void)addDebugTarget:(id<YYTextDebugTarget>)target {
+    if (target) _addDebugTarget(target);
+}
+
++ (void)removeDebugTarget:(id<YYTextDebugTarget>)target {
+    if (target) _removeDebugTarget(target);
+}
+
++ (YYTextDebugOption *)sharedDebugOption {
+    return _getSharedDebugOption();
+}
+
++ (void)setSharedDebugOption:(YYTextDebugOption *)option {
+    NSAssert([NSThread isMainThread], @"This method must be called on the main thread");
+    _setSharedDebugOption(option);
+}
+
+@end
+

+ 52 - 0
Pods/YYText/YYText/Component/YYTextEffectWindow.h

@@ -0,0 +1,52 @@
+//
+//  YYTextEffectWindow.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/2/25.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+#if __has_include(<YYText/YYText.h>)
+#import <YYText/YYTextMagnifier.h>
+#import <YYtext/YYTextSelectionView.h>
+#else
+#import "YYTextMagnifier.h"
+#import "YYTextSelectionView.h"
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A window to display magnifier and extra contents for text view.
+ 
+ @discussion Use `sharedWindow` to get the instance, don't create your own instance.
+ Typically, you should not use this class directly.
+ */
+@interface YYTextEffectWindow : UIWindow
+
+/// Returns the shared instance (returns nil in App Extension).
++ (nullable instancetype)sharedWindow;
+
+/// Show the magnifier in this window with a 'popup' animation. @param mag A magnifier.
+- (void)showMagnifier:(YYTextMagnifier *)mag;
+/// Update the magnifier content and position. @param mag A magnifier.
+- (void)moveMagnifier:(YYTextMagnifier *)mag;
+/// Remove the magnifier from this window with a 'shrink' animation. @param mag A magnifier.
+- (void)hideMagnifier:(YYTextMagnifier *)mag;
+
+
+/// Show the selection dot in this window if the dot is clipped by the selection view.
+/// @param selection A selection view.
+- (void)showSelectionDot:(YYTextSelectionView *)selection;
+/// Remove the selection dot from this window.
+/// @param selection A selection view.
+- (void)hideSelectionDot:(YYTextSelectionView *)selection;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 429 - 0
Pods/YYText/YYText/Component/YYTextEffectWindow.m

@@ -0,0 +1,429 @@
+//
+//  YYTextEffectWindow.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/2/25.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextEffectWindow.h"
+#import "YYTextKeyboardManager.h"
+#import "YYTextUtilities.h"
+#import "UIView+YYText.h"
+
+
+@implementation YYTextEffectWindow
+
++ (instancetype)sharedWindow {
+    static YYTextEffectWindow *one = nil;
+    if (one == nil) {
+        // iOS 9 compatible
+        NSString *mode = [NSRunLoop currentRunLoop].currentMode;
+        if (mode.length == 27 &&
+            [mode hasPrefix:@"UI"] &&
+            [mode hasSuffix:@"InitializationRunLoopMode"]) {
+            return nil;
+        }
+    }
+    
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        if (!YYTextIsAppExtension()) {
+            one = [self new];
+            one.frame = (CGRect){.size = YYTextScreenSize()};
+            one.userInteractionEnabled = NO;
+            one.windowLevel = UIWindowLevelStatusBar + 1;
+            one.hidden = NO;
+            
+            // for iOS 9:
+            one.opaque = NO;
+            one.backgroundColor = [UIColor clearColor];
+            one.layer.backgroundColor = [UIColor clearColor].CGColor;
+        }
+    });
+    return one;
+}
+
+// stop self from becoming the KeyWindow
+- (void)becomeKeyWindow {
+    [[YYTextSharedApplication().delegate window] makeKeyWindow];
+}
+
+- (UIViewController *)rootViewController {
+    for (UIWindow *window in [YYTextSharedApplication() windows]) {
+        if (self == window) continue;
+        if (window.hidden) continue;
+        UIViewController *topViewController = window.rootViewController;
+        if (topViewController) return topViewController;
+    }
+    UIViewController *viewController = [super rootViewController];
+    if (!viewController) {
+        viewController = [UIViewController new];
+        [super setRootViewController:viewController];
+    }
+    return viewController;
+}
+
+// Bring self to front
+- (void)_updateWindowLevel {
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return;
+    
+    UIWindow *top = app.windows.lastObject;
+    UIWindow *key = app.keyWindow;
+    if (key && key.windowLevel > top.windowLevel) top = key;
+    if (top == self) return;
+    self.windowLevel = top.windowLevel + 1;
+}
+
+- (YYTextDirection)_keyboardDirection {
+    CGRect keyboardFrame = [YYTextKeyboardManager defaultManager].keyboardFrame;
+    keyboardFrame = [[YYTextKeyboardManager defaultManager] convertRect:keyboardFrame toView:self];
+    if (CGRectIsNull(keyboardFrame) || CGRectIsEmpty(keyboardFrame)) return YYTextDirectionNone;
+    
+    if (CGRectGetMinY(keyboardFrame) == 0 &&
+        CGRectGetMinX(keyboardFrame) == 0 &&
+        CGRectGetMaxX(keyboardFrame) == CGRectGetWidth(self.frame))
+        return YYTextDirectionTop;
+    
+    if (CGRectGetMaxX(keyboardFrame) == CGRectGetWidth(self.frame) &&
+        CGRectGetMinY(keyboardFrame) == 0 &&
+        CGRectGetMaxY(keyboardFrame) == CGRectGetHeight(self.frame))
+        return YYTextDirectionRight;
+    
+    if (CGRectGetMaxY(keyboardFrame) == CGRectGetHeight(self.frame) &&
+        CGRectGetMinX(keyboardFrame) == 0 &&
+        CGRectGetMaxX(keyboardFrame) == CGRectGetWidth(self.frame))
+        return YYTextDirectionBottom;
+    
+    if (CGRectGetMinX(keyboardFrame) == 0 &&
+        CGRectGetMinY(keyboardFrame) == 0 &&
+        CGRectGetMaxY(keyboardFrame) == CGRectGetHeight(self.frame))
+        return YYTextDirectionLeft;
+    
+    return YYTextDirectionNone;
+}
+
+- (CGPoint)_correctedCaptureCenter:(CGPoint)center{
+    CGRect keyboardFrame = [YYTextKeyboardManager defaultManager].keyboardFrame;
+    keyboardFrame = [[YYTextKeyboardManager defaultManager] convertRect:keyboardFrame toView:self];
+    if (!CGRectIsNull(keyboardFrame) && !CGRectIsEmpty(keyboardFrame)) {
+        YYTextDirection direction = [self _keyboardDirection];
+        switch (direction) {
+            case YYTextDirectionTop: {
+                if (center.y < CGRectGetMaxY(keyboardFrame)) center.y = CGRectGetMaxY(keyboardFrame);
+            } break;
+            case YYTextDirectionRight: {
+                if (center.x > CGRectGetMinX(keyboardFrame)) center.x = CGRectGetMinX(keyboardFrame);
+            } break;
+            case YYTextDirectionBottom: {
+                if (center.y > CGRectGetMinY(keyboardFrame)) center.y = CGRectGetMinY(keyboardFrame);
+            } break;
+            case YYTextDirectionLeft: {
+                if (center.x < CGRectGetMaxX(keyboardFrame)) center.x = CGRectGetMaxX(keyboardFrame);
+            } break;
+            default: break;
+        }
+    }
+    return center;
+}
+
+- (CGPoint)_correctedCenter:(CGPoint)center forMagnifier:(YYTextMagnifier *)mag rotation:(CGFloat)rotation {
+    CGFloat degree = YYTextRadiansToDegrees(rotation);
+    
+    degree /= 45.0;
+    if (degree < 0) degree += (int)(-degree/8.0 + 1) * 8;
+    if (degree > 8) degree -= (int)(degree/8.0) * 8;
+    
+    CGFloat caretExt = 10;
+    if (degree <= 1 || degree >= 7) { //top
+        if (mag.type == YYTextMagnifierTypeCaret) {
+            if (center.y < caretExt)
+                center.y = caretExt;
+        } else if (mag.type == YYTextMagnifierTypeRanged) {
+            if (center.y < mag.bounds.size.height)
+                center.y = mag.bounds.size.height;
+        }
+    } else if (1 < degree && degree < 3) { // right
+        if (mag.type == YYTextMagnifierTypeCaret) {
+            if (center.x > self.bounds.size.width - caretExt)
+                center.x = self.bounds.size.width - caretExt;
+        } else if (mag.type == YYTextMagnifierTypeRanged) {
+            if (center.x > self.bounds.size.width - mag.bounds.size.height)
+                center.x = self.bounds.size.width - mag.bounds.size.height;
+        }
+    } else if (3 <= degree && degree <= 5) { // bottom
+        if (mag.type == YYTextMagnifierTypeCaret) {
+            if (center.y > self.bounds.size.height - caretExt)
+                center.y = self.bounds.size.height - caretExt;
+        } else if (mag.type == YYTextMagnifierTypeRanged) {
+            if (center.y > mag.bounds.size.height)
+                center.y = mag.bounds.size.height;
+        }
+    } else if (5 < degree && degree < 7) { // left
+        if (mag.type == YYTextMagnifierTypeCaret) {
+            if (center.x < caretExt)
+                center.x = caretExt;
+        } else if (mag.type == YYTextMagnifierTypeRanged) {
+            if (center.x < mag.bounds.size.height)
+                center.x = mag.bounds.size.height;
+        }
+    }
+
+    
+    CGRect keyboardFrame = [YYTextKeyboardManager defaultManager].keyboardFrame;
+    keyboardFrame = [[YYTextKeyboardManager defaultManager] convertRect:keyboardFrame toView:self];
+    if (!CGRectIsNull(keyboardFrame) && !CGRectIsEmpty(keyboardFrame)) {
+        YYTextDirection direction = [self _keyboardDirection];
+        switch (direction) {
+            case YYTextDirectionTop: {
+                if (mag.type == YYTextMagnifierTypeCaret) {
+                    if (center.y - mag.bounds.size.height / 2 < CGRectGetMaxY(keyboardFrame))
+                        center.y = CGRectGetMaxY(keyboardFrame) + mag.bounds.size.height / 2;
+                } else if (mag.type == YYTextMagnifierTypeRanged) {
+                    if (center.y < CGRectGetMaxY(keyboardFrame)) center.y = CGRectGetMaxY(keyboardFrame);
+                }
+            } break;
+            case YYTextDirectionRight: {
+                if (mag.type == YYTextMagnifierTypeCaret) {
+                    if (center.x + mag.bounds.size.height / 2 > CGRectGetMinX(keyboardFrame))
+                        center.x = CGRectGetMinX(keyboardFrame) - mag.bounds.size.width / 2;
+                } else if (mag.type == YYTextMagnifierTypeRanged) {
+                    if (center.x > CGRectGetMinX(keyboardFrame)) center.x = CGRectGetMinX(keyboardFrame);
+                }
+            } break;
+            case YYTextDirectionBottom: {
+                if (mag.type == YYTextMagnifierTypeCaret) {
+                    if (center.y + mag.bounds.size.height / 2 > CGRectGetMinY(keyboardFrame))
+                        center.y = CGRectGetMinY(keyboardFrame) - mag.bounds.size.height / 2;
+                } else if (mag.type == YYTextMagnifierTypeRanged) {
+                    if (center.y > CGRectGetMinY(keyboardFrame)) center.y = CGRectGetMinY(keyboardFrame);
+                }
+            } break;
+            case YYTextDirectionLeft: {
+                if (mag.type == YYTextMagnifierTypeCaret) {
+                    if (center.x - mag.bounds.size.height / 2 < CGRectGetMaxX(keyboardFrame))
+                        center.x = CGRectGetMaxX(keyboardFrame) + mag.bounds.size.width / 2;
+                } else if (mag.type == YYTextMagnifierTypeRanged) {
+                    if (center.x < CGRectGetMaxX(keyboardFrame)) center.x = CGRectGetMaxX(keyboardFrame);
+                }
+            } break;
+            default: break;
+        }
+    }
+    
+    return center;
+}
+
+/**
+ Capture screen snapshot and set it to magnifier.
+ @return Magnifier rotation radius.
+ */
+- (CGFloat)_updateMagnifier:(YYTextMagnifier *)mag {
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return 0;
+    
+    UIView *hostView = mag.hostView;
+    UIWindow *hostWindow = [hostView isKindOfClass:[UIWindow class]] ? (id)hostView : hostView.window;
+    if (!hostView || !hostWindow) return 0;
+    CGPoint captureCenter = [self yy_convertPoint:mag.hostCaptureCenter fromViewOrWindow:hostView];
+    captureCenter = [self _correctedCaptureCenter:captureCenter];
+    CGRect captureRect = {.size = mag.snapshotSize};
+    captureRect.origin.x = captureCenter.x - captureRect.size.width / 2;
+    captureRect.origin.y = captureCenter.y - captureRect.size.height / 2;
+    
+    CGAffineTransform trans = YYTextCGAffineTransformGetFromViews(hostView, self);
+    CGFloat rotation = YYTextCGAffineTransformGetRotation(trans);
+    
+    if (mag.captureDisabled) {
+        if (!mag.snapshot || mag.snapshot.size.width > 1) {
+            static UIImage *placeholder;
+            static dispatch_once_t onceToken;
+            dispatch_once(&onceToken, ^{
+                CGRect rect = mag.bounds;
+                rect.origin = CGPointZero;
+                UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
+                CGContextRef context = UIGraphicsGetCurrentContext();
+                [[UIColor colorWithWhite:1 alpha:0.8] set];
+                CGContextFillRect(context, rect);
+                placeholder = UIGraphicsGetImageFromCurrentImageContext();
+                UIGraphicsEndImageContext();
+            });
+            mag.captureFadeAnimation = YES;
+            mag.snapshot = placeholder;
+            mag.captureFadeAnimation = NO;
+        }
+        return rotation;
+    }
+    
+    UIGraphicsBeginImageContextWithOptions(captureRect.size, NO, 0);
+    CGContextRef context = UIGraphicsGetCurrentContext();
+    if (!context) return rotation;
+    
+    CGPoint tp = CGPointMake(captureRect.size.width / 2, captureRect.size.height / 2);
+    tp = CGPointApplyAffineTransform(tp, CGAffineTransformMakeRotation(rotation));
+    CGContextRotateCTM(context, -rotation);
+    CGContextTranslateCTM(context, tp.x - captureCenter.x, tp.y - captureCenter.y);
+    
+    NSMutableArray *windows = app.windows.mutableCopy;
+    UIWindow *keyWindow = app.keyWindow;
+    if (![windows containsObject:keyWindow]) [windows addObject:keyWindow];
+    [windows sortUsingComparator:^NSComparisonResult(UIWindow *w1, UIWindow *w2) {
+        if (w1.windowLevel < w2.windowLevel) return NSOrderedAscending;
+        else if (w1.windowLevel > w2.windowLevel) return NSOrderedDescending;
+        return NSOrderedSame;
+    }];
+    UIScreen *mainScreen = [UIScreen mainScreen];
+    for (UIWindow *window in windows) {
+        if (window.hidden || window.alpha <= 0.01) continue;
+        if (window.screen != mainScreen) continue;
+        if ([window isKindOfClass:self.class]) break; //don't capture window above self
+        CGContextSaveGState(context);
+        CGContextConcatCTM(context, YYTextCGAffineTransformGetFromViews(window, self));
+        [window.layer renderInContext:context]; //render
+        //[window drawViewHierarchyInRect:window.bounds afterScreenUpdates:NO]; //slower when capture whole window
+        CGContextRestoreGState(context);
+    }
+    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
+    UIGraphicsEndImageContext();
+    
+    if (mag.snapshot.size.width == 1) {
+        mag.captureFadeAnimation = YES;
+    }
+    mag.snapshot = image;
+    mag.captureFadeAnimation = NO;
+    return rotation;
+}
+
+- (void)showMagnifier:(YYTextMagnifier *)mag {
+    if (!mag) return;
+    if (mag.superview != self) [self addSubview:mag];
+    [self _updateWindowLevel];
+    CGFloat rotation = [self _updateMagnifier:mag];
+    CGPoint center = [self yy_convertPoint:mag.hostPopoverCenter fromViewOrWindow:mag.hostView];
+    CGAffineTransform trans = CGAffineTransformMakeRotation(rotation);
+    trans = CGAffineTransformScale(trans, 0.3, 0.3);
+    mag.transform = trans;
+    mag.center = center;
+    if (mag.type == YYTextMagnifierTypeRanged) {
+        mag.alpha = 0;
+    }
+    NSTimeInterval time = mag.type == YYTextMagnifierTypeCaret ? 0.08 : 0.1;
+    [UIView animateWithDuration:time delay:0 options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState animations:^{
+        if (mag.type == YYTextMagnifierTypeCaret) {
+            CGPoint newCenter = CGPointMake(0, -mag.fitSize.height / 2);
+            newCenter = CGPointApplyAffineTransform(newCenter, CGAffineTransformMakeRotation(rotation));
+            newCenter.x += center.x;
+            newCenter.y += center.y;
+            mag.center = [self _correctedCenter:newCenter forMagnifier:mag rotation:rotation];
+        } else {
+            mag.center = [self _correctedCenter:center forMagnifier:mag rotation:rotation];
+        }
+        mag.transform = CGAffineTransformMakeRotation(rotation);
+        mag.alpha = 1;
+    } completion:^(BOOL finished) {
+        
+    }];
+}
+
+- (void)moveMagnifier:(YYTextMagnifier *)mag {
+    if (!mag) return;
+    [self _updateWindowLevel];
+    CGFloat rotation = [self _updateMagnifier:mag];
+    CGPoint center = [self yy_convertPoint:mag.hostPopoverCenter fromViewOrWindow:mag.hostView];
+    if (mag.type == YYTextMagnifierTypeCaret) {
+        CGPoint newCenter = CGPointMake(0, -mag.fitSize.height / 2);
+        newCenter = CGPointApplyAffineTransform(newCenter, CGAffineTransformMakeRotation(rotation));
+        newCenter.x += center.x;
+        newCenter.y += center.y;
+        mag.center = [self _correctedCenter:newCenter forMagnifier:mag rotation:rotation];
+    } else {
+        mag.center = [self _correctedCenter:center forMagnifier:mag rotation:rotation];
+    }
+    mag.transform = CGAffineTransformMakeRotation(rotation);
+}
+
+- (void)hideMagnifier:(YYTextMagnifier *)mag {
+    if (!mag) return;
+    if (mag.superview != self) return;
+    CGFloat rotation = [self _updateMagnifier:mag];
+    CGPoint center = [self yy_convertPoint:mag.hostPopoverCenter fromViewOrWindow:mag.hostView];
+    NSTimeInterval time = mag.type == YYTextMagnifierTypeCaret ? 0.20 : 0.15;
+    [UIView animateWithDuration:time delay:0 options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState animations:^{
+        
+        CGAffineTransform trans = CGAffineTransformMakeRotation(rotation);
+        trans = CGAffineTransformScale(trans, 0.01, 0.01);
+        mag.transform = trans;
+        
+        if (mag.type == YYTextMagnifierTypeCaret) {
+            CGPoint newCenter = CGPointMake(0, -mag.fitSize.height / 2);
+            newCenter = CGPointApplyAffineTransform(newCenter, CGAffineTransformMakeRotation(rotation));
+            newCenter.x += center.x;
+            newCenter.y += center.y;
+            mag.center = [self _correctedCenter:newCenter forMagnifier:mag rotation:rotation];
+        } else {
+            mag.center = [self _correctedCenter:center forMagnifier:mag rotation:rotation];
+            mag.alpha = 0;
+        }
+        
+    } completion:^(BOOL finished) {
+        if (finished) {
+            [mag removeFromSuperview];
+            mag.transform = CGAffineTransformIdentity;
+            mag.alpha = 1;
+        }
+    }];
+}
+
+- (void)_updateSelectionGrabberDot:(YYSelectionGrabberDot *)dot selection:(YYTextSelectionView *)selection{
+    dot.mirror.hidden = YES;
+    if (selection.hostView.clipsToBounds == YES && dot.yy_visibleAlpha > 0.1) {
+        CGRect dotRect = [dot yy_convertRect:dot.bounds toViewOrWindow:self];
+        BOOL dotInKeyboard = NO;
+        
+        CGRect keyboardFrame = [YYTextKeyboardManager defaultManager].keyboardFrame;
+        keyboardFrame = [[YYTextKeyboardManager defaultManager] convertRect:keyboardFrame toView:self];
+        if (!CGRectIsNull(keyboardFrame) && !CGRectIsEmpty(keyboardFrame)) {
+            CGRect inter = CGRectIntersection(dotRect, keyboardFrame);
+            if (!CGRectIsNull(inter) && (inter.size.width > 1 || inter.size.height > 1)) {
+                dotInKeyboard = YES;
+            }
+        }
+        if (!dotInKeyboard) {
+            CGRect hostRect = [selection.hostView convertRect:selection.hostView.bounds toView:self];
+            CGRect intersection = CGRectIntersection(dotRect, hostRect);
+            if (YYTextCGRectGetArea(intersection) < YYTextCGRectGetArea(dotRect)) {
+                CGFloat dist = YYTextCGPointGetDistanceToRect(YYTextCGRectGetCenter(dotRect), hostRect);
+                if (dist < CGRectGetWidth(dot.frame) * 0.55) {
+                    dot.mirror.hidden = NO;
+                }
+            }
+        }
+    }
+    CGPoint center = [dot yy_convertPoint:CGPointMake(CGRectGetWidth(dot.frame) / 2, CGRectGetHeight(dot.frame) / 2) toViewOrWindow:self];
+    if (isnan(center.x) || isnan(center.y) || isinf(center.x) || isinf(center.y)) {
+        dot.mirror.hidden = YES;
+    } else {
+        dot.mirror.center = center;
+    }
+}
+
+- (void)showSelectionDot:(YYTextSelectionView *)selection {
+    if (!selection) return;
+    [self _updateWindowLevel];
+    [self insertSubview:selection.startGrabber.dot.mirror atIndex:0];
+    [self insertSubview:selection.endGrabber.dot.mirror atIndex:0];
+    [self _updateSelectionGrabberDot:selection.startGrabber.dot selection:selection];
+    [self _updateSelectionGrabberDot:selection.endGrabber.dot selection:selection];
+}
+
+- (void)hideSelectionDot:(YYTextSelectionView *)selection {
+    if (!selection) return;
+    [selection.startGrabber.dot.mirror removeFromSuperview];
+    [selection.endGrabber.dot.mirror removeFromSuperview];
+}
+
+@end

+ 87 - 0
Pods/YYText/YYText/Component/YYTextInput.h

@@ -0,0 +1,87 @@
+//
+//  YYTextInput.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/17.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ Text position affinity. For example, the offset appears after the last
+ character on a line is backward affinity, before the first character on
+ the following line is forward affinity.
+ */
+typedef NS_ENUM(NSInteger, YYTextAffinity) {
+    YYTextAffinityForward  = 0, ///< offset appears before the character
+    YYTextAffinityBackward = 1, ///< offset appears after the character
+};
+
+
+/**
+ A YYTextPosition object represents a position in a text container; in other words, 
+ it is an index into the backing string in a text-displaying view.
+ 
+ YYTextPosition has the same API as Apple's implementation in UITextView/UITextField,
+ so you can alse use it to interact with UITextView/UITextField.
+ */
+@interface YYTextPosition : UITextPosition <NSCopying>
+
+@property (nonatomic, readonly) NSInteger offset;
+@property (nonatomic, readonly) YYTextAffinity affinity;
+
++ (instancetype)positionWithOffset:(NSInteger)offset;
++ (instancetype)positionWithOffset:(NSInteger)offset affinity:(YYTextAffinity) affinity;
+
+- (NSComparisonResult)compare:(id)otherPosition;
+
+@end
+
+
+/**
+ A YYTextRange object represents a range of characters in a text container; in other words, 
+ it identifies a starting index and an ending index in string backing a text-displaying view.
+ 
+ YYTextRange has the same API as Apple's implementation in UITextView/UITextField,
+ so you can alse use it to interact with UITextView/UITextField.
+ */
+@interface YYTextRange : UITextRange <NSCopying>
+
+@property (nonatomic, readonly) YYTextPosition *start;
+@property (nonatomic, readonly) YYTextPosition *end;
+@property (nonatomic, readonly, getter=isEmpty) BOOL empty;
+
++ (instancetype)rangeWithRange:(NSRange)range;
++ (instancetype)rangeWithRange:(NSRange)range affinity:(YYTextAffinity) affinity;
++ (instancetype)rangeWithStart:(YYTextPosition *)start end:(YYTextPosition *)end;
++ (instancetype)defaultRange; ///< <{0,0} Forward>
+
+- (NSRange)asRange;
+
+@end
+
+
+/**
+ A YYTextSelectionRect object encapsulates information about a selected range of 
+ text in a text-displaying view.
+ 
+ YYTextSelectionRect has the same API as Apple's implementation in UITextView/UITextField,
+ so you can alse use it to interact with UITextView/UITextField.
+ */
+@interface YYTextSelectionRect : UITextSelectionRect <NSCopying>
+
+@property (nonatomic, readwrite) CGRect rect;
+@property (nonatomic, readwrite) UITextWritingDirection writingDirection;
+@property (nonatomic, readwrite) BOOL containsStart;
+@property (nonatomic, readwrite) BOOL containsEnd;
+@property (nonatomic, readwrite) BOOL isVertical;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 152 - 0
Pods/YYText/YYText/Component/YYTextInput.m

@@ -0,0 +1,152 @@
+//
+//  YYTextInput.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/17.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextInput.h"
+#import "YYTextUtilities.h"
+
+
+@implementation YYTextPosition
+
++ (instancetype)positionWithOffset:(NSInteger)offset {
+    return [self positionWithOffset:offset affinity:YYTextAffinityForward];
+}
+
++ (instancetype)positionWithOffset:(NSInteger)offset affinity:(YYTextAffinity)affinity {
+    YYTextPosition *p = [self new];
+    p->_offset = offset;
+    p->_affinity = affinity;
+    return p;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+    return [self.class positionWithOffset:_offset affinity:_affinity];
+}
+
+- (NSString *)description {
+    return [NSString stringWithFormat:@"<%@: %p> (%@%@)", self.class, self, @(_offset), _affinity == YYTextAffinityForward ? @"F":@"B"];
+}
+
+- (NSUInteger)hash {
+    return _offset * 2 + (_affinity == YYTextAffinityForward ? 1 : 0);
+}
+
+- (BOOL)isEqual:(YYTextPosition *)object {
+    if (!object) return NO;
+    return _offset == object.offset && _affinity == object.affinity;
+}
+
+- (NSComparisonResult)compare:(YYTextPosition *)otherPosition {
+    if (!otherPosition) return NSOrderedAscending;
+    if (_offset < otherPosition.offset) return NSOrderedAscending;
+    if (_offset > otherPosition.offset) return NSOrderedDescending;
+    if (_affinity == YYTextAffinityBackward && otherPosition.affinity == YYTextAffinityForward) return NSOrderedAscending;
+    if (_affinity == YYTextAffinityForward && otherPosition.affinity == YYTextAffinityBackward) return NSOrderedDescending;
+    return NSOrderedSame;
+}
+
+@end
+
+
+
+@implementation YYTextRange {
+    YYTextPosition *_start;
+    YYTextPosition *_end;
+}
+
+- (instancetype)init {
+    self = [super init];
+    if (!self) return nil;
+    _start = [YYTextPosition positionWithOffset:0];
+    _end = [YYTextPosition positionWithOffset:0];
+    return self;
+}
+
+- (YYTextPosition *)start {
+    return _start;
+}
+
+- (YYTextPosition *)end {
+    return _end;
+}
+
+- (BOOL)isEmpty {
+    return _start.offset == _end.offset;
+}
+
+- (NSRange)asRange {
+    return NSMakeRange(_start.offset, _end.offset - _start.offset);
+}
+
++ (instancetype)rangeWithRange:(NSRange)range {
+    return [self rangeWithRange:range affinity:YYTextAffinityForward];
+}
+
++ (instancetype)rangeWithRange:(NSRange)range affinity:(YYTextAffinity)affinity {
+    YYTextPosition *start = [YYTextPosition positionWithOffset:range.location affinity:affinity];
+    YYTextPosition *end = [YYTextPosition positionWithOffset:range.location + range.length affinity:affinity];
+    return [self rangeWithStart:start end:end];
+}
+
++ (instancetype)rangeWithStart:(YYTextPosition *)start end:(YYTextPosition *)end {
+    if (!start || !end) return nil;
+    if ([start compare:end] == NSOrderedDescending) {
+        YYTEXT_SWAP(start, end);
+    }
+    YYTextRange *range = [YYTextRange new];
+    range->_start = start;
+    range->_end = end;
+    return range;
+}
+
++ (instancetype)defaultRange {
+    return [self new];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+    return [self.class rangeWithStart:_start end:_end];
+}
+
+- (NSString *)description {
+    return [NSString stringWithFormat:@"<%@: %p> (%@, %@)%@", self.class, self, @(_start.offset), @(_end.offset - _start.offset), _end.affinity == YYTextAffinityForward ? @"F":@"B"];
+}
+
+- (NSUInteger)hash {
+    return (sizeof(NSUInteger) == 8 ? OSSwapInt64(_start.hash) : OSSwapInt32(_start.hash)) + _end.hash;
+}
+
+- (BOOL)isEqual:(YYTextRange *)object {
+    if (!object) return NO;
+    return [_start isEqual:object.start] && [_end isEqual:object.end];
+}
+
+@end
+
+
+
+@implementation YYTextSelectionRect
+
+@synthesize rect = _rect;
+@synthesize writingDirection = _writingDirection;
+@synthesize containsStart = _containsStart;
+@synthesize containsEnd = _containsEnd;
+@synthesize isVertical = _isVertical;
+
+- (id)copyWithZone:(NSZone *)zone {
+    YYTextSelectionRect *one = [self.class new];
+    one.rect = _rect;
+    one.writingDirection = _writingDirection;
+    one.containsStart = _containsStart;
+    one.containsEnd = _containsEnd;
+    one.isVertical = _isVertical;
+    return one;
+}
+
+@end

+ 98 - 0
Pods/YYText/YYText/Component/YYTextKeyboardManager.h

@@ -0,0 +1,98 @@
+//
+//  YYTextKeyboardManager.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/6/3.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ System keyboard transition information.
+ Use -[YYTextKeyboardManager convertRect:toView:] to convert frame to specified view.
+ */
+typedef struct {
+    BOOL fromVisible; ///< Keyboard visible before transition.
+    BOOL toVisible;   ///< Keyboard visible after transition.
+    CGRect fromFrame; ///< Keyboard frame before transition.
+    CGRect toFrame;   ///< Keyboard frame after transition.
+    NSTimeInterval animationDuration;       ///< Keyboard transition animation duration.
+    UIViewAnimationCurve animationCurve;    ///< Keyboard transition animation curve.
+    UIViewAnimationOptions animationOption; ///< Keybaord transition animation option.
+} YYTextKeyboardTransition;
+
+
+/**
+ The YYTextKeyboardObserver protocol defines the method you can use
+ to receive system keyboard change information.
+ */
+@protocol YYTextKeyboardObserver <NSObject>
+@optional
+- (void)keyboardChangedWithTransition:(YYTextKeyboardTransition)transition;
+@end
+
+
+/**
+ A YYTextKeyboardManager object lets you get the system keyboard information,
+ and track the keyboard visible/frame/transition.
+ 
+ @discussion You should access this class in main thread.
+ Compatible: iPhone/iPad with iOS6/7/8/9.
+ */
+@interface YYTextKeyboardManager : NSObject
+
+- (instancetype)init UNAVAILABLE_ATTRIBUTE;
++ (instancetype)new UNAVAILABLE_ATTRIBUTE;
+
+/// Get the default manager (returns nil in App Extension).
++ (nullable instancetype)defaultManager;
+
+/// Get the keyboard window. nil if there's no keyboard window.
+@property (nullable, nonatomic, readonly) UIWindow *keyboardWindow;
+
+/// Get the keyboard view. nil if there's no keyboard view.
+@property (nullable, nonatomic, readonly) UIView *keyboardView;
+
+/// Whether the keyboard is visible.
+@property (nonatomic, readonly, getter=isKeyboardVisible) BOOL keyboardVisible;
+
+/// Get the keyboard frame. CGRectNull if there's no keyboard view.
+/// Use convertRect:toView: to convert frame to specified view.
+@property (nonatomic, readonly) CGRect keyboardFrame;
+
+
+/**
+ Add an observer to manager to get keyboard change information.
+ This method makes a weak reference to the observer.
+ 
+ @param observer An observer. 
+ This method will do nothing if the observer is nil, or already added.
+ */
+- (void)addObserver:(id<YYTextKeyboardObserver>)observer;
+
+/**
+ Remove an observer from manager.
+ 
+ @param observer An observer.
+ This method will do nothing if the observer is nil, or not in manager.
+ */
+- (void)removeObserver:(id<YYTextKeyboardObserver>)observer;
+
+/**
+ Convert rect to specified view or window.
+ 
+ @param rect The frame rect.
+ @param view A specified view or window (pass nil to convert for main window).
+ @return The converted rect in specifeid view.
+ */
+- (CGRect)convertRect:(CGRect)rect toView:(nullable UIView *)view;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 521 - 0
Pods/YYText/YYText/Component/YYTextKeyboardManager.m

@@ -0,0 +1,521 @@
+//
+//  YYTextKeyboardManager.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/6/3.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextKeyboardManager.h"
+#import "YYTextUtilities.h"
+#import <objc/runtime.h>
+
+
+static int _YYTextKeyboardViewFrameObserverKey;
+
+/// Observer for view's frame/bounds/center/transform
+@interface _YYTextKeyboardViewFrameObserver : NSObject
+@property (nonatomic, copy) void (^notifyBlock)(UIView *keyboard);
+- (void)addToKeyboardView:(UIView *)keyboardView;
++ (instancetype)observerForView:(UIView *)keyboardView;
+@end
+
+
+@implementation _YYTextKeyboardViewFrameObserver {
+    __unsafe_unretained UIView *_keyboardView;
+}
+- (void)addToKeyboardView:(UIView *)keyboardView {
+    if (_keyboardView == keyboardView) return;
+    if (_keyboardView) {
+        [self removeFrameObserver];
+        objc_setAssociatedObject(_keyboardView, &_YYTextKeyboardViewFrameObserverKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+    }
+    _keyboardView = keyboardView;
+    if (keyboardView) {
+        [self addFrameObserver];
+    }
+    objc_setAssociatedObject(keyboardView, &_YYTextKeyboardViewFrameObserverKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+- (void)removeFrameObserver {
+    [_keyboardView removeObserver:self forKeyPath:@"frame"];
+    [_keyboardView removeObserver:self forKeyPath:@"center"];
+    [_keyboardView removeObserver:self forKeyPath:@"bounds"];
+    [_keyboardView removeObserver:self forKeyPath:@"transform"];
+    _keyboardView = nil;
+}
+
+- (void)addFrameObserver {
+    if (!_keyboardView) return;
+    [_keyboardView addObserver:self forKeyPath:@"frame" options:kNilOptions context:NULL];
+    [_keyboardView addObserver:self forKeyPath:@"center" options:kNilOptions context:NULL];
+    [_keyboardView addObserver:self forKeyPath:@"bounds" options:kNilOptions context:NULL];
+    [_keyboardView addObserver:self forKeyPath:@"transform" options:kNilOptions context:NULL];
+}
+
+- (void)dealloc {
+    [self removeFrameObserver];
+}
+
++ (instancetype)observerForView:(UIView *)keyboardView {
+    if (!keyboardView) return nil;
+    return objc_getAssociatedObject(keyboardView, &_YYTextKeyboardViewFrameObserverKey);
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+    
+    BOOL isPrior = [[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue];
+    if (isPrior) return;
+    
+    NSKeyValueChange changeKind = [[change objectForKey:NSKeyValueChangeKindKey] integerValue];
+    if (changeKind != NSKeyValueChangeSetting) return;
+    
+    id newVal = [change objectForKey:NSKeyValueChangeNewKey];
+    if (newVal == [NSNull null]) newVal = nil;
+    
+    if (_notifyBlock) {
+        _notifyBlock(_keyboardView);
+    }
+}
+
+@end
+
+
+
+@implementation YYTextKeyboardManager {
+    NSHashTable *_observers;
+    
+    CGRect _fromFrame;
+    BOOL _fromVisible;
+    UIInterfaceOrientation _fromOrientation;
+    
+    CGRect _notificationFromFrame;
+    CGRect _notificationToFrame;
+    NSTimeInterval _notificationDuration;
+    UIViewAnimationCurve _notificationCurve;
+    BOOL _hasNotification;
+    
+    CGRect _observedToFrame;
+    BOOL _hasObservedChange;
+    
+    BOOL _lastIsNotification;
+}
+
+- (instancetype)init {
+    @throw [NSException exceptionWithName:@"YYTextKeyboardManager init error" reason:@"Use 'defaultManager' to get instance." userInfo:nil];
+    return [super init];
+}
+
+- (instancetype)_init {
+    self = [super init];
+    _observers = [[NSHashTable alloc] initWithOptions:NSPointerFunctionsWeakMemory|NSPointerFunctionsObjectPointerPersonality capacity:0];
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(_keyboardFrameWillChangeNotification:)
+                                                 name:UIKeyboardWillChangeFrameNotification
+                                               object:nil];
+    // for iPad (iOS 9)
+    if ([UIDevice currentDevice].systemVersion.floatValue >= 9) {
+        [[NSNotificationCenter defaultCenter] addObserver:self
+                                                 selector:@selector(_keyboardFrameDidChangeNotification:)
+                                                     name:UIKeyboardDidChangeFrameNotification
+                                                   object:nil];
+    }
+    return self;
+}
+
+- (void)_initFrameObserver {
+    UIView *keyboardView = self.keyboardView;
+    if (!keyboardView) return;
+    __weak typeof(self) _self = self;
+    _YYTextKeyboardViewFrameObserver *observer = [_YYTextKeyboardViewFrameObserver observerForView:keyboardView];
+    if (!observer) {
+        observer = [_YYTextKeyboardViewFrameObserver new];
+        observer.notifyBlock = ^(UIView *keyboard) {
+            [_self _keyboardFrameChanged:keyboard];
+        };
+        [observer addToKeyboardView:keyboardView];
+    }
+}
+
+- (void)dealloc {
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
++ (instancetype)defaultManager {
+    static YYTextKeyboardManager *mgr = nil;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        if (!YYTextIsAppExtension()) {
+            mgr = [[self alloc] _init];
+        }
+    });
+    return mgr;
+}
+
++ (void)load {
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+        [self defaultManager];
+    });
+}
+
+- (void)addObserver:(id<YYTextKeyboardObserver>)observer {
+    if (!observer) return;
+    [_observers addObject:observer];
+}
+
+- (void)removeObserver:(id<YYTextKeyboardObserver>)observer {
+    if (!observer) return;
+    [_observers removeObject:observer];
+}
+
+- (UIWindow *)keyboardWindow {
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return nil;
+    
+    UIWindow *window = nil;
+    for (window in app.windows) {
+        if ([self _getKeyboardViewFromWindow:window]) return window;
+    }
+    window = app.keyWindow;
+    if ([self _getKeyboardViewFromWindow:window]) return window;
+    
+    NSMutableArray *kbWindows = nil;
+    for (window in app.windows) {
+        NSString *windowName = NSStringFromClass(window.class);
+        if ([self _systemVersion] < 9) {
+            // UITextEffectsWindow
+            if (windowName.length == 19 &&
+                [windowName hasPrefix:@"UI"] &&
+                [windowName hasSuffix:@"TextEffectsWindow"]) {
+                if (!kbWindows) kbWindows = [NSMutableArray new];
+                [kbWindows addObject:window];
+            }
+        } else {
+            // UIRemoteKeyboardWindow
+            if (windowName.length == 22 &&
+                [windowName hasPrefix:@"UI"] &&
+                [windowName hasSuffix:@"RemoteKeyboardWindow"]) {
+                if (!kbWindows) kbWindows = [NSMutableArray new];
+                [kbWindows addObject:window];
+            }
+        }
+    }
+    
+    if (kbWindows.count == 1) {
+        return kbWindows.firstObject;
+    }
+    return nil;
+}
+
+- (UIView *)keyboardView {
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return nil;
+    
+    UIWindow *window = nil;
+    UIView *view = nil;
+    for (window in app.windows) {
+        view = [self _getKeyboardViewFromWindow:window];
+        if (view) return view;
+    }
+    window = app.keyWindow;
+    view = [self _getKeyboardViewFromWindow:window];
+    if (view) return view;
+    return nil;
+}
+
+- (BOOL)isKeyboardVisible {
+    UIWindow *window = self.keyboardWindow;
+    if (!window) return NO;
+    UIView *view = self.keyboardView;
+    if (!view) return NO;
+    CGRect rect = CGRectIntersection(window.bounds, view.frame);
+    if (CGRectIsNull(rect)) return NO;
+    if (CGRectIsInfinite(rect)) return NO;
+    return rect.size.width > 0 && rect.size.height > 0;
+}
+
+- (CGRect)keyboardFrame {
+    UIView *keyboard = [self keyboardView];
+    if (!keyboard) return CGRectNull;
+    
+    CGRect frame = CGRectNull;
+    UIWindow *window = keyboard.window;
+    if (window) {
+        frame = [window convertRect:keyboard.frame toWindow:nil];
+    } else {
+        frame = keyboard.frame;
+    }
+    return frame;
+}
+
+#pragma mark - private
+
+- (double)_systemVersion {
+    static double v;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        v = [UIDevice currentDevice].systemVersion.doubleValue;
+    });
+    return v;
+}
+
+- (UIView *)_getKeyboardViewFromWindow:(UIWindow *)window {
+    /*
+     iOS 6/7:
+     UITextEffectsWindow
+        UIPeripheralHostView << keyboard
+     
+     iOS 8:
+     UITextEffectsWindow
+        UIInputSetContainerView
+            UIInputSetHostView << keyboard
+     
+     iOS 9:
+     UIRemoteKeyboardWindow
+        UIInputSetContainerView
+            UIInputSetHostView << keyboard
+     */
+    if (!window) return nil;
+    
+    // Get the window
+    NSString *windowName = NSStringFromClass(window.class);
+    if ([self _systemVersion] < 9) {
+        // UITextEffectsWindow
+        if (windowName.length != 19) return nil;
+        if (![windowName hasPrefix:@"UI"]) return nil;
+        if (![windowName hasSuffix:@"TextEffectsWindow"]) return nil;
+    } else {
+        // UIRemoteKeyboardWindow
+        if (windowName.length != 22) return nil;
+        if (![windowName hasPrefix:@"UI"]) return nil;
+        if (![windowName hasSuffix:@"RemoteKeyboardWindow"]) return nil;
+    }
+    
+    // Get the view
+    if ([self _systemVersion] < 8) {
+        // UIPeripheralHostView
+        for (UIView *view in window.subviews) {
+            NSString *viewName = NSStringFromClass(view.class);
+            if (viewName.length != 20) continue;
+            if (![viewName hasPrefix:@"UI"]) continue;
+            if (![viewName hasSuffix:@"PeripheralHostView"]) continue;
+            return view;
+        }
+    } else {
+        // UIInputSetContainerView
+        for (UIView *view in window.subviews) {
+            NSString *viewName = NSStringFromClass(view.class);
+            if (viewName.length != 23) continue;
+            if (![viewName hasPrefix:@"UI"]) continue;
+            if (![viewName hasSuffix:@"InputSetContainerView"]) continue;
+            // UIInputSetHostView
+            for (UIView *subView in view.subviews) {
+                NSString *subViewName = NSStringFromClass(subView.class);
+                if (subViewName.length != 18) continue;
+                if (![subViewName hasPrefix:@"UI"]) continue;
+                if (![subViewName hasSuffix:@"InputSetHostView"]) continue;
+                return subView;
+            }
+        }
+    }
+    
+    return nil;
+}
+
+- (void)_keyboardFrameWillChangeNotification:(NSNotification *)notif {
+    if (![notif.name isEqualToString:UIKeyboardWillChangeFrameNotification]) return;
+    NSDictionary *info = notif.userInfo;
+    if (!info) return;
+    
+    [self _initFrameObserver];
+    
+    NSValue *beforeValue = info[UIKeyboardFrameBeginUserInfoKey];
+    NSValue *afterValue = info[UIKeyboardFrameEndUserInfoKey];
+    NSNumber *curveNumber = info[UIKeyboardAnimationCurveUserInfoKey];
+    NSNumber *durationNumber = info[UIKeyboardAnimationDurationUserInfoKey];
+    
+    CGRect before = beforeValue.CGRectValue;
+    CGRect after = afterValue.CGRectValue;
+    UIViewAnimationCurve curve = curveNumber.integerValue;
+    NSTimeInterval duration = durationNumber.doubleValue;
+    
+    // ignore zero end frame
+    if (after.size.width <= 0 && after.size.height <= 0) return;
+    
+    _notificationFromFrame = before;
+    _notificationToFrame = after;
+    _notificationCurve = curve;
+    _notificationDuration = duration;
+    _hasNotification = YES;
+    _lastIsNotification = YES;
+    
+    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_notifyAllObservers) object:nil];
+    if (duration == 0) {
+        [self performSelector:@selector(_notifyAllObservers) withObject:nil afterDelay:0 inModes:@[NSRunLoopCommonModes]];
+    } else {
+        [self _notifyAllObservers];
+    }
+}
+
+- (void)_keyboardFrameDidChangeNotification:(NSNotification *)notif {
+    if (![notif.name isEqualToString:UIKeyboardDidChangeFrameNotification]) return;
+    NSDictionary *info = notif.userInfo;
+    if (!info) return;
+    
+    [self _initFrameObserver];
+    
+    NSValue *afterValue = info[UIKeyboardFrameEndUserInfoKey];
+    CGRect after = afterValue.CGRectValue;
+    
+    // ignore zero end frame
+    if (after.size.width <= 0 && after.size.height <= 0) return;
+    
+    _notificationToFrame = after;
+    _notificationCurve = UIViewAnimationCurveEaseInOut;
+    _notificationDuration = 0;
+    _hasNotification = YES;
+    _lastIsNotification = YES;
+    
+    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_notifyAllObservers) object:nil];
+    [self performSelector:@selector(_notifyAllObservers) withObject:nil afterDelay:0 inModes:@[NSRunLoopCommonModes]];
+}
+
+- (void)_keyboardFrameChanged:(UIView *)keyboard {
+    if (keyboard != self.keyboardView) return;
+    
+    UIWindow *window = keyboard.window;
+    if (window) {
+        _observedToFrame = [window convertRect:keyboard.frame toWindow:nil];
+    } else {
+        _observedToFrame = keyboard.frame;
+    }
+    _hasObservedChange = YES;
+    _lastIsNotification = NO;
+    
+    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_notifyAllObservers) object:nil];
+    [self performSelector:@selector(_notifyAllObservers) withObject:nil afterDelay:0 inModes:@[NSRunLoopCommonModes]];
+}
+
+- (void)_notifyAllObservers {
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return;
+    
+    UIView *keyboard = self.keyboardView;
+    UIWindow *window = keyboard.window;
+    if (!window) {
+        window = app.keyWindow;
+    }
+    if (!window) {
+        window = app.windows.firstObject;
+    }
+    
+    YYTextKeyboardTransition trans = {0};
+    
+    // from
+    if (_fromFrame.size.width == 0 && _fromFrame.size.height == 0) { // first notify
+        _fromFrame.size.width = window.bounds.size.width;
+        _fromFrame.size.height = trans.toFrame.size.height;
+        _fromFrame.origin.x = trans.toFrame.origin.x;
+        _fromFrame.origin.y = window.bounds.size.height;
+    }
+    trans.fromFrame = _fromFrame;
+    trans.fromVisible = _fromVisible;
+    
+    // to
+    if (_lastIsNotification || (_hasObservedChange && CGRectEqualToRect(_observedToFrame, _notificationToFrame))) {
+        trans.toFrame = _notificationToFrame;
+        trans.animationDuration = _notificationDuration;
+        trans.animationCurve = _notificationCurve;
+        trans.animationOption = _notificationCurve << 16;
+        
+        // Fix iPad(iOS7) keyboard frame error after rorate device when the keyboard is not docked to bottom.
+        if (((int)[self _systemVersion]) == 7) {
+            UIInterfaceOrientation ori = app.statusBarOrientation;
+            if (_fromOrientation != UIInterfaceOrientationUnknown && _fromOrientation != ori) {
+                switch (ori) {
+                    case UIInterfaceOrientationPortrait: {
+                        if (CGRectGetMaxY(trans.toFrame) != window.frame.size.height) {
+                            trans.toFrame.origin.y -= trans.toFrame.size.height;
+                        }
+                    } break;
+                    case UIInterfaceOrientationPortraitUpsideDown: {
+                        if (CGRectGetMinY(trans.toFrame) != 0) {
+                            trans.toFrame.origin.y += trans.toFrame.size.height;
+                        }
+                    } break;
+                    case UIInterfaceOrientationLandscapeLeft: {
+                        if (CGRectGetMaxX(trans.toFrame) != window.frame.size.width) {
+                            trans.toFrame.origin.x -= trans.toFrame.size.width;
+                        }
+                    } break;
+                    case UIInterfaceOrientationLandscapeRight: {
+                        if (CGRectGetMinX(trans.toFrame) != 0) {
+                            trans.toFrame.origin.x += trans.toFrame.size.width;
+                        }
+                    } break;
+                    default: break;
+                }
+            }
+        }
+    } else {
+        trans.toFrame = _observedToFrame;
+    }
+    
+    if (window && trans.toFrame.size.width > 0 && trans.toFrame.size.height > 0) {
+        CGRect rect = CGRectIntersection(window.bounds, trans.toFrame);
+        if (!CGRectIsNull(rect) && !CGRectIsEmpty(rect)) {
+            trans.toVisible = YES;
+        }
+    }
+    
+    if (!CGRectEqualToRect(trans.toFrame, _fromFrame)) {
+        for (id<YYTextKeyboardObserver> observer in _observers.copy) {
+            if ([observer respondsToSelector:@selector(keyboardChangedWithTransition:)]) {
+                [observer keyboardChangedWithTransition:trans];
+            }
+        }
+    }
+    
+    _hasNotification = NO;
+    _hasObservedChange = NO;
+    _fromFrame = trans.toFrame;
+    _fromVisible = trans.toVisible;
+    _fromOrientation = app.statusBarOrientation;
+}
+
+- (CGRect)convertRect:(CGRect)rect toView:(UIView *)view {
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return CGRectZero;
+    
+    if (CGRectIsNull(rect)) return rect;
+    if (CGRectIsInfinite(rect)) return rect;
+    
+    UIWindow *mainWindow = app.keyWindow;
+    if (!mainWindow) mainWindow = app.windows.firstObject;
+    if (!mainWindow) { // no window ?!
+        if (view) {
+            [view convertRect:rect fromView:nil];
+        } else {
+            return rect;
+        }
+    }
+    
+    rect = [mainWindow convertRect:rect fromWindow:nil];
+    if (!view) return [mainWindow convertRect:rect toWindow:nil];
+    if (view == mainWindow) return rect;
+    
+    UIWindow *toWindow = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window;
+    if (!mainWindow || !toWindow) return [mainWindow convertRect:rect toView:view];
+    if (mainWindow == toWindow) return [mainWindow convertRect:rect toView:view];
+    
+    // in different window
+    rect = [mainWindow convertRect:rect toView:mainWindow];
+    rect = [toWindow convertRect:rect fromWindow:mainWindow];
+    rect = [view convertRect:rect fromView:toWindow];
+    return rect;
+}
+
+@end

+ 0 - 0
Pods/YYText/YYText/Component/YYTextLayout.h


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików