{"id":22491990,"url":"https://github.com/zgjff/jjkit","last_synced_at":"2025-08-03T00:31:12.865Z","repository":{"id":56915985,"uuid":"132543236","full_name":"zgjff/JJKit","owner":"zgjff","description":"包括便捷扩展、路由、轮播图、转场动画的框架","archived":false,"fork":false,"pushed_at":"2024-01-19T05:57:45.000Z","size":4129,"stargazers_count":11,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-11-14T17:48:17.452Z","etag":null,"topics":["carouselview","router","swift","transition"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zgjff.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-05-08T02:35:33.000Z","updated_at":"2024-07-19T06:28:55.000Z","dependencies_parsed_at":"2022-08-21T03:50:45.518Z","dependency_job_id":null,"html_url":"https://github.com/zgjff/JJKit","commit_stats":{"total_commits":104,"total_committers":2,"mean_commits":52.0,"dds":0.125,"last_synced_commit":"d6ece39df8d8443c14da047f90c4b150226440a0"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zgjff%2FJJKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zgjff%2FJJKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zgjff%2FJJKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zgjff%2FJJKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zgjff","download_url":"https://codeload.github.com/zgjff/JJKit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228508030,"owners_count":17931263,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["carouselview","router","swift","transition"],"created_at":"2024-12-06T18:12:58.831Z","updated_at":"2024-12-06T18:12:59.514Z","avatar_url":"https://github.com/zgjff.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JJKit\n\u003e 包括便捷扩展、路由、轮播图、转场动画的框架\n---------\n[![MIT](https://img.shields.io/github/license/zgjff/JJKit)](https://www.apache.org/licenses/LICENSE-2.0.html)\n[![swift-5.5](https://img.shields.io/badge/swift-5.5-blue)](https://www.swift.org)\n![iOS-11.0](https://img.shields.io/badge/iOS-11.0-red)\n[![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/zgjff/JJKit)](https://github.com/zgjff/JJKit)\n[![Cocoapods](https://img.shields.io/cocoapods/v/JJKit)](https://cocoapods.org/pods/JJKit)\n\n\n# 功能列表\n=================\n|功能|名称|\n|:----:|:----:|\n|路由|[JJRouter](#一-路由-jjrouter)|\n|Toast|[JJToast](#二-toast-jjtoast)|\n|轮播图|[JJCarouselView](./Documentation/JJCarouselView.md)|\n|转场动画|[JJTransition](#三-弹窗转场动画-jjtransition)|\n|API扩展|[Extensions](./Documentation/Extensions.md)|\n\n# 一: 路由: JJRouter\n\n简单好用、支持block回调、转发、拦截功能的路由框架\n\n\n## 思路\n最基本思路: 自己负责自己的显示方式\n\u003e 即从A跳转到B,由B自己来决定是push还是present，亦或者使用自定义的转场动画来显示\n\n所以路由界面需要遵守`JJRouterDestination`协议并实现`func showDetail(withMatchRouterResult result: JJRouter.MatchResult, from sourceController: UIViewController)`方法\n\neg:\n```swift\nextension SystemPushController: JJRouterDestination {\n    func showDetail(withMatchRouterResult result: JJRouter.MatchResult, from sourceController: UIViewController) {\n        // push\n        sourceController.navigationController?.pushViewController(self, animated: true)\n        // present\n        sourceController.present(self, animated: true)\n        // 自定义转场动画--提供使present/dismiss动画跟系统push/pop动画一致的转场动画\n        let navi = UINavigationController(rootViewController: self)\n        navi.modalPresentationStyle = .fullScreen\n        navi.transitioningDelegate = pushPopStylePresentDelegate\n        sourceController.present(navi, animated: true)\n        // 自定义转场动画---居中弹窗\n        let pd = AlertPresentationController(show: self, from: sourceController) { ctx in\n            ctx.usingBlurBelowCoverAnimators(style: .regular)\n        }\n        transitioningDelegate = pd\n        sourceController.present(self, animated: true) {\n            let _ = pd\n        }\n    }\n}\n```\n\n## 使用方法\n## 1.1、注册\n### 1.1.1 实现路由源协议`JJRouterSource`\n```swift\nenum SimpleRouter: String, CaseIterable {\n    case systemPush = \"/app/systemPush\"\n    ...\n}\n\nextension SimpleRouter: JJRouterSource {\n    var routerPattern: String {\n        return rawValue\n    }\n\n    func makeRouterDestination(parameters: [String : String], context: Any?) -\u003e JJRouterDestination {\n        switch self {\n        case .systemPush:\n            return SystemPushController()\n        ...\n        }\n    }\n}\n\n```\n### 1.1.2 调用`register`方法\n```swift\nSimpleRouter.allCases.forEach { try! $0.register() }\n```\n\n## 1.2、跳转\n### 1.2.1 通过具体的`JJRouterSource`对象跳转路由\n```swift\n(try? JJRouter.default.open(SimpleRouter.systemPush))?.jump(from: self)\n```\n### 1.2.2 通过具体的`path`跳转路由\n```swift\n(try? JJRouter.default.open(\"/app/systemPush\"))?.jump(from: self)\n```\n### 1.2.3 通过具体的`URL`跳转路由\n```swift\nif let url = URL(string: \"https://www.appwebsite.com/app/systemPush/\") {\n    (try? JJRouter.default.open(url))?.jump(from: self)\n}\n```\n\n## 1.3、传参数\n### 1.3.1 通过实现`JJRouterSource`协议的具体对象传参数\n```swift\n// 注册\nenum PassParameterRouter {\n    case byEnum(p: String, q: Int)\n    ...\n}\nvar routerParameters: [String : String] {\n    switch self {\n        case let .byEnum(p: p, q: q):\n        return [\"p\": p, \"q\": \"\\(q)\"]\n        ...\n    }\n}\n// A\n(try? JJRouter.default.open(PassParameterRouter.byContext, context: 12))?.jump(from: self)\n// 参数: [\"p\": \"entry\", \"q\": 12]\n```\n### 1.3.2 通过`path`或者`URL`传参数\n```swift\n// 注册\nenum PassParameterRouter {\n    case byUrl = \"/app/passParameterByUrl/:pid/:name\"\n    ...\n}\n// A\n(try? JJRouter.default.open(\"/app/passParameterByUrl/12/jack\"))?.jump(from: self)\n// 参数: [\"pid\": \"12\", \"name\": \"jack\"]\n```\n\n### 1.3.3 通过`path`或者`URL`带`query`传参数\n```swift\n// 注册\nenum PassParameterRouter {\n    case byUrlWithQuery = \"/app/search\"\n    ...\n}\n// A\n(try? JJRouter.default.open(\"/app/search?name=lili\u0026age=18\"))?.jump(from: self)\n// 参数: [\"name\": \"lili\", \"age\": \"18\"]\n```\n\n### 1.3.4 通过`context`传参数\n```swift\n// 注册\nenum PassParameterRouter {\n    case byContext = \"/app/passParameterByContext\"\n    ...\n}\n// A\n(try? JJRouter.default.open(PassParameterRouter.byContext, context: 12))?.jump(from: self)(self)\n// B\nfunc showDetail(withMatchRouterResult result: JJRouter.MatchResult, from sourceController: UIViewController) {\n    if let pid = result.context as? Int {\n        self.pid = pid\n    }\n    sourceController.navigationController?.pushViewController(self, animated: true)\n    }\n```\n\n### 1.3.5 混和`URL`与`context`传参数\n```swift\n// 注册\nenum PassParameterRouter {\n    case mixUrlAndContext = \"/app/mixUrlAndContext/:pid/:text\"\n    ...\n}\n// A\n(try? JJRouter.default.open(\"/app/mixUrlAndContext/12/keke\", context: arc4random_uniform(2) == 0))?.jump(from: self)\n```\n\n### 1.3.6 将参数用于`UIViewController`的初始化\n```swift\n// 注册\nenum PassParameterRouter {\n    case parameterForInit = \"/app/parameterForInit/:id\"\n    ...\n}\nfunc makeRouterDestination(parameters: [String : String], context: Any?) -\u003e JJRouterDestination {\n    switch self {\n        case .parameterForInit:\n            let idstr = parameters[\"id\"] ?? \"\"\n            let numberFormatter = NumberFormatter()\n            let id = numberFormatter.number(from: idstr)?.intValue\n            return PassParametersForInitController(id: id ?? 0)\n        ...\n    }\n}\n// A\n(try? JJRouter.default.open(\"/app/parameterForInit/66\"))?.jump(from: self)\n// B\ninit(id: Int) {\n    pid = id\n    super.init(nibName: nil, bundle: nil)\n}\n```\n\n\n## 1.4、回调\n### 1.4.1 正常block回调: A跳转B, B通过路由block将数据回调给A\n```swift\n// A\n// 不缩写的代码逻辑应该是这样的\nlet result = try? JJRouter.default.open(BlockRouter.backBlock)\nlet router = result?.jump(from: self)\n// 当然也可以缩写\n// let router = (try? JJRouter.default.open(BlockRouter.backBlock))?.jump(from: self)\nrouter?.register(blockName: \"onSend\", callback: { obj in\n    print(\"get data: \\(obj) from router block\")\n})\n// B\ndismiss(animated: true) { [weak self] in\n    self?.router?.perform(blockName: \"onSend\", withObject: 5)\n}\n```\n\n### 1.4.2 非正常block回调: A跳转B, A通过路由block将实时数据回调给B\n\u003e 主要用于,A的数据是实时变化的,B需要拿到A的最新数据\n\n```swift\n// A\nlet router = (try? JJRouter.default.open(BlockRouter.frontBlockB))?.jump(from: self)\nrouter?.register(blockName: \"onNeedGetNewestData\", callback: { [weak self] obj in\n    guard let self = self,\n        let block = obj as? (Int) -\u003e () else {\n            return\n        }\n    block(self.data)\n})\n// B\nlet block: (Int) -\u003e () = { [weak self] data in\n    self?.button.setTitle(\"\\(data)\", for: [])\n}\nrouter?.perform(blockName: \"onNeedGetNewestData\", withObject: block)\n```\n\n### 1.4.3 转发block回调: A需要跳转B,但是条件达不到,需要跳转到其它路由界面C,此时可以正常拿到C的回调\n\u003e 这里A虽然是调用B的路由,但是仍然可以收到C的回调\n```swift\n// register 转发\nfunc register() throws {\n    try JJRouter.default.register(pattern: routerPattern, mapRouter: { matchResult in\n        guard case .mapBlock = self else {\n            return self\n        }\n        let needGotoLoginController = arc4random_uniform(2) == 0\n        if needGotoLoginController { // 需要登录,转发给登录路由\n            return SimpleRouter.login\n        }\n        return self\n    })\n}\n// A\nlet router = (try? JJRouter.default.open(BlockRouter.mapBlock))?.jump(from: self)\nrouter?.register(blockName: \"loginSuccess\", callback: { _ in\n    print(\"登录成功\")\n})\n```\n\n## 1.5、匹配到路由控制器与当前控制器属于同一类时情景\n\n### 1.5.1 3种操作\n```swift\n/// 匹配到的路由跟当前展示的界面相同时的操作\npublic enum MatchedSameRouterDestinationAction {\n    /// 不做任何操作\n    case none\n    /// 更新数据\n    case update\n    /// 展示新界面\n    case new\n}\n```\n\n### 1.5.2 匹配到相同类的协议方法事例\n```swift\n/// 当匹配到的路由跟当前展示的界面相同时的操作方法,默认返回`new`\n///\n/// 返回`none`时,不做任何操作\n///\n/// 返回`update`时,会调用`updateWhenRouterIdentifierIsSame`方法来更新当前界面\n///\n/// 返回`new`时,会调用`showDetail`来重新展示新的界面\n/// - Parameter result: 匹配结果\nfunc actionWhenMatchedRouterDestinationSameToCurrent(withNewMatchRouterResult result: JJRouter.MatchResult) -\u003e JJRouter.MatchedSameRouterDestinationAction {\n    return .update\n}\n\nfunc updateWhenRouterIdentifierIsSame(withNewMatchRouterResult result: JJRouter.MatchResult) {\n    pid = parseId(from: result.parameters)\n    title = \"\\(pid)\"\n}\n```\n\n# 二: Toast: JJToast\n\n泛型、可扩展、样式丰富多样、支持自定义显示样式\n\n\n## 思路\n\u003e `toast`分拆为样式加显示容器; 样式、显示容器高度抽象化,框架只在底层做相关的逻辑; 具体实现交由上层处理\n\n## 2.1: 抽象化相关协议\n### 2.1.2 `toast`样式组件协议\n#### 2.1.2.1: 最基本的`toast`样式组件协议\n\u003e 此协议统筹约束所有的样式组件内容,并且跟样式容器协议交互(容器也只跟此层交互): 此协议约定了一共5项内容, 具体如下:\n\n```swift\n/// `toast`样式组件协议\npublic protocol JJToastItemable: AnyObject {\n    associatedtype Options: JJToastItemOptions\n    /// toast样式组件代理\n    var delegate: JJToastableDelegate? { get set }\n    /// 配置\n    var options: Options { get }\n    /// 唯一标识符\n    var identifier: String { get }\n    /// 使用对应的`toast`样式配置以及要显示`toast`的view的size大小, 计算并布局`toast`样式\n    /// - Parameters:\n    ///   - options: 配置\n    ///   - size: 要显示`toast`的view的size大小\n    func layoutToastView(with options: Options, inViewSize size: CGSize)\n    /// 根据显示`toast`的view的size大小重置`toast`样式size\n    /// - Parameter size: 显示`toast`的view的size\n    func resetContentSizeWithViewSize(_ size: CGSize)\n}\n```\n\n#### 2.1.2.2: 3种常用的`toast`类型: 框架在`JJToastItemable`协议之上也抽离出了常用的3种`toast`类型, 此3种常用类型只是约束开发相应类型的大致方向\n\n* 文字: `JJTextToastItemable`\n```swift\npublic protocol JJTextToastItemable: JJToastItemable {\n    /// 展示文字内容\n    /// - Parameters:\n    ///   - text: 内容\n    ///   - labelToShow: label\n    func display(text: NSAttributedString, in labelToShow: UILabel)\n}\n```\n* 指示器: `JJIndicatorToastItemable`\n```swift\n  /// 显示指示器的 `toast`样式组件协议\npublic protocol JJIndicatorToastItemable: JJToastItemable {\n    /// 开始动画\n    func startAnimating()\n}\n```\n* 进度条: `JJProgressToastItemable`\n```swift\n/// 显示进度条的 `toast`样式组件协议\npublic protocol JJProgressToastItemable: JJToastItemable {\n    /// 设置进度条进度\n    /// - Parameters:\n    ///   - progress: 进度\n    ///   - flag: 是否开启动画\n    func setProgress(_ progress: Float, animated flag: Bool)\n}\n```\n#### 2.1.2.3: 混合两项`toast`的组合样式: \n框架提供了一种独特的`toast`样式具体实现: `JJMixTwoToastItem`, 它可以很方便的提供混合组合, 文字+文字、文字+指示器、文字+进度条、文字+图像、指示器+进度条、指示器+指示器。。。。\n\n它是一个泛型类: \n```swift\nJJMixTwoToastItem\u003cFirst: JJToastItemable, Second: JJToastItemable\u003e\n```\n所以你可以随意的组合任意两种样式\neg\n```swift\n1: 文字+文字\nJJMixTwoToastItem(first: JJTextToastItem(attributedString: NSAttributedString(string: \"标题\", attributes: [.font: UIFont.systemFont(ofSize: 22), .foregroundColor: UIColor.jRandom()])), second: JJTextToastItem(text: \"我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容\"))\n\n2: 指示器+文字\nJJMixTwoToastItem(first: JJActivityToastItem(), second: JJTextToastItem(text: \"我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容\"))\n\n3: 指示器+变换的文字\nJJMixTwoToastItem(first: JJActivityToastItem(), second: JJVaryTextToastItem(texts: [\"加载中\", \"加载中.\", \"加载中..\", \"加载中...\"]))\n\n4: 指示器+指示器\nJJMixTwoToastItem(first: JJArcrotationToastItem(), second: JJActivityToastItem())\n\n5: 文字+图像\nJJMixTwoToastItem(first: JJTextToastItem(text: \"进击的象🐘\"), second: JJImageToastItem(url: url, display: { url, imageView in\n    imageView.sd_setImage(with: url, completed: nil)\n}))\n\n....\n```\n\n### 2.1.3 `toast`容器组件协议\n\u003e 此协议统筹约束所有的容器逻辑: 此协议约定了一共10项内容, 具体如下:\n```swift\n/// `toast`容器协议\npublic protocol JJToastContainer: UIView, JJToastableDelegate, CAAnimationDelegate {\n    /// 配置\n    var options: JJToastContainerOptions { get set }\n    /// 状态\n    var state: JJToastState { get set }\n    /// 具体承载的`toast`样式\n    var toastItem: (any JJToastItemable)? { get }\n    /// 显示toast\n    func present(_ viewToShow: UIView, animated flag: Bool)\n    /// 隐藏toast\n    func dismiss(animated flag: Bool)\n    /// 在一定时间之后执行自动隐藏\n    func performAutoDismiss(after delay: TimeInterval)\n    /// 取消自动隐藏\n    func cancelperformAutoDismiss()\n    /// 观察屏幕方向改变\n    func addOrientationDidChangeObserver(action: @escaping (CGSize) -\u003e ()) -\u003e NSObjectProtocol?\n    /// 取消屏幕方向观察\n    func removeOrientationDidChangeObserver(_ observer: NSObjectProtocol?)\n    /// 移除\n    func remove()\n}\n```\n## 使用方法\n### 3.1 基本使用\n```swift\nlet color = UIColor.jRandom()\nlet texts = (1..\u003c11).reversed().map { NSAttributedString(string: \"\\($0)\", attributes: [.font: UIFont.systemFont(ofSize: 37), .foregroundColor: color]) }\nview.jj.makeToast(JJVaryTextToastItem(attributedStrings: texts))\n    .updateItem(options: { options in\n        options.loopCount = 1\n    })\n    .duration(.distantFuture)\n    .autoDismissOnTap()\n    .show(animated: true)\n```\n#### 3.1.2 生成\n使用`makeToast`可以生成链式操作对象: `JJToastDSL\u003cT\u003e where T: JJToastItemable`\n```swift\n/// 根据对应的`toast`样式生成相应的链式操作\n    /// - Parameter item: `toast`\n    /// - Returns: `toast`链式操作\nfunc makeToast\u003cT\u003e(_ item: T) -\u003e JJToastDSL\u003cT\u003e where T: JJToastItemable {\n    JJToastDSL(view: base, item: item)\n}\n```\n#### 3.1.3 配置`toast`样式\n因为`JJToastDSL`是泛型,所以可以调用`updateItem`方法,配置对应`toast`样式的配置\n```swift\n/// 修改`JJToastItemable`的配置\n/// - Parameter block: block配置\nfunc updateItem(options block: (_ options: inout T.Options) -\u003e ()) -\u003e Self {\n    block(\u0026itemOptions)\n    return self\n}\n```\n#### 3.1.4 配置容器样式，显示时间、显示隐藏动画: 具体方法如下\n```swift\n/// 使用渐变色容器\n.useContainer(JJGradientContainer(colors: [.jRandom(), .jRandom(), .jRandom()]))\n/// 显示动画\n.appearAnimations([.scaleX(0.2), .opacity(0.3)])\n/// 隐藏动画\n.disappearAnimations([.scaleY(0.2).opposite, .opacity(0.3).opposite])\n/// 点击自动消失\n.autoDismissOnTap()\n/// 时间:只要不主动隐藏就会永久显示\n.duration(.distantFuture)\n```\n### 3.2: 具体使用\n#### 3.2.1: 显示文字\n```swift\nview.jj.show(message: text)\n```\n#### 3.2.1: 显示系统指示器: \n```swift\nview.jj.showActivityIndicator()\n```\n#### 3.2.2: 指定位置显示网络图片\n```swift\n@IBAction func showWebImage() {\n    guard let url = URL(string: \"http://apng.onevcat.com/assets/elephant.png\") else {\n        return\n    }\n    view.jj.makeToast(JJImageToastItem(url: url, display: { url, imageView in\n        imageView.sd_setImage(with: url, completed: nil)\n    })).updateItem(options: { opt in\n        opt.imageSize = .fixed(CGSize(width: 150, height: 150))\n        opt.configUIImageView = { iv in\n            iv.contentMode = .scaleAspectFill\n            iv.clipsToBounds = true\n        }\n    })\n    .autoDismissOnTap()\n    .duration(.distantFuture)\n    .position(.center)\n    .show()\n}\n```\n#### 3.2.3: 使用纯色容器\n```swift\n@IBAction func showUsingColorContainerTextToast() {\n    view.jj.makeToast(JJTextToastItem(text: \"我是一个带色彩背景的toast\"))\n        .useContainer(JJColorfulContainer(color: .jRandom()))\n        .duration(.distantFuture)\n        .autoDismissOnTap()\n        .show()\n}\n```\n#### 3.2.4: 混合显示文字+指示器:\n```swift\n@IBAction func showMixActivityAndTextToast() {\n      view.jj.makeToast(JJMixTwoToastItem(first: JJActivityToastItem(), second: JJTextToastItem(text: \"我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容\")))\n          .duration(.distantFuture)\n          .autoDismissOnTap()\n          .show()\n  }\n```\n\n## 4: 自定义: \n你可以自定义任何你所需的样式, 然后`makeToast`使用对应的对象,后续的配置+显示+隐藏逻辑,框架都以配置好。\n\n\n# 三: 弹窗转场动画: JJTransition\n## 3.1: 弹窗驱动器`JJAlertPresentationController`\n\u003e `present`类型`modalPresentationStyle`为`custom`,继承自`UIPresentationController`\n\n### 3.1.1 简单使用事例\n以高斯模糊为蒙层,且居中弹出效果\n```swift\nlet pd = JJAlertPresentationController(show: self, from: sourceController) { ctx in\n    ctx.usingBlurBelowCoverAnimators(style: .dark)\n}\nA.present(B, animated: true) {\n    let _ = pd\n}\n```\n\n### 3.1.2 弹窗效果设置---必须在上下文`JJAlertPresentationContext`中设置\n\n#### 3.1.2.1 转场动画持续时间,默认0.2s\n```swift\npublic var duration: TimeInterval = 0.2\n```\n#### 3.1.2.2 弹出界面的其余部分点击事件,默认为自动`dismiss`\n```swift\npublic var belowCoverAction = JJAlertPresentationContext.BelowCoverAction.autodismiss(true)\n行为action为枚举值\npublic enum BelowCoverAction {\n    /// 是否自动dismiss\n    case autodismiss(_ auto: Bool)\n    /// 自定义动作\n    case customize(action: () -\u003e ())\n}\n```\n\u003e 可以在弹窗出现之后通过`AlertPresentationController`的`updateContext`方法随时更改此属性\n\u003e eg:可以在弹窗展示的时候为`.autodismiss(false)`,然后,在页面事件处理完成之后改为`.autodismiss(true)`\n\u003e 同时,默认的点击空白消失是带动画的.如果不想带动画,请设置为`.customize`,在block内部手动调用dismiss\n\n#### 3.1.2.3 转场动画中,弹出页面的`frame`,默认使弹出页面居中显示\n```swift\npublic var frameOfPresentedViewInContainerView: ((_ containerViewBounds: CGRect, _ preferredContentSize: CGSize) -\u003e (CGRect))? = Default.centerFrameOfPresentedView\n```\n\n#### 3.1.2.4 转场动画中,弹出页面的修饰包装view,默认4个圆角带阴影view\n```swift\npublic var presentationWrappingView: ((_ presentedViewControllerView: UIView, _ frameOfPresentedView: CGRect) -\u003e UIView)? = Default.shadowAllRoundedCornerWrappingView(10)\n```\n\n#### 3.1.2.5 转场动画中,`containerView`的效果,默认是暗灰色view\n```swift\npublic var belowCoverView: ((_ frame: CGRect) -\u003e UIView)? = Default.dimmingBelowCoverView\n```\n\n#### 3.1.2.6 转场动画的具体实现,默认是弹出居中view的动画效果\n```swift\npublic var transitionAnimator: ((_ fromView: UIView, _ toView: UIView, _ style: JJAlertPresentationContext.TransitionType, _ duration: TimeInterval, _ ctx: UIViewControllerContextTransitioning) -\u003e ())? = Default.centerTransitionAnimator\n```\n\n#### 3.1.2.7 转场动画中,`containerView`的展示动画效果,默认是暗灰色view的动画效果\n```swift\npublic var willPresentAnimatorForBelowCoverView: ((_ belowCoverView: UIView, _ coordinator: UIViewControllerTransitionCoordinator) -\u003e ())? = Default.dimmingBelowCoverViewAnimator(true)\n```\n\n#### 3.1.2.8 转场动画中,`containerView`的消失动画效果,默认是暗灰色view的动画效果\n```swift\npublic var willDismissAnimatorForBelowCoverView: ((_ belowCoverView: UIView, _ coordinator: UIViewControllerTransitionCoordinator) -\u003e ())? = Default.dimmingBelowCoverViewAnimator(false)\n```\n\n## 3.2: 提供使present/dismiss动画跟系统push/pop动画一致的转场动画协议`JJPushPopStylePresentDelegate`\n\n### 3.2.1使用方法\nA-\u003eB, B准守`JJPushPopStylePresentDelegate`协议,然后在在跳转的时候设置B的`transitioningDelegate`为自身\n```swift\nlet b = UIViewController()\nlet navi = UINavigationController(rootViewController: b)\nnavi.modalPresentationStyle = .fullScreen\nnavi.transitioningDelegate = b.pushPopStylePresentDelegate\na.present(navi, animated: true)\n```\n\n\n使用需求\n=================\n* iOS 11.0+\n* Swift 5.5+\n\n安装\n=================\n\nCocoapods\n```\nuse_frameworks!\npod 'JJKit'\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzgjff%2Fjjkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzgjff%2Fjjkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzgjff%2Fjjkit/lists"}