├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── .travis.yml
├── Example
├── JFPopup.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── JFPopup-Example.xcscheme
├── JFPopup.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── JFPopup
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── LaunchScreen.xib
│ │ └── Main.storyboard
│ ├── DrawerView.swift
│ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ └── face.imageset
│ │ │ ├── Contents.json
│ │ │ └── face@2x.png
│ ├── Info.plist
│ ├── JFPopup_Example-Bridging-Header.h
│ ├── OCViewController.h
│ ├── OCViewController.m
│ ├── PopupInViewController.swift
│ ├── PresentVCModeViewController.swift
│ ├── TestCustomViewController.swift
│ ├── UIViewController+JFPopupObjc.swift
│ └── ViewController.swift
├── Podfile
├── Podfile.lock
├── Pods
│ ├── JRBaseKit
│ │ ├── LICENSE
│ │ └── Sources
│ │ │ ├── JF.swift
│ │ │ ├── Size+Extenison.swift
│ │ │ ├── UIColor+Extension.swift
│ │ │ ├── UIImage+JFColor.swift
│ │ │ └── UIView+JFRect.swift
│ ├── Local Podspecs
│ │ └── JFPopup.podspec.json
│ ├── Manifest.lock
│ ├── Pods.xcodeproj
│ │ └── project.pbxproj
│ └── Target Support Files
│ │ ├── JFPopup
│ │ ├── JFPopup-Info.plist
│ │ ├── JFPopup-dummy.m
│ │ ├── JFPopup-prefix.pch
│ │ ├── JFPopup-umbrella.h
│ │ ├── JFPopup.debug.xcconfig
│ │ ├── JFPopup.modulemap
│ │ ├── JFPopup.release.xcconfig
│ │ └── ResourceBundle-JFPopup-JFPopup-Info.plist
│ │ ├── JRBaseKit
│ │ ├── JRBaseKit-Info.plist
│ │ ├── JRBaseKit-dummy.m
│ │ ├── JRBaseKit-prefix.pch
│ │ ├── JRBaseKit-umbrella.h
│ │ ├── JRBaseKit.debug.xcconfig
│ │ ├── JRBaseKit.modulemap
│ │ └── JRBaseKit.release.xcconfig
│ │ ├── Pods-JFPopup_Example
│ │ ├── Pods-JFPopup_Example-Info.plist
│ │ ├── Pods-JFPopup_Example-acknowledgements.markdown
│ │ ├── Pods-JFPopup_Example-acknowledgements.plist
│ │ ├── Pods-JFPopup_Example-dummy.m
│ │ ├── Pods-JFPopup_Example-frameworks.sh
│ │ ├── Pods-JFPopup_Example-umbrella.h
│ │ ├── Pods-JFPopup_Example.debug.xcconfig
│ │ ├── Pods-JFPopup_Example.modulemap
│ │ └── Pods-JFPopup_Example.release.xcconfig
│ │ └── Pods-JFPopup_Tests
│ │ ├── Pods-JFPopup_Tests-Info.plist
│ │ ├── Pods-JFPopup_Tests-acknowledgements.markdown
│ │ ├── Pods-JFPopup_Tests-acknowledgements.plist
│ │ ├── Pods-JFPopup_Tests-dummy.m
│ │ ├── Pods-JFPopup_Tests-umbrella.h
│ │ ├── Pods-JFPopup_Tests.debug.xcconfig
│ │ ├── Pods-JFPopup_Tests.modulemap
│ │ └── Pods-JFPopup_Tests.release.xcconfig
└── Tests
│ ├── Info.plist
│ └── Tests.swift
├── JFActionSheet.podspec
├── JFPopup.podspec
├── JFToast.podspec
├── LICENSE
├── Package.resolved
├── Package.swift
├── README.md
├── Sources
└── JFPopup
│ ├── Classes
│ ├── .gitkeep
│ ├── Core
│ │ ├── JFPopup.swift
│ │ ├── JFPopupAnimation.swift
│ │ ├── JFPopupController.swift
│ │ ├── JFPopupView.swift
│ │ ├── UIView+JFPopup.swift
│ │ └── UIViewContoller+JFPopup.swift
│ └── General
│ │ ├── ActionSheet
│ │ ├── JFPopupAction.swift
│ │ └── JFPopupActionSheetView.swift
│ │ ├── Alert
│ │ ├── JFAlertAction.swift
│ │ ├── JFAlertView+PopupView.swift
│ │ ├── JFAlertView+UIViewController.swift
│ │ └── JFAlertView.swift
│ │ └── Toast
│ │ ├── JFToastQueueTask.swift
│ │ ├── JFToastView+Objc.swift
│ │ └── JFToastView.swift
│ └── Resources
│ ├── .gitkeep
│ ├── fail@2x.png
│ ├── jf_loading@2x.png
│ └── success@2x.png
└── _Pods.xcodeproj
/.gitignore:
--------------------------------------------------------------------------------
1 | # macOS
2 | .DS_Store
3 |
4 | # Xcode
5 | build/
6 | *.pbxuser
7 | !default.pbxuser
8 | *.mode1v3
9 | !default.mode1v3
10 | *.mode2v3
11 | !default.mode2v3
12 | *.perspectivev3
13 | !default.perspectivev3
14 | xcuserdata/
15 | *.xccheckout
16 | profile
17 | *.moved-aside
18 | DerivedData
19 | *.hmap
20 | *.ipa
21 | .build/
22 |
23 | # Bundler
24 | .bundle
25 |
26 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
27 | # Carthage/Checkouts
28 |
29 | Carthage/Build
30 |
31 | # We recommend against adding the Pods directory to your .gitignore. However
32 | # you should judge for yourself, the pros and cons are mentioned at:
33 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
34 | #
35 | # Note: if you ignore the Pods directory, make sure to uncomment
36 | # `pod install` in .travis.yml
37 | #
38 | # Pods/
39 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # references:
2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/
3 | # * https://github.com/supermarin/xcpretty#usage
4 |
5 | osx_image: xcode7.3
6 | language: objective-c
7 | # cache: cocoapods
8 | # podfile: Example/Podfile
9 | # before_install:
10 | # - gem install cocoapods # Since Travis is not always on latest version
11 | # - pod install --project-directory=Example
12 | script:
13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/JFPopup.xcworkspace -scheme JFPopup-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty
14 | - pod lib lint
15 |
--------------------------------------------------------------------------------
/Example/JFPopup.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/JFPopup.xcodeproj/xcshareddata/xcschemes/JFPopup-Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
45 |
46 |
48 |
54 |
55 |
56 |
57 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
80 |
82 |
88 |
89 |
90 |
91 |
92 |
93 |
99 |
101 |
107 |
108 |
109 |
110 |
112 |
113 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/Example/JFPopup.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Example/JFPopup.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/JFPopup/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // JFPopup
4 | //
5 | // Created by fanjiaorng919 on 10/10/2021.
6 | // Copyright (c) 2021 fanjiaorng919. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/Example/JFPopup/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
22 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/Example/JFPopup/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Example/JFPopup/DrawerView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DrawerView.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/10.
6 | //
7 |
8 | import UIKit
9 |
10 | class DrawerView: UIView {
11 |
12 | var closeHandle: (() -> ())?
13 |
14 | lazy var closeBtn: UIButton = {
15 | var btn = UIButton(type: .system)
16 | if #available(iOS 13.0, *) {
17 | btn = UIButton(type: .close)
18 | } else {
19 | btn.setTitle("关闭", for: .normal)
20 | btn.setTitleColor(.black, for: .normal)
21 | }
22 | btn.jf.right = self.jf_width - 60
23 | btn.jf.top = 15 + CGFloat.jf.statusBarHeight()
24 | btn.jf.size = CGSize(width: 45, height: 45)
25 | btn.addTarget(self, action: #selector(closeAction), for: .touchUpInside)
26 | return btn
27 | }()
28 |
29 | @objc func closeAction() {
30 | self.closeHandle?()
31 | }
32 |
33 | override init(frame: CGRect) {
34 | super.init(frame: frame)
35 | self.backgroundColor = UIColor.jf.rgb(0x7e7eff)
36 | self.addSubview(self.closeBtn)
37 | }
38 |
39 | required init?(coder: NSCoder) {
40 | fatalError("init(coder:) has not been implemented")
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/Example/JFPopup/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "size" : "1024x1024",
46 | "scale" : "1x"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Example/JFPopup/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/JFPopup/Images.xcassets/face.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "face@2x.png",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Example/JFPopup/Images.xcassets/face.imageset/face@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryFans/JFPopup/8b0227215e754218280667cd656e22abc4d62821/Example/JFPopup/Images.xcassets/face.imageset/face@2x.png
--------------------------------------------------------------------------------
/Example/JFPopup/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Example/JFPopup/JFPopup_Example-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 | #import "OCViewController.h"
5 |
--------------------------------------------------------------------------------
/Example/JFPopup/OCViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // OCViewController.h
3 | // JFPopup_Example
4 | //
5 | // Created by 逸风 on 2021/10/10.
6 | // Copyright © 2021 CocoaPods. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface OCViewController : UIViewController
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/Example/JFPopup/OCViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // OCViewController.m
3 | // JFPopup_Example
4 | //
5 | // Created by 逸风 on 2021/10/10.
6 | // Copyright © 2021 CocoaPods. All rights reserved.
7 | //
8 |
9 | #import "OCViewController.h"
10 | #import "JFPopup_Example-Swift.h"
11 |
12 | @interface OCViewController ()
13 | @property (nonatomic, strong) UIButton *button;
14 | @property (nonatomic, strong) UIButton *button1;
15 | @end
16 |
17 | @implementation OCViewController
18 |
19 | - (UIButton *)button {
20 | if (!_button) {
21 | _button = [UIButton buttonWithType:UIButtonTypeCustom];
22 | [_button setTitle:@"不停点我" forState:UIControlStateNormal];
23 | [_button setBackgroundColor:UIColor.redColor];
24 | }
25 | return _button;
26 | }
27 |
28 | - (UIButton *)button1 {
29 | if (!_button1) {
30 | _button1 = [UIButton buttonWithType:UIButtonTypeCustom];
31 | [_button1 setTitle:@"Dismiss" forState:UIControlStateNormal];
32 | [_button1 setBackgroundColor:UIColor.blueColor];
33 | }
34 | return _button1;
35 | }
36 |
37 | - (void)viewDidLoad {
38 | [super viewDidLoad];
39 | self.title = @"OC";
40 | self.view.backgroundColor = UIColor.whiteColor;
41 | [self popup_actionSheetWith:YES actions:^NSArray * _Nonnull{
42 | return @[[[JFPopupAction alloc] initWith:@"拍摄" subTitle:@"照片或视频照片" autoDismiss:YES clickActionCallBack:^{
43 |
44 | }]];
45 | }];
46 | [JFToastView toastWithHit:@"你好我兼容Object-C"];
47 | [self.view addSubview:self.button];
48 | self.button.frame = CGRectMake(50, 150, 150, 150);
49 | [self.button addTarget:self action:@selector(clickMe) forControlEvents:UIControlEventTouchUpInside];
50 |
51 | [self.view addSubview:self.button1];
52 | self.button1.frame = CGRectMake(50, 315, 150, 150);
53 | [self.button1 addTarget:self action:@selector(clickMe1) forControlEvents:UIControlEventTouchUpInside];
54 | // Do any additional setup after loading the view.
55 | }
56 |
57 | - (void)clickMe1 {
58 | [self dismissViewControllerAnimated:YES completion:^{
59 |
60 | }];
61 | }
62 |
63 | - (void)clickMe {
64 | //暂时只兼容 这三种, 后续有issue 可以继续支持
65 | int random = arc4random() % 3;
66 | if (random == 0) {
67 | [JFToastView toastWithHit:@"你好我兼容Object-C"];
68 | } else if (random == 1) {
69 | [JFToastView toastWithIcon:JFToastObjcAssetTypeImageName imageName:@"face"];
70 | } else {
71 | [JFToastView toastWithHit:@"支付成功" type:JFToastObjcAssetTypeSuccess imageName:nil];
72 | }
73 | }
74 |
75 | /*
76 | #pragma mark - Navigation
77 |
78 | // In a storyboard-based application, you will often want to do a little preparation before navigation
79 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
80 | // Get the new view controller using [segue destinationViewController].
81 | // Pass the selected object to the new view controller.
82 | }
83 | */
84 |
85 | @end
86 |
--------------------------------------------------------------------------------
/Example/JFPopup/PopupInViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PopupInViewController.swift
3 | // JFPopup_Example
4 | //
5 | // Created by 逸风 on 2021/10/19.
6 | // Copyright © 2021 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import JFPopup
11 |
12 | class PopupInViewController: UIViewController {
13 |
14 | var count = 0
15 | let frame = CGRect(x: 15, y: 0, width: CGSize.jf.screenWidth() - 30, height: itemHeight)
16 | var originY: CGFloat = 0
17 |
18 | var scrollView: UIScrollView = {
19 | let scrollView = UIScrollView()
20 | scrollView.contentSize = CGSize(width: CGSize.jf.screenWidth(), height: 0)
21 | scrollView.alwaysBounceVertical = true
22 | scrollView.bounces = true
23 | return scrollView
24 | }()
25 |
26 | @discardableResult private func buildLabel(withTitle title: String) -> UILabel {
27 | var label = UILabel()
28 | label.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
29 | label.text = title + ":"
30 | label.textColor = UIColor.black
31 | self.scrollView.addSubview(label)
32 | label.frame = frame
33 | label.numberOfLines = 1
34 | label.sizeToFit()
35 | label.jf.height = label.jf.height + 30
36 | label.jf.origin.y = originY
37 | originY += label.jf.height
38 | return label
39 | }
40 |
41 | @discardableResult private func buildSubLabel(withTitle title: String) -> UILabel {
42 | var label = UILabel()
43 | label.text = title
44 | label.textColor = UIColor.gray
45 | label.font = UIFont.systemFont(ofSize: 13)
46 | self.scrollView.addSubview(label)
47 | label.frame = frame
48 | label.numberOfLines = 2
49 | label.sizeToFit()
50 | label.jf.origin.y = originY
51 | originY += label.jf.height
52 | return label
53 | }
54 |
55 | private func buildButton(withTitle title: String) -> UIButton {
56 | var btn = UIButton(type: .custom)
57 | btn.setTitle(title, for: .normal)
58 | btn.setTitleColor(.black, for: .normal)
59 | btn.titleLabel?.font = UIFont.systemFont(ofSize: 15)
60 | btn.contentHorizontalAlignment = .left
61 | btn.setBackgroundImage(UIImage.jf.color(0x000000,alpha: 0.1), for: .highlighted)
62 | self.scrollView.addSubview(btn)
63 | btn.frame = frame
64 | btn.jf.origin.y = originY
65 | addBottomLine(with: btn)
66 | originY += btn.jf.height
67 | return btn
68 | }
69 |
70 | private func addBottomLine(with actionItem: UIView) {
71 | var lineView = UIView()
72 | lineView.backgroundColor = UIColor.init(red: 221 / 255.0, green: 221 / 255.0, blue: 221 / 255.0, alpha: 1)
73 | actionItem.addSubview(lineView)
74 | lineView.jf.left = 0
75 | lineView.jf.top = actionItem.jf.height - 1
76 | lineView.jf.height = 0.5
77 | lineView.jf.width = actionItem.jf.width
78 | }
79 |
80 | override func viewDidLoad() {
81 | super.viewDidLoad()
82 |
83 | var t: CGFloat = 0
84 | if #available(iOS 11.0, *) {
85 | t = UIApplication.shared.keyWindow?.safeAreaInsets.top ?? 0
86 | }
87 | print("safe area top: " + "\(t)")
88 | self.title = "Popup From UIView"
89 | self.view.backgroundColor = .white
90 | self.view.addSubview(self.scrollView)
91 | self.scrollView.frame = self.view.frame
92 |
93 | self.buildLabel(withTitle: "通用组件示例")
94 | self.buildSubLabel(withTitle: "所有Popup type")
95 | self.buildSubLabel(withTitle: "self.view.popup.xxx, add 到当前 view")
96 | self.buildSubLabel(withTitle: "JFPopupView.popup.xxx add 到 window")
97 |
98 | let btn3 = self.buildButton(withTitle: "Drawer")
99 | btn3.addTarget(self, action: #selector(clickAction3), for: .touchUpInside)
100 |
101 | self.buildLabel(withTitle: "ActionSheet")
102 | let btn4 = self.buildButton(withTitle: "add to current view")
103 | btn4.addTarget(self, action: #selector(clickAction4), for: .touchUpInside)
104 |
105 | let btn5 = self.buildButton(withTitle: "add to window view")
106 | btn5.addTarget(self, action: #selector(clickAction5), for: .touchUpInside)
107 |
108 | self.buildLabel(withTitle: "Toast Usage (v1.1 add)")
109 |
110 | let btn6 = self.buildButton(withTitle: "默认toast,支持灵动岛否则默认剧中")
111 | btn6.addTarget(self, action: #selector(clickAction6), for: .touchUpInside)
112 |
113 | let btn7 = self.buildButton(withTitle: "自定义参数")
114 | btn7.addTarget(self, action: #selector(clickAction7), for: .touchUpInside)
115 |
116 | let btn8 = self.buildButton(withTitle: "默认Icon")
117 | btn8.addTarget(self, action: #selector(clickAction8), for: .touchUpInside)
118 |
119 | let btn9 = self.buildButton(withTitle: "自定义Icon,可以没文本")
120 | btn9.addTarget(self, action: #selector(clickAction9), for: .touchUpInside)
121 |
122 | self.buildLabel(withTitle: "Loading Usage (v1.3 add)")
123 |
124 | let btn10 = self.buildButton(withTitle: "常规不带文字")
125 | btn10.addTarget(self, action: #selector(clickAction10), for: .touchUpInside)
126 |
127 | let btn11 = self.buildButton(withTitle: "带文字")
128 | btn11.addTarget(self, action: #selector(clickAction11), for: .touchUpInside)
129 |
130 | let btn12 = self.buildButton(withTitle: "loading in view (灵动岛会强制keywindow,因为不在最顶层会被遮挡)")
131 | btn12.addTarget(self, action: #selector(clickAction12), for: .touchUpInside)
132 |
133 | self.buildLabel(withTitle: "Alert View Usage (v1.4 add)")
134 |
135 | let btn13 = self.buildButton(withTitle: "默认风格,自带取消按钮")
136 | btn13.addTarget(self, action: #selector(clickAction13), for: .touchUpInside)
137 |
138 | let btn14 = self.buildButton(withTitle: "Title 和 SubTitle可以二选一,单个按钮")
139 | btn14.addTarget(self, action: #selector(clickAction14), for: .touchUpInside)
140 |
141 | let btn15 = self.buildButton(withTitle: "完全自定义")
142 | btn15.addTarget(self, action: #selector(clickAction15), for: .touchUpInside)
143 |
144 | let btn16 = self.buildButton(withTitle: "从VC弹出也行")
145 | btn16.addTarget(self, action: #selector(clickAction16), for: .touchUpInside)
146 |
147 | self.scrollView.contentSize = CGSize(width: CGSize.jf.screenWidth(), height: originY + itemHeight)
148 | }
149 |
150 | @objc func clickAction16() {
151 | self.popup.alert {
152 | [.title("我是从VC Present 出来的"),
153 | .subTitle("用法和View一致,只是一个是self.popup.alert(self是UIViewControll),一个是JFPopupView.popup.alert"),
154 | .confirmAction([
155 | .text("知道了"),
156 | .tapActionCallback({
157 | JFPopupView.popup.toast(hit: "知道了")
158 | })
159 | ])
160 | ]
161 | }
162 | }
163 |
164 | @objc func clickAction15() {
165 | JFPopupView.popup.alert {[
166 | .title("不同标题颜色"),
167 | .titleColor(.red),
168 | .withoutAnimation(true),
169 | .subTitle("我是完全自定义的,标题颜色,action颜色,文本都支持修改,不带动画"),
170 | .subTitleColor(.black),
171 | .cancelAction([.textColor(.blue),.text("我是取消超出文本裁切"),.tapActionCallback({
172 | JFPopupView.popup.toast(hit: "点击了取消")
173 | })]),
174 | .confirmAction([
175 | .text("我是确定"),
176 | .textColor(.red),
177 | .tapActionCallback({
178 | JFPopupView.popup.toast(hit: "点击了确定")
179 | })
180 | ])
181 | ]}
182 | }
183 |
184 | @objc func clickAction14() {
185 | JFPopupView.popup.alert {[
186 | .subTitle("我是Title 和 SubTitle可以二选一,单个按钮"),
187 | .showCancel(false),
188 | .confirmAction([
189 | .text("知道了"),
190 | .tapActionCallback({
191 | JFPopupView.popup.toast(hit: "我知道了")
192 | })
193 | ])
194 | ]}
195 | }
196 |
197 | @objc func clickAction13() {
198 | JFPopupView.popup.alert {[
199 | .title("温馨提示"),
200 | .subTitle("我是默认风格,自带取消按钮"),
201 | .confirmAction([
202 | .text("知道了"),
203 | .tapActionCallback({
204 | JFPopupView.popup.toast(hit: "我知道了")
205 | })
206 | ])
207 | ]}
208 | }
209 |
210 | @objc func clickAction10() {
211 | DispatchQueue.main.async {
212 | JFPopupView.popup.loading()
213 | }
214 | DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
215 | JFPopupView.popup.hideLoading()
216 | JFPopupView.popup.toast(hit: "刷新成功")
217 | }
218 | }
219 |
220 | @objc func clickAction11() {
221 | JFPopupView.popup.loading(hit: "正在载入视频")
222 | DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
223 | JFPopupView.popup.hideLoading()
224 | JFPopupView.popup.toast(hit: "加载成功", icon: .success)
225 | }
226 | }
227 |
228 | @objc func clickAction12() {
229 | //只支持 controller.view, 默认keywindow
230 | JFPopupView.popup.loading(hit: "加载中", inView: self.view)
231 | DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
232 | JFPopupView.popup.hideLoading()
233 | JFPopupView.popup.toast(hit: "加载失败", icon: .fail)
234 | }
235 | }
236 |
237 | @objc func clickAction3() {
238 | JFPopupView.popup.drawer { mainContainer in
239 | let view = DrawerView(frame: CGRect(x: 0, y: 0, width: CGSize.jf.screenWidth() - 100, height: CGSize.jf.screenHeight()))
240 | view.backgroundColor = UIColor.jf.rgb(0x7e7eff)
241 | view.closeHandle = { [weak mainContainer] in
242 | mainContainer?.dismissPopupView(completion: { isFinished in
243 |
244 | })
245 | }
246 | return view
247 | } onDismissPopupView: { mainContainer in
248 | print("is close from tap bg \(String(describing: mainContainer?.isClosedFromTapBackground))")
249 | JFPopupView.popup.toast(hit: "Drawer 消失", icon: .success)
250 | }
251 | }
252 |
253 | @objc func clickAction6() {
254 | JFPopupView.popup.toast(hit: "默认toast,支持灵动岛")
255 | }
256 |
257 | @objc func clickAction8() {
258 | let random = arc4random() % 3
259 | if random == 0 {
260 | JFPopupView.popup.toast(hit: "支付成功", icon: .success)
261 | } else if random == 1 {
262 | JFPopupView.popup.toast(hit: "支付失败", icon: .fail)
263 | } else {
264 | JFPopupView.popup.toast(hit: "自定义", icon: .imageName(name: "face"))
265 | }
266 | }
267 |
268 | @objc func clickAction7() {
269 | // mainContainer 在当前view 弹出, 默认 keywindow
270 | // mainContainer in current view popup, default is keywindow
271 | // withoutAnimation 不用动画
272 | // enableUserInteraction 如果 true 相当于mask遮挡superview手势 不能触发
273 | JFPopupView.popup.toast {
274 | [
275 | .hit("不响应super view,带背景色,加大时长,不用动画,在当前view弹出,position top"),
276 | .enableUserInteraction(true),
277 | .bgColor(UIColor.jf.rgb(0x000000,alpha: 0.3)),
278 | .autoDismissDuration(.seconds(value: 3)),
279 | .mainContainer(self.view),
280 | .withoutAnimation(true),
281 | .position(.top)
282 | ]
283 | }
284 | }
285 |
286 | @objc func clickAction9() {
287 | var options: [JFToastOption] = [.icon(.imageName(name: "face"))]
288 | let random = arc4random() % 2
289 | if random == 0 {
290 | options += [.hit("Hello Word !")]
291 | }
292 | JFPopupView.popup.toast { options }
293 | }
294 |
295 | @objc func clickAction5() {
296 | self.view.popup.actionSheet {
297 | [
298 | JFPopupAction(with: "拍摄", subTitle: "照片或视频照片", clickActionCallBack: { [weak self] in
299 | self?.pushVC()
300 | }),
301 | JFPopupAction(with: "从手机相册选择", subTitle: nil, clickActionCallBack: {
302 |
303 | }),
304 | JFPopupAction(with: "用秒剪制作视频", subTitle: nil, clickActionCallBack: {
305 |
306 | }),
307 | ]
308 | }
309 | }
310 |
311 | @objc func clickAction4() {
312 | let highlightAttribute: [NSAttributedString.Key : Any] = [.font: UIFont.systemFont(ofSize: 14), .foregroundColor: UIColor.red]
313 | let subTitle = NSAttributedString(string: "照片或视频照片", attributes: highlightAttribute)
314 | JFPopupView.popup.actionSheet {
315 | [
316 | JFPopupAction(with: "拍摄", subAttributedTitle: subTitle, clickActionCallBack: { [weak self] in
317 | self?.pushVC()
318 | }),
319 | JFPopupAction(with: "从手机相册选择", subTitle: nil, clickActionCallBack: {
320 |
321 | }),
322 | JFPopupAction(with: "用秒剪制作视频", subTitle: nil, clickActionCallBack: {
323 |
324 | }),
325 | ]
326 | }
327 | }
328 |
329 | @objc func pushVC() {
330 | let vc = OCViewController()
331 | self.navigationController?.pushViewController(vc, animated: true)
332 | }
333 |
334 | }
335 |
--------------------------------------------------------------------------------
/Example/JFPopup/PresentVCModeViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PresentVCModeViewController.swift
3 | // JFPopup_Example
4 | //
5 | // Created by 逸风 on 2021/10/19.
6 | // Copyright © 2021 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import JFPopup
11 |
12 | //自定义实现动画
13 | extension PresentVCModeViewController: JFPopupAnimationProtocol {
14 | func present(with transitonContext: UIViewControllerContextTransitioning?, config: JFPopupConfig, contianerView: UIView, completion: ((Bool) -> ())?) {
15 | var contianerView = contianerView
16 | contianerView.frame.origin.y = -contianerView.jf.height
17 | contianerView.jf.centerX = CGSize.jf.screenWidth() / 2
18 | contianerView.layoutIfNeeded()
19 | UIView.animate(withDuration: 0.25, animations: {
20 | contianerView.jf.centerY = CGSize.jf.screenHeight() / 2
21 | contianerView.layoutIfNeeded()
22 | }) { (finished) in
23 | transitonContext?.completeTransition(true)
24 | completion?(finished)
25 | }
26 | }
27 |
28 | func dismiss(with transitonContext: UIViewControllerContextTransitioning?, config: JFPopupConfig, contianerView: UIView?, completion: ((Bool) -> ())?) {
29 | guard let contianerView = contianerView else {
30 | transitonContext?.completeTransition(true)
31 | completion?(false)
32 | return
33 | }
34 | UIView.animate(withDuration: 0.25, animations: {
35 | contianerView.superview?.alpha = 0
36 | contianerView.frame.origin.y = -contianerView.jf.height
37 | contianerView.layoutIfNeeded()
38 | }) { (finished) in
39 | transitonContext?.completeTransition(true)
40 | completion?(finished)
41 | }
42 | }
43 | }
44 |
45 | class PresentVCModeViewController: UIViewController {
46 |
47 | let frame = CGRect(x: 15, y: 0, width: CGSize.jf.screenWidth() - 30, height: itemHeight)
48 | var originY: CGFloat = 0
49 |
50 | @discardableResult private func buildLabel(withTitle title: String) -> UILabel {
51 | var label = UILabel()
52 | label.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
53 | label.text = title + ":"
54 | label.textColor = UIColor.black
55 | self.scrollView.addSubview(label)
56 | label.frame = frame
57 | label.numberOfLines = 1
58 | label.sizeToFit()
59 | label.jf.height = label.jf.height + 30
60 | label.jf.origin.y = originY
61 | originY += label.jf.height
62 | return label
63 | }
64 |
65 | @discardableResult private func buildSubLabel(withTitle title: String) -> UILabel {
66 | var label = UILabel()
67 | label.text = title
68 | label.textColor = UIColor.gray
69 | label.font = UIFont.systemFont(ofSize: 13)
70 | self.scrollView.addSubview(label)
71 | label.frame = frame
72 | label.numberOfLines = 2
73 | label.sizeToFit()
74 | label.jf.origin.y = originY
75 | originY += label.jf.height
76 | return label
77 | }
78 |
79 | private func buildButton(withTitle title: String) -> UIButton {
80 | var btn = UIButton(type: .custom)
81 | btn.setTitle(title, for: .normal)
82 | btn.setTitleColor(.black, for: .normal)
83 | btn.titleLabel?.font = UIFont.systemFont(ofSize: 15)
84 | btn.contentHorizontalAlignment = .left
85 | btn.setBackgroundImage(UIImage.jf.color(0x000000,alpha: 0.1), for: .highlighted)
86 | self.scrollView.addSubview(btn)
87 | btn.frame = frame
88 | btn.jf.origin.y = originY
89 | addBottomLine(with: btn)
90 | originY += btn.jf.height
91 | return btn
92 | }
93 |
94 | var scrollView: UIScrollView = {
95 | let scrollView = UIScrollView()
96 | scrollView.contentSize = CGSize(width: CGSize.jf.screenWidth(), height: 0)
97 | scrollView.alwaysBounceVertical = true
98 | scrollView.bounces = true
99 | return scrollView
100 | }()
101 |
102 | private func addBottomLine(with actionItem: UIView) {
103 | var lineView = UIView()
104 | lineView.backgroundColor = UIColor.init(red: 221 / 255.0, green: 221 / 255.0, blue: 221 / 255.0, alpha: 1)
105 | actionItem.addSubview(lineView)
106 | lineView.jf.left = 0
107 | lineView.jf.top = actionItem.jf.height - 1
108 | lineView.jf.height = 0.5
109 | lineView.jf.width = actionItem.jf.width
110 | }
111 |
112 | override func viewDidLoad() {
113 | super.viewDidLoad()
114 | self.title = "Present In VC Mode"
115 | self.view.backgroundColor = .white
116 | self.view.addSubview(self.scrollView)
117 | self.scrollView.frame = self.view.frame
118 |
119 | self.buildLabel(withTitle: "快速创建模式示例(闭包)")
120 | self.buildSubLabel(withTitle: "支持4种模式,左抽屉,右抽屉,底部Sheet,对话框,皆支持自定义View")
121 |
122 | let btn = self.buildButton(withTitle: "Drawer Right")
123 | btn.addTarget(self, action: #selector(clickAction), for: .touchUpInside)
124 |
125 | let btn1 = self.buildButton(withTitle: "Dialog")
126 | btn1.addTarget(self, action: #selector(clickAction1), for: .touchUpInside)
127 |
128 | let btn2 = self.buildButton(withTitle: "BottomSheet")
129 | btn2.addTarget(self, action: #selector(clickAction2), for: .touchUpInside)
130 |
131 | let btn3 = self.buildButton(withTitle: "Drawer Left")
132 | btn3.addTarget(self, action: #selector(clickAction3), for: .touchUpInside)
133 |
134 | self.buildLabel(withTitle: "通用组件示例")
135 |
136 | let btn4 = self.buildButton(withTitle: "微信ActionSheet 自带取消")
137 | btn4.addTarget(self, action: #selector(clickAction4), for: .touchUpInside)
138 |
139 | let btn6 = self.buildButton(withTitle: "微信ActionSheet 不带取消")
140 | btn6.addTarget(self, action: #selector(clickAction6), for: .touchUpInside)
141 |
142 | let btn7 = self.buildButton(withTitle: "微信ActionSheet 点击action不退出")
143 | btn7.addTarget(self, action: #selector(clickAction7), for: .touchUpInside)
144 |
145 | let btn13 = self.buildButton(withTitle: "微信ActionSheet Attributed SubTitle")
146 | btn13.addTarget(self, action: #selector(clickAction13), for: .touchUpInside)
147 |
148 | let btn12 = self.buildButton(withTitle: "通用 Alert View From Present VC")
149 | btn12.addTarget(self, action: #selector(clickAction12), for: .touchUpInside)
150 |
151 | self.buildLabel(withTitle: "兼容OC写法")
152 | self.buildSubLabel(withTitle: "见UIViewController+JFPopupObjc.swift")
153 |
154 | let btn5 = self.buildButton(withTitle: "OC Usage(自行写Extension)")
155 | btn5.addTarget(self, action: #selector(pushVC), for: .touchUpInside)
156 |
157 | self.buildLabel(withTitle: "VC模式创建")
158 |
159 | let btn8 = self.buildButton(withTitle: "继承JFPopupController,点击背景不允许退出")
160 | btn8.addTarget(self, action: #selector(clickAction8), for: .touchUpInside)
161 |
162 | let btn9 = self.buildButton(withTitle: "直接初始化方法创建")
163 | btn9.addTarget(self, action: #selector(clickAction9), for: .touchUpInside)
164 |
165 |
166 | self.buildLabel(withTitle: "自定义")
167 | let btn10 = self.buildButton(withTitle: "扩展自定义动画")
168 | btn10.addTarget(self, action: #selector(clickAction10), for: .touchUpInside)
169 |
170 | let btn11 = self.buildButton(withTitle: "自定义配置")
171 | btn11.addTarget(self, action: #selector(clickAction11), for: .touchUpInside)
172 |
173 | self.scrollView.contentSize = CGSize(width: CGSize.jf.screenWidth(), height: originY + itemHeight)
174 | }
175 |
176 | @objc func clickAction13() {
177 | let highlightAttribute: [NSAttributedString.Key : Any] = [.font: UIFont.systemFont(ofSize: 14), .foregroundColor: UIColor.red]
178 | let subTitle = NSAttributedString(string: "照片或视频照片", attributes: highlightAttribute)
179 | self.popup.actionSheet {
180 | [
181 | JFPopupAction(with: "拍摄", subAttributedTitle: subTitle, clickActionCallBack: { [weak self] in
182 | self?.pushVC()
183 | }),
184 | ]
185 | }
186 | }
187 |
188 | @objc func clickAction12() {
189 | self.popup.alert {[
190 | .title("从VC弹出alertView"),
191 | .subTitle("也支持从UIView弹出,更多用法请看《从UIView弹出》示例"),
192 | .confirmAction([
193 | .text("过去看"),
194 | .tapActionCallback({ [weak self] in
195 | self?.navigationController?.pushViewController(PopupInViewController(), animated: true)
196 | })
197 | ])
198 | ]}
199 | }
200 |
201 | @objc func clickAction11() {
202 | var config = JFPopupConfig.dialog
203 | config.absoluteRect = .init(x: 20, y: 150, width: 200, height: 200)
204 | config.bgColor = UIColor.jf.rgb(0x7e7eff,alpha: 0.5)
205 | self.popup.custom(with: config) {
206 | let view: UIView = {
207 | let view = UIView()
208 | view.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
209 | view.layer.cornerRadius = 12
210 | view.backgroundColor = .black
211 | let label = UILabel()
212 | label.text = "完全自定义Frame"
213 | label.textColor = .white
214 | view.addSubview(label)
215 | label.sizeToFit()
216 | label.center = view.center
217 | return view
218 | }()
219 | return view
220 | }
221 | }
222 |
223 | @objc func clickAction10() {
224 | var config = JFPopupConfig.dialog
225 | config.bgColor = .clear
226 | let vc = JFPopupController(with: config, popupProtocol: self) {
227 | let view: UIView = {
228 | let view = UIView()
229 | view.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
230 | view.layer.cornerRadius = 12
231 | view.backgroundColor = .black
232 | return view
233 | }()
234 | return view
235 | }
236 | vc.show(with: self)
237 | }
238 |
239 | @objc func clickAction9() {
240 | var config = JFPopupConfig.dialog
241 | config.bgColor = .clear
242 | let vc = JFPopupController(with: config) {
243 | let view: UIView = {
244 | let view = UIView()
245 | view.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
246 | view.layer.cornerRadius = 12
247 | view.backgroundColor = .black
248 | return view
249 | }()
250 | return view
251 | }
252 | vc.show(with: self)
253 | }
254 |
255 | @objc func clickAction8() {
256 | var config = JFPopupConfig.bottomSheet
257 | config.isDismissible = false
258 | let vc = TestCustomViewController(with: config)
259 | vc.show(with: self)
260 | }
261 |
262 | @objc func clickAction7() {
263 | self.popup.actionSheet {
264 | [
265 | JFPopupAction(with: "从手机相册选择", subTitle: nil, autoDismiss: false, clickActionCallBack: { [weak self] in
266 | print("我没退出")
267 | JFPopupView.popup.toast(hit: "3s后退出")
268 | DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
269 | self?.popup.dismissPopup()
270 | }
271 | }),
272 | ]
273 | }
274 | }
275 |
276 | @objc func clickAction6() {
277 | self.popup.actionSheet(with: false) {
278 | [
279 | JFPopupAction(with: "拍摄", subTitle: "照片或视频照片", clickActionCallBack: { [weak self] in
280 | self?.pushVC()
281 | }),
282 | JFPopupAction(with: "从手机相册选择", subTitle: nil, clickActionCallBack: {
283 |
284 | }),
285 | JFPopupAction(with: "用秒剪制作视频", subTitle: nil, clickActionCallBack: {
286 |
287 | }),
288 | ]
289 | }
290 | }
291 |
292 | @objc func clickAction4() {
293 | self.popup.actionSheet {
294 | [
295 | JFPopupAction(with: "拍摄", subTitle: "照片或视频照片", clickActionCallBack: { [weak self] in
296 | self?.pushVC()
297 | }),
298 | JFPopupAction(with: "从手机相册选择", subTitle: nil, clickActionCallBack: {
299 |
300 | }),
301 | JFPopupAction(with: "用秒剪制作视频", subTitle: nil, clickActionCallBack: {
302 |
303 | }),
304 | ]
305 | }
306 | }
307 |
308 | @objc func clickAction3() {
309 | //default left
310 | self.popup.drawer {
311 | let v = DrawerView(frame: CGRect(x: 0, y: 0, width: CGSize.jf.screenWidth(), height: CGSize.jf.screenHeight()))
312 | v.closeHandle = { [weak self] in
313 | self?.popup.dismissPopup()
314 | }
315 | return v
316 | }
317 | }
318 |
319 | @objc func pushVC() {
320 | let vc = OCViewController()
321 | self.navigationController?.pushViewController(vc, animated: true)
322 | }
323 |
324 | @objc func clickAction2() {
325 |
326 | self.popup.bottomSheet {
327 | let v = UIView(frame: CGRect(x: 0, y: 0, width: CGSize.jf.screenWidth(), height: 300))
328 | v.backgroundColor = .red
329 | return v
330 | }
331 | }
332 |
333 | @objc func clickAction1() {
334 | self.popup.dialog {
335 | let v = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
336 | v.backgroundColor = .red
337 | return v
338 | }
339 | }
340 |
341 | @objc func clickAction() {
342 | self.popup.drawer(with: .right) {
343 | let v = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: CGSize.jf.screenHeight()))
344 | v.backgroundColor = .red
345 | return v
346 | }
347 | }
348 | }
349 |
--------------------------------------------------------------------------------
/Example/JFPopup/TestCustomViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestCustomViewController.swift
3 | // JFPopup_Example
4 | //
5 | // Created by 逸风 on 2021/10/11.
6 | // Copyright © 2021 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import JFPopup
11 |
12 | class TestCustomViewController: JFPopupController {
13 |
14 | override func viewDidLoad() {
15 | super.viewDidLoad()
16 | // Do any additional setup after loading the view.
17 | }
18 |
19 | @objc func clickBtnAction() {
20 | self.closeVC(with: nil)
21 | }
22 |
23 | override func viewForContainer() -> UIView? {
24 | let view = UIView()
25 | view.backgroundColor = .black
26 | view.frame = CGRect(x: 0, y: 0, width: CGSize.jf.screenWidth(), height: 300)
27 | var btn = UIButton(frame: CGRect(x: 0, y: 0, width: 200, height: 100))
28 | btn.setTitle("点击才能退出", for: .normal)
29 | btn.addTarget(self, action: #selector(clickBtnAction), for: .touchUpInside)
30 | btn.setTitleColor(.white, for: .normal)
31 | view.addSubview(btn)
32 | btn.jf.centerY = view.jf.centerY
33 | btn.jf.centerX = view.jf.centerX
34 | return view
35 | }
36 |
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/Example/JFPopup/UIViewController+JFPopupObjc.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+Test.swift
3 | // JFPopup_Example
4 | //
5 | // Created by 逸风 on 2021/10/10.
6 | // Copyright © 2021 CocoaPods. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import JFPopup
11 |
12 | @objc public extension UIViewController {
13 |
14 | @objc func popup_dismiss() {
15 | self.popup.dismissPopup()
16 | }
17 |
18 | @objc func popup_actionSheet(with autoCancelAction: Bool = true, actions: (() -> [JFPopupAction])) {
19 | self.popup.actionSheet(with: autoCancelAction, actions: actions)
20 | }
21 |
22 | @objc func popup_bottomSheet(with isDismissible: Bool = true, enableDrag: Bool = true, bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4), container: () -> UIView) {
23 | self.popup.bottomSheet(with: isDismissible, enableDrag: enableDrag, bgColor: bgColor, container: container)
24 | }
25 |
26 | func popup_drawer(with direction: JFPopupAnimationDirection = .left, isDismissible: Bool = true, enableDrag: Bool = true, bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4), container: () -> UIView) {
27 | self.popup.drawer(with: direction, isDismissible: isDismissible, enableDrag: enableDrag, bgColor: bgColor, container: container)
28 | }
29 |
30 | func popup_dialog(with isDismissible: Bool = true, bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4), container: () -> UIView) {
31 | self.popup.dialog(with: isDismissible, bgColor: bgColor, container: container)
32 | }
33 |
34 | func popup_custom(with animationType: JFPopupAnimationType = .dialog, isDismissible: Bool = true, enableDrag: Bool = true, direction: JFPopupAnimationDirection = .left, bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4), container: () -> UIView) {
35 | self.popup.objc_custom(with: animationType, isDismissible: isDismissible, enableDrag: enableDrag, direction: direction, bgColor: bgColor, container: container)
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/Example/JFPopup/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // JFPopup
4 | //
5 | // Created by fanjiaorng919 on 10/10/2021.
6 | // Copyright (c) 2021 fanjiaorng919. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import JFPopup
11 |
12 | let itemHeight: CGFloat = 50
13 |
14 | class ViewController: UIViewController {
15 |
16 | let frame = CGRect(x: 15, y: 0, width: CGSize.jf.screenWidth() - 30, height: itemHeight)
17 | var originY: CGFloat = 0
18 |
19 | var scrollView: UIScrollView = {
20 | let scrollView = UIScrollView()
21 | scrollView.contentSize = CGSize(width: CGSize.jf.screenWidth(), height: 0)
22 | scrollView.alwaysBounceVertical = true
23 | scrollView.bounces = true
24 | return scrollView
25 | }()
26 |
27 | @discardableResult private func buildLabel(withTitle title: String) -> UILabel {
28 | var label = UILabel()
29 | label.text = title + ":"
30 | label.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
31 | label.textColor = UIColor.black
32 | self.scrollView.addSubview(label)
33 | label.frame = frame
34 | label.numberOfLines = 1
35 | label.sizeToFit()
36 | label.jf.height = label.jf.height + 30
37 | label.jf.origin.y = originY
38 | originY += label.jf.height
39 | return label
40 | }
41 |
42 | @discardableResult private func buildSubLabel(withTitle title: String) -> UILabel {
43 | var label = UILabel()
44 | label.text = title
45 | label.textColor = UIColor.gray
46 | label.font = UIFont.systemFont(ofSize: 13)
47 | self.scrollView.addSubview(label)
48 | label.frame = frame
49 | label.numberOfLines = 2
50 | label.sizeToFit()
51 | label.jf.origin.y = originY
52 | originY += label.jf.height
53 | return label
54 | }
55 |
56 | private func buildButton(withTitle title: String) -> UIButton {
57 | var btn = UIButton(type: .custom)
58 | btn.setTitle(title, for: .normal)
59 | btn.setTitleColor(.black, for: .normal)
60 | btn.titleLabel?.font = UIFont.systemFont(ofSize: 15)
61 | btn.contentHorizontalAlignment = .left
62 | btn.setBackgroundImage(UIImage.jf.color(0x000000,alpha: 0.1), for: .highlighted)
63 | self.scrollView.addSubview(btn)
64 | btn.frame = frame
65 | btn.jf.origin.y = originY
66 | addBottomLine(with: btn)
67 | originY += btn.jf.height
68 | return btn
69 | }
70 |
71 | private func addBottomLine(with actionItem: UIView) {
72 | var lineView = UIView()
73 | lineView.backgroundColor = UIColor.init(red: 221 / 255.0, green: 221 / 255.0, blue: 221 / 255.0, alpha: 1)
74 | actionItem.addSubview(lineView)
75 | lineView.jf.left = 0
76 | lineView.jf.top = actionItem.jf.height - 1
77 | lineView.jf.height = 0.5
78 | lineView.jf.width = actionItem.jf.width
79 | }
80 |
81 | override func viewDidLoad() {
82 | super.viewDidLoad()
83 | self.title = "Example"
84 | self.view.addSubview(self.scrollView)
85 | self.scrollView.frame = self.view.frame
86 | self.buildLabel(withTitle: "示例代码")
87 | self.buildSubLabel(withTitle: "支持从Controller 弹出, 也支持从View 弹出")
88 | self.buildSubLabel(withTitle: "V1.1 add ToastView, Usage请看(从UIView弹出)")
89 | let btn = self.buildButton(withTitle: "从Controller弹出")
90 | btn.addTarget(self, action: #selector(clickAction), for: .touchUpInside)
91 |
92 | let btn1 = self.buildButton(withTitle: "从UIView弹出")
93 | btn1.addTarget(self, action: #selector(clickAction1), for: .touchUpInside)
94 |
95 | let btn2 = self.buildButton(withTitle: "Objc兼容")
96 | btn2.addTarget(self, action: #selector(clickAction2), for: .touchUpInside)
97 |
98 | self.scrollView.contentSize = CGSize(width: CGSize.jf.screenWidth(), height: originY + itemHeight)
99 | }
100 |
101 | @objc func clickAction2() {
102 | // self.navigationController?.pushViewController(OCViewController(), animated: true)
103 | let vc = OCViewController()
104 | self.present(vc, animated: true)
105 | }
106 |
107 | @objc func clickAction1() {
108 | self.navigationController?.pushViewController(PopupInViewController(), animated: true)
109 | }
110 |
111 | @objc func clickAction() {
112 | self.navigationController?.pushViewController(PresentVCModeViewController(), animated: true)
113 | }
114 |
115 | }
116 |
117 |
--------------------------------------------------------------------------------
/Example/Podfile:
--------------------------------------------------------------------------------
1 | use_frameworks!
2 |
3 | platform :ios, '9.0'
4 |
5 | target 'JFPopup_Example' do
6 | pod 'JFPopup', :path => '../'
7 |
8 | post_install do |installer|
9 | installer.generated_projects.each do |project|
10 | project.targets.each do |target|
11 | target.build_configurations.each do |config|
12 | config.build_settings['CODE_SIGN_IDENTITY'] = ''
13 | end
14 | end
15 | end
16 | end
17 |
18 | target 'JFPopup_Tests' do
19 | inherit! :search_paths
20 |
21 |
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/Example/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - JFPopup (1.5.4):
3 | - JRBaseKit (~> 1.1.1)
4 | - JRBaseKit (1.1.1)
5 |
6 | DEPENDENCIES:
7 | - JFPopup (from `../`)
8 |
9 | SPEC REPOS:
10 | trunk:
11 | - JRBaseKit
12 |
13 | EXTERNAL SOURCES:
14 | JFPopup:
15 | :path: "../"
16 |
17 | SPEC CHECKSUMS:
18 | JFPopup: c3ada13bbda3cfe85bd71228e20a0b622eab72af
19 | JRBaseKit: e817f5516d9427f9dd249c99263f95fa260c3246
20 |
21 | PODFILE CHECKSUM: 3040b8d466bd38b0257fda44b4645a864d4520f5
22 |
23 | COCOAPODS: 1.11.3
24 |
--------------------------------------------------------------------------------
/Example/Pods/JRBaseKit/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2021 JerryFans
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Example/Pods/JRBaseKit/Sources/JF.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JF.swift
3 | // Example
4 | //
5 | // Created by JerryFans on 2021/8/4.
6 | //
7 | import UIKit
8 | public struct JF {
9 | let base: Base
10 | init(_ base: Base) {
11 | self.base = base
12 | }
13 | }
14 |
15 | public protocol JFCompatible {}
16 | public extension JFCompatible {
17 | static var jf: JF.Type {
18 | set {}
19 | get { JF.self }
20 | }
21 | var jf: JF {
22 | set {}
23 | get { JF(self) }
24 | }
25 | }
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Example/Pods/JRBaseKit/Sources/Size+Extenison.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Size+Extenison.swift
3 | // Example
4 | //
5 | // Created by JerryFans on 2021/8/4.
6 | //
7 |
8 | import UIKit
9 | extension Bool: JFCompatible {}
10 | public extension JF where Base == Bool {
11 | static func isBangsiPhone() -> Bool {
12 | var isBangs = false
13 | if #available(iOS 11.0, *) {
14 | isBangs = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0 > 0.0
15 | }
16 | return isBangs
17 | }
18 | }
19 |
20 | extension CGFloat: JFCompatible {}
21 | public extension JF where Base == CGFloat {
22 |
23 | static func navigationBarHeight() -> CGFloat {
24 | return self.safeAreaInsets().top + 44.0
25 | }
26 |
27 | static func statusBarHeight() -> CGFloat {
28 | return self.safeAreaInsets().top
29 | }
30 |
31 | static func safeAreaBottomHeight() -> CGFloat {
32 | return self.safeAreaInsets().bottom
33 | }
34 |
35 | static func safeAreaInsets() -> UIEdgeInsets {
36 | if #available(iOS 11.0, *) {
37 | return UIApplication.shared.windows.first?.safeAreaInsets ?? .zero
38 | } else {
39 | return .zero
40 | }
41 | }
42 | }
43 |
44 | extension CGSize: JFCompatible {}
45 | public extension JF where Base == CGSize {
46 |
47 | static func screenBounds() -> CGRect {
48 | return UIScreen.main.bounds
49 | }
50 |
51 | static func screenSize() -> CGSize {
52 | return UIScreen.main.bounds.size
53 | }
54 |
55 | static func screenWidth() -> CGFloat {
56 | return UIScreen.main.bounds.size.width
57 | }
58 |
59 | static func screenHeight() -> CGFloat {
60 | return UIScreen.main.bounds.size.height
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Example/Pods/JRBaseKit/Sources/UIColor+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+Extension.swift
3 | // Example
4 | //
5 | // Created by JerryFans on 2021/8/4.
6 | //
7 |
8 | import UIKit
9 | extension UIColor: JFCompatible {}
10 | public extension JF where Base: UIColor {
11 | //0xRRGGBB
12 | static func rgb(_ hex:UInt32, alpha:CGFloat = 1.0) -> UIColor {
13 | let divisor = CGFloat(255)
14 | let red = CGFloat((hex & 0xFF0000) >> 16) / divisor
15 | let green = CGFloat((hex & 0x00FF00) >> 8) / divisor
16 | let blue = CGFloat( hex & 0x0000FF ) / divisor
17 | return UIColor.init(red: red, green: green, blue: blue, alpha: alpha)
18 | }
19 |
20 | //0xRRGGBBAA
21 | static func rgba(_ hex:UInt32) -> UIColor {
22 | let divisor = CGFloat(255)
23 | let red = CGFloat((hex & 0xFF000000) >> 24) / divisor
24 | let green = CGFloat((hex & 0x00FF0000) >> 16) / divisor
25 | let blue = CGFloat((hex & 0x0000FF00) >> 8) / divisor
26 | let alpha = CGFloat( hex & 0x000000FF ) / divisor
27 | return UIColor.init(red: red, green: green, blue: blue, alpha: alpha == 0 ? 1 : alpha)
28 | }
29 |
30 | static func argb(_ hex:UInt32) -> UIColor {
31 | let divisor = CGFloat(255)
32 | let alpha = CGFloat((hex & 0xFF000000) >> 24) / divisor
33 | let red = CGFloat((hex & 0x00FF0000) >> 16) / divisor
34 | let green = CGFloat((hex & 0x0000FF00) >> 8) / divisor
35 | let blue = CGFloat( hex & 0x000000FF ) / divisor
36 | return UIColor.init(red: red, green: green, blue: blue, alpha: alpha == 0 ? 1 : alpha)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Example/Pods/JRBaseKit/Sources/UIImage+JFColor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+JFColor.swift
3 | // JRBaseKit
4 | //
5 | // Created by 逸风 on 2021/10/10.
6 | //
7 |
8 | import UIKit
9 |
10 | extension UIImage: JFCompatible {}
11 | public extension JF where Base: UIImage {
12 | static func color(_ hex:UInt32, alpha:CGFloat = 1.0) -> UIImage {
13 | return UIImage(customColor: UIColor.jf.rgb(hex, alpha: alpha))
14 | }
15 | }
16 |
17 | public extension UIImage {
18 | @objc convenience init(customColor: UIColor,size: CGSize = CGSize(width: 1, height: 1)) {
19 | let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
20 | UIGraphicsBeginImageContext(rect.size)
21 | let context = UIGraphicsGetCurrentContext();
22 | context?.setFillColor(customColor.cgColor);
23 | context?.fill(rect)
24 | let image = UIGraphicsGetImageFromCurrentImageContext()
25 | UIGraphicsEndImageContext()
26 | guard let cgImage = image?.cgImage else {
27 | self.init()
28 | return
29 | }
30 | self.init(cgImage: cgImage)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Example/Pods/JRBaseKit/Sources/UIView+JFRect.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+JFRect.swift
3 | // JRBaseKit
4 | //
5 | // Created by 逸风 on 2021/10/10.
6 | //
7 | import UIKit
8 |
9 | extension UIView: JFCompatible {}
10 | public extension JF where Base: UIView {
11 |
12 | var top: CGFloat {
13 | get { return base.jf_top }
14 | set { base.jf_top = newValue }
15 | }
16 |
17 | var left: CGFloat {
18 | get { return base.jf_left }
19 | set { base.jf_left = newValue }
20 | }
21 |
22 | var bottom: CGFloat {
23 | get { return base.jf_bottom }
24 | set { base.jf_bottom = newValue }
25 | }
26 |
27 | var right: CGFloat {
28 | get { return base.jf_right }
29 | set { base.jf_right = newValue }
30 | }
31 |
32 | var centerX: CGFloat {
33 | get { return base.jf_centerX }
34 | set { base.jf_centerX = newValue }
35 | }
36 |
37 | var centerY: CGFloat {
38 | get { return base.jf_centerY }
39 | set { base.jf_centerY = newValue }
40 | }
41 |
42 | var width: CGFloat {
43 | get { return base.jf_width }
44 | set { base.jf_width = newValue }
45 | }
46 |
47 | var height: CGFloat {
48 | get { return base.jf_height }
49 | set { base.jf_height = newValue }
50 | }
51 |
52 | var origin: CGPoint {
53 | get { return base.jf_origin }
54 | set { base.jf_origin = newValue }
55 | }
56 |
57 | var size: CGSize {
58 | get { return base.jf_size }
59 | set { base.jf_size = newValue }
60 | }
61 |
62 | @available(iOS 10.0, *)
63 | static func shake() {
64 | UISelectionFeedbackGenerator().selectionChanged()
65 | }
66 |
67 | @available(iOS 10.0, *)
68 | func shake() {
69 | UISelectionFeedbackGenerator().selectionChanged()
70 | }
71 |
72 | }
73 |
74 | //MARK: - For OC
75 | public extension UIView {
76 | @objc var jf_top: CGFloat {
77 | get {
78 | return self.frame.origin.y
79 | }
80 | set {
81 | var frame:CGRect = self.frame
82 | frame.origin.y = newValue
83 | self.frame = frame
84 | }
85 | }
86 |
87 | @objc var jf_left: CGFloat {
88 | get {
89 | return self.frame.origin.x
90 | }
91 | set {
92 | var frame:CGRect = self.frame
93 | frame.origin.x = newValue
94 | self.frame = frame
95 | }
96 | }
97 |
98 | @objc var jf_bottom: CGFloat {
99 | get {
100 | return self.frame.origin.y + self.frame.size.height
101 | }
102 | set {
103 | var frame:CGRect = self.frame
104 | frame.origin.y = newValue - frame.size.height
105 | self.frame = frame
106 | }
107 | }
108 |
109 | @objc var jf_right: CGFloat {
110 | get {
111 | return self.frame.origin.x + self.frame.size.width
112 | }
113 | set {
114 | var frame:CGRect = self.frame
115 | frame.origin.x = newValue - frame.size.width
116 | self.frame = frame
117 | }
118 | }
119 |
120 | @objc var jf_centerX: CGFloat {
121 | get {
122 | return self.center.x
123 | }
124 | set {
125 | self.center = .init(x: newValue, y: self.center.y)
126 | }
127 | }
128 |
129 | @objc var jf_centerY: CGFloat {
130 | get {
131 | return self.center.y
132 | }
133 | set {
134 | self.center = .init(x: self.center.x, y: newValue)
135 | }
136 | }
137 |
138 | @objc var jf_width: CGFloat {
139 | get {
140 | return self.bounds.width
141 | }
142 | set {
143 | var frame:CGRect = self.frame
144 | frame.size.width = newValue
145 | self.frame = frame
146 | }
147 | }
148 |
149 | @objc var jf_height: CGFloat {
150 | get {
151 | return self.bounds.height
152 | }
153 | set {
154 | var frame:CGRect = self.frame
155 | frame.size.height = newValue
156 | self.frame = frame
157 | }
158 | }
159 |
160 | @objc var jf_origin: CGPoint {
161 | get {
162 | return self.frame.origin
163 | }
164 | set {
165 | var frame:CGRect = self.frame
166 | frame.origin = newValue
167 | self.frame = frame
168 | }
169 | }
170 |
171 | @objc var jf_size: CGSize {
172 | get {
173 | return self.frame.size
174 | }
175 | set {
176 | var frame:CGRect = self.frame
177 | frame.size = newValue
178 | self.frame = frame
179 | }
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/Example/Pods/Local Podspecs/JFPopup.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "JFPopup",
3 | "version": "1.5.4",
4 | "summary": "A Swift Popup Module help you popup your custom view easily",
5 | "description": "*JFPopup can help you popup your custom view with any way*\n*Like popup a Drawer, a dialog, a bottomSheet,*\n*Also support Objc, but you should writeJFPopup extension with youself, usage see example.\n*Support many General Kit:\n*Version 1.0.0 support a Wechat Style ActionSheet\n*Version 1.2.0 support a ToastView\n*Version 1.3.0 support a LodingView\n*Version 1.4.0 support a AlertView\n*In the feature, will support more popup view",
6 | "homepage": "https://github.com/JerryFans/JFPopup",
7 | "license": {
8 | "type": "MIT",
9 | "file": "LICENSE"
10 | },
11 | "authors": {
12 | "JerryFans": "fanjiarong_haohao@163.com"
13 | },
14 | "source": {
15 | "git": "https://github.com/JerryFans/JFPopup.git",
16 | "tag": "1.5.4"
17 | },
18 | "platforms": {
19 | "ios": "9.0"
20 | },
21 | "source_files": "Sources/JFPopup/Classes/**/*",
22 | "swift_versions": [
23 | "4.0"
24 | ],
25 | "dependencies": {
26 | "JRBaseKit": [
27 | "~> 1.1.1"
28 | ]
29 | },
30 | "resource_bundles": {
31 | "JFPopup": [
32 | "Sources/JFPopup/Resources/*.png"
33 | ]
34 | },
35 | "swift_version": "4.0"
36 | }
37 |
--------------------------------------------------------------------------------
/Example/Pods/Manifest.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - JFPopup (1.5.4):
3 | - JRBaseKit (~> 1.1.1)
4 | - JRBaseKit (1.1.1)
5 |
6 | DEPENDENCIES:
7 | - JFPopup (from `../`)
8 |
9 | SPEC REPOS:
10 | trunk:
11 | - JRBaseKit
12 |
13 | EXTERNAL SOURCES:
14 | JFPopup:
15 | :path: "../"
16 |
17 | SPEC CHECKSUMS:
18 | JFPopup: c3ada13bbda3cfe85bd71228e20a0b622eab72af
19 | JRBaseKit: e817f5516d9427f9dd249c99263f95fa260c3246
20 |
21 | PODFILE CHECKSUM: 3040b8d466bd38b0257fda44b4645a864d4520f5
22 |
23 | COCOAPODS: 1.11.3
24 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JFPopup/JFPopup-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.5.4
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JFPopup/JFPopup-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_JFPopup : NSObject
3 | @end
4 | @implementation PodsDummy_JFPopup
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JFPopup/JFPopup-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JFPopup/JFPopup-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double JFPopupVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char JFPopupVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JFPopup/JFPopup.debug.xcconfig:
--------------------------------------------------------------------------------
1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/JFPopup
3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/JRBaseKit"
4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
5 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
6 | OTHER_LDFLAGS = $(inherited) -framework "JRBaseKit"
7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
8 | PODS_BUILD_DIR = ${BUILD_DIR}
9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_ROOT = ${SRCROOT}
11 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../..
12 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
13 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
14 | SKIP_INSTALL = YES
15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
16 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JFPopup/JFPopup.modulemap:
--------------------------------------------------------------------------------
1 | framework module JFPopup {
2 | umbrella header "JFPopup-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JFPopup/JFPopup.release.xcconfig:
--------------------------------------------------------------------------------
1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/JFPopup
3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/JRBaseKit"
4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
5 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
6 | OTHER_LDFLAGS = $(inherited) -framework "JRBaseKit"
7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
8 | PODS_BUILD_DIR = ${BUILD_DIR}
9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_ROOT = ${SRCROOT}
11 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../..
12 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
13 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
14 | SKIP_INSTALL = YES
15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
16 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JFPopup/ResourceBundle-JFPopup-JFPopup-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleIdentifier
8 | ${PRODUCT_BUNDLE_IDENTIFIER}
9 | CFBundleInfoDictionaryVersion
10 | 6.0
11 | CFBundleName
12 | ${PRODUCT_NAME}
13 | CFBundlePackageType
14 | BNDL
15 | CFBundleShortVersionString
16 | 1.5.4
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JRBaseKit/JRBaseKit-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.1.1
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JRBaseKit/JRBaseKit-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_JRBaseKit : NSObject
3 | @end
4 | @implementation PodsDummy_JRBaseKit
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JRBaseKit/JRBaseKit-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JRBaseKit/JRBaseKit-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double JRBaseKitVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char JRBaseKitVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JRBaseKit/JRBaseKit.debug.xcconfig:
--------------------------------------------------------------------------------
1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/JRBaseKit
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
5 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
6 | PODS_BUILD_DIR = ${BUILD_DIR}
7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
8 | PODS_ROOT = ${SRCROOT}
9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/JRBaseKit
10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
11 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
12 | SKIP_INSTALL = YES
13 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
14 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JRBaseKit/JRBaseKit.modulemap:
--------------------------------------------------------------------------------
1 | framework module JRBaseKit {
2 | umbrella header "JRBaseKit-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/JRBaseKit/JRBaseKit.release.xcconfig:
--------------------------------------------------------------------------------
1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/JRBaseKit
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
5 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
6 | PODS_BUILD_DIR = ${BUILD_DIR}
7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
8 | PODS_ROOT = ${SRCROOT}
9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/JRBaseKit
10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
11 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
12 | SKIP_INSTALL = YES
13 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
14 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Example/Pods-JFPopup_Example-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Example/Pods-JFPopup_Example-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## JFPopup
5 |
6 | Copyright (c) 2021 JerryFans
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy
9 | of this software and associated documentation files (the "Software"), to deal
10 | in the Software without restriction, including without limitation the rights
11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | copies of the Software, and to permit persons to whom the Software is
13 | furnished to do so, subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice shall be included in
16 | all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | THE SOFTWARE.
25 |
26 |
27 | ## JRBaseKit
28 |
29 | Copyright (c) 2021 JerryFans
30 |
31 | Permission is hereby granted, free of charge, to any person obtaining a copy
32 | of this software and associated documentation files (the "Software"), to deal
33 | in the Software without restriction, including without limitation the rights
34 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
35 | copies of the Software, and to permit persons to whom the Software is
36 | furnished to do so, subject to the following conditions:
37 |
38 | The above copyright notice and this permission notice shall be included in
39 | all copies or substantial portions of the Software.
40 |
41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
44 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
46 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
47 | THE SOFTWARE.
48 |
49 | Generated by CocoaPods - https://cocoapods.org
50 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Example/Pods-JFPopup_Example-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Copyright (c) 2021 JerryFans <fanjiarong_haohao@163.com>
18 |
19 | Permission is hereby granted, free of charge, to any person obtaining a copy
20 | of this software and associated documentation files (the "Software"), to deal
21 | in the Software without restriction, including without limitation the rights
22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | copies of the Software, and to permit persons to whom the Software is
24 | furnished to do so, subject to the following conditions:
25 |
26 | The above copyright notice and this permission notice shall be included in
27 | all copies or substantial portions of the Software.
28 |
29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35 | THE SOFTWARE.
36 |
37 | License
38 | MIT
39 | Title
40 | JFPopup
41 | Type
42 | PSGroupSpecifier
43 |
44 |
45 | FooterText
46 | Copyright (c) 2021 JerryFans
47 |
48 | Permission is hereby granted, free of charge, to any person obtaining a copy
49 | of this software and associated documentation files (the "Software"), to deal
50 | in the Software without restriction, including without limitation the rights
51 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
52 | copies of the Software, and to permit persons to whom the Software is
53 | furnished to do so, subject to the following conditions:
54 |
55 | The above copyright notice and this permission notice shall be included in
56 | all copies or substantial portions of the Software.
57 |
58 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
61 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
62 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
63 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
64 | THE SOFTWARE.
65 |
66 | License
67 | MIT
68 | Title
69 | JRBaseKit
70 | Type
71 | PSGroupSpecifier
72 |
73 |
74 | FooterText
75 | Generated by CocoaPods - https://cocoapods.org
76 | Title
77 |
78 | Type
79 | PSGroupSpecifier
80 |
81 |
82 | StringsTable
83 | Acknowledgements
84 | Title
85 | Acknowledgements
86 |
87 |
88 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Example/Pods-JFPopup_Example-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_JFPopup_Example : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_JFPopup_Example
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Example/Pods-JFPopup_Example-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 | set -u
4 | set -o pipefail
5 |
6 | function on_error {
7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure"
8 | }
9 | trap 'on_error $LINENO' ERR
10 |
11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then
12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy
13 | # frameworks to, so exit 0 (signalling the script phase was successful).
14 | exit 0
15 | fi
16 |
17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
19 |
20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
22 | BCSYMBOLMAP_DIR="BCSymbolMaps"
23 |
24 |
25 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
26 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
27 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
28 |
29 | # Copies and strips a vendored framework
30 | install_framework()
31 | {
32 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
33 | local source="${BUILT_PRODUCTS_DIR}/$1"
34 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
35 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
36 | elif [ -r "$1" ]; then
37 | local source="$1"
38 | fi
39 |
40 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
41 |
42 | if [ -L "${source}" ]; then
43 | echo "Symlinked..."
44 | source="$(readlink "${source}")"
45 | fi
46 |
47 | if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then
48 | # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied
49 | find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do
50 | echo "Installing $f"
51 | install_bcsymbolmap "$f" "$destination"
52 | rm "$f"
53 | done
54 | rmdir "${source}/${BCSYMBOLMAP_DIR}"
55 | fi
56 |
57 | # Use filter instead of exclude so missing patterns don't throw errors.
58 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
59 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
60 |
61 | local basename
62 | basename="$(basename -s .framework "$1")"
63 | binary="${destination}/${basename}.framework/${basename}"
64 |
65 | if ! [ -r "$binary" ]; then
66 | binary="${destination}/${basename}"
67 | elif [ -L "${binary}" ]; then
68 | echo "Destination binary is symlinked..."
69 | dirname="$(dirname "${binary}")"
70 | binary="${dirname}/$(readlink "${binary}")"
71 | fi
72 |
73 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
74 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
75 | strip_invalid_archs "$binary"
76 | fi
77 |
78 | # Resign the code if required by the build settings to avoid unstable apps
79 | code_sign_if_enabled "${destination}/$(basename "$1")"
80 |
81 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
82 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
83 | local swift_runtime_libs
84 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u)
85 | for lib in $swift_runtime_libs; do
86 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
87 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
88 | code_sign_if_enabled "${destination}/${lib}"
89 | done
90 | fi
91 | }
92 | # Copies and strips a vendored dSYM
93 | install_dsym() {
94 | local source="$1"
95 | warn_missing_arch=${2:-true}
96 | if [ -r "$source" ]; then
97 | # Copy the dSYM into the targets temp dir.
98 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
99 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
100 |
101 | local basename
102 | basename="$(basename -s .dSYM "$source")"
103 | binary_name="$(ls "$source/Contents/Resources/DWARF")"
104 | binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}"
105 |
106 | # Strip invalid architectures from the dSYM.
107 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then
108 | strip_invalid_archs "$binary" "$warn_missing_arch"
109 | fi
110 | if [[ $STRIP_BINARY_RETVAL == 0 ]]; then
111 | # Move the stripped file into its final destination.
112 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
113 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
114 | else
115 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
116 | mkdir -p "${DWARF_DSYM_FOLDER_PATH}"
117 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM"
118 | fi
119 | fi
120 | }
121 |
122 | # Used as a return value for each invocation of `strip_invalid_archs` function.
123 | STRIP_BINARY_RETVAL=0
124 |
125 | # Strip invalid architectures
126 | strip_invalid_archs() {
127 | binary="$1"
128 | warn_missing_arch=${2:-true}
129 | # Get architectures for current target binary
130 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
131 | # Intersect them with the architectures we are building for
132 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
133 | # If there are no archs supported by this binary then warn the user
134 | if [[ -z "$intersected_archs" ]]; then
135 | if [[ "$warn_missing_arch" == "true" ]]; then
136 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
137 | fi
138 | STRIP_BINARY_RETVAL=1
139 | return
140 | fi
141 | stripped=""
142 | for arch in $binary_archs; do
143 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then
144 | # Strip non-valid architectures in-place
145 | lipo -remove "$arch" -output "$binary" "$binary"
146 | stripped="$stripped $arch"
147 | fi
148 | done
149 | if [[ "$stripped" ]]; then
150 | echo "Stripped $binary of architectures:$stripped"
151 | fi
152 | STRIP_BINARY_RETVAL=0
153 | }
154 |
155 | # Copies the bcsymbolmap files of a vendored framework
156 | install_bcsymbolmap() {
157 | local bcsymbolmap_path="$1"
158 | local destination="${BUILT_PRODUCTS_DIR}"
159 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}""
160 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"
161 | }
162 |
163 | # Signs a framework with the provided identity
164 | code_sign_if_enabled() {
165 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
166 | # Use the current code_sign_identity
167 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
168 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"
169 |
170 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
171 | code_sign_cmd="$code_sign_cmd &"
172 | fi
173 | echo "$code_sign_cmd"
174 | eval "$code_sign_cmd"
175 | fi
176 | }
177 |
178 | if [[ "$CONFIGURATION" == "Debug" ]]; then
179 | install_framework "${BUILT_PRODUCTS_DIR}/JFPopup/JFPopup.framework"
180 | install_framework "${BUILT_PRODUCTS_DIR}/JRBaseKit/JRBaseKit.framework"
181 | fi
182 | if [[ "$CONFIGURATION" == "Release" ]]; then
183 | install_framework "${BUILT_PRODUCTS_DIR}/JFPopup/JFPopup.framework"
184 | install_framework "${BUILT_PRODUCTS_DIR}/JRBaseKit/JRBaseKit.framework"
185 | fi
186 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
187 | wait
188 | fi
189 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Example/Pods-JFPopup_Example-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_JFPopup_ExampleVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_JFPopup_ExampleVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Example/Pods-JFPopup_Example.debug.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/JFPopup" "${PODS_CONFIGURATION_BUILD_DIR}/JRBaseKit"
4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/JFPopup/JFPopup.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/JRBaseKit/JRBaseKit.framework/Headers"
6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
7 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
8 | OTHER_LDFLAGS = $(inherited) -framework "JFPopup" -framework "JRBaseKit"
9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
10 | PODS_BUILD_DIR = ${BUILD_DIR}
11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
13 | PODS_ROOT = ${SRCROOT}/Pods
14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
16 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Example/Pods-JFPopup_Example.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_JFPopup_Example {
2 | umbrella header "Pods-JFPopup_Example-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Example/Pods-JFPopup_Example.release.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/JFPopup" "${PODS_CONFIGURATION_BUILD_DIR}/JRBaseKit"
4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/JFPopup/JFPopup.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/JRBaseKit/JRBaseKit.framework/Headers"
6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
7 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
8 | OTHER_LDFLAGS = $(inherited) -framework "JFPopup" -framework "JRBaseKit"
9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
10 | PODS_BUILD_DIR = ${BUILD_DIR}
11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
13 | PODS_ROOT = ${SRCROOT}/Pods
14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
16 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Tests/Pods-JFPopup_Tests-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Tests/Pods-JFPopup_Tests-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 | Generated by CocoaPods - https://cocoapods.org
4 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Tests/Pods-JFPopup_Tests-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Generated by CocoaPods - https://cocoapods.org
18 | Title
19 |
20 | Type
21 | PSGroupSpecifier
22 |
23 |
24 | StringsTable
25 | Acknowledgements
26 | Title
27 | Acknowledgements
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Tests/Pods-JFPopup_Tests-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_JFPopup_Tests : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_JFPopup_Tests
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Tests/Pods-JFPopup_Tests-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_JFPopup_TestsVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_JFPopup_TestsVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Tests/Pods-JFPopup_Tests.debug.xcconfig:
--------------------------------------------------------------------------------
1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/JFPopup" "${PODS_CONFIGURATION_BUILD_DIR}/JRBaseKit"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/JFPopup/JFPopup.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/JRBaseKit/JRBaseKit.framework/Headers"
5 | OTHER_LDFLAGS = $(inherited) -framework "JFPopup" -framework "JRBaseKit"
6 | PODS_BUILD_DIR = ${BUILD_DIR}
7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
9 | PODS_ROOT = ${SRCROOT}/Pods
10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
12 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Tests/Pods-JFPopup_Tests.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_JFPopup_Tests {
2 | umbrella header "Pods-JFPopup_Tests-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-JFPopup_Tests/Pods-JFPopup_Tests.release.xcconfig:
--------------------------------------------------------------------------------
1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/JFPopup" "${PODS_CONFIGURATION_BUILD_DIR}/JRBaseKit"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/JFPopup/JFPopup.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/JRBaseKit/JRBaseKit.framework/Headers"
5 | OTHER_LDFLAGS = $(inherited) -framework "JFPopup" -framework "JRBaseKit"
6 | PODS_BUILD_DIR = ${BUILD_DIR}
7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
9 | PODS_ROOT = ${SRCROOT}/Pods
10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
12 |
--------------------------------------------------------------------------------
/Example/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Example/Tests/Tests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | import JFPopup
3 |
4 | class Tests: XCTestCase {
5 |
6 | override func setUp() {
7 | super.setUp()
8 | // Put setup code here. This method is called before the invocation of each test method in the class.
9 | }
10 |
11 | override func tearDown() {
12 | // Put teardown code here. This method is called after the invocation of each test method in the class.
13 | super.tearDown()
14 | }
15 |
16 | func testExample() {
17 | // This is an example of a functional test case.
18 | XCTAssert(true, "Pass")
19 | }
20 |
21 | func testPerformanceExample() {
22 | // This is an example of a performance test case.
23 | self.measure() {
24 | // Put the code you want to measure the time of here.
25 | }
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/JFActionSheet.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint JFToast.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = 'JFActionSheet'
11 | s.version = '1.3.0'
12 | s.summary = 'JFActionSheet is a part of JFPopup Module help you popup a wechat style ActionSheet view easily In Swift'
13 |
14 | # This description is used to generate tags and improve search results.
15 | # * Think: What does it do? Why did you write it? What is the focus?
16 | # * Try to keep it short, snappy and to the point.
17 | # * Write the description between the DESC delimiters below.
18 | # * Finally, don't worry about the indent, CocoaPods strips it!
19 |
20 | s.description = <<-DESC
21 | *support title and sub title*
22 | *support auto fill cancel button*
23 | *suport auto dismiss when you click*
24 | DESC
25 |
26 | s.homepage = 'https://github.com/JerryFans/JFPopup'
27 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
28 | s.license = { :type => 'MIT', :file => 'LICENSE' }
29 | s.author = { 'JerryFans' => 'fanjiarong_haohao@163.com' }
30 | s.source = { :git => 'https://github.com/JerryFans/JFPopup.git', :tag => s.version.to_s }
31 | # s.social_media_url = 'https://twitter.com/'
32 |
33 | s.ios.deployment_target = '9.0'
34 |
35 | s.source_files = 'JFPopup/Classes/Core/*','JFPopup/Classes/General/ActionSheet/*'
36 |
37 | s.swift_version = ['4.0']
38 |
39 | s.dependency 'JRBaseKit', '~> 0.9.0'
40 |
41 | # s.resource_bundles = {
42 | # 'JFPopup' => ['JFPopup/Assets/*.png'],
43 | # }
44 |
45 | # s.public_header_files = 'Pod/Classes/**/*.h'
46 | # s.frameworks = 'UIKit', 'MapKit'
47 |
48 | end
49 |
--------------------------------------------------------------------------------
/JFPopup.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint JFPopup.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = 'JFPopup'
11 | s.version = '1.5.7'
12 | s.summary = 'A Swift Popup Module help you popup your custom view easily'
13 |
14 | # This description is used to generate tags and improve search results.
15 | # * Think: What does it do? Why did you write it? What is the focus?
16 | # * Try to keep it short, snappy and to the point.
17 | # * Write the description between the DESC delimiters below.
18 | # * Finally, don't worry about the indent, CocoaPods strips it!
19 |
20 | s.description = <<-DESC
21 | *JFPopup can help you popup your custom view with any way*
22 | *Like popup a Drawer, a dialog, a bottomSheet,*
23 | *Also support Objc, but you should writeJFPopup extension with youself, usage see example.
24 | *Support many General Kit:
25 | *Version 1.0.0 support a Wechat Style ActionSheet
26 | *Version 1.2.0 support a ToastView
27 | *Version 1.3.0 support a LodingView
28 | *Version 1.4.0 support a AlertView
29 | *In the feature, will support more popup view
30 | DESC
31 |
32 | s.homepage = 'https://github.com/JerryFans/JFPopup'
33 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
34 | s.license = { :type => 'MIT', :file => 'LICENSE' }
35 | s.author = { 'JerryFans' => 'fanjiarong_haohao@163.com' }
36 | s.source = { :git => 'https://github.com/JerryFans/JFPopup.git', :tag => s.version.to_s }
37 | # s.social_media_url = 'https://twitter.com/'
38 |
39 | s.ios.deployment_target = '9.0'
40 |
41 | s.source_files = 'Sources/JFPopup/Classes/**/*'
42 |
43 | s.swift_version = ['4.0']
44 |
45 | s.dependency 'JRBaseKit', '~> 1.1.1'
46 |
47 | s.resource_bundles = {
48 | 'JFPopup' => ['Sources/JFPopup/Resources/*.png'],
49 | }
50 |
51 | # s.public_header_files = 'Pod/Classes/**/*.h'
52 | # s.frameworks = 'UIKit', 'MapKit'
53 |
54 | end
55 |
--------------------------------------------------------------------------------
/JFToast.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint JFToast.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = 'JFToast'
11 | s.version = '1.2.0'
12 | s.summary = 'JFToast is a part of JFPopup Module help you popup toast view easily In Swift'
13 |
14 | # This description is used to generate tags and improve search results.
15 | # * Think: What does it do? Why did you write it? What is the focus?
16 | # * Try to keep it short, snappy and to the point.
17 | # * Write the description between the DESC delimiters below.
18 | # * Finally, don't worry about the indent, CocoaPods strips it!
19 |
20 | s.description = <<-DESC
21 | *support three position, top center and bottom*
22 | *support only hint*
23 | *support only icon*
24 | *support hint + icon*
25 | DESC
26 |
27 | s.homepage = 'https://github.com/JerryFans/JFPopup'
28 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
29 | s.license = { :type => 'MIT', :file => 'LICENSE' }
30 | s.author = { 'JerryFans' => 'fanjiarong_haohao@163.com' }
31 | s.source = { :git => 'https://github.com/JerryFans/JFPopup.git', :tag => s.version.to_s }
32 | # s.social_media_url = 'https://twitter.com/'
33 |
34 | s.ios.deployment_target = '9.0'
35 |
36 | s.source_files = 'Sources/JFPopup/Classes/Core/*','JFPopup/Classes/General/Toast/*'
37 |
38 | s.swift_version = ['4.0']
39 |
40 | s.dependency 'JRBaseKit', '~> 1.1.1'
41 |
42 | s.resource_bundles = {
43 | 'JFPopup' => ['Sources/JFPopup/Resources/*.png'],
44 | }
45 |
46 | # s.public_header_files = 'Pod/Classes/**/*.h'
47 | # s.frameworks = 'UIKit', 'MapKit'
48 |
49 | end
50 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2021 JerryFans
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "jrbasekit",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/JerryFans/JRBaseKit.git",
7 | "state" : {
8 | "revision" : "38674789638f65db94778e18ac01b1134573d788",
9 | "version" : "1.1.0"
10 | }
11 | }
12 | ],
13 | "version" : 2
14 | }
15 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.8
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "JFPopup",
8 | platforms: [.iOS(.v11)],
9 | products: [
10 | .library(
11 | name: "JFPopup",
12 | targets: ["JFPopup"]),
13 | ],
14 | dependencies: [
15 | .package(url: "https://github.com/JerryFans/JRBaseKit.git", from: "1.1.0"),
16 | ],
17 | targets: [
18 | .target(
19 | name: "JFPopup",
20 | dependencies: [
21 | .product(name: "JRBaseKit", package: "JRBaseKit")
22 | ],
23 | resources: [.process("Resources")])
24 | ]
25 | )
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JFPopup
2 |
3 | [](https://cocoapods.org/pods/JFPopup)
4 | [](https://cocoapods.org/pods/JFPopup)
5 | [](https://cocoapods.org/pods/JFPopup)
6 | [](https://cocoapods.org/pods/JFPopup)
7 | [](https://cocoapods.org/pods/JFPopup)
8 |
9 | JFPopup is a Swift Module help you popup your custom view easily.
10 |
11 | Support 3 way to popup, Drawer, Dialog and BottomSheet.
12 |
13 | ## Installation
14 |
15 | ### cocoapods
16 |
17 | ```ruby
18 | pod 'JFPopup', '1.5.4'
19 | ```
20 |
21 | ### Swift Package Manager
22 |
23 | After 1.5.4
24 |
25 | ```ruby
26 |
27 | https://github.com/JerryFans/JFPopup.git
28 |
29 | ```
30 |
31 |
32 | ## Example
33 |
34 | To run the example project, clone the repo, and run `pod install` from the Example directory first.
35 |
36 | Toast Style Support iPhone 14 Pro+ Dynamic Island(灵动岛)
37 |
38 | 
39 |
40 | ## Quick Create your popup view
41 |
42 | ### Dialog
43 |
44 | 对话框模式,类似UIAlertConroller, 你也可以编写你的自定义AlertView
45 |
46 | ```
47 | self.popup.dialog {
48 | let v = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
49 | v.backgroundColor = .red
50 | return v
51 | }
52 | ```
53 |
54 | 
55 | 
56 |
57 | ### Drawer
58 | 抽屉模式,支持左右抽屉,宽度自定义,最大可以全屏,
59 |
60 | ```
61 | //default left
62 | self.popup.drawer {
63 | let v = DrawerView(frame: CGRect(x: 0, y: 0, width: CGSize.jf.screenWidth(), height: CGSize.jf.screenHeight()))
64 | v.closeHandle = { [weak self] in
65 | self?.popup.dismiss()
66 | }
67 | return v
68 | }
69 |
70 | self.popup.drawer(with: .right) {
71 | let v = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: CGSize.jf.screenHeight()))
72 | v.backgroundColor = .red
73 | return v
74 | }
75 | ```
76 |
77 | 
78 |
79 | ### Bottomsheet
80 |
81 | 类似Flutter Bottomsheet, 底部往上弹出一个容器。 你也可以给予此创建你的个人自定义风格UIActionSheet. 底下有微信风格的组件已封装
82 |
83 | ```
84 | self.popup.bottomSheet {
85 | let v = UIView(frame: CGRect(x: 0, y: 0, width: CGSize.jf.screenWidth(), height: 300))
86 | v.backgroundColor = .red
87 | return v
88 | }
89 | ```
90 |
91 | 
92 |
93 | ### 通用组件
94 |
95 | - v1.0,暂时只有一款微信风格ActionSheet, 基于上面bottomSheet打造,后续会基于上面基础popup,打造更多基础组件
96 |
97 | - v1.1 新增JFToastView, 支持多种Toast
98 |
99 | - v1.3 新增Loading 样式弹窗, 支持多种格式,详情看下面
100 | - v1.4 新增微信 AlertView 样式弹窗, 支持多种格式,详情看下面
101 | - v1.5 JFToastView 支持iPhone14 Pro系列 灵动岛 (Toast Style Support iPhone14 Pro + Dynamic Island)
102 | - v1.5.2 修复偶现可能无法获取顶层keywindow导致的问题
103 | - v1.5.3 修复连续触发Loading show and hide偶现无法移除View的问题
104 |
105 | ### AlertView
106 |
107 | 1、默认风格,自带取消按钮
108 |
109 | ```
110 | JFPopupView.popup.alert {[
111 | .title("温馨提示"),
112 | .subTitle("我是默认风格,自带取消按钮"),
113 | .confirmAction([
114 | .text("知道了"),
115 | .tapActionCallback({
116 | JFPopupView.popup.toast(hit: "我知道了")
117 | })
118 | ])
119 | ]}
120 | ```
121 |
122 | 2、Title 和 SubTitle可以二选一,不要Cancel
123 |
124 | ```
125 | JFPopupView.popup.alert {[
126 | .subTitle("我是Title 和 SubTitle可以二选一,单个按钮"),
127 | .showCancel(false),
128 | .confirmAction([
129 | .text("知道了"),
130 | .tapActionCallback({
131 | JFPopupView.popup.toast(hit: "我知道了")
132 | })
133 | ])
134 | ]}
135 | ```
136 |
137 | 3、自定义颜色、不用动画等
138 |
139 | ```
140 | JFPopupView.popup.alert {[
141 | .title("不同标题颜色"),
142 | .titleColor(.red),
143 | .withoutAnimation(true),
144 | .subTitle("我是完全自定义的,标题颜色,action颜色,文本都支持修改,不带动画"),
145 | .subTitleColor(.black),
146 | .cancelAction([.textColor(.blue),.text("我是取消超出文本裁切"),.tapActionCallback({
147 | JFPopupView.popup.toast(hit: "点击了取消")
148 | })]),
149 | .confirmAction([
150 | .text("我是确定"),
151 | .textColor(.red),
152 | .tapActionCallback({
153 | JFPopupView.popup.toast(hit: "点击了确定")
154 | })
155 | ])
156 | ]}
157 | ```
158 |
159 | 4、也可以Present到VC 效果一样
160 |
161 | ```
162 | //self 是 UIViewController
163 | self.popup.alert {
164 | [.title("我是从VC Present 出来的"),
165 | .subTitle("用法和View一致,只是一个是self.popup.alert(self是UIViewControll),一个是JFPopupView.popup.alert"),
166 | .confirmAction([
167 | .text("知道了"),
168 | .tapActionCallback({
169 | JFPopupView.popup.toast(hit: "知道了")
170 | })
171 | ])
172 | ]
173 | }
174 | ```
175 |
176 | 
177 |
178 | ### Loading
179 |
180 | ```
181 | 1、only loading icon
182 |
183 | JFPopupView.popup.loading()
184 | DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
185 | JFPopupView.popup.hideLoading()
186 | }
187 |
188 | 2、 loading + hit
189 |
190 | JFPopupView.popup.loading(hit: "正在载入视频")
191 | DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
192 | JFPopupView.popup.hideLoading()
193 | }
194 |
195 | 3、 loading in view (default in keywindow)
196 |
197 | //只支持 controller.view, 默认keywindow
198 | JFPopupView.popup.loading(hit: "加载中", inView: self.view)
199 | DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
200 | JFPopupView.popup.hideLoading()
201 | }
202 |
203 |
204 | ```
205 |
206 | 
207 |
208 | ### Toast
209 |
210 | 已适配iPhone 14 Pro系列 灵动岛 (support dynamic island toast)
211 |
212 | ```
213 | 1、only hit
214 |
215 | JFPopupView.popup.toast(hit: "普通toast,默认superview可以响应")
216 |
217 | 2、 hit + icon (内置success和fail, 支持自定义)
218 |
219 | JFPopupView.popup.toast(hit: "支付成功", icon: .success)
220 |
221 |
222 | JFPopupView.popup.toast(hit: "自定义Icon", icon: .imageName(name: "face"))
223 |
224 | 3、完全自定义
225 |
226 | JFPopupView.popup.toast {
227 | [
228 | .hit("不响应super view,带背景色,加大时长,不用动画,在当前view弹出,position top"),
229 | .enableUserInteraction(true),
230 | .bgColor(UIColor.jf.rgb(0x000000,alpha: 0.3)),
231 | .autoDismissDuration(.seconds(value: 3)),
232 | .mainContainer(self.view),
233 | .withoutAnimation(true),
234 | .position(.top)
235 | ]
236 | }
237 |
238 | ```
239 |
240 | 
241 | 
242 |
243 |
244 | ### ActionSheet
245 |
246 | ```
247 | self.popup.actionSheet {
248 | [
249 | JFPopupAction(with: "拍摄", subTitle: "照片或视频照片", clickActionCallBack: { [weak self] in
250 | self?.pushVC()
251 | }),
252 | JFPopupAction(with: "从手机相册选择", subTitle: nil, clickActionCallBack: {
253 |
254 | }),
255 | JFPopupAction(with: "用秒剪制作视频", subTitle: nil, clickActionCallBack: {
256 |
257 | }),
258 | ]
259 | }
260 | ```
261 |
262 | 
263 |
264 | ## VC模式创建
265 |
266 | 此方法推荐兼容OC情况下使用,或者你的popup View代码量非常大 需要一个vc来管理。
267 |
268 | 继承写法 (类似继承UITableView,dataSoure写在内部)
269 |
270 | ```
271 | var config = JFPopupConfig.bottomSheet
272 | config.isDismissible = false
273 | let vc = TestCustomViewController(with: config)
274 | vc.show(with: self)
275 | ```
276 | 闭包写法
277 |
278 | ```
279 | var config = JFPopupConfig.dialog
280 | config.bgColor = .clear
281 | let vc = JFPopupController(with: config, popupProtocol: self) {
282 | let view: UIView = {
283 | let view = UIView()
284 | view.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
285 | view.layer.cornerRadius = 12
286 | view.backgroundColor = .black
287 | return view
288 | }()
289 | return view
290 | }
291 | vc.show(with: self)
292 | ```
293 |
294 | ## Author
295 |
296 | JerryFans, fanjiarong_haohao@163.com
297 |
298 | ## License
299 |
300 | JFPopup is available under the MIT license. See the LICENSE file for more info.
301 |
302 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryFans/JFPopup/8b0227215e754218280667cd656e22abc4d62821/Sources/JFPopup/Classes/.gitkeep
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/Core/JFPopup.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Popup.swift
3 | // PopupKit
4 | //
5 | // Created by 逸风 on 2021/10/9.
6 | //
7 | import JRBaseKit
8 | import UIKit
9 |
10 | fileprivate let supportDynamicIsLandList = [
11 | "iPhone 14 Pro",
12 | "iPhone 14 Pro Max"
13 | ]
14 |
15 | public var JFIsSupportDynamicIsLand: Bool {
16 | get {
17 | var t: CGFloat = 0
18 | if #available(iOS 11.0, *) {
19 | t = UIApplication.shared.keyWindow?.safeAreaInsets.top ?? 0
20 | }
21 | return t == 59.0
22 | }
23 | }
24 |
25 | public struct JFPopup {
26 | public let base: Base
27 | init(_ base: Base) {
28 | self.base = base
29 | }
30 | }
31 |
32 | public protocol JFPopupCompatible {}
33 | public extension JFPopupCompatible {
34 | static var popup: JFPopup.Type {
35 | set {}
36 | get { JFPopup.self }
37 | }
38 | var popup: JFPopup {
39 | set {}
40 | get { JFPopup(self) }
41 | }
42 | }
43 |
44 | public protocol JFPopupProtocol {
45 | var dataSource: JFPopupDataSource? { get set }
46 | var popupProtocol: JFPopupAnimationProtocol? { get set }
47 | var container: UIView? { get set }
48 | var config: JFPopupConfig { get set }
49 | func autoDismissHandle()
50 | func dismissPopupView(completion: @escaping ((_ isFinished: Bool) -> ()))
51 | }
52 |
53 | @objc public enum JFPopupAnimationDirection: Int {
54 | case unowned
55 | case left
56 | case right
57 | }
58 |
59 | @objc public enum JFPopupAnimationType: Int {
60 | case dialog
61 | case bottomSheet
62 | case drawer
63 | }
64 |
65 | @objc public protocol JFPopupDataSource: AnyObject {
66 | @objc func viewForContainer() -> UIView?
67 | }
68 |
69 | public protocol JFPopupAnimationProtocol: AnyObject {
70 | func present(with transitonContext: UIViewControllerContextTransitioning?, config: JFPopupConfig, contianerView: UIView, completion: ((_ isFinished: Bool) -> ())?)
71 | func dismiss(with transitonContext: UIViewControllerContextTransitioning?, config: JFPopupConfig, contianerView: UIView?, completion: ((_ isFinished: Bool) -> ())?)
72 | }
73 |
74 | public enum JFTimerDuration {
75 |
76 | ///纳秒
77 | case nanoseconds(value: UInt32)
78 | ///微妙
79 | case microseconds(value: UInt32)
80 | ///毫秒
81 | case milliseconds(value: UInt32)
82 | ///秒
83 | case seconds(value: UInt32)
84 | ///分钟
85 | case minutes(value: UInt32)
86 |
87 | public func timeDuration() -> DispatchTimeInterval {
88 | switch self {
89 | case .nanoseconds(value: let value):
90 | return .nanoseconds(Int(value))
91 | case .microseconds(value: let value):
92 | return .microseconds(Int(value))
93 | case .milliseconds(value: let value):
94 | return .milliseconds(Int(value))
95 | case .seconds(value: let value):
96 | return .seconds(Int(value))
97 | case .minutes(value: let value):
98 | return .seconds(Int(value) * 60)
99 | }
100 | }
101 | }
102 |
103 | public enum JFToastPosition {
104 | case center
105 | case top
106 | case bottom
107 | case dynamicIsland //新增灵动岛位置动画
108 | }
109 |
110 | public struct JFPopupConfig {
111 |
112 | ///background view colod
113 | public var bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4)
114 | ///enableUserInteraction, if true, your gesture can not transmit to super view
115 | public var enableUserInteraction = true
116 | ///popup style
117 | public var animationType: JFPopupAnimationType = .dialog
118 | ///popup the view without animation
119 | public var withoutAnimation = false
120 | ///if trie tap background can dismiss popup view
121 | public var isDismissible = true
122 | /// enable drag gesture
123 | public var enableDrag = true
124 | /// use in drawer type , from left or right direction
125 | public var direction: JFPopupAnimationDirection = .unowned
126 |
127 | ///if true will auto dismiss popup view, use duration, default is false
128 | public var enableAutoDismiss = false
129 | ///auto dismiss duration, default is 2 seconds
130 | public var autoDismissDuration: JFTimerDuration = .seconds(value: 2)
131 |
132 | ///toast view position
133 | public var toastPosition: JFToastPosition = JFIsSupportDynamicIsLand ? .dynamicIsland : .center
134 |
135 | ///show with a absolute rect , only use in dialog mode popup
136 | public var absoluteRect: CGRect?
137 |
138 | /// static style config
139 | public static var dialog = JFPopupConfig(enableDrag: false)
140 | public static var bottomSheet = JFPopupConfig(animationType: .bottomSheet)
141 | public static var drawer = JFPopupConfig(animationType: .drawer, direction: .left)
142 | }
143 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/Core/JFPopupAnimation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JFPopupAnimation.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/9.
6 | //
7 |
8 | import UIKit
9 | import QuartzCore
10 |
11 | class JFPopupAnimation: NSObject {
12 |
13 | static func present(with transitonContext: UIViewControllerContextTransitioning?, config: JFPopupConfig, contianerView: UIView, completion: ((_ isFinished: Bool) -> ())?) {
14 | let type = config.animationType
15 | switch type {
16 | case .bottomSheet:
17 | do {
18 | contianerView.frame.origin.y = CGSize.jf.screenSize().height
19 | contianerView.layoutIfNeeded()
20 | UIView.animate(withDuration: 0.25, animations: {
21 | contianerView.frame.origin.y = CGSize.jf.screenSize().height - contianerView.frame.size.height
22 | contianerView.layoutIfNeeded()
23 | }) { (finished) in
24 | transitonContext?.completeTransition(true)
25 | completion?(finished)
26 | }
27 | }
28 | break
29 | case .dialog:
30 | do {
31 | let originSize = contianerView.jf.size
32 | if config.toastPosition == .dynamicIsland {
33 | contianerView.jf_size = CGSize(width: 120, height: 34)
34 | contianerView.center = CGPoint(x: CGSize.jf.screenSize().width / 2, y: 27)
35 | }
36 | contianerView.layoutIfNeeded()
37 | let updateV = {
38 | contianerView.center = CGPoint(x: CGSize.jf.screenSize().width / 2, y: CGSize.jf.screenSize().height / 2)
39 | if let absoluteRect = config.absoluteRect {
40 | contianerView.frame = absoluteRect
41 | } else if config.toastPosition == .top {
42 | contianerView.jf_top = CGFloat.jf.navigationBarHeight() + 15
43 | } else if config.toastPosition == .bottom {
44 | contianerView.jf_bottom = CGSize.jf.screenHeight() - CGFloat.jf.safeAreaBottomHeight() - 15
45 | } else if config.toastPosition == .dynamicIsland {
46 | contianerView.jf_size = originSize
47 | contianerView.center = CGPoint(x: CGSize.jf.screenSize().width / 2, y: originSize.height / 2 + 10)
48 | }
49 | contianerView.layoutIfNeeded()
50 | }
51 | if config.toastPosition == .dynamicIsland && config.absoluteRect == nil {
52 | UIView.animate(withDuration: 0.25) {
53 | updateV()
54 | } completion: { finished in
55 | transitonContext?.completeTransition(true)
56 | completion?(finished)
57 | }
58 | return
59 | }
60 | updateV()
61 | contianerView.alpha = 0
62 | UIView.animate(withDuration: config.withoutAnimation ? 0 : 0.25, delay: 0, options: .curveEaseInOut) {
63 | contianerView.alpha = 1
64 | } completion: { finished in
65 | transitonContext?.completeTransition(true)
66 | completion?(true)
67 | }
68 | }
69 | break
70 | case .drawer:
71 | do {
72 | var originY: CGFloat = -contianerView.frame.size.width
73 | var targetY: CGFloat = 0
74 | if config.direction == .right {
75 | originY = CGSize.jf.screenWidth()
76 | targetY = CGSize.jf.screenWidth() - contianerView.frame.size.width
77 | }
78 | contianerView.frame.origin.x = originY
79 | contianerView.center.y = CGSize.jf.screenHeight() / 2
80 | contianerView.layoutIfNeeded()
81 | UIView.animate(withDuration: 0.25, animations: {
82 | contianerView.frame.origin.x = targetY
83 | contianerView.layoutIfNeeded()
84 | }) { (finished) in
85 | transitonContext?.completeTransition(true)
86 | completion?(finished)
87 | }
88 | }
89 | break
90 |
91 | }
92 | }
93 |
94 | static func dismiss(with transitonContext: UIViewControllerContextTransitioning?, config: JFPopupConfig, contianerView: UIView?, completion: ((_ isFinished: Bool) -> ())?) {
95 | let type = config.animationType
96 | switch type {
97 | case .bottomSheet:
98 | do {
99 | guard let _ = contianerView else {
100 | transitonContext?.completeTransition(true)
101 | completion?(true)
102 | return
103 | }
104 | UIView.animate(withDuration: 0.25, animations: {
105 | contianerView?.superview?.alpha = 0
106 | contianerView?.frame.origin.y = CGSize.jf.screenSize().height
107 | contianerView?.layoutIfNeeded()
108 | }) { (finished) in
109 | transitonContext?.completeTransition(true)
110 | completion?(finished)
111 | }
112 | }
113 | break
114 | case .dialog:
115 | do {
116 | UIView.animate(withDuration: 0.25, animations: {
117 | if config.toastPosition == .dynamicIsland && config.absoluteRect == nil {
118 | contianerView?.layer.cornerRadius = 17
119 | contianerView?.jf_size = CGSize(width: 120, height: 34)
120 | contianerView?.center = CGPoint(x: CGSize.jf.screenSize().width / 2, y: 27)
121 | }
122 | contianerView?.subviews.forEach({ v in
123 | if config.toastPosition == .dynamicIsland && config.absoluteRect == nil {
124 | v.isHidden = true
125 | } else {
126 | v.alpha = 0
127 | }
128 | })
129 | contianerView?.alpha = 0
130 | }) { (finished) in
131 | transitonContext?.completeTransition(true)
132 | completion?(finished)
133 | }
134 | }
135 | break
136 | case .drawer:
137 | do {
138 | guard let view = contianerView else {
139 | transitonContext?.completeTransition(true)
140 | completion?(true)
141 | return
142 | }
143 | var targetY: CGFloat = -view.frame.size.width
144 | if config.direction == .right {
145 | targetY = CGSize.jf.screenWidth()
146 | }
147 | UIView.animate(withDuration: 0.25, animations: {
148 | contianerView?.superview?.alpha = 0
149 | contianerView?.frame.origin.x = targetY
150 | contianerView?.layoutIfNeeded()
151 | }) { (finished) in
152 | transitonContext?.completeTransition(true)
153 | completion?(finished)
154 | }
155 | }
156 | break
157 | }
158 | }
159 |
160 | }
161 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/Core/JFPopupController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JFPopupController.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/9.
6 | //
7 |
8 | import UIKit
9 |
10 | extension JFPopupController: JFPopupProtocol {
11 |
12 | public func dismissPopupView(completion: ((Bool) -> ())) {
13 | self.closeVC(with: nil)
14 | }
15 |
16 | public func autoDismissHandle() {
17 | guard self.config.enableAutoDismiss else { return }
18 | DispatchQueue.main.asyncAfter(deadline: .now() + self.config.autoDismissDuration.timeDuration()) {
19 | self.dismissPopupView { isFinished in
20 |
21 | }
22 | }
23 | }
24 |
25 |
26 |
27 | }
28 |
29 | open class JFPopupController: UIViewController,JFPopupDataSource {
30 |
31 | //JFPopupProtocol
32 | public weak var dataSource: JFPopupDataSource?
33 | public weak var popupProtocol: JFPopupAnimationProtocol?
34 | public var container: UIView?
35 | public var config: JFPopupConfig = .dialog
36 | //JFPopupProtocol
37 |
38 |
39 | //JFPopupDataSource
40 | @objc open func viewForContainer() -> UIView? {
41 | return nil
42 | }
43 | //JFPopupDataSource
44 |
45 | weak var transitionContext: UIViewControllerAnimatedTransitioning?
46 | var isShow = false
47 | var beginTouchPoint: CGPoint = .zero
48 | var beginFrame: CGRect = .zero
49 |
50 | deinit {
51 | print("JFPopupController dealloc")
52 | }
53 |
54 | public init(with config: JFPopupConfig) {
55 | super.init(nibName: nil, bundle: nil)
56 | self.transitionContext = self
57 | self.config = config
58 | }
59 |
60 | public init(with config: JFPopupConfig, container: (() -> UIView)?) {
61 | super.init(nibName: nil, bundle: nil)
62 | self.container = container?()
63 | self.transitionContext = self
64 | self.config = config
65 | }
66 |
67 | public init(with config: JFPopupConfig, popupProtocol: JFPopupAnimationProtocol? = nil, container: (() -> UIView)?) {
68 | super.init(nibName: nil, bundle: nil)
69 | self.popupProtocol = popupProtocol
70 | self.container = container?()
71 | self.transitionContext = self
72 | self.config = config
73 | }
74 |
75 | required public init?(coder: NSCoder) {
76 | fatalError("init(coder:) has not been implemented")
77 | }
78 |
79 | open override func viewDidLoad() {
80 | super.viewDidLoad()
81 | self.navigationController?.setNavigationBarHidden(true, animated: false)
82 | if self.popupProtocol == nil {
83 | self.popupProtocol = self
84 | }
85 | if let container = self.container {
86 | self.view.addSubview(container)
87 | } else {
88 | if self.dataSource == nil {
89 | self.dataSource = self
90 | }
91 | if let v = self.dataSource?.viewForContainer() {
92 | self.view.addSubview(v)
93 | self.container = v
94 | }
95 | }
96 | self.view.backgroundColor = self.config.bgColor
97 | let panGest: UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(onPan(gest:)))
98 | panGest.delegate = self
99 | self.view.addGestureRecognizer(panGest)
100 | let tapGest = UITapGestureRecognizer(target: self, action: #selector(tapBGAction))
101 | tapGest.delegate = self
102 | self.view.addGestureRecognizer(tapGest)
103 | }
104 |
105 | @objc open func show(with vc: UIViewController) {
106 | let navi = UINavigationController.init(rootViewController: self)
107 | navi.transitioningDelegate = self
108 | navi.modalPresentationStyle = .custom
109 | self.isShow = true
110 | vc.present(navi, animated: true) {
111 | }
112 | }
113 |
114 | @objc func tapBGAction() {
115 | guard self.config.isDismissible else { return }
116 | self.closeVC(with: nil)
117 | }
118 |
119 | @objc open func closeVC(with completion: (() -> Void)?) {
120 | self.dismiss(animated: true, completion: completion)
121 | }
122 |
123 | open override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
124 | self.isShow = false
125 | super.dismiss(animated: flag, completion: completion)
126 | }
127 |
128 | @objc private func onPan(gest: UIPanGestureRecognizer) {
129 | guard self.config.enableDrag else {
130 | self.tapBGAction()
131 | return
132 | }
133 | guard let container = self.container else { return }
134 | switch gest.state {
135 | case .began:
136 | self.beginFrame = container.frame
137 | self.beginTouchPoint = gest.location(in: self.view)
138 | break
139 | case .changed:
140 | guard self.config.animationType != .dialog else { break }
141 | self.container?.frame = self.getRectForPan(pan: gest)
142 | break
143 | case .ended,.cancelled:
144 | guard self.config.animationType != .dialog else {
145 | self.tapBGAction()
146 | break
147 | }
148 | self.container?.frame = self.getRectForPan(pan: gest)
149 | let isClosed: Bool = self.checkGestToClose(gest: gest)
150 | if isClosed == true {
151 | self.tapBGAction()
152 | } else {
153 | UIView.animate(withDuration: 0.2) {
154 | self.container?.frame = self.beginFrame
155 | }
156 | }
157 | break
158 |
159 | default:
160 | break
161 | }
162 | }
163 |
164 | private func checkGestToClose(gest: UIPanGestureRecognizer) -> Bool {
165 | if self.config.animationType == .drawer {
166 | if self.config.direction == .left {
167 | return gest.velocity(in: container).x < 0
168 | } else {
169 | return gest.velocity(in: container).x > 0
170 | }
171 | } else if self.config.animationType == .bottomSheet {
172 | return gest.velocity(in: container).y > 0
173 | }
174 | return false
175 | }
176 |
177 | private func getRectForPan(pan: UIPanGestureRecognizer) -> CGRect {
178 | guard let view = self.container else { return .zero }
179 | var rect: CGRect = view.frame
180 | let currentTouch = pan.location(in: self.view)
181 | if self.config.animationType == .drawer {
182 | let xRate = (self.beginTouchPoint.x - self.beginFrame.origin.x) / self.beginFrame.size.width
183 | let currentTouchDeltaX = xRate * view.jf.width
184 | var x = currentTouch.x - currentTouchDeltaX
185 | if x < self.beginFrame.origin.x && self.config.direction == .right {
186 | x = self.beginFrame.origin.x
187 | } else if x > self.beginFrame.origin.x && self.config.direction == .left {
188 | x = self.beginFrame.origin.x
189 | }
190 |
191 | rect.origin.x = x
192 | } else if self.config.animationType == .bottomSheet {
193 | let yRate = (self.beginTouchPoint.y - self.beginFrame.origin.y) / self.beginFrame.size.height
194 | let currentTouchDeltaY = yRate * view.jf.height
195 | var y = currentTouch.y - currentTouchDeltaY
196 | if y < self.beginFrame.origin.y {
197 | y = self.beginFrame.origin.y
198 | }
199 | rect.origin.y = y
200 | }
201 | return rect
202 | }
203 | }
204 |
205 | extension JFPopupController: UIViewControllerTransitioningDelegate,UIViewControllerAnimatedTransitioning,UIGestureRecognizerDelegate {
206 |
207 | public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
208 | let isTapGest = gestureRecognizer is UITapGestureRecognizer
209 | let point = gestureRecognizer.location(in: gestureRecognizer.view)
210 | let rect = self.container?.frame ?? .zero
211 | let inContianer = rect.contains(point)
212 | if isTapGest {
213 | if inContianer {
214 | return false
215 | }
216 | if self.config.isDismissible == false {
217 | return false
218 | }
219 | } else {
220 | if self.config.enableDrag == false || self.config.isDismissible == false {
221 | return false
222 | }
223 | }
224 | return true
225 | }
226 |
227 | public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
228 | 0.25
229 | }
230 |
231 | public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
232 | return self.transitionContext
233 | }
234 |
235 | public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
236 | return self.transitionContext
237 | }
238 |
239 | public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
240 | if self.isShow == true {
241 | self.present(transitonContext: transitionContext)
242 | } else {
243 | self.dismiss(transitonContext: transitionContext)
244 | }
245 | }
246 |
247 | func present(transitonContext: UIViewControllerContextTransitioning) {
248 | let toNavi: UINavigationController = transitonContext.viewController(forKey:.to) as! UINavigationController
249 | let containerView = transitonContext.containerView
250 | containerView.addSubview(toNavi.view)
251 | guard let contianerView = self.container else {
252 | transitonContext.completeTransition(true)
253 | return
254 | }
255 | self.popupProtocol?.present(with: transitonContext, config: self.config, contianerView: contianerView, completion: { [weak self] isFinished in
256 | self?.autoDismissHandle()
257 | })
258 | }
259 |
260 | func dismiss(transitonContext: UIViewControllerContextTransitioning) {
261 | self.popupProtocol?.dismiss(with: transitonContext, config: self.config, contianerView: self.container, completion: nil)
262 | }
263 |
264 | }
265 |
266 | extension JFPopupController: JFPopupAnimationProtocol {
267 |
268 | public func dismiss(with transitonContext: UIViewControllerContextTransitioning?, config: JFPopupConfig, contianerView: UIView?, completion: ((Bool) -> ())?) {
269 | JFPopupAnimation.dismiss(with: transitonContext, config: self.config, contianerView: contianerView, completion: completion)
270 | }
271 |
272 | public func present(with transitonContext: UIViewControllerContextTransitioning?, config: JFPopupConfig, contianerView: UIView, completion: ((Bool) -> ())?) {
273 | JFPopupAnimation.present(with: transitonContext, config: self.config, contianerView: contianerView, completion: completion)
274 | }
275 | }
276 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/Core/JFPopupView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JFPopupView.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/18.
6 | //
7 |
8 | import UIKit
9 |
10 | /// popup va custom view use in view, not controller
11 | public extension JFPopup where Base: JFPopupView {
12 |
13 | /// popup a bottomSheet with your custom view
14 | /// - Parameters:
15 | /// - isDismissible: default true, will tap bg auto dismiss
16 | /// - enableDrag: default true, will enable drag animate
17 | /// - bgColor: background view color
18 | /// - container: your custom view
19 | /// - onDismissPopupView: call when popup view dismissed (dealloc)
20 | @discardableResult static func bottomSheet(with isDismissible: Bool = true, enableDrag: Bool = true, bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4), yourView: UIView? = nil, container: @escaping (_ mainContainer: JFPopupView?) -> UIView, onDismissPopupView: ((_ mainContainer: JFPopupView?) -> Void)? = nil) -> JFPopupView? {
21 | var config: JFPopupConfig = .bottomSheet
22 | config.isDismissible = isDismissible
23 | config.enableDrag = enableDrag
24 | config.bgColor = bgColor
25 | return self.custom(with: config, yourView: yourView) { mainContainer in
26 | container(mainContainer)
27 | } onDismissPopupView: { mainContainer in
28 | onDismissPopupView?(mainContainer)
29 | }
30 | }
31 |
32 |
33 | /// popup a drawer style view with your custom view
34 | /// - Parameters:
35 | /// - direction: left or right
36 | /// - isDismissible: default true, will tap bg auto dismiss
37 | /// - enableDrag: default true, will enable drag animate
38 | /// - bgColor: background view color
39 | /// - container: your custom view
40 | /// - onDismissPopupView: call when popup view dismissed (dealloc)
41 | @discardableResult static func drawer(with direction: JFPopupAnimationDirection = .left, isDismissible: Bool = true, enableDrag: Bool = true, bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4), yourView: UIView? = nil, container: @escaping (_ mainContainer: JFPopupView?) -> UIView, onDismissPopupView: ((_ mainContainer: JFPopupView?) -> Void)? = nil) -> JFPopupView? {
42 | var config: JFPopupConfig = .drawer
43 | config.direction = direction
44 | config.isDismissible = isDismissible
45 | config.enableDrag = enableDrag
46 | config.bgColor = bgColor
47 | return self.custom(with: config, yourView: yourView) { mainContainer in
48 | container(mainContainer)
49 | } onDismissPopupView: { mainContainer in
50 | onDismissPopupView?(mainContainer)
51 | }
52 | }
53 |
54 |
55 | /// popup a dialog style view with your custom view
56 | /// - Parameters:
57 | /// - isDismissible: default true, will tap bg auto dismiss
58 | /// - bgColor: background view color
59 | /// - container: your custom view
60 | /// - onDismissPopupView: call when popup view dismissed (dealloc)
61 | @discardableResult static func dialog(with isDismissible: Bool = true, bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4), yourView: UIView? = nil, container: @escaping (_ mainContainer: JFPopupView?) -> UIView, onDismissPopupView: ((_ mainContainer: JFPopupView?) -> Void)? = nil) -> JFPopupView? {
62 | var config: JFPopupConfig = .dialog
63 | config.isDismissible = isDismissible
64 | config.bgColor = bgColor
65 | config.toastPosition = .center
66 | return self.custom(with: config, yourView: yourView) { mainContainer in
67 | container(mainContainer)
68 | } onDismissPopupView: { mainContainer in
69 | onDismissPopupView?(mainContainer)
70 | }
71 | }
72 |
73 | /// popup a custom view with custom config
74 | /// - Parameters:
75 | /// - config: popup config
76 | /// - container: your custom view
77 | /// - onDismissPopupView: call when popup view dismissed (dealloc)
78 | @discardableResult static func custom(with config: JFPopupConfig, yourView: UIView? = nil, container: @escaping (_ mainContainer: JFPopupView?) -> UIView?, onDismissPopupView: ((_ mainContainer: JFPopupView?) -> Void)? = nil) -> JFPopupView? {
79 | if Thread.current != Thread.main {
80 | return DispatchQueue.main.sync {
81 | let v = JFPopupView(with: config) { mainContainer in
82 | container(mainContainer)
83 | }
84 | v.dismissedHanlde = { [weak v] in
85 | onDismissPopupView?(v)
86 | }
87 | v.popup(into: yourView)
88 | return v
89 | }
90 | } else {
91 | let v = JFPopupView(with: config) { mainContainer in
92 | container(mainContainer)
93 | }
94 | v.dismissedHanlde = { [weak v] in
95 | onDismissPopupView?(v)
96 | }
97 | v.popup(into: yourView)
98 | return v
99 | }
100 | }
101 | }
102 |
103 | extension JFPopupView: JFPopupProtocol {
104 |
105 | public func dismissPopupView(completion: @escaping ((Bool) -> ())) {
106 | self.popupProtocol?.dismiss(with: nil, config: self.config, contianerView: self.container, completion: { [weak self] isFinished in
107 | self?.removeFromSuperview()
108 | completion(isFinished)
109 | self?.dismissedHanlde?()
110 | })
111 | }
112 |
113 | public func autoDismissHandle() {
114 | DispatchQueue.main.asyncAfter(deadline: .now() + self.config.autoDismissDuration.timeDuration()) {
115 | self.dismissPopupView { isFinished in
116 |
117 | }
118 | }
119 | }
120 |
121 |
122 | }
123 |
124 | public class JFPopupView: UIView {
125 |
126 | var beginTouchPoint: CGPoint = .zero
127 | var beginFrame: CGRect = .zero
128 |
129 | public weak var dataSource: JFPopupDataSource?
130 |
131 | public weak var popupProtocol: JFPopupAnimationProtocol?
132 |
133 | public var container: UIView?
134 |
135 | public var config: JFPopupConfig = .dialog
136 |
137 | //use in onDismiss CallBack, some one want to know which is dismiss from tap background
138 | /*
139 |
140 | onDismissPopupView: { mainContainer in
141 | if mainContainer?.isClosedFromTapBackground {
142 | //xxx
143 | }
144 | }
145 |
146 | */
147 | public var isClosedFromTapBackground = false
148 |
149 | typealias DismissedHanlde = () -> Void
150 | var dismissedHanlde: DismissedHanlde?
151 |
152 | deinit {
153 | print("JFPopupView dealloc")
154 | }
155 |
156 | public init(with config: JFPopupConfig, container: ((_ mainContainer: JFPopupView?) -> UIView?)?) {
157 | super.init(frame: UIScreen.main.bounds)
158 | self.container = container?(self)
159 | self.config = config
160 | self.configSubview()
161 | self.configGest()
162 | }
163 |
164 | public init(with config: JFPopupConfig, popupProtocol: JFPopupAnimationProtocol? = nil, container: ((_ mainContainer: JFPopupView?) -> UIView?)?) {
165 | super.init(frame: UIScreen.main.bounds)
166 | self.popupProtocol = popupProtocol
167 | self.container = container?(self)
168 | self.config = config
169 | self.configSubview()
170 | self.configGest()
171 | }
172 |
173 | func configSubview() {
174 | self.isUserInteractionEnabled = self.config.enableUserInteraction
175 | self.backgroundColor = self.config.bgColor
176 | if self.popupProtocol == nil {
177 | self.popupProtocol = self
178 | }
179 | if let container = self.container {
180 | self.addSubview(container)
181 | } else {
182 | if self.dataSource == nil {
183 | self.dataSource = self
184 | }
185 | if let v = self.dataSource?.viewForContainer() {
186 | self.addSubview(v)
187 | self.container = v
188 | }
189 | }
190 | }
191 |
192 | func configGest() {
193 | let panGest: UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(onPan(gest:)))
194 | panGest.delegate = self
195 | self.addGestureRecognizer(panGest)
196 | let tapGest = UITapGestureRecognizer(target: self, action: #selector(tapBGAction))
197 | tapGest.delegate = self
198 | self.addGestureRecognizer(tapGest)
199 | }
200 |
201 | @objc func tapBGAction() {
202 | guard self.config.isDismissible else { return }
203 | self.isClosedFromTapBackground = true
204 | self.dismissPopupView { [weak self] isFinished in
205 |
206 | }
207 | }
208 |
209 | func popup(into yourView: UIView? = nil) {
210 | guard let view = self.container else { return }
211 | guard let window = UIApplication.shared.windows.first(where: \.isKeyWindow) else { return }
212 | if let view = yourView {
213 | view.addSubview(self)
214 | } else {
215 | window.addSubview(self)
216 | }
217 | self.popupProtocol?.present(with: nil, config: self.config, contianerView: view, completion: { [weak self] isFinished in
218 | guard self?.config.enableAutoDismiss == true else { return }
219 | self?.autoDismissHandle()
220 | })
221 | }
222 |
223 | override init(frame: CGRect) {
224 | super.init(frame: frame)
225 | }
226 |
227 | required init?(coder: NSCoder) {
228 | fatalError("init(coder:) has not been implemented")
229 | }
230 |
231 | }
232 |
233 | extension JFPopupView: UIGestureRecognizerDelegate {
234 |
235 | @objc private func onPan(gest: UIPanGestureRecognizer) {
236 | guard self.config.enableDrag else {
237 | self.tapBGAction()
238 | return
239 | }
240 | guard let container = self.container else { return }
241 | switch gest.state {
242 | case .began:
243 | self.beginFrame = container.frame
244 | self.beginTouchPoint = gest.location(in: self)
245 | break
246 | case .changed:
247 | guard self.config.animationType != .dialog else { break }
248 | self.container?.frame = self.getRectForPan(pan: gest)
249 | break
250 | case .ended,.cancelled:
251 | guard self.config.animationType != .dialog else {
252 | self.tapBGAction()
253 | break
254 | }
255 | self.container?.frame = self.getRectForPan(pan: gest)
256 | let isClosed: Bool = self.checkGestToClose(gest: gest)
257 | if isClosed == true {
258 | self.tapBGAction()
259 | } else {
260 | UIView.animate(withDuration: 0.2) {
261 | self.container?.frame = self.beginFrame
262 | }
263 | }
264 | break
265 |
266 | default:
267 | break
268 | }
269 | }
270 |
271 | private func checkGestToClose(gest: UIPanGestureRecognizer) -> Bool {
272 | if self.config.animationType == .drawer {
273 | if self.config.direction == .left {
274 | return gest.velocity(in: container).x < 0
275 | } else {
276 | return gest.velocity(in: container).x > 0
277 | }
278 | } else if self.config.animationType == .bottomSheet {
279 | return gest.velocity(in: container).y > 0
280 | }
281 | return false
282 | }
283 |
284 | private func getRectForPan(pan: UIPanGestureRecognizer) -> CGRect {
285 | guard let view = self.container else { return .zero }
286 | var rect: CGRect = view.frame
287 | let currentTouch = pan.location(in: self)
288 | if self.config.animationType == .drawer {
289 | let xRate = (self.beginTouchPoint.x - self.beginFrame.origin.x) / self.beginFrame.size.width
290 | let currentTouchDeltaX = xRate * view.jf.width
291 | var x = currentTouch.x - currentTouchDeltaX
292 | if x < self.beginFrame.origin.x && self.config.direction == .right {
293 | x = self.beginFrame.origin.x
294 | } else if x > self.beginFrame.origin.x && self.config.direction == .left {
295 | x = self.beginFrame.origin.x
296 | }
297 |
298 | rect.origin.x = x
299 | } else if self.config.animationType == .bottomSheet {
300 | let yRate = (self.beginTouchPoint.y - self.beginFrame.origin.y) / self.beginFrame.size.height
301 | let currentTouchDeltaY = yRate * view.jf.height
302 | var y = currentTouch.y - currentTouchDeltaY
303 | if y < self.beginFrame.origin.y {
304 | y = self.beginFrame.origin.y
305 | }
306 | rect.origin.y = y
307 | }
308 | return rect
309 | }
310 |
311 | public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
312 | let isTapGest = gestureRecognizer is UITapGestureRecognizer
313 | let point = gestureRecognizer.location(in: gestureRecognizer.view)
314 | let rect = self.container?.frame ?? .zero
315 | let inContianer = rect.contains(point)
316 | if isTapGest {
317 | if inContianer {
318 | return false
319 | }
320 | if self.config.isDismissible == false {
321 | return false
322 | }
323 | } else {
324 | if self.config.enableDrag == false || self.config.isDismissible == false {
325 | return false
326 | }
327 | }
328 | return true
329 | }
330 | }
331 |
332 | extension JFPopupView: JFPopupDataSource {
333 | @objc public func viewForContainer() -> UIView? {
334 | return nil
335 | }
336 | }
337 |
338 | extension JFPopupView: JFPopupAnimationProtocol {
339 | public func dismiss(with transitonContext: UIViewControllerContextTransitioning?, config: JFPopupConfig, contianerView: UIView?, completion: ((Bool) -> ())?) {
340 | JFPopupAnimation.dismiss(with: transitonContext, config: self.config, contianerView: contianerView, completion: completion)
341 | }
342 |
343 | public func present(with transitonContext: UIViewControllerContextTransitioning?, config: JFPopupConfig, contianerView: UIView, completion: ((Bool) -> ())?) {
344 | JFPopupAnimation.present(with: transitonContext, config: self.config, contianerView: contianerView, completion: completion)
345 | }
346 | }
347 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/Core/UIView+JFPopup.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+JFPopup.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/19.
6 | //
7 |
8 | import UIKit
9 |
10 | extension UIView: JFPopupCompatible {}
11 | public extension JFPopup where Base: UIView {
12 |
13 | /// popup a bottomSheet with your custom view
14 | /// - Parameters:
15 | /// - isDismissible: default true, will tap bg auto dismiss
16 | /// - enableDrag: default true, will enable drag animate
17 | /// - bgColor: background view color
18 | /// - container: your custom view
19 | @discardableResult func bottomSheet(with isDismissible: Bool = true, enableDrag: Bool = true, bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4), container: @escaping (_ mainContainer: JFPopupView?) -> UIView) -> JFPopupView? {
20 | return JFPopupView.popup.bottomSheet(with: isDismissible, enableDrag: enableDrag, bgColor: bgColor, yourView: base) { mainContainer in
21 | container(mainContainer)
22 | }
23 | }
24 |
25 |
26 | /// popup a drawer style view with your custom view
27 | /// - Parameters:
28 | /// - direction: left or right
29 | /// - isDismissible: default true, will tap bg auto dismiss
30 | /// - enableDrag: default true, will enable drag animate
31 | /// - bgColor: background view color
32 | /// - container: your custom view
33 | @discardableResult func drawer(with direction: JFPopupAnimationDirection = .left, isDismissible: Bool = true, enableDrag: Bool = true, bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4), container: @escaping (_ mainContainer: JFPopupView?) -> UIView) -> JFPopupView? {
34 | return JFPopupView.popup.drawer(with: direction, isDismissible: isDismissible, enableDrag: enableDrag, bgColor: bgColor, yourView: base) { mainContainer in
35 | container(mainContainer)
36 | }
37 | }
38 |
39 |
40 | /// popup a dialog style view with your custom view
41 | /// - Parameters:
42 | /// - isDismissible: default true, will tap bg auto dismiss
43 | /// - bgColor: background view color
44 | /// - container: your custom view
45 | @discardableResult func dialog(with isDismissible: Bool = true, bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4), container: @escaping (_ mainContainer: JFPopupView?) -> UIView) -> JFPopupView? {
46 | return JFPopupView.popup.dialog(with: isDismissible, bgColor: bgColor, yourView: base) { mainContainer in
47 | container(mainContainer)
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/Core/UIViewContoller+JFPopup.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewContoller+Popup.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/9.
6 | //
7 |
8 | import UIKit
9 | extension UIViewController: JFPopupCompatible {}
10 |
11 | public extension JFPopup where Base: UIViewController {
12 |
13 | func dismissPopup() {
14 | if let navi = base.presentedViewController as? UINavigationController, let vc = navi.viewControllers.first as? JFPopupController {
15 | vc.closeVC(with: nil)
16 | }
17 | }
18 |
19 | /// popup a bottomSheet with your custom view
20 | /// - Parameters:
21 | /// - isDismissible: default true, will tap bg auto dismiss
22 | /// - enableDrag: default true, will enable drag animate
23 | /// - bgColor: background view color
24 | /// - container: your custom view
25 | func bottomSheet(with isDismissible: Bool = true, enableDrag: Bool = true, bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4), container: () -> UIView) {
26 | var config: JFPopupConfig = .bottomSheet
27 | config.isDismissible = isDismissible
28 | config.enableDrag = enableDrag
29 | config.bgColor = bgColor
30 | let view = container()
31 | let vc = JFPopupController(with: config) {
32 | view
33 | }
34 | vc.show(with: base)
35 | }
36 |
37 |
38 | /// popup a drawer style view with your custom view
39 | /// - Parameters:
40 | /// - direction: left or right
41 | /// - isDismissible: default true, will tap bg auto dismiss
42 | /// - enableDrag: default true, will enable drag animate
43 | /// - bgColor: background view color
44 | /// - container: your custom view
45 | func drawer(with direction: JFPopupAnimationDirection = .left, isDismissible: Bool = true, enableDrag: Bool = true, bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4), container: () -> UIView) {
46 | var config: JFPopupConfig = .drawer
47 | config.direction = direction
48 | config.isDismissible = isDismissible
49 | config.enableDrag = enableDrag
50 | config.bgColor = bgColor
51 | self.custom(with: config, container: container)
52 | }
53 |
54 |
55 | /// popup a dialog style view with your custom view
56 | /// - Parameters:
57 | /// - isDismissible: default true, will tap bg auto dismiss
58 | /// - bgColor: background view color
59 | /// - container: your custom view
60 | func dialog(with isDismissible: Bool = true, bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4), container: () -> UIView) {
61 | var config: JFPopupConfig = .dialog
62 | config.isDismissible = isDismissible
63 | config.bgColor = bgColor
64 | config.toastPosition = .center
65 | self.custom(with: config, container: container)
66 | }
67 |
68 | /// popup a custom view with custom config
69 | /// - Parameters:
70 | /// - config: popup config
71 | /// - container: your custom view
72 | func custom(with config: JFPopupConfig, container: () -> UIView?) {
73 | guard let view = container() else {
74 | return
75 | }
76 | let vc = JFPopupController(with: config) {
77 | view
78 | }
79 | vc.show(with: base)
80 | }
81 |
82 | // adapt objc extension
83 | func objc_custom(with animationType: JFPopupAnimationType = .dialog, isDismissible: Bool = true, enableDrag: Bool = true, direction: JFPopupAnimationDirection = .left, bgColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4), container: () -> UIView) {
84 | var config = JFPopupConfig()
85 | config.animationType = animationType
86 | config.isDismissible = isDismissible
87 | config.enableDrag = enableDrag
88 | config.direction = direction
89 | config.bgColor = bgColor
90 | self.custom(with: config, container: container)
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/General/ActionSheet/JFPopupAction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JFPopupAction.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/9.
6 | //
7 |
8 | import UIKit
9 |
10 | @objc public class JFPopupAction: NSObject {
11 |
12 | var title: String?
13 | var subTitle: String?
14 | var subAttributedTitle: NSAttributedString?
15 | var clickActionCallBack: (() -> ())?
16 | var autoDismiss: Bool = true //auto dismiss when you click action
17 |
18 | @objc public convenience init(with title: String?, subTitle: String?, autoDismiss: Bool = true, clickActionCallBack: @escaping (() -> ())) {
19 | self.init()
20 | self.title = title
21 | self.subTitle = subTitle
22 | self.autoDismiss = autoDismiss
23 | self.clickActionCallBack = clickActionCallBack
24 | }
25 |
26 | @objc public convenience init(with title: String?, subAttributedTitle: NSAttributedString?, autoDismiss: Bool = true, clickActionCallBack: @escaping (() -> ())) {
27 | self.init()
28 | self.title = title
29 | self.subAttributedTitle = subAttributedTitle
30 | self.autoDismiss = autoDismiss
31 | self.clickActionCallBack = clickActionCallBack
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/General/ActionSheet/JFPopupActionSheetView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JFPopupActionSheetView.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/9.
6 | //
7 |
8 | import UIKit
9 |
10 | public extension JFPopup where Base: UIViewController {
11 | /// popup a actionSheet
12 | /// - Parameters:
13 | /// - autoCancelAction: is true, will add cancel action in the last
14 | /// - actions: the actions item
15 | func actionSheet(with autoCancelAction: Bool = true, actions: (() -> [JFPopupAction])) {
16 | self.bottomSheet(with: true, enableDrag: false) {
17 | let v = JFPopupActionSheetView(with: actions(), autoCancelAction: autoCancelAction)
18 | v.autoDismissHandle = {
19 | dismissPopup()
20 | }
21 | return v
22 | }
23 | }
24 | }
25 |
26 | public extension JFPopup where Base: UIView {
27 | /// popup a actionSheet
28 | /// - Parameters:
29 | /// - autoCancelAction: is true, will add cancel action in the last
30 | /// - actions: the actions item
31 | @discardableResult func actionSheet(with autoCancelAction: Bool = true, actions: @escaping (() -> [JFPopupAction])) -> JFPopupView? {
32 | return JFPopupView.popup.bottomSheet(with: true, enableDrag: false, yourView: base) { mainContainer in
33 | let v = JFPopupActionSheetView(with: actions(), autoCancelAction: autoCancelAction)
34 | v.autoDismissHandle = { [weak mainContainer] in
35 | mainContainer?.dismissPopupView(completion: { isFinished in
36 |
37 | })
38 | }
39 | return v
40 | }
41 | }
42 | }
43 |
44 | public extension JFPopup where Base: JFPopupView {
45 | /// popup a actionSheet
46 | /// - Parameters:
47 | /// - autoCancelAction: is true, will add cancel action in the last
48 | /// - actions: the actions item
49 | @discardableResult static func actionSheet(with autoCancelAction: Bool = true, yourView: UIView? = nil, actions: @escaping (() -> [JFPopupAction])) -> JFPopupView? {
50 | return self.bottomSheet(with: true, enableDrag: false, yourView: yourView) { mainContainer in
51 | let v = JFPopupActionSheetView(with: actions(), autoCancelAction: autoCancelAction)
52 | v.autoDismissHandle = { [weak mainContainer] in
53 | mainContainer?.dismissPopupView(completion: { isFinished in
54 |
55 | })
56 | }
57 | return v
58 | }
59 | }
60 | }
61 |
62 | class JFPopupActionView: UIView {
63 |
64 | var clickActionHandle: (() -> ())?
65 |
66 | let titleLabel: UILabel = {
67 | let label = UILabel()
68 | label.textColor = UIColor.init(red: 19 / 255.0, green: 20 / 255.0, blue: 19 / 255.0, alpha: 1)
69 | label.font = UIFont.systemFont(ofSize: 16)
70 | return label
71 | }()
72 |
73 | let subTitleLabel: UILabel = {
74 | let label = UILabel()
75 | label.textColor = UIColor.init(red: 135 / 255.0, green: 135 / 255.0, blue: 135 / 255.0, alpha: 1)
76 | label.font = UIFont.systemFont(ofSize: 12)
77 | return label
78 | }()
79 |
80 | lazy var verStackView: UIStackView = {
81 | let stackView = UIStackView(arrangedSubviews: [self.titleLabel,self.subTitleLabel])
82 | stackView.alignment = .center
83 | stackView.spacing = 5
84 | stackView.distribution = .fillProportionally
85 | stackView.axis = .vertical
86 | return stackView
87 | }()
88 |
89 | var lineView: UIView = {
90 | let view = UIView()
91 | view.backgroundColor = UIColor.init(red: 221 / 255.0, green: 221 / 255.0, blue: 221 / 255.0, alpha: 1)
92 | return view
93 | }()
94 |
95 | lazy var clickBtn: UIButton = {
96 | let btn = UIButton(type: .custom)
97 | btn.addTarget(self, action: #selector(clickAction), for: .touchUpInside)
98 | btn.setBackgroundImage(UIImage.jf.color(0x000000,alpha: 0.1), for: .highlighted)
99 | return btn
100 | }()
101 |
102 | @objc func clickAction() {
103 | self.clickActionHandle?()
104 | }
105 |
106 | override init(frame: CGRect) {
107 | super.init(frame: frame)
108 | self.backgroundColor = .white
109 | self.addSubview(self.verStackView)
110 | self.verStackView.translatesAutoresizingMaskIntoConstraints = false
111 | self.addConstraints(
112 | [
113 | NSLayoutConstraint(item: self.verStackView, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 0),
114 | NSLayoutConstraint(item: self.verStackView, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: 0),
115 | NSLayoutConstraint(item: self.verStackView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0)
116 | ]
117 | )
118 | self.addSubview(self.lineView)
119 | self.lineView.translatesAutoresizingMaskIntoConstraints = false
120 | self.addConstraints(
121 | [
122 | NSLayoutConstraint(item: self.lineView, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 0),
123 | NSLayoutConstraint(item: self.lineView, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: 0),
124 | NSLayoutConstraint(item: self.lineView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0),
125 | NSLayoutConstraint(item: self.lineView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 0.5)
126 | ]
127 | )
128 | self.addSubview(self.clickBtn)
129 | self.clickBtn.translatesAutoresizingMaskIntoConstraints = false
130 | self.addConstraints([
131 | NSLayoutConstraint(item: self.clickBtn, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 0),
132 | NSLayoutConstraint(item: self.clickBtn, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: 0),
133 | NSLayoutConstraint(item: self.clickBtn, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0),
134 | NSLayoutConstraint(item: self.clickBtn, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0)
135 | ])
136 | }
137 |
138 | required init?(coder: NSCoder) {
139 | fatalError("init(coder:) has not been implemented")
140 | }
141 | }
142 |
143 | public class JFPopupActionSheetView: UIView {
144 |
145 | var actions: [JFPopupAction]?
146 | var autoDismissHandle: (() -> ())?
147 | var autoCancelAction: Bool = true
148 |
149 | public convenience init(with actions: [JFPopupAction], autoCancelAction: Bool = true) {
150 | self.init(frame: .zero)
151 | self.autoCancelAction = autoCancelAction
152 | self.actions = actions
153 | self.configSubview()
154 | }
155 |
156 | override init(frame: CGRect) {
157 | super.init(frame: frame)
158 | }
159 |
160 | required init(coder: NSCoder) {
161 | fatalError("init(coder:) has not been implemented")
162 | }
163 |
164 | func configSubview() {
165 | self.backgroundColor = UIColor.init(red: 245 / 255.0, green: 245 / 255.0, blue: 245 / 255.0, alpha: 1)
166 | guard let actions = self.actions, actions.count > 0 else { return }
167 | var originY: CGFloat = 0
168 | var views: [UIView] = []
169 | for element in actions.enumerated() {
170 | let action = element.element
171 | var actionHeight: CGFloat = 0
172 | let view = JFPopupActionView()
173 | view.clickActionHandle = { [weak self] in
174 | if action.autoDismiss {
175 | self?.autoDismissHandle?()
176 | }
177 | action.clickActionCallBack?()
178 | }
179 | if let title = action.title {
180 | view.titleLabel.text = title
181 | view.titleLabel.isHidden = false
182 | view.titleLabel.sizeToFit()
183 | actionHeight += view.titleLabel.frame.size.height
184 | }
185 | if let subTitle = action.subTitle {
186 | view.subTitleLabel.text = subTitle
187 | view.subTitleLabel.isHidden = false
188 | view.subTitleLabel.sizeToFit()
189 | actionHeight += 5
190 | actionHeight += view.subTitleLabel.frame.size.height
191 | } else if let subAttTitle = action.subAttributedTitle {
192 | view.subTitleLabel.attributedText = subAttTitle
193 | view.subTitleLabel.isHidden = false
194 | view.subTitleLabel.sizeToFit()
195 | actionHeight += 5
196 | actionHeight += view.subTitleLabel.frame.size.height
197 | }
198 | view.lineView.isHidden = element.offset == (actions.count - 1)
199 | actionHeight += 30
200 | view.frame = CGRect(origin: CGPoint(x: 0, y: originY), size: CGSize(width: CGSize.jf.screenWidth(), height: actionHeight))
201 | originY += actionHeight
202 | self.addSubview(view)
203 | views.append(view)
204 | }
205 |
206 | if var view = views.last, self.autoCancelAction == false {
207 | view.jf.height += CGFloat.jf.safeAreaBottomHeight()
208 | self.frame.size.height = view.jf.bottom
209 | } else {
210 | var view = JFPopupActionView()
211 | view.titleLabel.text = "取消"
212 | view.clickActionHandle = { [weak self] in
213 | self?.autoDismissHandle?()
214 | }
215 | view.titleLabel.sizeToFit()
216 | view.jf.left = 0
217 | view.jf.top = originY + 5
218 | view.jf.width = CGSize.jf.screenWidth()
219 | view.jf.height = view.titleLabel.jf.height + 30 + CGFloat.jf.safeAreaBottomHeight()
220 | for constraint in view.constraints {
221 | if let first = constraint.firstItem, first.isEqual(view.verStackView) && constraint.firstAttribute == .centerY {
222 | constraint.constant = -CGFloat.jf.safeAreaBottomHeight() / 2
223 | break
224 | }
225 | }
226 | self.addSubview(view)
227 | self.frame.size.height = view.jf.bottom
228 | }
229 | self.frame.size.width = CGSize.jf.screenWidth()
230 |
231 | let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 12, height: 12))
232 | let maskLayer = CAShapeLayer()
233 | maskLayer.frame = self.bounds
234 | maskLayer.path = path.cgPath
235 | self.layer.mask = maskLayer
236 | }
237 |
238 | }
239 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/General/Alert/JFAlertAction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JFAlertAction.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/22.
6 | //
7 |
8 | import UIKit
9 |
10 | public typealias TapActionCallBack = () -> ()
11 |
12 | let JFAlertCancelColor = UIColor(red: 20 / 255.0, green: 20 / 255.0, blue: 20 / 255.0, alpha: 1)
13 | let JFAlertSureColor = UIColor(red: 69 / 255.0, green: 85 / 255.0, blue: 130 / 255.0, alpha: 1)
14 |
15 | public enum JFAlertActionOption {
16 | case textColor(UIColor)
17 | case text(String)
18 | case tapActionCallback(TapActionCallBack)
19 |
20 | static var cancel: [JFAlertActionOption] = [
21 | .text("取消"),.textColor(JFAlertCancelColor),
22 | ]
23 | }
24 |
25 | class JFAlertAction: NSObject {
26 |
27 | var options: [JFAlertActionOption] = []
28 | var clickBtnCallBack: (() -> ())?
29 | var tapActionCallback: TapActionCallBack?
30 | var defaultColor: UIColor = UIColor(red: 20 / 255.0, green: 20 / 255.0, blue: 20 / 255.0, alpha: 1)
31 |
32 | convenience init(with options: [JFAlertActionOption], defaultColor: UIColor) {
33 | self.init()
34 | self.options = options
35 | self.defaultColor = defaultColor
36 | }
37 |
38 | override init() {
39 | super.init()
40 | }
41 |
42 | deinit {
43 | print("JFAlertAction dealloc")
44 | }
45 |
46 | @objc func clickAction() {
47 | self.clickBtnCallBack?()
48 | self.tapActionCallback?()
49 | }
50 |
51 | func buildActionButton() -> UIButton? {
52 | var color = self.defaultColor
53 | var text: String?
54 | for option in options {
55 | switch option {
56 | case .textColor(let uIColor):
57 | color = uIColor
58 | case .text(let t):
59 | text = t
60 | case .tapActionCallback(let tap):
61 | self.tapActionCallback = tap
62 | }
63 | }
64 | guard let text = text else {
65 | return nil
66 | }
67 | let button: UIButton = {
68 | let btn = UIButton(type: .custom)
69 | btn.setTitle(text, for: .normal)
70 | btn.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .medium)
71 | btn.setTitleColor(color, for: .normal)
72 | btn.setBackgroundImage(UIImage.jf.color(0xffffff), for: .normal)
73 | btn.setBackgroundImage(UIImage.jf.color(0x000000,alpha: 0.1), for: .highlighted)
74 | btn.addTarget(self, action: #selector(clickAction), for: .touchUpInside)
75 | return btn
76 | }()
77 | return button
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/General/Alert/JFAlertView+PopupView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JFAlertView+PopupView.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/22.
6 | //
7 |
8 | import UIKit
9 |
10 | public extension JFPopup where Base: JFPopupView {
11 |
12 | @discardableResult static func alert(options: () -> [JFAlertOption]) -> JFPopupView? {
13 | let allOptions = options()
14 | var config: JFPopupConfig = .dialog
15 | var alertConfig = JFAlertConfig()
16 | config.enableUserInteraction = true
17 | config.enableAutoDismiss = false
18 | config.isDismissible = false
19 | config.toastPosition = .center
20 | for option in allOptions {
21 | switch option {
22 | case .title(let string):
23 | alertConfig.title = string
24 | case .titleColor(let uIColor):
25 | alertConfig.titleColor = uIColor
26 | case .subTitle(let string):
27 | alertConfig.subTitle = string
28 | case .subTitleColor(let uIColor):
29 | alertConfig.subTitleColor = uIColor
30 | case .showCancel(let bool):
31 | alertConfig.showCancel = bool
32 | case .cancelAction(let actions):
33 | alertConfig.cancelAction = actions
34 | case .confirmAction(let actions):
35 | alertConfig.confirmAction = actions
36 | case .withoutAnimation(let bool):
37 | config.withoutAnimation = bool
38 | }
39 | }
40 | guard alertConfig.title != nil || alertConfig.subTitle != nil else {
41 | assert(alertConfig.title != nil || alertConfig.subTitle != nil, "title or subTitle only can one value nil")
42 | return nil
43 | }
44 | let popupView = self.custom(with: config, yourView: nil) { mainContainer in
45 | JFAlertView(with: alertConfig)
46 | }
47 | return popupView
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/General/Alert/JFAlertView+UIViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JFAlertView+UIViewController.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/22.
6 | //
7 |
8 | import UIKit
9 |
10 | public extension JFPopup where Base: UIViewController {
11 | func alert(options: () -> [JFAlertOption]) {
12 | let allOptions = options()
13 | var config: JFPopupConfig = .dialog
14 | var alertConfig = JFAlertConfig()
15 | config.enableUserInteraction = true
16 | config.enableAutoDismiss = false
17 | config.isDismissible = false
18 | for option in allOptions {
19 | switch option {
20 | case .title(let string):
21 | alertConfig.title = string
22 | case .titleColor(let uIColor):
23 | alertConfig.titleColor = uIColor
24 | case .subTitle(let string):
25 | alertConfig.subTitle = string
26 | case .subTitleColor(let uIColor):
27 | alertConfig.subTitleColor = uIColor
28 | case .showCancel(let bool):
29 | alertConfig.showCancel = bool
30 | case .cancelAction(let actions):
31 | alertConfig.cancelAction = actions
32 | case .confirmAction(let actions):
33 | alertConfig.confirmAction = actions
34 | case .withoutAnimation(let bool):
35 | config.withoutAnimation = bool
36 | }
37 | }
38 | guard alertConfig.title != nil || alertConfig.subTitle != nil else {
39 | assert(alertConfig.title != nil || alertConfig.subTitle != nil, "title or subTitle only can one value nil")
40 | return
41 | }
42 | guard let alertView = JFAlertView(with: alertConfig) else { return }
43 | alertView.clickActionHandle = {
44 | dismissPopup()
45 | }
46 | self.dialog(with: false, bgColor: config.bgColor) {
47 | alertView
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/General/Alert/JFAlertView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JFAlertView.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/21.
6 | //
7 |
8 | import UIKit
9 |
10 | public enum JFAlertOption {
11 | case title(String)
12 | case titleColor(UIColor)
13 | case subTitle(String)
14 | case subTitleColor(UIColor)
15 | case showCancel(Bool)
16 | case cancelAction([JFAlertActionOption])
17 | case confirmAction([JFAlertActionOption])
18 | case withoutAnimation(Bool)
19 | }
20 |
21 | public struct JFAlertConfig {
22 | var title: String?
23 | var titleColor: UIColor = UIColor(red: 20 / 255.0, green: 20 / 255.0, blue: 20 / 255.0, alpha: 1)
24 | var subTitle: String?
25 | var subTitleColor: UIColor = UIColor(red: 94 / 255.0, green: 94 / 255.0, blue: 94 / 255.0, alpha: 1)
26 | var showCancel = true
27 | var cancelAction: [JFAlertActionOption]?
28 | var confirmAction: [JFAlertActionOption]?
29 | var itemSpacing: CGFloat = 20
30 | var contentInset = UIEdgeInsets.init(top: 30, left: 20, bottom: 30, right: 20)
31 |
32 | }
33 |
34 | class JFAlertView: UIView {
35 |
36 | let margin: CGFloat = 15 * UIScreen.main.scale
37 |
38 | var cancelAction: JFAlertAction?
39 | var confirmAction: JFAlertAction?
40 |
41 | lazy var titleLabel: UILabel = {
42 | let label = UILabel()
43 | label.textColor = self.config.titleColor
44 | label.numberOfLines = 0
45 | label.textAlignment = .center
46 | label.font = UIFont.systemFont(ofSize: 18, weight: .medium)
47 | label.isHidden = true
48 | return label
49 | }()
50 |
51 | lazy var subTitleLabel: UILabel = {
52 | let label = UILabel()
53 | label.textColor = self.config.subTitleColor
54 | label.numberOfLines = 0
55 | label.textAlignment = .center
56 | label.font = UIFont.systemFont(ofSize: 16)
57 | label.isHidden = true
58 | return label
59 | }()
60 |
61 | lazy var verStackView: UIStackView = {
62 | let stackView = UIStackView(arrangedSubviews: [self.titleLabel,self.subTitleLabel])
63 | stackView.alignment = .center
64 | stackView.spacing = self.config.itemSpacing
65 | stackView.axis = .vertical
66 | stackView.distribution = .fill
67 | return stackView
68 | }()
69 |
70 | let bottomView: UIView = {
71 | let view = UIView()
72 | view.backgroundColor = UIColor.init(red: 221 / 255.0, green: 221 / 255.0, blue: 221 / 255.0, alpha: 1)
73 | return view
74 | }()
75 |
76 | var config: JFAlertConfig = JFAlertConfig()
77 | var clickActionHandle: (() -> ())?
78 |
79 | public convenience init?(with config: JFAlertConfig) {
80 | //subTitle or title must have one value
81 | guard config.subTitle != nil || config.title != nil else {
82 | return nil
83 | }
84 | self.init(frame: .zero)
85 | self.config = config
86 | self.configSubview()
87 | }
88 |
89 | override init(frame: CGRect) {
90 | super.init(frame: CGRect(x: CGSize.jf.screenWidth(), y: CGSize.jf.screenHeight(), width: CGSize.jf.screenWidth(), height: CGSize.jf.screenHeight()))
91 | self.layer.cornerRadius = 10
92 | self.layer.masksToBounds = true
93 | self.backgroundColor = .white
94 | }
95 |
96 | func configAutolayout() {
97 | self.bottomView.translatesAutoresizingMaskIntoConstraints = false
98 | self.addSubview(self.bottomView)
99 | self.addConstraints([
100 | NSLayoutConstraint(item: self.bottomView, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1, constant: 0),
101 | NSLayoutConstraint(item: self.bottomView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1, constant: 0),
102 | NSLayoutConstraint(item: self.bottomView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0),
103 | NSLayoutConstraint(item: self.bottomView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 55),
104 | ])
105 |
106 | self.addSubview(self.verStackView)
107 | self.verStackView.translatesAutoresizingMaskIntoConstraints = false
108 | self.verStackView.addConstraints([
109 | NSLayoutConstraint(item: self.titleLabel, attribute: .width, relatedBy: .lessThanOrEqual, toItem: nil, attribute: .width, multiplier: 1, constant: CGSize.jf.screenWidth() - (margin * 2 + self.config.contentInset.left + self.config.contentInset.right)),
110 | NSLayoutConstraint(item: self.subTitleLabel, attribute: .width, relatedBy: .lessThanOrEqual, toItem: nil, attribute: .width, multiplier: 1, constant: CGSize.jf.screenWidth() - (margin * 2 + self.config.contentInset.left + self.config.contentInset.right)),
111 | ])
112 | self.addConstraints(
113 | [
114 | NSLayoutConstraint(item: self.verStackView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0),
115 | NSLayoutConstraint(item: self.verStackView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: -27.5),
116 | ]
117 | )
118 | }
119 |
120 | func configSubview() {
121 |
122 | self.configAutolayout()
123 | var cancelAction: [JFAlertActionOption]? = JFAlertActionOption.cancel
124 | if let cancel = self.config.cancelAction {
125 | cancelAction = cancel
126 | }
127 | if self.config.showCancel == false {
128 | cancelAction = nil
129 | }
130 | var arrangedSubviews: [UIView] = []
131 | if let cancel = cancelAction {
132 | let action = JFAlertAction(with: cancel, defaultColor: JFAlertCancelColor)
133 | action.clickBtnCallBack = { [weak self] in
134 | if let supV = self?.superview as? JFPopupView {
135 | supV.dismissPopupView { isFinished in
136 |
137 | }
138 | } else {
139 | self?.clickActionHandle?()
140 | }
141 | }
142 | self.cancelAction = action
143 | if let btn = action.buildActionButton() {
144 | arrangedSubviews.append(btn)
145 | }
146 | }
147 | if let confirm = config.confirmAction {
148 | let action = JFAlertAction(with: confirm, defaultColor: JFAlertSureColor)
149 | action.clickBtnCallBack = { [weak self] in
150 | if let supV = self?.superview as? JFPopupView {
151 | supV.dismissPopupView { isFinished in
152 |
153 | }
154 | } else {
155 | self?.clickActionHandle?()
156 | }
157 | }
158 | self.confirmAction = action
159 | if let btn = action.buildActionButton() {
160 | arrangedSubviews.append(btn)
161 | }
162 | }
163 |
164 | if arrangedSubviews.count > 0 {
165 | if let btn1 = arrangedSubviews.first, let btn2 = arrangedSubviews.last {
166 | let stackView = UIStackView(arrangedSubviews: arrangedSubviews)
167 | stackView.backgroundColor = .clear
168 | stackView.alignment = .bottom
169 | stackView.spacing = 1
170 | stackView.axis = .horizontal
171 | stackView.distribution = .fillEqually
172 |
173 | var constraints: [NSLayoutConstraint] = []
174 | constraints.append(NSLayoutConstraint(item: btn1, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 54))
175 | if btn1 != btn2 {
176 | constraints.append(NSLayoutConstraint(item: btn2, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 54))
177 | }
178 | self.bottomView.addSubview(stackView)
179 | stackView.translatesAutoresizingMaskIntoConstraints = false
180 | stackView.addConstraints(constraints)
181 |
182 | self.addConstraints([
183 | NSLayoutConstraint(item: stackView, attribute: .left, relatedBy: .equal, toItem: self.bottomView, attribute: .left, multiplier: 1, constant: 0),
184 | NSLayoutConstraint(item: stackView, attribute: .right, relatedBy: .equal, toItem: self.bottomView, attribute: .right, multiplier: 1, constant: 0),
185 | NSLayoutConstraint(item: stackView, attribute: .bottom, relatedBy: .equal, toItem: self.bottomView, attribute: .bottom, multiplier: 1, constant: 0),
186 | NSLayoutConstraint(item: stackView, attribute: .top, relatedBy: .equal, toItem: self.bottomView, attribute: .top, multiplier: 1, constant: 1),
187 | ])
188 | }
189 | }
190 |
191 | if let title = self.config.title {
192 | self.titleLabel.text = title
193 | self.titleLabel.isHidden = false
194 | }
195 |
196 | if let subTitle = self.config.subTitle {
197 | self.subTitleLabel.text = subTitle
198 | self.subTitleLabel.isHidden = false
199 | }
200 | self.layoutIfNeeded()
201 | let titleSize = self.titleLabel.frame.size
202 | let subTitleSize = self.subTitleLabel.frame.size
203 | var height: CGFloat = self.config.contentInset.bottom + self.config.contentInset.top
204 | let width = CGSize.jf.screenWidth() - margin * 2
205 |
206 | if titleSize != .zero {
207 | height += titleSize.height
208 | }
209 |
210 | if subTitleSize != .zero {
211 | height += titleSize != .zero ? self.config.itemSpacing : 0
212 | height += subTitleSize.height
213 | }
214 | height += 55
215 | self.frame = CGRect(x: 0, y: 0, width: width, height: height)
216 |
217 | }
218 |
219 | required init?(coder: NSCoder) {
220 | fatalError("init(coder:) has not been implemented")
221 | }
222 |
223 | }
224 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/General/Toast/JFToastQueueTask.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JFToastQueueTask.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/21.
6 | //
7 |
8 | import UIKit
9 |
10 | class JFToastQueueTask: NSObject {
11 |
12 | var config: JFPopupConfig?
13 | var toastConfig: JFToastConfig?
14 | weak var mainContainer: UIView?
15 | weak var popupView: JFPopupView?
16 |
17 | override init() {
18 | super.init()
19 | }
20 |
21 | convenience init(with config: JFPopupConfig, toastConfig: JFToastConfig?, mainContainer: UIView?, popupView: JFPopupView?) {
22 | self.init()
23 | self.config = config
24 | self.toastConfig = toastConfig
25 | self.mainContainer = mainContainer
26 | self.popupView = popupView
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/General/Toast/JFToastView+Objc.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JFToastView+Objc.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/19.
6 | //
7 |
8 | import UIKit
9 |
10 | @objc public enum JFToastObjcAssetType: Int {
11 | case unknow
12 | case success
13 | case fail
14 | case imageName
15 | }
16 |
17 | public extension JFToastView {
18 |
19 | @objc class func toast(hit: String) {
20 | JFToastView.toast(hit: hit, type: .unknow, imageName: nil)
21 | }
22 |
23 | @objc class func toast(icon: JFToastObjcAssetType, imageName: String?) {
24 | JFToastView.toast(hit: nil, type: icon, imageName: imageName)
25 | }
26 |
27 | @objc class func toast(hit: String?, type: JFToastObjcAssetType, imageName: String?) {
28 | var options: [JFToastOption] = []
29 | if let hit = hit {
30 | options += [.hit(hit)]
31 | }
32 | if type != .unknow {
33 | var iconType: JFToastAssetIconType?
34 | if type == .success {
35 | iconType = .success
36 | } else if type == .fail {
37 | iconType = .fail
38 | } else if let name = imageName, type == .imageName {
39 | iconType = .imageName(name: name, imageType: "png")
40 | }
41 | if let icon = iconType {
42 | options += [.icon(icon)]
43 | }
44 | }
45 | JFPopupView.popup.toast {
46 | options
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Classes/General/Toast/JFToastView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JFToastView.swift
3 | // JFPopup
4 | //
5 | // Created by 逸风 on 2021/10/16.
6 | //
7 |
8 | import UIKit
9 |
10 | //only loading style have queue
11 | private var JFLoadingViewsQueue: [JFToastQueueTask] = []
12 |
13 | public enum JFToastOption {
14 | case hit(String)
15 | case icon(JFToastAssetIconType)
16 | case enableAutoDismiss(Bool)
17 | case enableUserInteraction(Bool)
18 | case autoDismissDuration(JFTimerDuration)
19 | case bgColor(UIColor)
20 | case mainContainer(UIView)
21 | case withoutAnimation(Bool)
22 | case position(JFToastPosition)
23 | case enableRotation(Bool)
24 | case contentInset(UIEdgeInsets)
25 | case itemSpacing(CGFloat)
26 | }
27 |
28 | public enum JFToastAssetIconType {
29 |
30 | case success
31 | case fail
32 | case imageName(name: String, imageType: String = "png")
33 |
34 | func getImageName() -> (name: String, imageType: String)? {
35 | switch self {
36 | case .success:
37 | return ("success","png")
38 | case .fail:
39 | return ("fail","png")
40 | case .imageName(name: let name, imageType: let imageType):
41 | return (name,imageType)
42 | }
43 | }
44 | }
45 |
46 | public struct JFToastConfig {
47 | var title: String?
48 | var assetIcon: JFToastAssetIconType?
49 | var enableDynamicIsLand: Bool = false
50 | var enableRotation: Bool = false
51 | var contentInset: UIEdgeInsets = .init(top: 12, left: 25, bottom: 12, right: 25)
52 | var itemSpacing: CGFloat = 5.0
53 | }
54 |
55 | public class JFToastView: UIView {
56 |
57 | let titleLabel: UILabel = {
58 | let label = UILabel()
59 | label.textColor = .white
60 | label.numberOfLines = 0
61 | label.textAlignment = .center
62 | label.font = UIFont.systemFont(ofSize: 15)
63 | label.isHidden = true
64 | return label
65 | }()
66 |
67 | let iconImgView: UIImageView = {
68 | let imageView = UIImageView()
69 | imageView.contentMode = .scaleAspectFit
70 | imageView.isHidden = true
71 | return imageView
72 | }()
73 |
74 | lazy var verStackView: UIStackView = {
75 | let stackView = UIStackView(arrangedSubviews: [self.iconImgView,self.titleLabel])
76 | stackView.alignment = .center
77 | stackView.spacing = self.config.itemSpacing
78 | stackView.axis = .vertical
79 | stackView.distribution = .fill
80 | return stackView
81 | }()
82 |
83 | var config: JFToastConfig = JFToastConfig()
84 |
85 | public convenience init?(with config: JFToastConfig) {
86 | //assetIcon or title must have one value
87 | guard config.assetIcon != nil || config.title != nil else {
88 | return nil
89 | }
90 | self.init(frame: .zero)
91 | self.config = config
92 | self.configSubview()
93 | }
94 |
95 | override init(frame: CGRect) {
96 | super.init(frame: CGRect(x: CGSize.jf.screenWidth(), y: CGSize.jf.screenHeight(), width: CGSize.jf.screenWidth(), height: CGSize.jf.screenHeight()))
97 | self.backgroundColor = .black
98 | self.layer.cornerRadius = self.config.enableDynamicIsLand ? 17 : 10
99 | }
100 |
101 | func configSubview() {
102 | self.addSubview(self.verStackView)
103 | self.verStackView.translatesAutoresizingMaskIntoConstraints = false
104 | self.verStackView.addConstraints([
105 | NSLayoutConstraint(item: self.titleLabel, attribute: .width, relatedBy: .lessThanOrEqual, toItem: nil, attribute: .width, multiplier: 1, constant: CGSize.jf.screenWidth() - 30 - self.config.contentInset.left - self.config.contentInset.right),
106 | ])
107 | self.addConstraints(
108 | [
109 | NSLayoutConstraint(item: self.verStackView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0),
110 | NSLayoutConstraint(item: self.verStackView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: self.config.enableDynamicIsLand ? 34 / 2 : 0),
111 | ]
112 | )
113 | if let title = self.config.title {
114 | self.titleLabel.text = title
115 | self.titleLabel.isHidden = false
116 | }
117 |
118 | if let assetIconType = self.config.assetIcon, let result = assetIconType.getImageName(), let image = self.getBundleImage(with: result.name, imageType: result.imageType) {
119 | self.iconImgView.image = image
120 | self.iconImgView.isHidden = false
121 | }
122 |
123 | self.layoutIfNeeded()
124 | let titleSize = self.titleLabel.frame.size
125 | let iconSize = self.iconImgView.frame.size
126 | var height: CGFloat = self.config.contentInset.bottom + self.config.contentInset.top + (self.config.enableDynamicIsLand ? 34 : 0)
127 | let horInset = CGFloat(self.config.contentInset.left + self.config.contentInset.right)
128 | let dynamicisLandSize = 120.0 + 20.0 + (iconSize == .zero ? 20 : 0)
129 | var contentWidth = max(titleSize.width, iconSize.width)
130 | if iconSize.width > 0 && iconSize.width + horInset > titleSize.width {
131 | contentWidth = iconSize.width
132 | }
133 | var width = contentWidth + horInset
134 |
135 | if titleSize != .zero {
136 | height += titleSize.height
137 | if self.config.enableDynamicIsLand {
138 | self.layer.cornerRadius = height / 2
139 | }
140 | }
141 |
142 | if iconSize != .zero {
143 | height += titleSize != .zero ? self.config.itemSpacing : 0
144 | height += iconSize.height
145 | if titleSize == .zero {
146 | width = height
147 | }
148 | if self.config.enableDynamicIsLand {
149 | self.layer.cornerRadius = 20
150 | }
151 | }
152 | if self.config.enableDynamicIsLand {
153 | width = max(width, dynamicisLandSize)
154 | }
155 | self.frame = CGRect(x: 0, y: 0, width: width, height: height)
156 | if config.enableRotation {
157 | self.addRotationAnimation()
158 | }
159 | }
160 |
161 | func addRotationAnimation() {
162 | if self.iconImgView.layer.animationKeys() == nil {
163 | let baseAni = CABasicAnimation(keyPath: "transform.rotation.z")
164 | let toValue: CGFloat = .pi * 2.0
165 | baseAni.toValue = toValue
166 | baseAni.duration = 1.0
167 | baseAni.isCumulative = true
168 | baseAni.repeatCount = MAXFLOAT
169 | baseAni.isRemovedOnCompletion = false
170 | self.iconImgView.layer.add(baseAni, forKey: "rotationAnimation")
171 | }
172 | }
173 |
174 | required init?(coder: NSCoder) {
175 | fatalError("init(coder:) has not been implemented")
176 | }
177 |
178 | private func getBundleImage(with imageName: String, imageType: String = "png") -> UIImage? {
179 | if let img = UIImage(named: imageName) {
180 | return img
181 | }
182 | if let path = Bundle.main.path(forResource: imageName, ofType: imageType), let image = UIImage(contentsOfFile: path) {
183 | return image
184 | }
185 | //support SPM
186 | if let bundlePath = Bundle.main.path(forResource: "JFPopup_JFPopup", ofType: "bundle") {
187 | if let imgPath = Bundle(path: bundlePath)?.path(forResource: imageName + "@2x", ofType: imageType), let image = UIImage(contentsOfFile: imgPath) {
188 | return image
189 | }
190 | return nil
191 | }
192 | guard let frameWorkPath = Bundle.main.path(forResource: "Frameworks", ofType: nil)?.appending("/JFPopup.framework") else { return nil }
193 | guard let bundlePath = Bundle(path: frameWorkPath)?.path(forResource: "JFPopup", ofType: "bundle") else { return nil }
194 | if let imgPath = Bundle(path: bundlePath)?.path(forResource: imageName + "@2x", ofType: imageType), let image = UIImage(contentsOfFile: imgPath) {
195 | return image
196 | }
197 | return nil
198 | }
199 | }
200 |
201 | public extension JFPopup where Base: JFPopupView {
202 |
203 | static func hideLoading() {
204 |
205 | let work = DispatchWorkItem {
206 | let firstTask = JFLoadingViewsQueue.first
207 | if firstTask != nil {
208 | JFLoadingViewsQueue.removeFirst()
209 | }
210 | if let v = firstTask?.popupView {
211 | v.dismissPopupView { _ in }
212 | }
213 | }
214 |
215 | if Thread.current == Thread.main {
216 | work.perform()
217 | } else {
218 | DispatchQueue.main.sync(execute: work)
219 | }
220 |
221 | }
222 |
223 | static func loading() {
224 | self.loading(hit: nil)
225 | }
226 |
227 | static func loading(hit: String?) {
228 | self.loading(hit: hit, inView: nil)
229 | }
230 |
231 |
232 | ///show loading view
233 | /// - Parameters:
234 | /// - hit: message
235 | /// - inView: only support keywindow or ontroller.view, default keywindow
236 | static func loading(hit: String?, inView: UIView?) {
237 | var options: [JFToastOption] = [
238 | .enableAutoDismiss(false),
239 | .icon(.imageName(name: "jf_loading")),
240 | .enableRotation(true),
241 | .itemSpacing(15)]
242 | options += [.enableUserInteraction(true)]
243 | if let view = inView, !JFIsSupportDynamicIsLand {
244 | options += [.mainContainer(view)]
245 | }
246 | if let hit = hit {
247 | options += [.hit(hit)]
248 | options += [.contentInset(.init(top: 30, left: 47, bottom: 30, right: 47))]
249 | } else {
250 | options += [.contentInset(.init(top: 35, left: 35, bottom: 35, right: 35))]
251 | }
252 | JFPopupView.popup.toast { options }
253 | }
254 |
255 | static func toast(hit: String) {
256 | self.toast {
257 | [.hit(hit)]
258 | }
259 | }
260 |
261 | static func toast(hit: String, icon: JFToastAssetIconType) {
262 | self.toast {
263 | [.hit(hit),.icon(icon)]
264 | }
265 | }
266 |
267 | @discardableResult static func toast(options: () -> [JFToastOption]) -> JFPopupView? {
268 | let allOptions = options()
269 | var mainView: UIView?
270 | var config: JFPopupConfig = .dialog
271 | var toastConfig = JFToastConfig()
272 | config.bgColor = .clear
273 | config.enableUserInteraction = false
274 | config.enableAutoDismiss = true
275 | config.isDismissible = false
276 | toastConfig.enableDynamicIsLand = config.toastPosition == .dynamicIsland
277 | for option in allOptions {
278 | switch option {
279 | case .hit(let hit):
280 | toastConfig.title = hit
281 | break
282 | case .icon(let icon):
283 | toastConfig.assetIcon = icon
284 | break
285 | case .enableUserInteraction(let enable):
286 | config.enableUserInteraction = enable
287 | break
288 | case .enableAutoDismiss(let autoDismiss):
289 | config.enableAutoDismiss = autoDismiss
290 | break
291 | case .autoDismissDuration(let duration):
292 | config.autoDismissDuration = duration
293 | break
294 | case .bgColor(let bgColor):
295 | config.bgColor = bgColor
296 | break
297 | case .mainContainer(let view):
298 | mainView = view
299 | break
300 | case .withoutAnimation(let without):
301 | config.withoutAnimation = without
302 | break
303 | case .position(let pos):
304 | config.toastPosition = pos
305 | toastConfig.enableDynamicIsLand = config.toastPosition == .dynamicIsland && JFIsSupportDynamicIsLand
306 | break
307 | case .enableRotation(let enable):
308 | toastConfig.enableRotation = enable
309 | break
310 | case .contentInset(let inset):
311 | toastConfig.contentInset = inset
312 | break
313 | case .itemSpacing(let spcaing):
314 | toastConfig.itemSpacing = spcaing
315 | break
316 | }
317 | }
318 |
319 | guard toastConfig.title != nil || toastConfig.assetIcon != nil else {
320 | assert(toastConfig.title != nil || toastConfig.assetIcon != nil, "title or assetIcon only can one value nil")
321 | return nil
322 | }
323 | guard JFLoadingViewsQueue.count == 0 else {
324 | print("only can show single loading need manual dismiss) view in the same time")
325 | return nil
326 | }
327 | let popupView = self.custom(with: config, yourView: mainView) { mainContainer in
328 | JFToastView(with: toastConfig)
329 | }
330 | if config.enableAutoDismiss == false {
331 | Self.safeAppendToastTask(task: JFToastQueueTask(with: config, toastConfig: toastConfig, mainContainer: mainView, popupView: popupView))
332 | }
333 | return popupView
334 | }
335 |
336 | private static func safeAppendToastTask(task: JFToastQueueTask) {
337 | let work = DispatchWorkItem {
338 | JFLoadingViewsQueue.append(task)
339 | }
340 | if Thread.current == Thread.main {
341 | work.perform()
342 | } else {
343 | DispatchQueue.main.sync(execute: work)
344 | }
345 | }
346 | }
347 |
--------------------------------------------------------------------------------
/Sources/JFPopup/Resources/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryFans/JFPopup/8b0227215e754218280667cd656e22abc4d62821/Sources/JFPopup/Resources/.gitkeep
--------------------------------------------------------------------------------
/Sources/JFPopup/Resources/fail@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryFans/JFPopup/8b0227215e754218280667cd656e22abc4d62821/Sources/JFPopup/Resources/fail@2x.png
--------------------------------------------------------------------------------
/Sources/JFPopup/Resources/jf_loading@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryFans/JFPopup/8b0227215e754218280667cd656e22abc4d62821/Sources/JFPopup/Resources/jf_loading@2x.png
--------------------------------------------------------------------------------
/Sources/JFPopup/Resources/success@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JerryFans/JFPopup/8b0227215e754218280667cd656e22abc4d62821/Sources/JFPopup/Resources/success@2x.png
--------------------------------------------------------------------------------
/_Pods.xcodeproj:
--------------------------------------------------------------------------------
1 | Example/Pods/Pods.xcodeproj
--------------------------------------------------------------------------------