├── JavenKit.podspec ├── JavenKit ├── .DS_Store ├── Bundle+JavenKit.swift ├── JWAlertViewController │ ├── .DS_Store │ ├── Action │ │ └── JWAlertAction.swift │ ├── Animator │ │ ├── JWAlertDismissTransitioning.swift │ │ └── JWAlertPresentTransitioning.swift │ ├── Controller │ │ └── JWAlertController.swift │ └── View │ │ └── JWActionButton.swift ├── JWAutoScrollView │ └── JWAutoScrollView.swift ├── JWCalendarViewController │ ├── .DS_Store │ ├── View │ │ ├── JWCalendarHeaderView.swift │ │ ├── JWCalendarSectionView.swift │ │ ├── JWCalendarView.swift │ │ └── JWCalendarViewCell.swift │ └── ViewController │ │ └── JWCalendarViewController.swift ├── JWDatePickerView │ ├── JWDatePickerKeyBoardView.swift │ ├── JWDatePickerView.swift │ └── JWPickerKeyBoardView.swift ├── JWMessageView │ ├── .DS_Store │ └── JWMessageView.swift ├── JWPhotoBrowserViewController │ ├── .DS_Store │ ├── Controller │ │ └── JWPhotoBrowserViewController.swift │ ├── Model │ │ └── JWPhotoBrowerItem.swift │ ├── Transitioning │ │ ├── JWPhotoDismissBrowserTransitioning.swift │ │ ├── JWPhotoInteractiveTransitioning.swift │ │ ├── JWPhotoPresentBrowserTransitioning.swift │ │ └── JWPhotoTapDismissTransioning.swift │ └── View │ │ └── JWPhotoBrowserCell.swift ├── JWProgressHUD │ ├── .DS_Store │ ├── JWProgressHUD.swift │ └── JWProgressHUDLoadingView.swift ├── JWWebBrowser │ ├── JWWebViewController.swift │ └── JWWebViewProgressView.swift └── JavenKit.bundle │ ├── .DS_Store │ ├── Bom.png │ ├── JWMessage.png │ ├── error.png │ ├── highlightBg.png │ ├── loading1.png │ ├── loading2.png │ ├── loading3.png │ ├── normalBg.png │ ├── normalBlackBg.png │ └── success.png ├── KitDemo.zip ├── LICENSE ├── README.md └── showGif ├── .DS_Store ├── JWAlertController.gif ├── JWAutoScrollView.gif ├── JWCalendarViewController1.gif ├── JWCalendarViewController2.gif ├── JWMessageView.gif ├── JWPhotoBrowserViewController.gif ├── JWPickerView.gif ├── JWProgressHUD.gif └── JWWebView.gif /JavenKit.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | 4 | s.name = "JavenKit" 5 | s.version = "0.0.8" 6 | s.summary = "Swift kit" 7 | s.homepage = "https://github.com/GitHubOfJW/JavenKit" 8 | s.license = "MIT" 9 | s.author = { "Javen" => "1284627282@qq.com" } 10 | s.platform = :ios, "8.0" 11 | s.source = { :git => "https://github.com/GitHubOfJW/JavenKit.git", :tag => "#{s.version}" } 12 | s.source_files = "JavenKit","JavenKit/**/*.{swift}" 13 | s.resources = "JavenKit/JavenKit.bundle" 14 | s.framework = "UIKit" 15 | end 16 | 17 | 18 | -------------------------------------------------------------------------------- /JavenKit/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/.DS_Store -------------------------------------------------------------------------------- /JavenKit/Bundle+JavenKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bundle+JavenKit.swift 3 | // Pods 4 | // 5 | // Created by 朱建伟 on 2017/1/11. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | extension Bundle { 12 | 13 | class func image(named:String) -> UIImage? { 14 | let bundle = Bundle(path:Bundle(for: JWAutoScrollView.classForCoder()).path(forResource: "JavenKit", ofType: "bundle")!) 15 | 16 | let path = bundle?.path(forResource: named, ofType: "png") 17 | return UIImage(contentsOfFile: path!) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /JavenKit/JWAlertViewController/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JWAlertViewController/.DS_Store -------------------------------------------------------------------------------- /JavenKit/JWAlertViewController/Action/JWAlertAction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWAlertAction.swift 3 | // CarServer 4 | // 5 | // Created by 朱建伟 on 2016/11/6. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public enum JWAlertActionStyle:Int{ 12 | case `default` 13 | case cancel 14 | case destructive 15 | } 16 | 17 | public class JWAlertAction: NSObject { 18 | 19 | 20 | 21 | public typealias JWActionClosure = (JWAlertAction)->Void 22 | 23 | 24 | //初始化 25 | public convenience init(title:String,style:JWAlertActionStyle,handler:@escaping JWActionClosure) { 26 | self.init() 27 | 28 | //设置标题 29 | self.title = title 30 | 31 | 32 | //回掉 33 | self.handler = handler 34 | 35 | //样式 36 | self.actionStyle = style 37 | } 38 | 39 | 40 | //类型 41 | var actionStyle:JWAlertActionStyle = JWAlertActionStyle.default 42 | 43 | 44 | //回调 45 | var handler:JWActionClosure? 46 | 47 | 48 | //title 49 | var title:String? 50 | 51 | 52 | 53 | private override init() { 54 | super.init() 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /JavenKit/JWAlertViewController/Animator/JWAlertDismissTransitioning.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWAlertDismissTransitioning.swift 3 | // JWAlertViewController_Demo 4 | // 5 | // Created by 朱建伟 on 2016/12/17. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class JWAlertDismissTransitioning: NSObject,UIViewControllerAnimatedTransitioning,CAAnimationDelegate{ 12 | 13 | 14 | var preferredStyle:JWAlertControllerStyle = JWAlertControllerStyle.alert 15 | 16 | 17 | //返回动画之行的时间 18 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 19 | if self.preferredStyle == .alert { 20 | return 0.25 21 | }else if self.preferredStyle == .actionSheet{ 22 | return 0.3 23 | } 24 | return 0.4 25 | } 26 | 27 | //执行动画 28 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 29 | 30 | //动画容器 31 | let containerView = transitionContext.containerView 32 | 33 | //fromView 34 | let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) 35 | 36 | //toView 37 | let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) 38 | 39 | //添加到容器上 40 | 41 | if let fromV = fromView { 42 | fromV.frame = UIScreen.main.bounds 43 | containerView.addSubview(fromV) 44 | 45 | if self.preferredStyle == .alert{ 46 | //动画 47 | let animation:CAKeyframeAnimation = CAKeyframeAnimation(keyPath: "transform.scale") 48 | animation.values = [1.0,1.2,0.8] 49 | animation.isRemovedOnCompletion = false 50 | animation.duration = self.transitionDuration(using: transitionContext) 51 | animation.fillMode = kCAFillModeForwards 52 | animation.delegate = self 53 | animation.setValue(transitionContext, forKey: "transitionContext") 54 | fromV.layer.add(animation, forKey: "scaleAnimator") 55 | }else if self.preferredStyle == .actionSheet{ 56 | //动画 57 | let animation:CAKeyframeAnimation = CAKeyframeAnimation(keyPath: "transform.translation.y") 58 | animation.values = [0,-20,-20,fromV.bounds.height] 59 | // animation.fromValue = 0 60 | // animation.toValue = fromV.bounds.height 61 | animation.isRemovedOnCompletion = false 62 | animation.duration = self.transitionDuration(using: transitionContext) 63 | animation.fillMode = kCAFillModeForwards 64 | animation.delegate = self 65 | animation.setValue(transitionContext, forKey: "transitionContext") 66 | fromV.layer.add(animation, forKey: "actionsheetAnimator") 67 | }else if self.preferredStyle == .popover{ 68 | //动画 69 | let animation:CABasicAnimation = CABasicAnimation(keyPath: "opacity") 70 | animation.fromValue = 1 71 | animation.toValue = 0 72 | animation.isRemovedOnCompletion = false 73 | animation.duration = self.transitionDuration(using: transitionContext) 74 | animation.fillMode = kCAFillModeForwards 75 | animation.delegate = self 76 | animation.setValue(transitionContext, forKey: "transitionContext") 77 | fromV.layer.add(animation, forKey: "opacityAnimator") 78 | } 79 | }else{ 80 | transitionContext.completeTransition(false) 81 | } 82 | 83 | if let toV = toView{ 84 | toV.frame = UIScreen.main.bounds 85 | containerView.addSubview(toV) 86 | } 87 | 88 | 89 | 90 | } 91 | 92 | func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { 93 | if let value = anim.value(forKeyPath: "transitionContext"){ 94 | if let transitionContext:UIViewControllerContextTransitioning = value as? UIViewControllerContextTransitioning{ 95 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 96 | } 97 | } 98 | } 99 | 100 | func animationEnded(_ transitionCompleted: Bool) { 101 | 102 | } 103 | 104 | 105 | } 106 | -------------------------------------------------------------------------------- /JavenKit/JWAlertViewController/Animator/JWAlertPresentTransitioning.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWAlertTransitioning.swift 3 | // JWAlertViewController_Demo 4 | // 5 | // Created by 朱建伟 on 2016/12/17. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class JWAlertPresentTransitioning: NSObject,UIViewControllerAnimatedTransitioning ,CAAnimationDelegate{ 12 | 13 | 14 | var preferredStyle:JWAlertControllerStyle = JWAlertControllerStyle.alert 15 | 16 | //返回动画之行的时间 17 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 18 | if self.preferredStyle == .alert { 19 | return 0.25 20 | }else if self.preferredStyle == .actionSheet{ 21 | return 0.3 22 | } 23 | return 0.4 24 | } 25 | 26 | //执行动画 27 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 28 | 29 | //动画容器 30 | let containerView = transitionContext.containerView 31 | 32 | //fromView 33 | let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) 34 | 35 | //toView 36 | let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) 37 | 38 | //添加到容器上 39 | 40 | if let fromV = fromView { 41 | fromV.frame = UIScreen.main.bounds 42 | containerView.addSubview(fromV) 43 | } 44 | 45 | if let toV = toView{ 46 | toV.frame = UIScreen.main.bounds 47 | containerView.addSubview(toV) 48 | 49 | if self.preferredStyle == .alert{ 50 | //动画 51 | let animation:CAKeyframeAnimation = CAKeyframeAnimation(keyPath: "transform.scale") 52 | animation.values = [0.8,1.2,1.0] 53 | animation.isRemovedOnCompletion = false 54 | animation.duration = self.transitionDuration(using: transitionContext) 55 | animation.fillMode = kCAFillModeForwards 56 | animation.delegate = self 57 | animation.setValue(transitionContext, forKey: "transitionContext") 58 | toV.layer.add(animation, forKey: "alertAnimator") 59 | }else if self.preferredStyle == .actionSheet{ 60 | //动画 61 | let animation:CAKeyframeAnimation = CAKeyframeAnimation(keyPath: "transform.translation.y") 62 | animation.values = [toV.bounds.height,-20,20,0] 63 | // animation.fromValue = toV.bounds.height 64 | // animation.toValue = 0 65 | animation.isRemovedOnCompletion = false 66 | animation.duration = self.transitionDuration(using: transitionContext) 67 | animation.fillMode = kCAFillModeForwards 68 | animation.delegate = self 69 | animation.setValue(transitionContext, forKey: "transitionContext") 70 | toV.layer.add(animation, forKey: "actionsheetAnimator") 71 | }else if self.preferredStyle == .popover{ 72 | //动画 73 | let animation:CABasicAnimation = CABasicAnimation(keyPath: "opacity") 74 | animation.fromValue = 0 75 | animation.toValue = 1 76 | animation.isRemovedOnCompletion = false 77 | animation.duration = self.transitionDuration(using: transitionContext) 78 | animation.fillMode = kCAFillModeForwards 79 | animation.delegate = self 80 | animation.setValue(transitionContext, forKey: "transitionContext") 81 | toV.layer.add(animation, forKey: "opacityAnimator") 82 | } 83 | }else{ 84 | transitionContext.completeTransition(false) 85 | } 86 | 87 | 88 | 89 | } 90 | 91 | func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { 92 | if let value = anim.value(forKeyPath: "transitionContext"){ 93 | if let transitionContext:UIViewControllerContextTransitioning = value as? UIViewControllerContextTransitioning{ 94 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 95 | } 96 | } 97 | } 98 | 99 | func animationEnded(_ transitionCompleted: Bool) { 100 | 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /JavenKit/JWAlertViewController/Controller/JWAlertController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWAlertViewController.swift 3 | // CarServer 4 | // 5 | // Created by 朱建伟 on 2016/11/6. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public enum JWAlertControllerStyle:Int{ 12 | case actionSheet//ActionSheet 13 | case alert//alertView 14 | case popover//带箭头的悬浮小菜单 15 | } 16 | 17 | 18 | public class JWAlertController: UIViewController ,UIViewControllerTransitioningDelegate{ 19 | 20 | private var preferredStyle:JWAlertControllerStyle = JWAlertControllerStyle.actionSheet 21 | 22 | private let containerViewPadding:CGFloat = 20 23 | 24 | 25 | //蒙版 26 | private let coverView:UIButton = UIButton() 27 | 28 | //展示的View 29 | private let containerView:UIView = UIView() 30 | 31 | //边框阴影的view 32 | private let shadowView:UIButton = UIButton() 33 | 34 | //标题 35 | private let titleLabel:UILabel = UILabel() 36 | 37 | //内容 38 | private let messageLabel:UILabel = UILabel() 39 | 40 | //lineView 41 | private let lineView:UIView = UIView() 42 | 43 | //按钮的view 44 | private let operationView:UIView = UIView() 45 | 46 | //箭头图层 47 | private let arrowLayer:CAShapeLayer = CAShapeLayer() 48 | 49 | 50 | //按钮数组 51 | private var actiontBtnArray:[JWActionButton] = [JWActionButton]() 52 | 53 | //数组 54 | private var actions:[JWAlertAction] = [JWAlertAction]() 55 | 56 | 57 | //popver 58 | private var sourceViewRect:CGRect = CGRect() 59 | 60 | //箭头 61 | private let arrowSize:CGSize = CGSize(width: 12, height: 8) 62 | 63 | //具体内容 64 | var message:String? 65 | 66 | 67 | //初始化 68 | public convenience init(preferredStyle: JWAlertControllerStyle,sourceViewRect:CGRect?){ 69 | self.init() 70 | 71 | self.modalPresentationStyle = .custom 72 | 73 | if preferredStyle == .popover{ 74 | if let sourceViewRect = sourceViewRect{ 75 | self.sourceViewRect = sourceViewRect 76 | self.preferredStyle = preferredStyle 77 | } 78 | } 79 | 80 | self.transitioningDelegate = self 81 | 82 | } 83 | 84 | //初始化 85 | public convenience init(preferredStyle: JWAlertControllerStyle,sourceView:UIView?){ 86 | self.init() 87 | 88 | self.modalPresentationStyle = .custom 89 | 90 | if preferredStyle == .popover{ 91 | if let sourceView = sourceView{ 92 | self.sourceViewRect = sourceView.convert(sourceView.bounds, to: UIApplication.shared.keyWindow) 93 | self.preferredStyle = preferredStyle 94 | } 95 | } 96 | 97 | self.transitioningDelegate = self 98 | 99 | } 100 | 101 | //初始化 102 | public convenience init(preferredStyle: JWAlertControllerStyle){ 103 | self.init() 104 | 105 | self.modalPresentationStyle = .custom 106 | 107 | self.preferredStyle = preferredStyle 108 | 109 | 110 | self.transitioningDelegate = self 111 | 112 | } 113 | 114 | //初始化 115 | public convenience init(title: String?, message: String?, preferredStyle: JWAlertControllerStyle){ 116 | self.init() 117 | 118 | 119 | self.modalPresentationStyle = .custom 120 | 121 | 122 | //标题 123 | if let t = title { 124 | self.title = t 125 | } 126 | 127 | //内容 128 | if let msg = message{ 129 | self.message = msg 130 | } 131 | 132 | 133 | //样式 134 | self.preferredStyle = preferredStyle 135 | 136 | 137 | self.transitioningDelegate = self 138 | 139 | } 140 | 141 | 142 | 143 | //加载 144 | override public func viewDidLoad() { 145 | super.viewDidLoad() 146 | 147 | view.backgroundColor = UIColor.clear 148 | 149 | //蒙版 150 | coverView.backgroundColor = UIColor.clear 151 | coverView.tag = -1 152 | coverView.addTarget(self, action: #selector(JWAlertController.actionBtnClick(btn:)), for: UIControlEvents.touchUpInside) 153 | view.addSubview(coverView) 154 | 155 | //边框阴影的view 156 | shadowView.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.45).cgColor 157 | shadowView.layer.shadowRadius = 20 158 | shadowView.layer.shadowOffset = CGSize(width: 0, height:10) 159 | shadowView.layer.shadowOpacity = 1 160 | shadowView.layer.masksToBounds = false 161 | view.addSubview(shadowView) 162 | 163 | 164 | 165 | //展示的View 166 | containerView.backgroundColor = UIColor.white 167 | containerView.layer.cornerRadius = 13.0 168 | containerView.layer.masksToBounds = true 169 | shadowView.addSubview(containerView) 170 | 171 | 172 | if preferredStyle == .popover{ 173 | shadowView.layer.shadowRadius = 10 174 | shadowView.layer.shadowOffset = CGSize(width: 0, height:5) 175 | containerView.layer.cornerRadius = 5.0 176 | } 177 | 178 | 179 | //标题 180 | titleLabel.numberOfLines = 0 181 | titleLabel.isUserInteractionEnabled = true 182 | titleLabel.isHidden = true 183 | titleLabel.font = UIFont.boldSystemFont(ofSize: 20) 184 | titleLabel.textAlignment = NSTextAlignment.center 185 | titleLabel.textColor = UIColor(red: 94/255.0, green: 96/255.0, blue: 102/255.0, alpha: 1) 186 | containerView.addSubview(titleLabel) 187 | 188 | //内容 189 | messageLabel.numberOfLines = 0 190 | messageLabel.isUserInteractionEnabled = true 191 | messageLabel.isHidden = true 192 | messageLabel.font = UIFont.systemFont(ofSize:16) 193 | messageLabel.textAlignment = NSTextAlignment.center 194 | messageLabel.textColor = UIColor(red: 94/255.0, green: 96/255.0, blue: 102/255.0, alpha: 1) 195 | containerView.addSubview(messageLabel) 196 | 197 | //线 198 | lineView.backgroundColor = UIColor(white: 0.85, alpha: 1) 199 | containerView.addSubview(lineView) 200 | 201 | 202 | //按钮的view 203 | operationView.backgroundColor = UIColor(white: 0.85, alpha: 1) 204 | containerView.addSubview(operationView) 205 | 206 | 207 | 208 | 209 | //如果按钮大于2个 隐藏线 210 | self.lineView.isHidden = (self.actions.count > 2 || self.actions.count == 1 || preferredStyle != .alert) 211 | 212 | //移除按钮 213 | for btn in self.actiontBtnArray{ 214 | btn.removeFromSuperview() 215 | } 216 | 217 | self.actiontBtnArray.removeAll() 218 | 219 | let normalColor:UIColor = UIColor(red: 94/255.0, green: 96/255.0, blue: 102/255.0, alpha: 1) 220 | 221 | let highlightedColor = UIColor(red: 104/255.0, green: 106/255.0, blue: 112/255.0, alpha: 1) 222 | //添加 223 | for action in self.actions{ 224 | let actionBtn:JWActionButton = JWActionButton() 225 | actionBtn.tag = self.actiontBtnArray.count 226 | 227 | actionBtn.lineView.isHidden = !(self.actions.count > 2 || self.actions.count == 1 || preferredStyle != .alert)// self.actions.count <= 2 228 | 229 | 230 | actionBtn.titleLabel?.font = UIFont.systemFont(ofSize: 14) 231 | actionBtn.addTarget(self, action: #selector(JWAlertController.actionBtnClick(btn:)), for: UIControlEvents.touchUpInside) 232 | 233 | operationView.addSubview(actionBtn) 234 | 235 | //设置标题 236 | actionBtn.setTitle(action.title, for: UIControlState.normal) 237 | actionBtn.setTitle(action.title, for: UIControlState.selected) 238 | 239 | 240 | if preferredStyle == .popover{ 241 | actionBtn.lineMargin = 10 242 | actionBtn.lineView.isHidden = self.actiontBtnArray.count < 1 243 | actionBtn.setBackgroundImage(UIImage(named:"normalBlackBg"), for: UIControlState.normal) 244 | actionBtn.setBackgroundImage(UIImage(named:"normalBlackBg"), for: UIControlState.highlighted) 245 | actionBtn.setTitleColor(UIColor.white, for: UIControlState.normal) 246 | actionBtn.setTitleColor(UIColor.white, for: UIControlState.highlighted) 247 | }else{ 248 | 249 | actionBtn.setBackgroundImage(UIImage(named:"normalBg"), for: UIControlState.normal) 250 | actionBtn.setBackgroundImage(UIImage(named:"highlightBg"), for: UIControlState.highlighted) 251 | 252 | switch action.actionStyle { 253 | case .cancel: 254 | actionBtn.setTitleColor(highlightedColor, for: UIControlState.normal) 255 | 256 | break 257 | case .default: 258 | actionBtn.setTitleColor(normalColor, for: UIControlState.normal) 259 | break 260 | case .destructive: 261 | actionBtn.setTitleColor(UIColor.red, for: UIControlState.normal) 262 | break 263 | } 264 | } 265 | 266 | self.actiontBtnArray.append(actionBtn) 267 | } 268 | 269 | 270 | //添加箭头 271 | self.arrowLayer.fillColor = UIColor.black.cgColor 272 | self.view.layer.addSublayer(self.arrowLayer) 273 | 274 | } 275 | 276 | override public func viewWillLayoutSubviews() { 277 | super.viewWillLayoutSubviews() 278 | 279 | 280 | self.coverView.frame = view.bounds 281 | 282 | //布局控件 283 | var shadowViewW:CGFloat = preferredStyle == .alert ? view.bounds.width * 0.75 : view.bounds.width * 0.9 284 | 285 | var btnH:CGFloat = 45 286 | 287 | 288 | //如果是popover模式 289 | if preferredStyle == .popover{ 290 | shadowViewW = 85 291 | btnH = 40 292 | } 293 | 294 | let containViewW:CGFloat = shadowViewW 295 | 296 | //标题 297 | let titleW:CGFloat = containViewW - 2*containerViewPadding 298 | let titleX:CGFloat = containerViewPadding 299 | var titleH:CGFloat = 0 300 | var titleY:CGFloat = 0 301 | 302 | //计算高度 303 | if let title = self.title{ 304 | if title != ""{ 305 | titleY = 20 306 | titleH = boundingRect(size: CGSize(width:titleW,height:UIScreen.main.bounds.height), font:titleLabel.font, str: title).height + 5 307 | titleLabel.isHidden = false 308 | titleLabel.text = title 309 | } 310 | } 311 | titleLabel.frame = CGRect(x: titleX, y: titleY, width: titleW, height: titleH) 312 | 313 | 314 | //内容 315 | let messageX:CGFloat = titleX 316 | let messageW:CGFloat = titleW 317 | var messageH:CGFloat = 0 318 | var messageY:CGFloat = titleLabel.frame.maxY 319 | 320 | //计算高度 321 | if let message = self.message{ 322 | if message != ""{ 323 | messageY = titleLabel.frame.maxY + 15 324 | messageH = boundingRect(size: CGSize(width:messageW,height:UIScreen.main.bounds.height), font:messageLabel.font, str: message).height + 5 325 | 326 | messageLabel.isHidden = false 327 | messageLabel.text = message 328 | } 329 | } 330 | messageLabel.frame = CGRect(x: messageX, y: messageY, width: messageW, height: messageH) 331 | 332 | 333 | //线 334 | let lineW:CGFloat = containViewW - containerViewPadding*2 335 | let lineH:CGFloat = ((messageLabel.isHidden && titleLabel.isHidden) ? 0 : 0.5) 336 | let lineX:CGFloat = (containViewW - lineW)/2 337 | let lineY:CGFloat = messageLabel.frame.maxY + ((messageLabel.isHidden && titleLabel.isHidden) ? 0 : 20) 338 | lineView.frame = CGRect(x: lineX, y: lineY, width: lineW, height: lineH) 339 | 340 | //按钮的view 341 | let operationY:CGFloat = lineView.frame.maxY 342 | let operationW:CGFloat = containViewW 343 | let operationX:CGFloat = (containViewW - operationW)/2 344 | 345 | 346 | let operationH:CGFloat = (self.actions.count > 2 || preferredStyle != .alert) ? btnH * CGFloat(self.actions.count) : btnH 347 | operationView.frame = CGRect(x: operationX, y: operationY, width: operationW, height: operationH) 348 | 349 | 350 | let btnW:CGFloat = (self.actions.count > 2 || self.actions.count == 1 || preferredStyle != .alert) ? containViewW : (containViewW/2 - 0.25) 351 | 352 | 353 | //布局按钮 354 | var i:Int = 0 355 | for btn in self.actiontBtnArray{ 356 | let btnX:CGFloat = (self.actions.count > 2 || preferredStyle != .alert) ? 0 : (btnW+0.5) * CGFloat(i) 357 | let btnY:CGFloat = (self.actions.count > 2 || preferredStyle != .alert) ? (btnH * CGFloat(i)) : 0 358 | 359 | if (messageLabel.isHidden && titleLabel.isHidden) && i == 0 && self.preferredStyle == .actionSheet { 360 | btn.lineView.isHidden = true 361 | } 362 | 363 | btn.frame = CGRect(x: btnX, y: btnY, width: btnW, height: btnH) 364 | //累计 365 | i = i+1 366 | } 367 | 368 | 369 | 370 | 371 | //展示的View 372 | let containViewH:CGFloat = operationView.frame.maxY 373 | let containViewX:CGFloat = (shadowViewW - containViewW)/2 374 | let containViewY:CGFloat = 0 375 | containerView.frame = CGRect(x: containViewX, y: containViewY, width: containViewW, height: containViewH) 376 | 377 | //边框阴影的view 378 | let shadowViewH:CGFloat = containViewH 379 | var shadowViewX:CGFloat = (view.bounds.width - shadowViewW) / 2 380 | var shadowViewY:CGFloat = self.preferredStyle == .actionSheet ? (view.bounds.height - shadowViewH - 30) : (view.bounds.height - shadowViewH)/2 381 | 382 | 383 | if preferredStyle == .popover{ 384 | self.arrowLayer.isHidden = false 385 | let margin:CGFloat = 5 386 | var arrowPointX:CGFloat = 0 387 | var arrowPointY:CGFloat = 0 388 | 389 | //下半部分 390 | if (sourceViewRect.maxY + sourceViewRect.minY)/2 > view.bounds.height/2{ 391 | //右边 392 | if (sourceViewRect.minX + sourceViewRect.maxX)/2 > view.bounds.width/2{ 393 | 394 | shadowViewX = (sourceViewRect.minX + sourceViewRect.maxX)/2 - shadowViewW / 2 395 | 396 | //判断超出 397 | if shadowViewX + shadowViewW > view.bounds.width { 398 | shadowViewX = view.bounds.width - shadowViewW - margin 399 | } 400 | 401 | 402 | shadowViewY = (sourceViewRect.minY - self.arrowSize.height - shadowViewH ) 403 | 404 | arrowPointX = (sourceViewRect.minX+sourceViewRect.maxX)/2 405 | arrowPointY = sourceViewRect.minY 406 | }else{//左边 407 | 408 | shadowViewX = (sourceViewRect.minX + sourceViewRect.maxX)/2 - shadowViewW / 2 409 | 410 | shadowViewY = (sourceViewRect.minY - self.arrowSize.height - shadowViewH) 411 | 412 | //判断超出 413 | if shadowViewX < margin { 414 | shadowViewX = margin 415 | } 416 | 417 | arrowPointX = (sourceViewRect.minX+sourceViewRect.maxX)/2 418 | arrowPointY = sourceViewRect.minY 419 | 420 | } 421 | }else{//上半部分 422 | //右边 423 | if (sourceViewRect.minX + sourceViewRect.maxX)/2 > view.bounds.width/2{ 424 | 425 | shadowViewX = (sourceViewRect.minX + sourceViewRect.maxX)/2 - shadowViewW / 2 426 | 427 | shadowViewY = (sourceViewRect.maxY + self.arrowSize.height) 428 | 429 | 430 | //判断超出 431 | if shadowViewX + shadowViewW > view.bounds.width - margin{ 432 | shadowViewX = view.bounds.width - shadowViewW - margin 433 | } 434 | 435 | 436 | arrowPointX = (sourceViewRect.minX+sourceViewRect.maxX)/2 437 | arrowPointY = sourceViewRect.maxY 438 | 439 | 440 | 441 | }else{//左边 442 | shadowViewX = (sourceViewRect.minX + sourceViewRect.maxX)/2 - shadowViewW / 2 443 | 444 | shadowViewY = (sourceViewRect.maxY + self.arrowSize.height) 445 | 446 | //判断超出 447 | if shadowViewX < margin { 448 | shadowViewX = margin 449 | } 450 | 451 | arrowPointX = (sourceViewRect.minX+sourceViewRect.maxX)/2 452 | arrowPointY = sourceViewRect.maxY 453 | } 454 | } 455 | 456 | shadowView.frame = CGRect(x: shadowViewX, y: shadowViewY, width: shadowViewW, height: shadowViewH) 457 | 458 | //计算背景 459 | self.arrowLayer.path = self.getPath(arrowPoint: CGPoint(x:arrowPointX,y:arrowPointY), containFrame: self.shadowView.frame).cgPath 460 | 461 | }else{ 462 | self.arrowLayer.isHidden = true 463 | shadowView.frame = CGRect(x: shadowViewX, y: shadowViewY, width: shadowViewW, height: shadowViewH) 464 | } 465 | 466 | } 467 | 468 | 469 | //action按钮点击 470 | func actionBtnClick(btn:UIButton) { 471 | 472 | //如果是alert 点击了背景按钮 473 | if self.preferredStyle == JWAlertControllerStyle.alert && btn.tag == -1 474 | { 475 | return 476 | }else if btn.tag == -1{ 477 | dismiss(animated: true, completion: { 478 | 479 | }) 480 | return 481 | }else{ 482 | dismiss(animated: true, completion: { 483 | let action = self.actions[btn.tag] 484 | if let ac = action.handler{ 485 | ac(action) 486 | } 487 | }) 488 | } 489 | } 490 | 491 | 492 | //添加action 493 | public func addAction(actionTitle:String,actionStyle:JWAlertActionStyle,handler:@escaping JWAlertAction.JWActionClosure) { 494 | let alertAction:JWAlertAction = JWAlertAction(title: actionTitle, style: actionStyle, handler: handler) 495 | addAction(action: alertAction) 496 | } 497 | 498 | 499 | //添加action 500 | public func addAction(action:JWAlertAction) { 501 | //如果是默认 502 | actions.append(action) 503 | 504 | //排序 505 | actions.sort { (action1, action2) -> Bool in 506 | 507 | if action1.actionStyle == JWAlertActionStyle.destructive{ 508 | if action2.actionStyle == JWAlertActionStyle.destructive{ 509 | return false 510 | }else{ 511 | return true 512 | } 513 | }else if action1.actionStyle == JWAlertActionStyle.cancel{ 514 | //如果是取消 515 | if action2.actionStyle == JWAlertActionStyle.cancel{ 516 | if preferredStyle == JWAlertControllerStyle.alert{ 517 | return false 518 | } 519 | return true 520 | }else 521 | { 522 | if preferredStyle == JWAlertControllerStyle.alert{ 523 | return true 524 | } 525 | return false 526 | } 527 | } 528 | return false 529 | } 530 | } 531 | 532 | 533 | 534 | 535 | public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 536 | self.modalPresentationStyle = .custom 537 | let presentTrasition:JWAlertPresentTransitioning = JWAlertPresentTransitioning() 538 | presentTrasition.preferredStyle = self.preferredStyle 539 | return presentTrasition 540 | } 541 | 542 | public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 543 | self.modalPresentationStyle = .custom 544 | let dismissTrasition:JWAlertDismissTransitioning = JWAlertDismissTransitioning() 545 | dismissTrasition.preferredStyle = self.preferredStyle 546 | return dismissTrasition 547 | } 548 | 549 | 550 | 551 | //计算高度 552 | func boundingRect(size:CGSize,font:UIFont,str:String) -> CGSize { 553 | let size:CGSize = NSString(string: str).boundingRect(with:size, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName:font], context: nil).size 554 | if size.height < 20{ 555 | return CGSize(width: size.width+5, height: 20) 556 | } 557 | return CGSize(width: size.width+5, height:size.height+2) 558 | } 559 | 560 | 561 | private func getPath(arrowPoint:CGPoint,containFrame:CGRect) -> UIBezierPath { 562 | 563 | // let path:UIBezierPath = UIBezierPath(roundedRect:containFrame, cornerRadius: 5) 564 | 565 | let path:UIBezierPath = UIBezierPath() 566 | 567 | path.lineCapStyle = .round 568 | path.lineJoinStyle = .round 569 | 570 | 571 | let arrowCenterX:CGFloat = (arrowPoint.x - containFrame.minX) + containFrame.minX 572 | 573 | let arrowCenterY:CGFloat = arrowPoint.y 574 | 575 | 576 | let centerPoint:CGPoint = CGPoint(x:arrowCenterX, y: arrowCenterY) 577 | 578 | 579 | if arrowPoint.y < containFrame.minY{//尖头在上面 580 | path.move(to:CGPoint(x: centerPoint.x-arrowSize.width/2, y: containFrame.minY)) 581 | path.addLine(to: CGPoint(x: arrowPoint.x, y: arrowPoint.y+1)) 582 | path.addLine(to: CGPoint(x: centerPoint.x + arrowSize.width/2, y: containFrame.minY)) 583 | 584 | path.close() 585 | 586 | }else{//在下面 587 | 588 | path.move(to:CGPoint(x: centerPoint.x-arrowSize.width/2, y: containFrame.maxY)) 589 | path.addLine(to: CGPoint(x: arrowPoint.x, y: arrowPoint.y-1)) 590 | path.addLine(to: CGPoint(x: centerPoint.x + arrowSize.width/2, y: containFrame.maxY)) 591 | 592 | path.close() 593 | } 594 | 595 | 596 | return path 597 | } 598 | 599 | } 600 | 601 | 602 | -------------------------------------------------------------------------------- /JavenKit/JWAlertViewController/View/JWActionButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWActionButton.swift 3 | // JWAlertViewController_Demo 4 | // 5 | // Created by 朱建伟 on 2016/12/17. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class JWActionButton: UIButton { 12 | 13 | let lineView:UIView = UIView() 14 | 15 | var lineMargin:CGFloat = 0 16 | 17 | //初始化 18 | override init(frame: CGRect) { 19 | super.init(frame: frame) 20 | 21 | 22 | 23 | lineView.backgroundColor = UIColor(white: 0.85, alpha: 1) 24 | 25 | 26 | addSubview(lineView) 27 | 28 | 29 | } 30 | 31 | 32 | override func layoutSubviews() { 33 | super.layoutSubviews() 34 | 35 | let lineW:CGFloat = self.bounds.size.width - 2 * self.lineMargin 36 | let lineX:CGFloat = (self.bounds.size.width - lineW)/2 37 | let lineY:CGFloat = 0 38 | let lineH:CGFloat = 0.5 39 | self.lineView.frame = CGRect(x: lineX, y: lineY, width: lineW, height: lineH) 40 | 41 | 42 | } 43 | 44 | required init?(coder aDecoder: NSCoder) { 45 | fatalError("init(coder:) has not been implemented") 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /JavenKit/JWAutoScrollView/JWAutoScrollView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWAutoScrollView.swift 3 | // CarServer 4 | // 5 | // Created by 朱建伟 on 2016/10/18. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | import Foundation 9 | import UIKit 10 | 11 | public protocol JWAutoScrollViewDelegate:NSObjectProtocol { 12 | 13 | //返回总个数 14 | func autoScrollViewNumberOfPage() -> Int 15 | //设置图片 直接返回按钮,由自己去操作 16 | func autoScrollView(autoScrollView:JWAutoScrollView,index:Int,imageButton:JWAutoScrollViewButton) 17 | //点击了选项 18 | func autoScrollView(autoScrollView:JWAutoScrollView,didSelectedItemAt itemIndex:Int) 19 | //停止滚动 20 | func autoScrollView(autoScrollView:JWAutoScrollView,didEndAnimationAt itemIndex:Int) 21 | } 22 | 23 | 24 | public class JWAutoScrollView: UIView,UIScrollViewDelegate { 25 | 26 | var bottomPrompt:String?{ 27 | willSet{ 28 | if let value = newValue{ 29 | if value.compare("") != ComparisonResult.orderedSame{ 30 | bottomLabel?.isHidden = false 31 | bottomLabel?.text = value 32 | pageControl?.isHidden = true 33 | }else{ 34 | pageControl?.isHidden = self.pageCount <= 1 35 | bottomLabel?.text = "" 36 | bottomLabel?.isHidden = true 37 | } 38 | }else{ 39 | pageControl?.isHidden = self.pageCount <= 1 40 | bottomLabel?.text = "" 41 | bottomLabel?.isHidden = true 42 | } 43 | } 44 | } 45 | 46 | //状态 47 | var isScrolling:Bool = false 48 | 49 | private var timer:Timer? 50 | 51 | private var btnArray:[JWAutoScrollViewButton] = [JWAutoScrollViewButton]() 52 | 53 | private let expand:Int = 200 54 | 55 | private var preIndex = 0 56 | 57 | private var currentIndex = 0 58 | 59 | private var bottomLabel:UILabel? 60 | 61 | public weak var delegate:JWAutoScrollViewDelegate?{ 62 | didSet{ 63 | if self.superview != nil{ 64 | if self.delegate != nil{ 65 | self.reloadData() 66 | } 67 | } 68 | } 69 | 70 | } 71 | 72 | private var pageCount:Int = 0 73 | 74 | //scrollView 75 | private var bgScrollView:UIScrollView? 76 | 77 | //pageControl 78 | private var pageControl:UIPageControl? 79 | 80 | //初始化 81 | override init(frame: CGRect) { 82 | super.init(frame: frame) 83 | 84 | 85 | //初始化scrollView 86 | bgScrollView = UIScrollView() 87 | bgScrollView?.showsVerticalScrollIndicator = false 88 | bgScrollView?.showsHorizontalScrollIndicator = false 89 | bgScrollView?.isDirectionalLockEnabled = true 90 | // bgScrollView?.bounces = false 91 | bgScrollView?.delegate = self 92 | 93 | bgScrollView?.addSubview(UIView(frame: CGRect())) 94 | 95 | for _ in 0...2{ 96 | let imageView = JWAutoScrollViewButton() 97 | imageView.adjustsImageWhenHighlighted = false 98 | imageView.contentMode = UIViewContentMode.scaleAspectFill 99 | // imageView.backgroundColor = UIColor.red 100 | imageView.imageView?.contentMode = UIViewContentMode.scaleAspectFill 101 | imageView.tag = 0 102 | imageView.addTarget(self, action: #selector(JWAutoScrollView.itemBtnClick(btn:)), for: UIControlEvents.touchUpInside) 103 | bgScrollView?.addSubview(imageView) 104 | btnArray.append(imageView) 105 | } 106 | addSubview(bgScrollView!) 107 | 108 | //初始化pageConrol 109 | pageControl = UIPageControl() 110 | pageControl?.isEnabled = false 111 | pageControl?.isEnabled = false 112 | pageControl?.currentPage = 0; 113 | pageControl?.currentPageIndicatorTintColor = UIColor.orange 114 | pageControl?.pageIndicatorTintColor = UIColor.white 115 | addSubview(pageControl!) 116 | 117 | bottomLabel = UILabel() 118 | bottomLabel?.font = UIFont.systemFont(ofSize: 14) 119 | bottomLabel?.backgroundColor = UIColor.red//UIColor(red: (80/255.0), green: (80/255.0), blue: (80/255.0), alpha: 0.8); 120 | bottomLabel?.textAlignment = NSTextAlignment.center 121 | bottomLabel?.textColor = UIColor.white 122 | bottomLabel?.isHidden = true 123 | addSubview(bottomLabel!) 124 | } 125 | 126 | //点击了按钮 127 | internal func itemBtnClick(btn:JWAutoScrollViewButton) { 128 | 129 | bgScrollView?.isPagingEnabled = true 130 | 131 | if let d = delegate { 132 | d.autoScrollView(autoScrollView: self, didSelectedItemAt: btn.tag) 133 | } 134 | } 135 | 136 | //MARK:代理 137 | 138 | public func scrollViewDidScroll(_ scrollView: UIScrollView) { 139 | 140 | if pageCount<=1 { 141 | return 142 | } 143 | 144 | let page = scrollView.contentOffset.x / scrollView.bounds.width 145 | 146 | let index:Int = lroundf(Float(page)) 147 | 148 | currentIndex = index 149 | 150 | pageControl?.currentPage = index%pageCount 151 | 152 | refreshData() 153 | 154 | } 155 | 156 | 157 | 158 | //开始拖拽 159 | public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { 160 | bgScrollView?.isPagingEnabled = true 161 | timerEnd() 162 | } 163 | 164 | 165 | public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { 166 | timerStart() 167 | } 168 | 169 | 170 | //定时开始 171 | func timerStart() { 172 | //低于1张不轮播 173 | if pageCount <= 1 { 174 | return 175 | } 176 | 177 | 178 | if timer == nil { 179 | timer = Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(JWAutoScrollView.handler), userInfo:nil, repeats: true) 180 | } 181 | } 182 | 183 | //处理 184 | internal func handler(){ 185 | bgScrollView?.isPagingEnabled = false 186 | currentIndex = currentIndex+1 187 | 188 | //判断越界 189 | if currentIndex >= expand * pageCount { 190 | 191 | currentIndex = expand*pageCount/2-1 192 | preIndex = currentIndex 193 | 194 | var index = 0 195 | for view:JWAutoScrollViewButton in btnArray{ 196 | let x:CGFloat = CGFloat(index+currentIndex-1)*(bgScrollView?.bounds.width)! 197 | let w:CGFloat = (bgScrollView?.bounds.width)! 198 | let h:CGFloat = (bgScrollView?.bounds.height)! 199 | view.frame = CGRect(x: x, y: 0, width: w, height: h) 200 | index += 1 201 | } 202 | //归位 203 | bgScrollView?.setContentOffset(CGPoint(x:CGFloat(currentIndex)*(bgScrollView?.bounds.width)!,y:0), animated: false) 204 | currentIndex = currentIndex + 1; 205 | } 206 | 207 | isScrolling = true 208 | bgScrollView?.setContentOffset(CGPoint(x:CGFloat(currentIndex)*(bgScrollView?.bounds.width)!,y:0), animated: true) 209 | 210 | } 211 | 212 | //定时结束 213 | func timerEnd() { 214 | //低于1张不轮播 215 | if pageCount <= 1 { 216 | return 217 | } 218 | 219 | if self.timer != nil{ 220 | self.timer?.invalidate() 221 | self.timer = nil 222 | } 223 | } 224 | 225 | 226 | //更细UI和数据 227 | internal func refreshData(){ 228 | if currentIndex > preIndex {//往后滑动 229 | if btnArray.count > 0 { 230 | let view:JWAutoScrollViewButton = btnArray.first! 231 | let x:CGFloat = CGFloat(currentIndex+1)*bgScrollView!.bounds.width 232 | 233 | 234 | btnArray.removeFirst() 235 | btnArray.append(view) 236 | 237 | 238 | 239 | if currentIndex <= expand * pageCount - 1 { 240 | if let d = delegate { 241 | d.autoScrollView(autoScrollView: self, index: (currentIndex+1)%pageCount, imageButton: view) 242 | } 243 | } 244 | 245 | //设置当前索引 246 | view.tag = (currentIndex+1)%pageCount 247 | view.frame = CGRect(x: x, y: 0, width: (bgScrollView!.bounds.width), height: (bgScrollView?.bounds.height)!) 248 | 249 | 250 | } 251 | } 252 | else if currentIndex < preIndex//往前滑动 253 | { 254 | 255 | if btnArray.count > 0 { 256 | let view:JWAutoScrollViewButton = btnArray.last! 257 | let x:CGFloat = CGFloat(currentIndex-1)*bgScrollView!.bounds.width 258 | 259 | btnArray.removeLast() 260 | btnArray.insert(view, at: 0) 261 | 262 | if currentIndex-1 >= 0 { 263 | if let d = delegate { 264 | d.autoScrollView(autoScrollView: self, index: (currentIndex-1)%pageCount, imageButton: view) 265 | } 266 | } 267 | 268 | //设置当前索引 269 | view.tag = (currentIndex-1)%pageCount 270 | view.frame = CGRect(x: x, y: 0, width: (bgScrollView!.bounds.width), height: (bgScrollView?.bounds.height)!) 271 | 272 | 273 | } 274 | } 275 | preIndex = currentIndex 276 | } 277 | 278 | //刷新 279 | func reloadData() { 280 | if let d = delegate{ 281 | self.pageControl?.isHidden = false 282 | let count:Int = d.autoScrollViewNumberOfPage() 283 | //0 就处理掉 284 | if count <= 0 { 285 | if self.bottomPrompt != nil && self.bottomPrompt?.compare("") != ComparisonResult.orderedSame{ 286 | pageControl?.isHidden = true 287 | }else{ 288 | pageControl?.isHidden = false 289 | } 290 | return 291 | } 292 | 293 | //低于1张不轮播 294 | if count > 1 { 295 | if self.bottomPrompt != nil && self.bottomPrompt?.compare("") != ComparisonResult.orderedSame{ 296 | pageControl?.isHidden = true 297 | }else{ 298 | pageControl?.isHidden = false 299 | } 300 | }else 301 | { 302 | pageControl?.isHidden = true 303 | } 304 | 305 | pageCount = count 306 | 307 | //设置页数 308 | pageControl?.numberOfPages = count 309 | 310 | if count <= 1{ 311 | self.bgScrollView?.isScrollEnabled = false 312 | }else{ 313 | self.bgScrollView?.isScrollEnabled = true 314 | } 315 | 316 | //初始化位置 317 | var index:Int = 0 318 | for view:JWAutoScrollViewButton in btnArray 319 | { 320 | let tempIndex = index+expand*pageCount/2-1 321 | let x:CGFloat = (bgScrollView?.bounds.width)! * CGFloat(tempIndex) 322 | let w:CGFloat = (bgScrollView?.bounds.width)! 323 | let h:CGFloat = (bgScrollView?.bounds.height)! 324 | 325 | if (pageCount == 1&&index != 1) { 326 | view.frame = CGRect(x: x, y: 0, width: w, height: h) 327 | 328 | index += 1 329 | continue 330 | } 331 | 332 | 333 | view.frame = CGRect(x: x, y: 0, width: w, height: h) 334 | 335 | 336 | if let d = delegate { 337 | if (tempIndex % pageCount) < pageCount { 338 | d.autoScrollView(autoScrollView: self, index: tempIndex%pageCount, imageButton: view) 339 | //设置当前索引 340 | view.tag = tempIndex%pageCount 341 | } 342 | } 343 | 344 | 345 | index += 1 346 | } 347 | 348 | preIndex = expand * pageCount/2 349 | bgScrollView?.contentOffset = CGPoint(x: CGFloat(expand * pageCount/2) * (bgScrollView?.bounds.width)!, y: 0) 350 | 351 | bgScrollView?.contentSize = CGSize(width:CGFloat(expand * pageCount)*(bgScrollView?.bounds.width)!, height: (bgScrollView?.bounds.height)!) 352 | 353 | 354 | //开启定时 355 | timerStart() 356 | } 357 | } 358 | 359 | //停止滚动 360 | public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { 361 | 362 | isScrolling = false 363 | 364 | 365 | if pageCount<=1 { 366 | return 367 | } 368 | 369 | let page = scrollView.contentOffset.x / scrollView.bounds.width 370 | 371 | let index:Int = lroundf(Float(page)) 372 | 373 | if let d = self.delegate{ 374 | d.autoScrollView(autoScrollView: self, didEndAnimationAt: index%pageCount) 375 | } 376 | } 377 | 378 | 379 | 380 | required public init?(coder aDecoder: NSCoder) { 381 | fatalError("init(coder:) has not been implemented") 382 | } 383 | 384 | 385 | override public func layoutSubviews() { 386 | super.layoutSubviews() 387 | 388 | let bgScX:CGFloat = 0 389 | let bgScY:CGFloat = 0 390 | let bgScW:CGFloat = bounds.width 391 | let bgScH:CGFloat = bounds.height 392 | 393 | let flag:Bool = (self.bgScrollView?.frame.equalTo(CGRect()))! 394 | 395 | self.bgScrollView?.frame = CGRect(x: bgScX, y: bgScY, width: bgScW, height: bgScH) 396 | 397 | 398 | let pageControlW:CGFloat = bounds.width 399 | let pageControlH:CGFloat = 30 400 | let pageControlX:CGFloat = (bounds.width - pageControlW)/2 401 | let pageControlY:CGFloat = (bounds.height - pageControlH) 402 | self.pageControl?.frame = CGRect(x: pageControlX, y: pageControlY, width: pageControlW, height: pageControlH) 403 | 404 | 405 | let bottomW:CGFloat = bounds.width 406 | let bottomH:CGFloat = 25 407 | let bottomX:CGFloat = (bounds.width - bottomW)/2 408 | let bottomY:CGFloat = (bounds.height - bottomH) 409 | self.bottomLabel?.frame = CGRect(x: bottomX, y: bottomY, width: bottomW, height: bottomH) 410 | 411 | 412 | if flag { 413 | reloadData() 414 | } 415 | } 416 | 417 | 418 | override public func removeFromSuperview() { 419 | super.removeFromSuperview() 420 | timerEnd() 421 | } 422 | 423 | 424 | } 425 | 426 | public class JWAutoScrollViewButton: UIButton { 427 | override public func titleRect(forContentRect contentRect: CGRect) -> CGRect { 428 | return contentRect 429 | } 430 | 431 | override public func imageRect(forContentRect contentRect: CGRect) -> CGRect { 432 | return contentRect 433 | } 434 | } 435 | 436 | -------------------------------------------------------------------------------- /JavenKit/JWCalendarViewController/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JWCalendarViewController/.DS_Store -------------------------------------------------------------------------------- /JavenKit/JWCalendarViewController/View/JWCalendarHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWCalendarHeaderView.swift 3 | // KitDemo 4 | // 5 | // Created by 朱建伟 on 2017/1/5. 6 | // Copyright © 2017年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class JWCalendarHeaderView: UIView { 12 | 13 | //容器 14 | let containView:UIView = UIView() 15 | 16 | //头部提示部分 17 | let topPromptView:UIView = UIView() 18 | 19 | private let lineView:UIView = UIView() 20 | 21 | 22 | 23 | //背景 24 | private let normalLayer:CAShapeLayer = CAShapeLayer() 25 | private let selectedLayer:CAShapeLayer = CAShapeLayer() 26 | private let disabledLayer:CAShapeLayer = CAShapeLayer() 27 | 28 | 29 | private let normalLabel:UILabel = UILabel() 30 | private let selectedLabel:UILabel = UILabel() 31 | private let disabledLabel:UILabel = UILabel() 32 | 33 | 34 | //边距 35 | var sectionInset:UIEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10){ 36 | didSet{ 37 | self.setNeedsLayout() 38 | } 39 | } 40 | 41 | override init(frame: CGRect) { 42 | super.init(frame: frame) 43 | 44 | self.backgroundColor = UIColor(white: 0.94, alpha: 1) 45 | 46 | 47 | //layer 48 | normalLayer.fillColor = dayNormalColor.cgColor 49 | topPromptView.layer.addSublayer(normalLayer) 50 | 51 | selectedLayer.fillColor = daySelectedColor.cgColor 52 | topPromptView.layer.addSublayer(selectedLayer) 53 | 54 | disabledLayer.fillColor = dayDisabledColor.cgColor 55 | topPromptView.layer.addSublayer(disabledLayer) 56 | 57 | normalLabel.font = UIFont.systemFont(ofSize: 14) 58 | normalLabel.text = "普通" 59 | normalLabel.textColor = UIColor(white: 0.3, alpha: 1) 60 | topPromptView.addSubview(normalLabel) 61 | 62 | selectedLabel.font = UIFont.systemFont(ofSize: 14) 63 | selectedLabel.text = "选中" 64 | selectedLabel.textColor = UIColor(white: 0.3, alpha: 1) 65 | topPromptView.addSubview(selectedLabel) 66 | 67 | disabledLabel.font = UIFont.systemFont(ofSize: 14) 68 | disabledLabel.text = "禁用" 69 | disabledLabel.textColor = UIColor(white: 0.3, alpha: 1) 70 | topPromptView.addSubview(disabledLabel) 71 | 72 | 73 | 74 | addSubview(topPromptView) 75 | addSubview(containView) 76 | 77 | let weekTitleArray:[String] = ["日","一","二","三","四","五","六"] 78 | 79 | for index in 0..<7{ 80 | let weekLabel:UILabel = UILabel() 81 | weekLabel.font = UIFont.systemFont(ofSize: 17) 82 | weekLabel.textColor = dayNormalTextColor 83 | weekLabel.textAlignment = .center 84 | weekLabel.text = weekTitleArray[index] 85 | containView.addSubview(weekLabel) 86 | } 87 | 88 | lineView.backgroundColor = UIColor(white: 0.86, alpha: 1) 89 | // lineView.isHidden = true 90 | self.addSubview(lineView) 91 | } 92 | 93 | 94 | override public func layoutSubviews() { 95 | super.layoutSubviews() 96 | 97 | let topPromptH:CGFloat = (self.bounds.height - self.sectionInset.top - self.sectionInset.bottom)*0.45 98 | 99 | let layerWH:CGFloat = topPromptH * 0.5 100 | 101 | let layerX:CGFloat = 0 102 | let layerY:CGFloat = (topPromptH - layerWH) / 2 103 | 104 | let textW:CGFloat = 60 105 | 106 | let normalLayerFrame = CGRect(x: layerX, y: layerY, width: layerWH, height: layerWH) 107 | normalLayer.path = UIBezierPath(rect:normalLayerFrame).cgPath 108 | normalLabel.frame = CGRect(x: normalLayerFrame.maxX+5, y: 0, width: textW, height: topPromptH) 109 | 110 | 111 | 112 | let selectedLayerFrame = CGRect(x: normalLabel.frame.maxX+5, y: layerY, width: layerWH, height: layerWH) 113 | selectedLayer.path = UIBezierPath(rect:selectedLayerFrame).cgPath 114 | 115 | selectedLabel.frame = CGRect(x: selectedLayerFrame.maxX+5, y: 0, width: textW, height: topPromptH) 116 | 117 | 118 | 119 | let disabledLayerFrame = CGRect(x: selectedLabel.frame.maxX+5, y: layerY, width: layerWH, height: layerWH) 120 | disabledLayer.path = UIBezierPath(rect:disabledLayerFrame).cgPath 121 | disabledLabel.frame = CGRect(x: disabledLayerFrame.maxX+5, y: 0, width: textW, height: topPromptH) 122 | 123 | 124 | let topPromptY:CGFloat = self.sectionInset.top 125 | let topPromptW:CGFloat = disabledLabel.frame.maxX 126 | let topPromptX:CGFloat = (self.bounds.width - topPromptW) / 2 127 | self.topPromptView.frame = CGRect(x: topPromptX, y: topPromptY, width: topPromptW, height: topPromptH) 128 | 129 | 130 | let containerX:CGFloat = self.sectionInset.left 131 | let containerY:CGFloat = self.topPromptView.frame.maxY 132 | let containerW:CGFloat = self.bounds.width - self.sectionInset.left - self.sectionInset.right 133 | let containerH:CGFloat = (self.bounds.height - self.sectionInset.top - self.sectionInset.bottom)*0.55 134 | 135 | containView.frame = CGRect(x: containerX, y: containerY, width: containerW, height: containerH) 136 | 137 | var index:Int = 0 138 | 139 | let weekWidth:CGFloat = containerW / 7 140 | for weekLabel in containView.subviews{ 141 | 142 | let x:CGFloat = weekWidth * CGFloat(index) 143 | let y:CGFloat = 0 144 | let w:CGFloat = weekWidth 145 | let h:CGFloat = containerH 146 | 147 | weekLabel.frame = CGRect(x: x, y: y, width: w, height: h) 148 | 149 | index = index + 1 150 | } 151 | 152 | lineView.frame = CGRect(x: 0, y: self.bounds.height - 0.5, width: self.bounds.width, height: 0.5) 153 | } 154 | 155 | required public init?(coder aDecoder: NSCoder) { 156 | fatalError("init(coder:) has not been implemented") 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /JavenKit/JWCalendarViewController/View/JWCalendarSectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWCalendarSectionView.swift 3 | // KitDemo 4 | // 5 | // Created by 朱建伟 on 2017/1/4. 6 | // Copyright © 2017年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class JWCalendarSectionView: UICollectionReusableView { 12 | 13 | //边距 14 | var sectionInset:UIEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10){ 15 | didSet{ 16 | self.setNeedsLayout() 17 | } 18 | } 19 | 20 | let dateLabel:UILabel = UILabel() 21 | 22 | let lineView:UIView = UIView() 23 | 24 | private let bgView = UIView() 25 | 26 | //初始化 27 | override init(frame: CGRect) { 28 | super.init(frame: frame) 29 | 30 | self.backgroundColor = UIColor.clear 31 | 32 | self.bgView.backgroundColor = UIColor.white 33 | 34 | dateLabel.font = UIFont.systemFont(ofSize: 17) 35 | dateLabel.textAlignment = .center 36 | dateLabel.textColor = dayNormalTextColor 37 | 38 | self.addSubview(bgView) 39 | bgView.addSubview(dateLabel) 40 | 41 | 42 | lineView.backgroundColor = UIColor(white: 0.86, alpha: 1) 43 | lineView.isHidden = true 44 | self.addSubview(lineView) 45 | } 46 | 47 | 48 | 49 | //布局 50 | override public func layoutSubviews() { 51 | super.layoutSubviews() 52 | 53 | bgView.frame = CGRect(x: self.sectionInset.left, y: self.sectionInset.top, width: self.bounds.width - self.sectionInset.left - self.sectionInset.right, height: self.bounds.height - self.sectionInset.top - self.sectionInset.bottom) 54 | 55 | lineView.frame = CGRect(x: 0, y: self.bounds.height - 1, width: self.bounds.width, height: 0.5) 56 | 57 | } 58 | 59 | 60 | required public init?(coder aDecoder: NSCoder) { 61 | fatalError("init(coder:) has not been implemented") 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /JavenKit/JWCalendarViewController/View/JWCalendarView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWCalendarView.swift 3 | // KitDemo 4 | // 5 | // Created by 朱建伟 on 2017/1/3. 6 | // Copyright © 2017年 zhujianwei. All rights reserved. 7 | // 8 | 9 | 10 | import UIKit 11 | 12 | 13 | //协议 14 | public protocol JWCalendarViewDelegate: NSObjectProtocol { 15 | 16 | //天数 17 | func numberOfDay(in calendarView:JWCalendarView) -> Int 18 | 19 | //缩进 20 | func placeholders(in calendarView:JWCalendarView) -> Int; 21 | 22 | //单天的状态 23 | func dayState(in calendarView:JWCalendarView,dayIndex:Int) -> JWCalendarView.DayItemType; 24 | 25 | //间隙 26 | func rowPadding(in calendarView:JWCalendarView)-> CGFloat; 27 | 28 | //间隙 29 | func columnPadding(in calendarView:JWCalendarView)-> CGFloat; 30 | 31 | } 32 | 33 | 34 | //展示日期的View 35 | public class JWCalendarView: UIView { 36 | 37 | //类型 38 | public enum DayItemType:Int { 39 | case normal//普通 40 | case selected//选中 41 | case disabled//禁用 42 | } 43 | 44 | 45 | 46 | 47 | weak var delegate:JWCalendarViewDelegate? 48 | 49 | 50 | 51 | //刷新 52 | func reloadData() { 53 | self.setNeedsDisplay() 54 | } 55 | 56 | 57 | override init(frame: CGRect) { 58 | super.init(frame: frame) 59 | 60 | self.backgroundColor = UIColor.white 61 | } 62 | 63 | required public init?(coder aDecoder: NSCoder) { 64 | fatalError("init(coder:) has not been implemented") 65 | } 66 | 67 | 68 | //绘制 69 | override public func draw(_ rect: CGRect) { 70 | super.draw(rect) 71 | 72 | if let delegate = self.delegate{ 73 | //天数 74 | let numberOfDays:Int = delegate.numberOfDay(in: self) 75 | 76 | if numberOfDays <= 0{ 77 | return 78 | } 79 | 80 | //缩进 81 | let placeholders:Int = delegate.placeholders(in: self) 82 | 83 | //总行数 84 | let totlaRows:Int = (numberOfDays+placeholders + 7 - 1)/7 85 | 86 | //行间距 87 | let rowPadding:CGFloat = delegate.rowPadding(in: self) 88 | 89 | //列间距 90 | let columnPadding:CGFloat = delegate.columnPadding(in: self) 91 | 92 | //按钮宽度 93 | let itemW:CGFloat = (self.bounds.width-(8*columnPadding))/7 94 | 95 | //高度 96 | let itemH:CGFloat = (self.bounds.height-(CGFloat(totlaRows+1)*rowPadding))/CGFloat(totlaRows) 97 | 98 | //遍历刷新 99 | for index in 0...numberOfDays{ 100 | 101 | //计算位置 102 | let x:CGFloat = CGFloat((index + placeholders) % 7)*(itemW+columnPadding)+columnPadding 103 | let y:CGFloat = CGFloat((index + placeholders) / 7)*(itemH+rowPadding)+rowPadding 104 | 105 | let rect:CGRect = CGRect(x: x, y: y, width: itemW, height: itemH) 106 | 107 | let state:DayItemType = delegate.dayState(in: self, dayIndex: index) 108 | 109 | 110 | //上下文 111 | let context:CGContext = UIGraphicsGetCurrentContext()! 112 | 113 | let fontSize:CGFloat = itemH * 0.5 114 | 115 | 116 | 117 | let titleStr:NSString = NSString(string:String(format:"%zd",index+1)) 118 | 119 | let titleSize:CGSize = titleStr.boundingRect(with: CGSize(width:itemW,height:itemH), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName:UIFont.systemFont(ofSize: fontSize)], context: nil).size 120 | 121 | let offSetX:CGFloat = (itemW - titleSize.width)/2 122 | let offSetY:CGFloat = (itemH - titleSize.height)/2 123 | 124 | 125 | //绘制背景 126 | switch state { 127 | case .normal: 128 | //普通 129 | context.setFillColor(dayNormalColor.cgColor) 130 | context.addEllipse(in: rect) 131 | context.fillPath() 132 | 133 | //文字 134 | titleStr.draw(at: CGPoint(x:rect.minX+offSetX,y: rect.minY + offSetY) , withAttributes: [NSFontAttributeName:UIFont.systemFont(ofSize: fontSize),NSForegroundColorAttributeName:dayNormalTextColor]) 135 | 136 | break 137 | case .selected: 138 | //选中 139 | context.setFillColor(daySelectedColor.cgColor) 140 | context.addEllipse(in: rect) 141 | context.fillPath() 142 | 143 | //文字 144 | titleStr.draw(at: CGPoint(x:rect.minX+offSetX,y: rect.minY + offSetY) , withAttributes: [NSFontAttributeName:UIFont.systemFont(ofSize: fontSize),NSForegroundColorAttributeName:daySelectedTextColor]) 145 | break 146 | case .disabled: 147 | //禁用 148 | context.setFillColor(dayDisabledColor.cgColor) 149 | context.addEllipse(in: rect) 150 | context.fillPath() 151 | 152 | //文字 153 | titleStr.draw(at: CGPoint(x:rect.minX+offSetX,y: rect.minY + offSetY) , withAttributes: [NSFontAttributeName:UIFont.systemFont(ofSize: fontSize),NSForegroundColorAttributeName:dayDisabledTextColor]) 154 | break 155 | } 156 | 157 | } 158 | } 159 | 160 | } 161 | 162 | 163 | 164 | } 165 | -------------------------------------------------------------------------------- /JavenKit/JWCalendarViewController/View/JWCalendarViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWCalendarViewCell.swift 3 | // KitDemo 4 | // 5 | // Created by 朱建伟 on 2017/1/4. 6 | // Copyright © 2017年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | //类型 12 | public enum DayItemState:Int { 13 | case none//不在时间范围内,置灰色 14 | case placeholder//占位 15 | case normal//普通 16 | case selected//选中 17 | case disabled//禁用 18 | } 19 | 20 | 21 | public class JWCalendarViewCell: UICollectionViewCell { 22 | 23 | public var dayItemState:DayItemState = .normal{ 24 | willSet{ 25 | 26 | self.drawView.dayItemState = newValue 27 | 28 | switch newValue { 29 | case .placeholder: 30 | 31 | break 32 | case .normal: 33 | self.titleLabel.textColor = dayNormalTextColor 34 | break 35 | case .selected: 36 | self.titleLabel.textColor = daySelectedTextColor 37 | break 38 | case .disabled: 39 | self.titleLabel.textColor = dayDisabledTextColor 40 | break 41 | case .none: 42 | self.titleLabel.textColor = dayNoneTextColor 43 | break 44 | } 45 | 46 | 47 | } 48 | } 49 | 50 | //标签 51 | let titleLabel:UILabel = UILabel() 52 | 53 | 54 | let bottomLineView:UIView = UIView() 55 | let leftLineView:UIView = UIView() 56 | 57 | let topLineView:UIView = UIView() 58 | let rightLineView:UIView = UIView() 59 | 60 | 61 | private let bgView:UIView = UIView() 62 | 63 | private let drawView:JWCalendarDrawView = JWCalendarDrawView() 64 | 65 | 66 | //初始化 67 | override init(frame: CGRect) { 68 | super.init(frame: frame) 69 | 70 | self.contentView.backgroundColor = UIColor.white 71 | 72 | bgView.backgroundColor = UIColor.white 73 | self.contentView.addSubview(bgView) 74 | 75 | self.drawView.backgroundColor = UIColor.white 76 | self.bgView.addSubview(drawView) 77 | 78 | 79 | 80 | //控件 81 | titleLabel.font = UIFont.systemFont(ofSize: 17) 82 | titleLabel.textColor = dayNormalTextColor 83 | titleLabel.textAlignment = .center 84 | self.bgView.addSubview(titleLabel) 85 | 86 | 87 | 88 | bottomLineView.backgroundColor = UIColor(white: 0.86, alpha: 1) 89 | bottomLineView.isHidden = true 90 | self.contentView.addSubview(bottomLineView) 91 | 92 | leftLineView.backgroundColor = UIColor(white: 0.86, alpha: 1) 93 | leftLineView.isHidden = true 94 | self.contentView.addSubview(leftLineView) 95 | 96 | 97 | topLineView.backgroundColor = UIColor(white: 0.86, alpha: 1) 98 | topLineView.isHidden = true 99 | self.contentView.addSubview(topLineView) 100 | 101 | rightLineView.backgroundColor = UIColor(white: 0.86, alpha: 1) 102 | rightLineView.isHidden = true 103 | self.contentView.addSubview(rightLineView) 104 | 105 | } 106 | 107 | 108 | 109 | //布局 110 | override public func layoutSubviews() { 111 | super.layoutSubviews() 112 | 113 | bgView.frame = CGRect(x: 0, y: 0, width: self.contentView.bounds.width, height: self.contentView.bounds.height) 114 | 115 | let x:CGFloat = 5 116 | let w:CGFloat = (self.bgView.bounds.width - x*2) 117 | let h:CGFloat = w 118 | let y:CGFloat = ( self.bgView.bounds.height - h )/2 119 | titleLabel.frame = CGRect(x: x, y: y, width: w, height: h) 120 | 121 | self.drawView.frame = CGRect(x: x, y: y, width: w, height: h) 122 | 123 | 124 | bottomLineView.frame = CGRect(x: 0, y: self.contentView.bounds.height - 0.5, width: self.bgView.bounds.width, height: 0.5) 125 | 126 | 127 | leftLineView.frame = CGRect(x: 0, y: 0, width: 0.5, height: self.contentView.bounds.height) 128 | 129 | 130 | topLineView.frame = CGRect(x: 0, y: 0, width: self.contentView.bounds.width, height: 0.5) 131 | 132 | 133 | rightLineView.frame = CGRect(x: self.contentView.bounds.width - 0.5, y: 0, width: 0.5, height: self.contentView.bounds.height) 134 | 135 | } 136 | 137 | override public func point(inside point: CGPoint, with event: UIEvent?) -> Bool { 138 | if self.dayItemState == .selected || self.dayItemState == .normal{ 139 | return super.point(inside: point, with: event) 140 | }else{ 141 | return false 142 | } 143 | } 144 | 145 | 146 | required public init?(coder aDecoder: NSCoder) { 147 | fatalError("init(coder:) has not been implemented") 148 | } 149 | } 150 | 151 | 152 | //绘制背景 153 | 154 | class JWCalendarDrawView: UIView { 155 | var dayItemState:DayItemState = DayItemState.normal{ 156 | didSet{ 157 | self.setNeedsDisplay() 158 | } 159 | } 160 | 161 | override init(frame: CGRect) { 162 | super.init(frame: frame) 163 | 164 | self.backgroundColor = UIColor.white 165 | } 166 | 167 | required public init?(coder aDecoder: NSCoder) { 168 | fatalError("init(coder:) has not been implemented") 169 | } 170 | 171 | override public func draw(_ rect: CGRect) { 172 | super.draw(rect) 173 | 174 | self.backgroundColor?.setFill() 175 | 176 | let bgPath:UIBezierPath = UIBezierPath(rect: rect) 177 | bgPath.fill() 178 | 179 | 180 | let path:UIBezierPath = UIBezierPath(ovalIn: rect) 181 | 182 | switch self.dayItemState { 183 | case .placeholder: 184 | UIColor.clear.setFill() 185 | break 186 | case .normal: 187 | dayNormalColor.setFill() 188 | break 189 | case .selected: 190 | daySelectedColor.setFill() 191 | break 192 | case .disabled: 193 | dayDisabledColor.setFill() 194 | break 195 | case .none: 196 | dayNoneColor.setFill() 197 | break 198 | } 199 | path.fill() 200 | 201 | 202 | } 203 | 204 | 205 | } 206 | -------------------------------------------------------------------------------- /JavenKit/JWCalendarViewController/ViewController/JWCalendarViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWCalendarViewController.swift 3 | // KitDemo 4 | // 5 | // Created by 朱建伟 on 2017/1/4. 6 | // Copyright © 2017年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | //如果要修改不同状态按钮的颜色,修改它 13 | 14 | //不在范围内 15 | let dayNoneColor:UIColor = UIColor.white 16 | let dayNoneTextColor:UIColor = UIColor(white: 0.7, alpha: 1) 17 | 18 | //普通 19 | let dayNormalColor:UIColor = UIColor.white 20 | let dayNormalTextColor:UIColor = UIColor(white: 0.1, alpha: 1) 21 | 22 | //选中 23 | let daySelectedColor:UIColor = UIColor.red 24 | let daySelectedTextColor:UIColor = UIColor.white 25 | 26 | //禁用 27 | let dayDisabledColor:UIColor = UIColor.green 28 | let dayDisabledTextColor:UIColor = UIColor.white 29 | 30 | 31 | //协议 32 | public protocol JWCalendarViewControllerDelegate:NSObjectProtocol { 33 | 34 | //点击了确定 35 | func calendarViewController(calendarViewController:JWCalendarViewController,clickConfirm daySelectdItems:[(Date,DateComponents,DayItemState)],and dayDisabledItems:[(Date,DateComponents,DayItemState)]); 36 | 37 | //点击了单天 38 | func calendarViewController(calendarViewController:JWCalendarViewController,didSelectedDate dayItems:(Date,DateComponents,DayItemState)) 39 | } 40 | 41 | 42 | public class JWCalendarViewController: UIViewController ,UICollectionViewDelegate,UICollectionViewDataSource{ 43 | 44 | //是否开启网格模式 45 | public var enableGrid:Bool = true{ 46 | didSet{ 47 | self.collectionView?.reloadData() 48 | } 49 | } 50 | 51 | public weak var delegate:JWCalendarViewControllerDelegate? 52 | 53 | //单选 54 | public var singleSelected:Bool = false{ 55 | willSet{ 56 | //单选 57 | if !singleSelected{ 58 | if let item = self.navigationItem.rightBarButtonItem{ 59 | item.title = "确定" 60 | 61 | item.isEnabled = true 62 | } 63 | }else{ 64 | if let item = self.navigationItem.rightBarButtonItem{ 65 | item.title = "" 66 | item.isEnabled = false 67 | } 68 | } 69 | } 70 | } 71 | 72 | 73 | public var minDate:Date = Date(){ 74 | willSet{ 75 | 76 | if self.maxDate == nil{ 77 | self.maxDate = calendar.date(byAdding: .month, value: maxRange, to:newValue) 78 | }else{ 79 | 80 | let dayOffset:Int = calendar.dateComponents([.day], from:self.getDateRemoveStopLastUnit(date:newValue, lastUnit: .month, isFromDate: true), to:self.getDateRemoveStopLastUnit(date:self.maxDate!, lastUnit: .month, isFromDate: false)).day!+1 81 | 82 | if dayOffset < 1{ 83 | self.maxDate = calendar.date(byAdding: .month, value: maxRange, to:newValue) 84 | } 85 | 86 | } 87 | 88 | 89 | self.removeAll(forState: DayItemState.none) 90 | let dateComps:DateComponents = calendar.dateComponents([.year,.month,.day,.hour,.minute,.second], from:newValue) 91 | self.addDateCompsArray(dateCompsCount: dateComps.day!-1, usingClosure: {(index) in 92 | var comps:DateComponents = DateComponents() 93 | comps.year = dateComps.year 94 | comps.month = dateComps.month 95 | comps.day = index + 1 96 | return (comps,DayItemState.none) 97 | }) 98 | 99 | } 100 | } 101 | 102 | public var maxDate:Date?{ 103 | willSet{ 104 | if let maxDate = newValue{ 105 | 106 | let dayOffset:Int = calendar.dateComponents([.day], from:self.getDateRemoveStopLastUnit(date:self.minDate, lastUnit: .month, isFromDate: true), to:self.getDateRemoveStopLastUnit(date:maxDate, lastUnit: .month, isFromDate: false)).day!+1 107 | 108 | if dayOffset < 1{ 109 | self.minDate = calendar.date(byAdding: .month, value: -maxRange, to:maxDate)! 110 | } 111 | 112 | 113 | self.removeAll(forState: DayItemState.none) 114 | let dateComps:DateComponents = calendar.dateComponents([.year,.month,.day,.hour,.minute,.second], from:maxDate) 115 | self.addDateCompsArray(dateCompsCount:numberOfDaysInMonth(year:dateComps.year!, month: dateComps.month!) - dateComps.day!, usingClosure: {(index) in 116 | var comps:DateComponents = DateComponents() 117 | comps.year = dateComps.year 118 | comps.month = dateComps.month 119 | comps.day = dateComps.day! + index + 1 120 | return (comps,DayItemState.none) 121 | }) 122 | 123 | } 124 | } 125 | } 126 | 127 | private var currentDate:Date? 128 | 129 | 130 | //展示某天 131 | public func scrollToCurrent(dateComps:DateComponents,animated:Bool){ 132 | if let date = calendar.date(from: dateComps){ 133 | scrollToCurrent(date: date, animated: animated) 134 | } 135 | } 136 | 137 | 138 | //展示某天 139 | public func scrollToCurrent(date:Date,animated:Bool){ 140 | //比最小时间小 retrun 141 | if date.compare(self.minDate) == .orderedAscending{ 142 | return 143 | } 144 | 145 | //如果有最大值 146 | if let maxDate = self.maxDate{ 147 | //比最大值大 148 | if maxDate.compare(date) == .orderedAscending{ 149 | return 150 | } 151 | }else{ 152 | 153 | let monthOffset:Int = calendar.dateComponents([.month], from:self.getDateRemoveStopLastUnit(date:self.minDate, lastUnit: .month, isFromDate: true), to:self.getDateRemoveStopLastUnit(date:date, lastUnit: .month, isFromDate: false)).month!+1 154 | 155 | 156 | if monthOffset < maxRange{ 157 | 158 | if let colletionView = self.collectionView{ 159 | colletionView.scrollToItem(at: IndexPath(item: 0, section: monthOffset), at: UICollectionViewScrollPosition.centeredVertically, animated: animated) 160 | 161 | self.currentDate = nil 162 | }else{ 163 | 164 | self.currentDate = date 165 | } 166 | } 167 | } 168 | } 169 | 170 | 171 | //添加日期 172 | public func addDate(date:Date,forState state:DayItemState) { 173 | let dateComps = calendar.dateComponents([.year,.month,.day,.weekday], from: date) 174 | let monthCountKey:Int = numberOfDaysInMonth(year: dateComps.year!, month: dateComps.month!) 175 | 176 | let dayKey:String = String(format:"%zd-%zd-%zd",dateComps.year!,dateComps.month!,dateComps.day!) 177 | 178 | if state == .selected{ 179 | self.selectedCount = self.selectedCount + 1 180 | } 181 | 182 | self.daysContainer[monthCountKey]?[dateComps.day! - 1][dayKey] = (date,dateComps,state) 183 | } 184 | 185 | 186 | //添加日期 187 | public func addDate(dateComps:DateComponents,forState state:DayItemState) { 188 | 189 | let tempDate:Date? = calendar.date(from: dateComps) 190 | 191 | let monthCountKey:Int = numberOfDaysInMonth(year: dateComps.year!, month: dateComps.month!) 192 | 193 | 194 | let dayKey:String = String(format:"%zd-%zd-%zd",dateComps.year!,dateComps.month!,dateComps.day!) 195 | 196 | if let date = tempDate{ 197 | self.daysContainer[monthCountKey]?[dateComps.day! - 1][dayKey] = (date,dateComps,state) 198 | 199 | if state == .selected{ 200 | self.selectedCount = self.selectedCount + 1 201 | 202 | } 203 | } 204 | 205 | } 206 | 207 | //添加日期 208 | public func addDates(dateArray:[Date],forState state:DayItemState) { 209 | for date in dateArray{ 210 | addDate(date: date, forState: state) 211 | } 212 | } 213 | 214 | 215 | // closure 参数 Int:index 索引 返回 Date DateItemState 216 | public func addDateArray(dateCount:Int,usingClosure:@escaping (Int)->(Date,DayItemState)) { 217 | for index in 0..(DateComponents,DayItemState)) { 235 | for index in 0..Date) { 315 | for index in 0..DateComponents) { 348 | for index in 0.. 0 369 | 370 | } 371 | 372 | self.minDate = Date() 373 | 374 | self.view.backgroundColor = UIColor.white 375 | 376 | let layout:UICollectionViewFlowLayout = UICollectionViewFlowLayout() 377 | 378 | layout.sectionInset = UIEdgeInsets(top:0, left: 10, bottom: 8, right: 10) 379 | 380 | layout.minimumInteritemSpacing = 0.5 381 | layout.minimumLineSpacing = 0.5 382 | 383 | layout.headerReferenceSize = CGSize(width: view.bounds.width, height: 40) 384 | 385 | 386 | let itemW:CGFloat = (view.bounds.width - layout.sectionInset.left - layout.sectionInset.right - 6 * layout.minimumInteritemSpacing-1)/7 387 | 388 | layout.itemSize = CGSize(width: itemW, height: itemW + 5) 389 | 390 | 391 | self.collectionView = UICollectionView(frame: CGRect(x: 0, y:65 , width: view.bounds.width, height: view.bounds.height-65), collectionViewLayout: layout) 392 | self.collectionView?.backgroundColor = UIColor.white 393 | self.collectionView?.delegate = self 394 | self.collectionView?.dataSource = self 395 | 396 | self.view.addSubview(collectionView!) 397 | 398 | self.view.addSubview(self.headerView) 399 | self.headerView.frame = CGRect(x:0, y: self.navigationController != nil ? 64 : 0, width: self.view.bounds.width, height: 65) 400 | 401 | 402 | //注册 403 | self.collectionView!.register(JWCalendarViewCell.classForCoder(), forCellWithReuseIdentifier: reuseIdentifier) 404 | 405 | self.collectionView?.register(JWCalendarSectionView.classForCoder(), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: reuseHeaderIdentifier) 406 | 407 | //当前日期 408 | if let date = self.currentDate{ 409 | self.scrollToCurrent(date: date, animated: false) 410 | } 411 | } 412 | 413 | 414 | public func numberOfSections(in collectionView: UICollectionView) -> Int { 415 | if let maxDate = self.maxDate{ 416 | let monthOffset:Int = calendar.dateComponents([.month], from:self.getDateRemoveStopLastUnit(date:self.minDate, lastUnit: .month, isFromDate: true), to:self.getDateRemoveStopLastUnit(date:maxDate, lastUnit: .month, isFromDate: false)).month!+1 417 | 418 | return monthOffset > 0 ? monthOffset : 1 419 | }else{ 420 | return maxRange 421 | } 422 | } 423 | 424 | 425 | public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 426 | 427 | let tempDate:Date = calendar.date(byAdding: Calendar.Component.month, value:section-1, to:self.minDate)! 428 | 429 | let date:Date = calendar.date(bySetting: Calendar.Component.day, value: 1, of: tempDate)! 430 | 431 | let dateComps:DateComponents = calendar.dateComponents([.year,.month,.day,.weekday], from: date) 432 | 433 | sectionCompsArray[section] = dateComps 434 | 435 | 436 | return numberOfDaysInMonth(year: dateComps.year!, month: dateComps.month!) + dateComps.weekday!-1 437 | } 438 | 439 | 440 | 441 | 442 | public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 443 | 444 | let cell:JWCalendarViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! JWCalendarViewCell 445 | 446 | 447 | cell.leftLineView.isHidden = true 448 | cell.topLineView.isHidden = true 449 | 450 | 451 | if sectionCompsArray.keys.contains(indexPath.section){ 452 | if let comps = sectionCompsArray[indexPath.section]{ 453 | 454 | let week = comps.weekday! - 1 455 | 456 | if indexPath.item < week { 457 | cell.dayItemState = .placeholder 458 | cell.titleLabel.text = "" 459 | if self.enableGrid{ 460 | cell.bottomLineView.isHidden = false 461 | 462 | if indexPath.item == week-1{ 463 | cell.rightLineView.isHidden = false 464 | }else{ 465 | cell.rightLineView.isHidden = true 466 | } 467 | } 468 | }else 469 | { 470 | cell.titleLabel.text = String(format: "%zd", indexPath.item + 1 - week) 471 | 472 | 473 | if self.enableGrid{ 474 | cell.bottomLineView.isHidden = false 475 | cell.rightLineView.isHidden = false 476 | 477 | if (indexPath.item)%7 == 0 { 478 | cell.leftLineView.isHidden = false 479 | } 480 | 481 | if (indexPath.item) < 7{ 482 | cell.topLineView.isHidden = false 483 | } 484 | }else{ 485 | cell.bottomLineView.isHidden = true 486 | cell.rightLineView.isHidden = true 487 | } 488 | 489 | } 490 | } 491 | }else{ 492 | // 493 | print("过掉了") 494 | } 495 | 496 | return cell 497 | } 498 | 499 | 500 | public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { 501 | 502 | if sectionCompsArray.keys.contains(indexPath.section){ 503 | let calendarCell:JWCalendarViewCell = cell as! JWCalendarViewCell 504 | if let comps = sectionCompsArray[indexPath.section]{ 505 | 506 | let week = comps.weekday! - 1 507 | 508 | if indexPath.item >= week { 509 | let monthDayCount:Int = numberOfDaysInMonth(year: comps.year!, month: comps.month!) 510 | 511 | let dayKey:String = String(format:"%zd-%zd-%zd",comps.year!,comps.month!,indexPath.item + 1 - week) 512 | 513 | 514 | if (self.daysContainer[monthDayCount]?[indexPath.item - week].keys.contains(dayKey))!{ 515 | 516 | let state = (self.daysContainer[monthDayCount]?[indexPath.item - week][dayKey]!.2)! 517 | calendarCell.dayItemState = state 518 | }else{ 519 | calendarCell.dayItemState = .normal 520 | } 521 | 522 | } 523 | } 524 | } 525 | } 526 | 527 | //选中了单天 528 | public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 529 | 530 | if sectionCompsArray.keys.contains(indexPath.section){ 531 | if var comps = sectionCompsArray[indexPath.section]{ 532 | 533 | let week = comps.weekday! - 1 534 | 535 | let day:Int = indexPath.item + 1 - week 536 | 537 | comps.day = day 538 | 539 | let state = changeDayState(dateComps: comps) 540 | 541 | if let delegate = self.delegate{ 542 | delegate.calendarViewController(calendarViewController: self, didSelectedDate: (calendar.date(from: comps)!,comps,state)) 543 | } 544 | 545 | UIView.performWithoutAnimation { 546 | collectionView.reloadItems(at: [indexPath]) 547 | } 548 | 549 | } 550 | 551 | } 552 | 553 | } 554 | 555 | 556 | 557 | public func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { 558 | 559 | let headerView:JWCalendarSectionView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: reuseHeaderIdentifier, for: indexPath) as! JWCalendarSectionView 560 | 561 | if sectionCompsArray.keys.contains(indexPath.section){ 562 | 563 | if let comps = sectionCompsArray[indexPath.section]{ 564 | 565 | let layout:UICollectionViewFlowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout 566 | 567 | headerView.lineView.isHidden = self.enableGrid 568 | if self.enableGrid{ 569 | headerView.dateLabel.text = String(format: "%zd月",(sectionCompsArray[indexPath.section]?.month!)!) 570 | 571 | let week:Int = (comps.weekday)! - 1 572 | 573 | let offsetX:CGFloat = (layout.itemSize.width + layout.minimumInteritemSpacing) * CGFloat(week) 574 | 575 | headerView.dateLabel.frame = CGRect(x: offsetX, y: 0, width: layout.itemSize.width, height: layout.headerReferenceSize.height-headerView.sectionInset.top-headerView.sectionInset.bottom) 576 | 577 | }else{ 578 | headerView.dateLabel.text = String(format: "%zd年%02zd月",comps.year!,comps.month!) 579 | headerView.dateLabel.frame = CGRect(x: 0, y: 0, width: 100, height: layout.headerReferenceSize.height-headerView.sectionInset.top-headerView.sectionInset.bottom) 580 | } 581 | } 582 | } 583 | 584 | return headerView 585 | 586 | } 587 | 588 | /** 589 | * 是否是闰年 590 | */ 591 | public func isLeapYear(year:Int) -> Bool { 592 | if (year%4==0) { 593 | if (year%100==0) { 594 | if (year%400==0) { 595 | return true 596 | } 597 | //能被 4 100 整除 不能被400 整除的 不是闰年 598 | return false 599 | } 600 | //能被4整除 不能被100整除的 是闰年 601 | return true 602 | } 603 | //不能为4整除 不是闰年 604 | return false 605 | 606 | } 607 | 608 | /** 609 | * 根据对应的年 和月 返回当月对应的天数 610 | */ 611 | public func numberOfDaysInMonth(year:Int,month:Int) -> Int { 612 | // 31 28 31 30 31 30 31 31 30 31 30 31 613 | let daysOfMonth:[Int] = [31,28,31,30,31,30,31,31,30,31,30,31] 614 | 615 | let index:Int = (month - 1) 616 | var days:Int = daysOfMonth[index] 617 | 618 | if (days == 28) { 619 | if (isLeapYear(year: year)){ 620 | days = 29 621 | } 622 | } 623 | return days 624 | } 625 | 626 | 627 | public func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool { 628 | return true 629 | } 630 | 631 | public func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { 632 | return true 633 | } 634 | 635 | public func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath) -> Bool { 636 | return false 637 | } 638 | 639 | public func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool { 640 | return false 641 | } 642 | 643 | public func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) { 644 | 645 | } 646 | 647 | private let reuseIdentifier = "Cell" 648 | private let reuseHeaderIdentifier = "header" 649 | 650 | 651 | var sectionCompsArray:[Int:DateComponents] = [Int:DateComponents]() 652 | 653 | private let calendar:Calendar = Calendar(identifier: Calendar.Identifier.gregorian) 654 | 655 | var collectionView:UICollectionView? 656 | 657 | 658 | //选中的数据字段 659 | private var daysContainer:[Int:[[String:(Date,DateComponents,DayItemState)]]] = { 660 | var daysContainer = [Int:[[String:(Date,DateComponents,DayItemState)]]]() 661 | 662 | //31天 663 | daysContainer[31] = [[String:(Date,DateComponents,DayItemState)]]() 664 | for index in 0..<31{ 665 | daysContainer[31]?.append([String:(Date,DateComponents,DayItemState)]()) 666 | } 667 | 668 | //30天 669 | daysContainer[30] = [[String:(Date,DateComponents,DayItemState)]]() 670 | for index in 0..<30{ 671 | daysContainer[30]?.append([String:(Date,DateComponents,DayItemState)]()) 672 | } 673 | 674 | //29天 675 | daysContainer[29] = [[String:(Date,DateComponents,DayItemState)]]() 676 | for index in 0..<29{ 677 | daysContainer[29]?.append([String:(Date,DateComponents,DayItemState)]()) 678 | } 679 | 680 | //28天 681 | daysContainer[28] = [[String:(Date,DateComponents,DayItemState)]]() 682 | for index in 0..<28{ 683 | daysContainer[28]?.append([String:(Date,DateComponents,DayItemState)]()) 684 | } 685 | 686 | return daysContainer 687 | }() 688 | 689 | 690 | //改变状态 主要是 selected 与 normal 之间的切换 691 | private func changeDayState(dateComps:DateComponents) -> DayItemState{ 692 | 693 | 694 | let monthCountKey:Int = numberOfDaysInMonth(year: dateComps.year!, month: dateComps.month!) 695 | 696 | let dayKey:String = String(format:"%zd-%zd-%zd",dateComps.year!,dateComps.month!,dateComps.day!) 697 | 698 | 699 | if (self.daysContainer[monthCountKey]?[dateComps.day! - 1].keys.contains(dayKey))!{ 700 | self.removeDate(dateComps: dateComps) 701 | return .normal 702 | }else{ 703 | self.addDate(dateComps: dateComps, forState: DayItemState.selected) 704 | return .selected 705 | } 706 | } 707 | 708 | 709 | //设置选中的天数的个数 710 | private var selectedCount:Int = 0 { 711 | willSet{ 712 | 713 | if newValue > 0{ 714 | if let item = self.navigationItem.rightBarButtonItem{ 715 | item.isEnabled = true 716 | } 717 | }else{ 718 | if let item = self.navigationItem.rightBarButtonItem{ 719 | item.isEnabled = false 720 | } 721 | } 722 | } 723 | } 724 | 725 | //点击确定 726 | func clickConfirm() { 727 | 728 | var selectedItems:[(Date,DateComponents,DayItemState)] = [(Date,DateComponents,DayItemState)]() 729 | 730 | var disabledItems:[(Date,DateComponents,DayItemState)] = [(Date,DateComponents,DayItemState)]() 731 | 732 | for (_,group) in self.daysContainer{ 733 | for dayDict in group { 734 | for (_,dayItem) in dayDict{ 735 | if dayItem.2 == .selected{ 736 | selectedItems.append(dayItem) 737 | }else if dayItem.2 == .disabled{ 738 | disabledItems.append(dayItem) 739 | } 740 | } 741 | } 742 | } 743 | 744 | if selectedItems.count > 0{ 745 | if let delegate = self.delegate{ 746 | delegate.calendarViewController(calendarViewController: self, clickConfirm: selectedItems, and: disabledItems) 747 | } 748 | } 749 | 750 | 751 | } 752 | 753 | //去掉多余的日期部分,保证计算日期的差的准确度 754 | public func getDateRemoveStopLastUnit(date:Date,lastUnit:Calendar.Component,isFromDate:Bool) -> Date { 755 | //初始化 756 | let unitArray:[Calendar.Component] = [.year,.month,.day,.hour,.minute,.second] 757 | 758 | let dateComps:DateComponents = self.calendar.dateComponents([.year,.month,.day,.hour,.minute,.second], from: date) 759 | 760 | 761 | var flag:Bool = true 762 | var addDateComps:DateComponents = DateComponents() 763 | 764 | //遍历 765 | for unit in unitArray{ 766 | switch unit { 767 | case .year: 768 | if flag { 769 | addDateComps.year = 0 770 | }else{ 771 | addDateComps.year = -(dateComps.year)! 772 | } 773 | //判断 774 | if unit == lastUnit{ 775 | flag = false 776 | } 777 | break 778 | case .month: 779 | if flag { 780 | addDateComps.month = 0 781 | }else{ 782 | if !isFromDate{ 783 | addDateComps.month = -(dateComps.month)!+2 784 | }else{ 785 | addDateComps.month = -(dateComps.month)!+1 786 | } 787 | } 788 | //判断 789 | if unit == lastUnit{ 790 | 791 | flag = false 792 | } 793 | break 794 | case .day: 795 | if flag { 796 | addDateComps.day = 0 797 | }else{ 798 | if !isFromDate{ 799 | addDateComps.day = -(dateComps.day)!+2 800 | }else{ 801 | addDateComps.day = -(dateComps.day)!+1 802 | } 803 | } 804 | //判断 805 | if unit == lastUnit{ 806 | 807 | flag = false 808 | } 809 | break 810 | case .hour: 811 | if flag { 812 | addDateComps.hour = 0 813 | }else{ 814 | if !isFromDate{ 815 | addDateComps.hour = -(dateComps.hour)!+1 816 | }else{ 817 | addDateComps.hour = -(dateComps.hour)! 818 | } 819 | 820 | } 821 | //判断 822 | if unit == lastUnit{ 823 | 824 | flag = false 825 | } 826 | break 827 | case .minute: 828 | if flag { 829 | addDateComps.minute = 0 830 | }else{ 831 | if !isFromDate{ 832 | addDateComps.minute = -(dateComps.minute)!+1 833 | }else{ 834 | addDateComps.minute = -(dateComps.minute)! 835 | } 836 | } 837 | //判断 838 | if unit == lastUnit{ 839 | 840 | flag = false 841 | } 842 | break 843 | case .second: 844 | if flag { 845 | addDateComps.second = 0 846 | }else{ 847 | if !isFromDate{ 848 | addDateComps.second = -(dateComps.second)!+1 849 | }else{ 850 | addDateComps.second = -(dateComps.second)! 851 | } 852 | } 853 | //判断 854 | if unit == lastUnit{ 855 | flag = false 856 | } 857 | break 858 | default: 859 | break 860 | } 861 | } 862 | 863 | 864 | 865 | let lastDate:Date = self.calendar.date(byAdding: addDateComps, to: date)! 866 | 867 | return lastDate 868 | } 869 | } 870 | -------------------------------------------------------------------------------- /JavenKit/JWDatePickerView/JWDatePickerKeyBoardView.swift: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // JWDatePickerKeyBoardView.swift 4 | // CarServer 5 | // 6 | // Created by 朱建伟 on 2016/10/17. 7 | // Copyright © 2016年 zhujianwei. All rights reserved. 8 | // 9 | 10 | import UIKit 11 | 12 | public class JWDatePickerKeyBoardView: UIView { 13 | 14 | public typealias ConfirmDateClosure = (Date) -> () 15 | 16 | public var didConfirmDateClosure:ConfirmDateClosure? 17 | 18 | 19 | private var isRemove:Bool = false 20 | 21 | public var font:UIFont?{ 22 | willSet{ 23 | if let f = font { 24 | titleLabel?.font = f 25 | titleLabel?.text = "日期选择" 26 | cancelBtn?.titleLabel?.font = f 27 | confirmBtn?.titleLabel?.font = f 28 | } 29 | } 30 | } 31 | 32 | 33 | public var title:String?{ 34 | willSet{ 35 | if let t = newValue{ 36 | titleLabel?.text = t 37 | } 38 | } 39 | } 40 | 41 | //蒙版 42 | private var cover:UIControl = { 43 | let view:UIControl = UIControl(frame: UIScreen.main.bounds) 44 | view.backgroundColor = UIColor.black 45 | view.alpha = 0.4 46 | 47 | return view 48 | }() 49 | 50 | //取消按钮 51 | private var cancelBtn:UIButton? 52 | 53 | //确定按钮 54 | private var confirmBtn:UIButton? 55 | 56 | //头部标题 57 | private var titleLabel:UILabel? 58 | 59 | //头部View 60 | private var titleView:UIView? 61 | 62 | //pickerView 63 | public let pickerView:JWDatePickerView = JWDatePickerView() 64 | 65 | override public init(frame: CGRect) { 66 | super.init(frame: frame) 67 | 68 | backgroundColor = UIColor.white 69 | 70 | cover.addTarget(self, action: #selector(JWDatePickerKeyBoardView.exitKeyBoard), for: UIControlEvents.touchUpInside) 71 | 72 | // FontPrompt_120_218_317_416_515_614_712_810_908 73 | font = UIFont.systemFont(ofSize: 18) 74 | 75 | //标题View 76 | titleView = UIView() 77 | titleView?.backgroundColor = UIColor.orange 78 | addSubview(titleView!) 79 | 80 | //取消按钮 81 | cancelBtn = UIButton() 82 | cancelBtn?.addTarget(self, action:#selector(JWDatePickerKeyBoardView.btnClick(btn:)), for: UIControlEvents.touchUpInside) 83 | cancelBtn?.titleLabel?.font = font 84 | cancelBtn?.setTitle("取消", for: UIControlState.normal) 85 | cancelBtn?.setTitle("取消", for: UIControlState.selected) 86 | cancelBtn?.setTitleColor(UIColor.white, for: UIControlState.normal) 87 | cancelBtn?.tag = 0 88 | titleView?.addSubview(cancelBtn!) 89 | 90 | //确定按钮 91 | confirmBtn = UIButton() 92 | confirmBtn?.addTarget(self, action:#selector(JWDatePickerKeyBoardView.btnClick(btn:)), for: UIControlEvents.touchUpInside) 93 | confirmBtn?.titleLabel?.font = font 94 | confirmBtn?.setTitle("确定", for: UIControlState.normal) 95 | confirmBtn?.setTitle("确定", for: UIControlState.selected) 96 | confirmBtn?.setTitleColor(UIColor.white, for: UIControlState.normal) 97 | confirmBtn?.tag = 1 98 | titleView?.addSubview(confirmBtn!) 99 | 100 | //标题 101 | titleLabel = UILabel() 102 | titleLabel?.textAlignment = NSTextAlignment.center 103 | titleLabel?.font = font 104 | titleLabel?.text = title 105 | titleLabel?.textColor = UIColor.white 106 | titleView?.addSubview(titleLabel!) 107 | 108 | //pickerView 109 | addSubview(pickerView) 110 | } 111 | 112 | 113 | internal func btnClick(btn:UIButton) { 114 | if btn.tag == 1 { 115 | if let closure = didConfirmDateClosure{ 116 | if let date = pickerView.date{ 117 | closure(date) 118 | } 119 | } 120 | } 121 | 122 | cover.alpha = 0.021; 123 | UIApplication.shared.keyWindow?.endEditing(true) 124 | 125 | } 126 | 127 | override public func layoutSubviews() { 128 | super.layoutSubviews() 129 | 130 | //titleView 131 | 132 | 133 | let titleViewX:CGFloat = 0 134 | let titleViewY:CGFloat = 0 135 | let titleViewW:CGFloat = bounds.width 136 | let titleViewH:CGFloat = 50 137 | titleView?.frame = CGRect(x: titleViewX, y: titleViewY, width: titleViewW, height: titleViewH) 138 | 139 | let cancelX:CGFloat = 0 140 | let cancelY:CGFloat = 0 141 | let cancelW:CGFloat = 80 142 | let cancelH:CGFloat = titleViewH 143 | cancelBtn?.frame = CGRect(x: cancelX, y: cancelY, width: cancelW, height: cancelH) 144 | 145 | let confirmW:CGFloat = cancelW 146 | let confirmH:CGFloat = titleViewH 147 | let confirmX:CGFloat = bounds.width - confirmW 148 | let confirmY:CGFloat = 0 149 | confirmBtn?.frame = CGRect(x: confirmX, y: confirmY, width: confirmW, height: confirmH) 150 | 151 | let titleW:CGFloat = bounds.width - cancelW*2 152 | let titleH:CGFloat = titleViewH 153 | let titleX:CGFloat = (cancelBtn?.frame.maxX)! 154 | let titleY:CGFloat = 0 155 | titleLabel?.frame = CGRect(x: titleX, y: titleY, width: titleW, height: titleH) 156 | 157 | 158 | let pickerX:CGFloat = 0 159 | let pickerY:CGFloat = titleViewH 160 | let pickerW:CGFloat = titleViewW 161 | let pickerH:CGFloat = bounds.size.height - titleViewH 162 | pickerView.frame = CGRect(x: pickerX, y: pickerY, width: pickerW, height: pickerH) 163 | 164 | } 165 | 166 | required public init?(coder aDecoder: NSCoder) { 167 | fatalError("init(coder:) has not been implemented") 168 | } 169 | 170 | 171 | /** 172 | * 此方法当 自定键盘移动到窗口上调用 173 | */ 174 | override public func didMoveToWindow() { 175 | super.didMoveToWindow() 176 | if isRemove == false { 177 | let window:UIWindow = UIApplication.shared.keyWindow! 178 | cover.frame = UIScreen.main.bounds 179 | cover.alpha = 0.4 180 | window.addSubview(cover) 181 | }else 182 | { 183 | isRemove = false 184 | } 185 | } 186 | 187 | 188 | /** 189 | * 移除时 将蒙板一并移除 190 | */ 191 | override public func removeFromSuperview() { 192 | isRemove = true 193 | cover.removeFromSuperview() 194 | 195 | super.removeFromSuperview() 196 | } 197 | 198 | 199 | func exitKeyBoard() { 200 | cover.alpha = 0.021 201 | UIApplication.shared.keyWindow?.endEditing(true) 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /JavenKit/JWDatePickerView/JWPickerKeyBoardView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWDatePickerKeyBoardView.swift 3 | // CarServer 4 | // 5 | // Created by 朱建伟 on 2016/10/17. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class JWPickerKeyBoardView: UIView ,UIPickerViewDelegate,UIPickerViewDataSource{ 12 | 13 | public typealias ConfirmDateClosure = (Int) -> () 14 | 15 | public var didConfirmClosure:ConfirmDateClosure? 16 | 17 | public var numberClosure:(()->Int)? 18 | 19 | public var titleClosure:((Int)->String)? 20 | 21 | public var isRemove:Bool = false 22 | 23 | public var font:UIFont?{ 24 | willSet{ 25 | if let f = font { 26 | titleLabel?.font = f 27 | titleLabel?.text = "日期选择" 28 | cancelBtn?.titleLabel?.font = f 29 | confirmBtn?.titleLabel?.font = f 30 | } 31 | } 32 | } 33 | 34 | 35 | public var title:String?{ 36 | willSet{ 37 | if let t = newValue{ 38 | titleLabel?.text = t 39 | } 40 | } 41 | } 42 | 43 | //蒙版 44 | private var cover:UIControl = { 45 | let view:UIControl = UIControl(frame: UIScreen.main.bounds) 46 | view.backgroundColor = UIColor.black 47 | view.alpha = 0.4 48 | 49 | return view 50 | }() 51 | 52 | //取消按钮 53 | private var cancelBtn:UIButton? 54 | 55 | //确定按钮 56 | private var confirmBtn:UIButton? 57 | 58 | //头部标题 59 | private var titleLabel:UILabel? 60 | 61 | //头部View 62 | private var titleView:UIView? 63 | 64 | //pickerView 65 | public let pickerView:UIPickerView = UIPickerView() 66 | 67 | public override init(frame: CGRect) { 68 | super.init(frame: frame) 69 | 70 | backgroundColor = UIColor.white 71 | 72 | pickerView.delegate = self 73 | pickerView.dataSource = self 74 | 75 | cover.addTarget(self, action: #selector(JWDatePickerKeyBoardView.exitKeyBoard), for: UIControlEvents.touchUpInside) 76 | 77 | // FontPrompt_120_218_317_416_515_614_712_810_908 78 | font = UIFont.systemFont(ofSize: 18) 79 | 80 | //标题View 81 | titleView = UIView() 82 | let rgb:CGFloat = 220/255.0 83 | titleView?.backgroundColor = UIColor(red: rgb, green: rgb, blue: rgb, alpha: 1) 84 | addSubview(titleView!) 85 | 86 | //取消按钮 87 | cancelBtn = UIButton() 88 | cancelBtn?.addTarget(self, action:#selector(JWDatePickerKeyBoardView.btnClick(btn:)), for: UIControlEvents.touchUpInside) 89 | cancelBtn?.titleLabel?.font = font 90 | cancelBtn?.setTitle("取消", for: UIControlState.normal) 91 | cancelBtn?.setTitle("取消", for: UIControlState.selected) 92 | cancelBtn?.setTitleColor(UIColor.orange, for: UIControlState.normal) 93 | cancelBtn?.tag = 0 94 | titleView?.addSubview(cancelBtn!) 95 | 96 | //确定按钮 97 | confirmBtn = UIButton() 98 | confirmBtn?.addTarget(self, action:#selector(JWDatePickerKeyBoardView.btnClick(btn:)), for: UIControlEvents.touchUpInside) 99 | confirmBtn?.titleLabel?.font = font 100 | confirmBtn?.setTitle("确定", for: UIControlState.normal) 101 | confirmBtn?.setTitle("确定", for: UIControlState.selected) 102 | confirmBtn?.setTitleColor(UIColor.orange, for: UIControlState.normal) 103 | confirmBtn?.tag = 1 104 | titleView?.addSubview(confirmBtn!) 105 | 106 | //标题 107 | titleLabel = UILabel() 108 | titleLabel?.textAlignment = NSTextAlignment.center 109 | titleLabel?.font = font 110 | titleLabel?.text = title 111 | titleLabel?.textColor = UIColor.darkGray 112 | titleView?.addSubview(titleLabel!) 113 | 114 | //pickerView 115 | addSubview(pickerView) 116 | } 117 | 118 | 119 | public func 120 | numberOfComponents(in pickerView: UIPickerView) -> Int { 121 | return 1 122 | } 123 | 124 | 125 | 126 | public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 127 | if let closure = self.numberClosure{ 128 | if self.titleClosure != nil{ 129 | return closure() 130 | } 131 | } 132 | 133 | return 0 134 | 135 | } 136 | 137 | //返回选项高度 138 | public func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { 139 | return pickerView.bounds.height/5 140 | } 141 | 142 | 143 | //返回label 144 | public func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { 145 | 146 | let title:String = titleClosure!(row) 147 | 148 | var label:UILabel? = nil 149 | 150 | if view != nil { 151 | label = view as! UILabel? 152 | }else{ 153 | label = UILabel() 154 | label?.textAlignment = NSTextAlignment.center 155 | label?.textColor = UIColor.darkGray 156 | // Font_120_218_317_416_515_614_712_810_908 157 | label?.font = UIFont.systemFont(ofSize: 18) 158 | label?.text = title 159 | } 160 | return label! 161 | } 162 | 163 | private var currentIndex:Int = 0 164 | public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 165 | currentIndex = row 166 | } 167 | 168 | 169 | internal func btnClick(btn:UIButton) { 170 | if btn.tag == 1 { 171 | if let closure = didConfirmClosure{ 172 | closure(self.currentIndex) 173 | } 174 | } 175 | 176 | cover.alpha = 0.021; 177 | UIApplication.shared.keyWindow?.endEditing(true) 178 | 179 | } 180 | 181 | override public func layoutSubviews() { 182 | super.layoutSubviews() 183 | 184 | 185 | let titleViewX:CGFloat = 0 186 | let titleViewY:CGFloat = 0 187 | let titleViewW:CGFloat = bounds.width 188 | let titleViewH:CGFloat = 50 189 | titleView?.frame = CGRect(x: titleViewX, y: titleViewY, width: titleViewW, height: titleViewH) 190 | 191 | let cancelX:CGFloat = 0 192 | let cancelY:CGFloat = 0 193 | let cancelW:CGFloat = 80 194 | let cancelH:CGFloat = titleViewH 195 | cancelBtn?.frame = CGRect(x: cancelX, y: cancelY, width: cancelW, height: cancelH) 196 | 197 | let confirmW:CGFloat = cancelW 198 | let confirmH:CGFloat = titleViewH 199 | let confirmX:CGFloat = bounds.width - confirmW 200 | let confirmY:CGFloat = 0 201 | confirmBtn?.frame = CGRect(x: confirmX, y: confirmY, width: confirmW, height: confirmH) 202 | 203 | let titleW:CGFloat = bounds.width - cancelW*2 204 | let titleH:CGFloat = titleViewH 205 | let titleX:CGFloat = (cancelBtn?.frame.maxX)! 206 | let titleY:CGFloat = 0 207 | titleLabel?.frame = CGRect(x: titleX, y: titleY, width: titleW, height: titleH) 208 | 209 | 210 | let pickerX:CGFloat = 0 211 | let pickerY:CGFloat = titleViewH 212 | let pickerW:CGFloat = titleViewW 213 | let pickerH:CGFloat = bounds.size.height - titleViewH 214 | pickerView.frame = CGRect(x: pickerX, y: pickerY, width: pickerW, height: pickerH) 215 | 216 | } 217 | 218 | required public init?(coder aDecoder: NSCoder) { 219 | fatalError("init(coder:) has not been implemented") 220 | } 221 | 222 | 223 | /** 224 | * 此方法当 自定键盘移动到窗口上调用 225 | */ 226 | override public func didMoveToWindow() { 227 | super.didMoveToWindow() 228 | if isRemove == false { 229 | let window:UIWindow = UIApplication.shared.keyWindow! 230 | cover.frame = UIScreen.main.bounds 231 | cover.alpha = 0.4 232 | window.addSubview(cover) 233 | }else 234 | { 235 | isRemove = false 236 | } 237 | } 238 | 239 | 240 | /** 241 | * 移除时 将蒙板一并移除 242 | */ 243 | override public func removeFromSuperview() { 244 | isRemove = true 245 | cover.removeFromSuperview() 246 | 247 | super.removeFromSuperview() 248 | } 249 | 250 | 251 | func exitKeyBoard() { 252 | cover.alpha = 0.021 253 | UIApplication.shared.keyWindow?.endEditing(true) 254 | } 255 | 256 | public func reloadData() { 257 | self.currentIndex = 0 258 | pickerView.reloadAllComponents() 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /JavenKit/JWMessageView/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JWMessageView/.DS_Store -------------------------------------------------------------------------------- /JavenKit/JWMessageView/JWMessageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWMessageView.swift 3 | // JWMessageView 4 | // 5 | // Created by 朱建伟 on 2016/12/1. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class JWMessageView: UIView,UIDynamicAnimatorDelegate { 12 | 13 | public var messageCount:Int = 0 { 14 | 15 | willSet{ 16 | 17 | if newValue <= 0{ 18 | self.isHidden = true 19 | }else{ 20 | self.isHidden = false 21 | } 22 | 23 | self.messageButton.isHidden = false 24 | 25 | let tempCenter:CGPoint = self.center 26 | 27 | let fontStr:String = String(format: "%@", newValue > 99 ? "99+":String(format:"%zd",newValue)) 28 | 29 | 30 | 31 | let size:CGSize = NSString(string: fontStr).boundingRect(with: CGSize(width:UIScreen.main.bounds.width,height:UIScreen.main.bounds.height), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName:messageFont], context: nil).size 32 | 33 | self.frame = CGRect(x: 0, y: 0, width: size.width + size.height, height: size.height + 5) 34 | 35 | self.center = tempCenter 36 | 37 | //消息 38 | messageButton.setTitle(fontStr, for: UIControlState.normal) 39 | messageButton.setTitle(fontStr, for: UIControlState.highlighted) 40 | 41 | } 42 | } 43 | 44 | 45 | let RangeSizeWH:CGFloat = 100 46 | 47 | public var messageFont:UIFont = UIFont.systemFont(ofSize: 10) 48 | 49 | //消息label 50 | private var messageButton:UIButton = UIButton() 51 | 52 | //消息layer 53 | private var messageLayer:CAShapeLayer = CAShapeLayer() 54 | 55 | //消失的后显示的图片 56 | private var disappearedImageView:UIImageView = UIImageView() 57 | 58 | 59 | //拖拽消息的手势 60 | private var messageDragPan:UIPanGestureRecognizer? 61 | 62 | 63 | //初始化 64 | public override init(frame: CGRect) { 65 | super.init(frame: frame) 66 | 67 | //消息的layer 68 | messageLayer.fillColor = UIColor.red.cgColor 69 | layer.addSublayer(messageLayer) 70 | 71 | 72 | //消息 73 | messageButton.titleLabel?.font = messageFont 74 | 75 | messageButton.setTitleColor(UIColor.white, for: UIControlState.normal) 76 | messageButton.setTitleColor(UIColor.white, for: UIControlState.highlighted) 77 | 78 | messageButton.setBackgroundImage(Bundle.image(named:"JWMessage"), for: UIControlState.normal) 79 | messageButton.setBackgroundImage(Bundle.image(named:"JWMessage"), for: UIControlState.highlighted) 80 | 81 | messageButton.addTarget(self, action: #selector(JWMessageView.messageTouchUpInside(btn:)), for: UIControlEvents.touchUpInside) 82 | 83 | 84 | 85 | 86 | addSubview(messageButton) 87 | 88 | 89 | //消失后显示的图片 90 | disappearedImageView.image = Bundle.image(named:"Bom") 91 | disappearedImageView.isHidden = true 92 | addSubview(disappearedImageView) 93 | 94 | 95 | //拖拽消息的手势 96 | messageDragPan = UIPanGestureRecognizer(target: self, action: #selector(JWMessageView.messageDragHandler(panReg:))) 97 | 98 | addGestureRecognizer(messageDragPan!) 99 | 100 | 101 | } 102 | 103 | 104 | private let scale:CGFloat = 0.8 105 | 106 | 107 | //根据两个点返回两个 108 | private func getMessagePath(sourcePoint:CGPoint,destinationPoint:CGPoint) -> UIBezierPath { 109 | //路径 110 | let messagePath:UIBezierPath = UIBezierPath() 111 | messagePath.lineJoinStyle = .round 112 | messagePath.lineCapStyle = .round 113 | 114 | let lineLength:CGFloat = CGFloat(sqrtf(powf(Float(destinationPoint.y - sourcePoint.y), 2) + powf(Float(destinationPoint.x - sourcePoint.x), 2))) 115 | 116 | 117 | let radius:CGFloat = (bounds.height/2*scale - (bounds.height/24)/RangeSizeWH/2*lineLength*scale) 118 | 119 | let minRadius:CGFloat = (bounds.height/12)*scale - (bounds.height/12)/RangeSizeWH/2*lineLength*scale 120 | 121 | 122 | if lineLength >= RangeSizeWH*0.9 123 | { 124 | return messagePath 125 | } 126 | 127 | 128 | let reduceAngle:CGFloat = CGFloat(M_PI_4/2) 129 | 130 | 131 | //获取偏移角度 132 | let offSetAngle:CGFloat = atan2(destinationPoint.y - sourcePoint.y,destinationPoint.x - sourcePoint.x) + reduceAngle - CGFloat(M_PI_2) 133 | 134 | 135 | let center:CGPoint = CGPoint(x:self.bounds.width/2,y:self.bounds.height/2) 136 | 137 | let firstSourcePoint:CGPoint = self.getCirclePoint(radius: radius, center:center, angle:offSetAngle) 138 | 139 | //移动到对应点 140 | messagePath.move(to:firstSourcePoint ) 141 | 142 | 143 | //添加半圆 144 | messagePath.addArc(withCenter:center, radius:radius, startAngle: offSetAngle, endAngle: offSetAngle - CGFloat(M_PI) - reduceAngle * 2, clockwise: false) 145 | 146 | 147 | //弧线中点 148 | let messageLayerCenter = CGPoint(x:(destinationPoint.x + sourcePoint.x)/2, y: (destinationPoint.y + sourcePoint.y)/2) 149 | 150 | 151 | //添加弧线 152 | let firstControlPoint:CGPoint = getCirclePoint(radius: minRadius, center:messageLayerCenter, angle: offSetAngle - CGFloat(M_PI) - reduceAngle * 2) 153 | 154 | //目标点 155 | let firstCurvePoint:CGPoint = getCirclePoint(radius: radius, center: destinationPoint, angle: offSetAngle - CGFloat(M_PI) - reduceAngle * 2) 156 | 157 | messagePath.addQuadCurve(to: firstCurvePoint, controlPoint: firstControlPoint) 158 | 159 | 160 | //第二个半弧 161 | messagePath.addArc(withCenter: destinationPoint, radius: radius, startAngle: offSetAngle - CGFloat(M_PI) - reduceAngle * 2, endAngle: offSetAngle, clockwise: true) 162 | 163 | 164 | 165 | //添加第二个弧线 166 | 167 | let secondControlPoint:CGPoint = getCirclePoint(radius: minRadius, center:messageLayerCenter, angle: offSetAngle) 168 | 169 | 170 | messagePath.addQuadCurve(to: firstSourcePoint, controlPoint: secondControlPoint) 171 | 172 | 173 | 174 | return messagePath 175 | } 176 | 177 | 178 | //圆上的点 179 | private func getCirclePoint(radius:CGFloat,center:CGPoint,angle:CGFloat) -> CGPoint { 180 | 181 | let pointX:CGFloat = CGFloat(cosf(Float(angle))) * radius 182 | let pointY:CGFloat = CGFloat(sinf(Float(angle))) * radius 183 | 184 | return CGPoint(x: pointX+center.x, y: pointY+center.y) 185 | } 186 | 187 | 188 | 189 | var messageMoveFlag:Bool = false 190 | //处理手势 191 | func messageDragHandler(panReg:UIPanGestureRecognizer) { 192 | //获取偏移量 193 | let translationPoint:CGPoint = panReg.translation(in: panReg.view) 194 | 195 | switch panReg.state { 196 | case .changed://改变 197 | messageButton.transform = CGAffineTransform(translationX: translationPoint.x, y: translationPoint.y) 198 | disappearedImageView.transform = CGAffineTransform(translationX: translationPoint.x, y: translationPoint.y) 199 | 200 | //路径 201 | self.messageLayer.path = getMessagePath(sourcePoint: CGPoint(x:self.bounds.width/2,y:self.bounds.height/2), destinationPoint: CGPoint(x:self.bounds.width/2+translationPoint.x,y:self.bounds.height/2+translationPoint.y)).cgPath 202 | 203 | break 204 | case .failed: 205 | fallthrough 206 | case .cancelled: 207 | fallthrough 208 | case .ended://结束 209 | 210 | //路径恢复掉 211 | self.messageLayer.path = UIBezierPath().cgPath 212 | 213 | let lineLength:CGFloat = CGFloat(sqrtf(powf(Float(translationPoint.y), 2) + powf(Float(translationPoint.x), 2))) 214 | 215 | //处理消息 216 | messageHanlder(alwaysShow: lineLength < RangeSizeWH*0.9) 217 | 218 | break 219 | case .began://开始 220 | 221 | //1.停止抖动的动画 222 | // messageTouchEnd(btn: messageButton) 223 | 224 | //2.隐藏图片 225 | self.disappearedImageView.isHidden = true 226 | self.messageButton.isHidden = false 227 | 228 | //3.开启移动模式 229 | self.messageMoveFlag = true 230 | 231 | 232 | break 233 | default: 234 | break 235 | } 236 | } 237 | 238 | 239 | //消息处理 240 | private func messageHanlder(alwaysShow:Bool) { 241 | //判断是否显示 242 | if alwaysShow{ 243 | self.messageButton.isHidden = false 244 | self.disappearedImageView.isHidden = true 245 | self.messageDragPan?.isEnabled = true 246 | 247 | 248 | self.messageMoveFlag = false 249 | self.messageButton.transform = CGAffineTransform.identity 250 | disappearedImageView.transform = CGAffineTransform.identity 251 | self.setNeedsLayout() 252 | }else{ 253 | 254 | self.messageButton.isHidden = true 255 | self.disappearedImageView.isHidden = false 256 | self.messageDragPan?.isEnabled = false 257 | self.messageButton.transform = CGAffineTransform.identity 258 | 259 | 260 | 261 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.25, execute: { 262 | UIView.animate(withDuration: 0.25, animations: { 263 | self.disappearedImageView.alpha = 0 264 | }, completion: { (finished) in 265 | self.disappearedImageView.alpha = 1 266 | self.disappearedImageView.isHidden = true 267 | 268 | self.messageDragPan?.isEnabled = true 269 | self.messageMoveFlag = false 270 | self.setNeedsLayout() 271 | self.disappearedImageView.transform = CGAffineTransform.identity 272 | }) 273 | }) 274 | } 275 | } 276 | 277 | 278 | 279 | func messageTouchUpInside(btn:UIButton){ 280 | messageHanlder(alwaysShow: false) 281 | } 282 | 283 | 284 | 285 | 286 | 287 | override public func layoutSubviews() { 288 | super.layoutSubviews() 289 | 290 | 291 | if !messageMoveFlag{ 292 | //消息 293 | messageButton.frame = self.bounds 294 | messageButton.layer.cornerRadius = self.bounds.height/2 295 | messageButton.layer.masksToBounds = true 296 | 297 | 298 | //图片 299 | let imageH:CGFloat = self.bounds.height + 10 300 | let imageW:CGFloat = imageH 301 | let imageX:CGFloat = (self.bounds.width - imageW)/2 302 | let imageY:CGFloat = (self.bounds.height - imageW)/2 303 | disappearedImageView.frame = CGRect(x: imageX, y: imageY, width: imageW, height: imageH) 304 | } 305 | } 306 | 307 | 308 | required public init?(coder aDecoder: NSCoder) { 309 | fatalError("init(coder:) has not been implemented") 310 | } 311 | 312 | } 313 | -------------------------------------------------------------------------------- /JavenKit/JWPhotoBrowserViewController/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JWPhotoBrowserViewController/.DS_Store -------------------------------------------------------------------------------- /JavenKit/JWPhotoBrowserViewController/Controller/JWPhotoBrowserViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWPhotoBrowserViewController.swift 3 | // JWCoreImageBrowser 4 | // 5 | // Created by 朱建伟 on 2016/11/28. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | public class JWPhotoBrowserViewController: UICollectionViewController,UIViewControllerTransitioningDelegate{ 13 | 14 | let PBReuseIdentifier:String = "reuseIdentifier" 15 | 16 | 17 | //如果是图片直接调用 不是则下载完成后调用 18 | public typealias JWPhotoCompletionClosure = (UIImage?)->Void 19 | 20 | //图片获取 21 | public typealias JWPhotoHanlderClosure = ((Int,UIImageView,@escaping JWPhotoCompletionClosure) -> Void) 22 | 23 | //返回对应的View 24 | public typealias JWPhotoSourceViewClosure = (Int)->((UIView?,UIImage?))? 25 | 26 | 27 | //数组 28 | var photoSource:[JWPhotoBrowerItem] = [JWPhotoBrowerItem]() 29 | 30 | //初始化 31 | convenience public init(photoCount:Int,showIndex:Int,thumbnailClosure: @escaping JWPhotoHanlderClosure,bigImageClosure: @escaping JWPhotoHanlderClosure,sourceViewClosure:JWPhotoSourceViewClosure){ 32 | 33 | 34 | //1.初始化布局 35 | let layout:UICollectionViewFlowLayout = UICollectionViewFlowLayout() 36 | layout.scrollDirection = .horizontal 37 | layout.itemSize = UIScreen.main.bounds.size 38 | layout.minimumLineSpacing = 0 39 | layout.minimumInteritemSpacing = 0 40 | self.init(collectionViewLayout:layout) 41 | 42 | 43 | //2.创建 44 | self.photoSource.removeAll() 45 | for i:Int in 0.. Int { 138 | return 1 139 | } 140 | 141 | 142 | override public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 143 | return photoSource.count 144 | } 145 | 146 | 147 | 148 | override public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 149 | 150 | //1.获取cell 151 | let cell:JWPhotoBrowserCell = collectionView.dequeueReusableCell(withReuseIdentifier: PBReuseIdentifier, for: indexPath) as! JWPhotoBrowserCell 152 | 153 | //2.设置cell 154 | cell.photoBrowserItem = photoSource[indexPath.item] 155 | 156 | weak var weakSelf = self 157 | cell.dismissClosure = { 158 | (item,photoView)->Void in 159 | 160 | weakSelf?.dismissTapTransitioning.sourceView = photoView 161 | weakSelf?.dismissTapTransitioning.destinationFrame = item.sourceRect 162 | weakSelf?.dismissTapTransitioning.desinationImage = item.bigImage ?? item.thumbnail 163 | 164 | weakSelf?.dismiss(animated: true, completion: { 165 | 166 | }) 167 | } 168 | 169 | //3.返回 170 | return cell 171 | } 172 | 173 | 174 | 175 | override public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { 176 | 177 | //1.获取cell 178 | let cell:JWPhotoBrowserCell = cell as! JWPhotoBrowserCell 179 | 180 | //2. 181 | cell.handler() 182 | } 183 | 184 | override public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 185 | // print("选中了:\(indexPath)") 186 | } 187 | 188 | override public func scrollViewDidScroll(_ scrollView: UIScrollView) { 189 | 190 | let page = scrollView.contentOffset.x / scrollView.bounds.width 191 | 192 | let showIndex:Int = lroundf(Float(page))+1 193 | 194 | let m_attr:NSMutableAttributedString = NSMutableAttributedString(string:String(format:"%zd",showIndex), attributes: [NSFontAttributeName:UIFont.systemFont(ofSize: 30),NSForegroundColorAttributeName:UIColor.white]) 195 | 196 | m_attr.append(self.lastAttributeString!) 197 | 198 | IndexPromptLabel.attributedText = m_attr 199 | } 200 | 201 | override public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { 202 | IndexPromptLabel.isHidden = false 203 | self.IndexPromptLabel.tag = 0 204 | } 205 | 206 | override public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 207 | 208 | if IndexPromptLabel.tag == 0{ 209 | IndexPromptLabel.tag = 1 210 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1, execute: 211 | { 212 | if self.IndexPromptLabel.tag == 1{ 213 | UIView.animate(withDuration: 0.25, animations: { 214 | self.IndexPromptLabel.isHidden = true 215 | }, completion: {(finished)->Void in 216 | self.IndexPromptLabel.tag = 1 217 | }) 218 | }else{ 219 | self.IndexPromptLabel.layer.removeAllAnimations() 220 | } 221 | }) 222 | } 223 | } 224 | 225 | private override init(collectionViewLayout layout: UICollectionViewLayout) { 226 | super.init(collectionViewLayout: layout) 227 | } 228 | 229 | 230 | required public init?(coder aDecoder: NSCoder) { 231 | fatalError("init(coder:) has not been implemented") 232 | } 233 | 234 | 235 | //上下滑动退出 236 | var dismissTransitioning:JWPhotoDismissBrowserTransitioning = JWPhotoDismissBrowserTransitioning() 237 | 238 | //点击后退出 239 | var dismissTapTransitioning:JWPhotoTapDismissTransioning = JWPhotoTapDismissTransioning() 240 | 241 | //弹出 242 | var presentTransitioning:JWPhotoPresentBrowserTransitioning = JWPhotoPresentBrowserTransitioning() 243 | 244 | 245 | public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 246 | 247 | if self.interactionTransitioning.isStart{ 248 | return dismissTransitioning 249 | }else{ 250 | return dismissTapTransitioning 251 | } 252 | } 253 | 254 | public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 255 | return presentTransitioning 256 | } 257 | 258 | var interactionTransitioning:JWPhotoInteractiveTransitioning = JWPhotoInteractiveTransitioning() 259 | 260 | public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?{ 261 | return self.interactionTransitioning.isStart ? self.interactionTransitioning : nil 262 | } 263 | 264 | 265 | 266 | override public func viewWillAppear(_ animated: Bool) { 267 | super.viewWillAppear(animated) 268 | 269 | interactionTransitioning.addPopInteractiveTransition(browserViewController: self) 270 | 271 | } 272 | 273 | 274 | } 275 | -------------------------------------------------------------------------------- /JavenKit/JWPhotoBrowserViewController/Model/JWPhotoBrowerItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWPhotoBrowerItem.swift 3 | // JWCoreImageBrowser 4 | // 5 | // Created by 朱建伟 on 2016/11/28. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class JWPhotoBrowerItem: NSObject { 12 | 13 | var index:Int = 0 14 | 15 | //缩略图 16 | var thumbnail:UIImage? 17 | 18 | //大图 19 | var bigImage:UIImage? 20 | 21 | 22 | //完成回掉 23 | var thumbnailClosure:JWPhotoBrowserViewController.JWPhotoHanlderClosure? 24 | 25 | //完成回掉 26 | var bigImageClosure:JWPhotoBrowserViewController.JWPhotoHanlderClosure? 27 | 28 | 29 | //sourceFrame 30 | var sourceRect:CGRect? 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /JavenKit/JWPhotoBrowserViewController/Transitioning/JWPhotoDismissBrowserTransitioning.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWPhotoBrowserTransitioning.swift 3 | // JWCoreImageBrowser 4 | // 5 | // Created by 朱建伟 on 2016/11/29. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class JWPhotoDismissBrowserTransitioning: NSObject,UIViewControllerAnimatedTransitioning { 12 | 13 | 14 | public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval{ 15 | return 0.3 16 | } 17 | 18 | var directionIsTop = true 19 | 20 | public func animateTransition(using transitionContext: UIViewControllerContextTransitioning){ 21 | 22 | let fromView:UIView = transitionContext.view(forKey: UITransitionContextViewKey.from)! 23 | let toView:UIView = transitionContext.view(forKey: UITransitionContextViewKey.to)! 24 | 25 | let containerView:UIView = transitionContext.containerView 26 | 27 | //设置frame 适配 28 | containerView.frame = UIScreen.main.bounds 29 | fromView.frame = UIScreen.main.bounds 30 | toView.frame = UIScreen.main.bounds 31 | 32 | containerView.addSubview(toView) 33 | containerView.addSubview(fromView) 34 | 35 | 36 | //设置动画 37 | UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: { 38 | ()-> Void in 39 | fromView.backgroundColor = UIColor.clear 40 | 41 | }, completion: { 42 | (isFinished) -> Void in 43 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 44 | }) 45 | } 46 | 47 | 48 | public func animationEnded(_ transitionCompleted: Bool){ 49 | 50 | } 51 | 52 | 53 | 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /JavenKit/JWPhotoBrowserViewController/Transitioning/JWPhotoInteractiveTransitioning.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWPhotoInteractiveTransitioning.swift 3 | // JWCoreImageBrowser 4 | // 5 | // Created by 朱建伟 on 2016/11/29. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class JWPhotoInteractiveTransitioning:UIPercentDrivenInteractiveTransition { 12 | 13 | private weak var browserVc:JWPhotoBrowserViewController? 14 | 15 | func addPopInteractiveTransition(browserViewController:JWPhotoBrowserViewController) { 16 | let pan:UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(JWPhotoInteractiveTransitioning.handlerPanReg(pan:))) 17 | pan.maximumNumberOfTouches = 1 18 | pan.minimumNumberOfTouches = 1 19 | //将传入的控制器保存,因为要利用它触发转场操作 20 | self.browserVc = browserViewController 21 | browserViewController.view.addGestureRecognizer(pan) 22 | } 23 | 24 | 25 | var isStart:Bool = false 26 | //处理 27 | func handlerPanReg(pan:UIPanGestureRecognizer) { 28 | let translationY:CGFloat = pan.translation(in: pan.view).y 29 | 30 | let percent:CGFloat = abs(translationY) / (((browserVc?.view.bounds.height)!)) 31 | 32 | // print("translationY:\(translationY)") 33 | switch pan.state { 34 | case .changed://改变 35 | self.update(percent) 36 | //设置位置 37 | self.browserVc?.operationView.transform = CGAffineTransform(translationX: 0, y: translationY) 38 | break 39 | case .began://开始 40 | self.isStart = true 41 | 42 | 43 | //获取到oprationView 44 | if (self.browserVc?.collectionView?.visibleCells.count)! > 0{ 45 | let cell:JWPhotoBrowserCell = self.browserVc!.collectionView?.visibleCells.first as! JWPhotoBrowserCell 46 | 47 | let rect:CGRect = cell.photoImageView.convert(cell.photoImageView.bounds, to: browserVc!.view) 48 | 49 | self.browserVc?.operationView.image = cell.photoImageView.image //cell.photoImageView.snapshotView(afterScreenUpdates:false)! 50 | 51 | self.browserVc?.operationView.frame = rect 52 | 53 | self.browserVc?.view.addSubview((self.browserVc?.operationView)!) 54 | } 55 | 56 | self.browserVc?.collectionView?.isHidden = true 57 | 58 | self.browserVc?.dismiss(animated: true, completion: { 59 | 60 | }) 61 | 62 | break 63 | case .failed://结束 取消 64 | fallthrough 65 | case .cancelled: 66 | fallthrough 67 | case .ended: 68 | self.isStart = false 69 | if abs(translationY) > ((browserVc?.view.bounds.height)!)/8 70 | { 71 | if let oprationView = self.browserVc?.operationView{ 72 | //默认向上滑动 73 | var transform = CGAffineTransform(translationX: 0, y:-(browserVc?.view.bounds.height)!)// oprationView.frame.maxY 74 | 75 | //下滑 76 | if translationY > 0 { 77 | transform = CGAffineTransform(translationX: 0, y: (browserVc?.view.bounds.height)!)//(self.browserVc?.view.bounds.height)! - oprationView.frame.minY 78 | } 79 | 80 | UIView.animate(withDuration: Double(self.duration * (1.0 - percent)), animations: { 81 | oprationView.transform = transform 82 | }, completion: { (finished) in 83 | oprationView.removeFromSuperview() 84 | // self.browserVc?.collectionView?.isHidden = true 85 | }) 86 | } 87 | self.finish() 88 | }else{ 89 | 90 | if let oprationView = browserVc?.operationView { 91 | UIView.animate(withDuration: Double(self.duration * (1.0 - percent)), animations: { 92 | oprationView.transform = CGAffineTransform.identity 93 | }, completion: { (finished) in 94 | oprationView.removeFromSuperview() 95 | self.browserVc?.collectionView?.isHidden = false 96 | }) 97 | } 98 | self.cancel() 99 | } 100 | break 101 | default: 102 | break 103 | } 104 | } 105 | 106 | 107 | } 108 | -------------------------------------------------------------------------------- /JavenKit/JWPhotoBrowserViewController/Transitioning/JWPhotoPresentBrowserTransitioning.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWPhotoPresentBrowserTransitioning.swift 3 | // JWCoreImageBrowser 4 | // 5 | // Created by 朱建伟 on 2016/11/29. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class JWPhotoPresentBrowserTransitioning: NSObject,UIViewControllerAnimatedTransitioning { 12 | 13 | weak var sourceView:UIView? 14 | 15 | weak var sourceImage:UIImage? 16 | 17 | public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval{ 18 | return 0.3 19 | } 20 | 21 | 22 | public func animateTransition(using transitionContext: UIViewControllerContextTransitioning){ 23 | 24 | let fromView:UIView = transitionContext.view(forKey: UITransitionContextViewKey.from)! 25 | let toView:UIView = transitionContext.view(forKey: UITransitionContextViewKey.to)! 26 | 27 | let containerView:UIView = transitionContext.containerView 28 | 29 | //设置frame 适配 30 | containerView.frame = UIScreen.main.bounds 31 | fromView.frame = UIScreen.main.bounds 32 | toView.frame = UIScreen.main.bounds 33 | 34 | containerView.addSubview(toView) 35 | containerView.addSubview(fromView) 36 | 37 | 38 | 39 | // let r = CGFloat(arc4random_uniform(255))/255.0 40 | // let g = CGFloat(arc4random_uniform(255))/255.0 41 | // let b = CGFloat(arc4random_uniform(255))/255.0 42 | let color:UIColor = UIColor.black// UIColor(red: r, green: g, blue: b, alpha: 1) 43 | 44 | let coverView:UIView = UIView() 45 | coverView.backgroundColor = color 46 | coverView.alpha = 0 47 | coverView.frame = toView.bounds 48 | containerView.addSubview(coverView) 49 | 50 | 51 | 52 | let tempView:UIImageView = UIImageView() 53 | 54 | let tempViewW:CGFloat = toView.bounds.width 55 | var tempViewH:CGFloat = toView.bounds.width 56 | let tempViewX:CGFloat = (toView.bounds.width - tempViewW)/2 57 | var tempViewY:CGFloat = (toView.bounds.height - tempViewH)/2 58 | 59 | 60 | var flag = true 61 | 62 | if let sView = sourceView{ 63 | tempView.frame = sView.convert(sView.bounds, to: toView) 64 | containerView.addSubview(tempView) 65 | 66 | if let image = sourceImage{ 67 | tempView.clipsToBounds = true 68 | tempView.contentMode = UIViewContentMode.scaleAspectFill 69 | tempView.image = image 70 | tempViewH = tempViewW / image.size.width * image.size.height 71 | tempViewY = (toView.bounds.height - tempViewH)/2 72 | 73 | flag = false 74 | //设置动画 75 | UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: { 76 | ()-> Void in 77 | coverView.alpha = 1 78 | tempView.frame = CGRect(x: tempViewX, y: tempViewY, width: tempViewW, height: tempViewH) 79 | 80 | }, completion: { 81 | (isFinished) -> Void in 82 | toView.backgroundColor = color 83 | coverView.removeFromSuperview() 84 | tempView.removeFromSuperview() 85 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 86 | }) 87 | } 88 | } 89 | 90 | //什么都没有push动画 91 | if flag{ 92 | coverView.transform = CGAffineTransform(translationX: toView.bounds.width, y: 0) 93 | UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: { 94 | coverView.alpha = 1 95 | coverView.transform = CGAffineTransform.identity 96 | }, completion: { (finished) in 97 | toView.backgroundColor = color 98 | coverView.removeFromSuperview() 99 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 100 | }) 101 | } 102 | } 103 | 104 | public func animationEnded(_ transitionCompleted: Bool){ 105 | 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /JavenKit/JWPhotoBrowserViewController/Transitioning/JWPhotoTapDismissTransioning.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWPhotoTapDismissTransioning.swift 3 | // JWCoreImageBrowser 4 | // 5 | // Created by 朱建伟 on 2016/11/29. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class JWPhotoTapDismissTransioning: NSObject,UIViewControllerAnimatedTransitioning{ 12 | 13 | //图片 14 | weak var sourceView:UIView? 15 | 16 | //目标rect 17 | var destinationFrame:CGRect? 18 | 19 | //图片 20 | var desinationImage:UIImage? 21 | 22 | 23 | public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval{ 24 | return 0.3 25 | } 26 | 27 | 28 | var directionIsTop = true 29 | 30 | 31 | public func animateTransition(using transitionContext: UIViewControllerContextTransitioning){ 32 | 33 | let fromView:UIView = transitionContext.view(forKey: UITransitionContextViewKey.from)! 34 | let toView:UIView = transitionContext.view(forKey: UITransitionContextViewKey.to)! 35 | 36 | let containerView:UIView = transitionContext.containerView 37 | 38 | //设置frame 适配 39 | containerView.frame = UIScreen.main.bounds 40 | fromView.frame = UIScreen.main.bounds 41 | toView.frame = UIScreen.main.bounds 42 | 43 | containerView.addSubview(toView) 44 | containerView.addSubview(fromView) 45 | 46 | 47 | var flag = true 48 | let tempView:UIImageView = UIImageView() 49 | 50 | if let destinationFrame = self.destinationFrame{ 51 | if let scView = sourceView{ 52 | 53 | if let image = self.desinationImage{ 54 | let cover:UIView = UIView() 55 | cover.frame = fromView.bounds 56 | cover.backgroundColor = UIColor.black 57 | containerView.addSubview(cover) 58 | 59 | 60 | let sourceRect = scView.convert(scView.bounds, to: fromView) 61 | tempView.image = image 62 | tempView.frame = sourceRect 63 | containerView.addSubview(tempView) 64 | tempView.contentMode = UIViewContentMode.scaleAspectFill 65 | tempView.clipsToBounds = true 66 | 67 | flag = false 68 | 69 | fromView.alpha = 0 70 | 71 | UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: { 72 | tempView.frame = destinationFrame 73 | cover.alpha = 0 74 | }, completion: { (isFinshed) in 75 | tempView.removeFromSuperview() 76 | cover.removeFromSuperview() 77 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 78 | fromView.alpha = 1 79 | }) 80 | } 81 | } 82 | } 83 | 84 | if flag { 85 | //设置动画 86 | UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: { 87 | ()-> Void in 88 | fromView.alpha = 0 89 | }, completion: { 90 | (isFinished) -> Void in 91 | fromView.alpha = 1 92 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 93 | }) 94 | } 95 | } 96 | 97 | 98 | public func animationEnded(_ transitionCompleted: Bool){ 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /JavenKit/JWPhotoBrowserViewController/View/JWPhotoBrowserCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWPhotoBrowserCell.swift 3 | // JWCoreImageBrowser 4 | // 5 | // Created by 朱建伟 on 2016/11/28. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class JWPhotoBrowserCell: UICollectionViewCell,UIScrollViewDelegate{ 12 | 13 | //图片item 14 | var photoBrowserItem:JWPhotoBrowerItem?{ 15 | 16 | willSet{ 17 | 18 | self.photoImageView.backgroundColor = UIColor.orange 19 | 20 | self.setNeedsLayout() 21 | } 22 | } 23 | 24 | 25 | //bgScrollView 26 | let bgScrollView:UIScrollView = UIScrollView() 27 | 28 | 29 | //imageView 30 | let photoImageView:UIImageView = UIImageView() 31 | 32 | //加载中 33 | private let indicatorView:UIActivityIndicatorView = UIActivityIndicatorView() 34 | 35 | //双击放大缩小 36 | private var doubleTapReg:UITapGestureRecognizer! 37 | 38 | 39 | private var tapPoint:CGPoint = CGPoint.zero 40 | 41 | private var isDobleClick:Bool = false 42 | 43 | //双击放大 44 | func handlerDoubleTapReg(reg:UITapGestureRecognizer) { 45 | 46 | self.tapPoint = reg.location(in: reg.view) 47 | 48 | //如果小于1.5 49 | if self.currentScale == self.bgScrollView.minimumZoomScale || self.currentScale < (bgScrollView.maximumZoomScale + bgScrollView.minimumZoomScale) / 2{ 50 | isDobleClick = true 51 | self.bgScrollView.setZoomScale(self.bgScrollView.maximumZoomScale, animated: true) 52 | }else if self.currentScale > (bgScrollView.maximumZoomScale + bgScrollView.minimumZoomScale) / 2 || self.currentScale == self.bgScrollView.maximumZoomScale 53 | { 54 | isDobleClick = false 55 | self.bgScrollView.setZoomScale(self.bgScrollView.minimumZoomScale, animated: true) 56 | 57 | } 58 | 59 | } 60 | 61 | 62 | var currentScale:CGFloat = 1 63 | func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) { 64 | 65 | self.currentScale = scale 66 | 67 | isDobleClick = false 68 | } 69 | 70 | func viewForZooming(in scrollView: UIScrollView) -> UIView? { 71 | return self.photoImageView 72 | } 73 | 74 | func scrollViewDidZoom(_ scrollView: UIScrollView) { 75 | //当捏或移动时,需要对center重新定义以达到正确显示未知 76 | let xCenter:CGFloat = scrollView.contentSize.width > scrollView.bounds.width ? scrollView.contentSize.width/2:scrollView.center.x 77 | 78 | let yCenter:CGFloat = scrollView.contentSize.height > scrollView.bounds.height ? scrollView.contentSize.height/2 : scrollView.center.y; 79 | 80 | //如果是双击 判断contentSize 81 | if isDobleClick{ 82 | 83 | var offsetX:CGFloat = 0 84 | var offsetY:CGFloat = 0 85 | 86 | //宽度超出scrollView 87 | if scrollView.contentSize.width > scrollView.bounds.width{ 88 | //根据坐标点计算偏移量 89 | 90 | offsetX = tapPoint.x * bgScrollView.maximumZoomScale - scrollView.bounds.width/2 91 | 92 | //判断越界 93 | if offsetX > scrollView.contentSize.width - scrollView.bounds.width{ 94 | offsetX = scrollView.contentSize.width - scrollView.bounds.width 95 | }else if offsetX < 0{ 96 | offsetX = 0 97 | } 98 | } 99 | 100 | //高度超出scrollView 101 | if scrollView.contentSize.height > scrollView.bounds.height{ 102 | //根据坐标点计算偏移量 103 | offsetY = tapPoint.y * bgScrollView.maximumZoomScale - scrollView.bounds.height/2 104 | 105 | //判断越界 106 | if offsetY > scrollView.contentSize.height - scrollView.bounds.height{ 107 | offsetY = scrollView.contentSize.height - scrollView.bounds.height 108 | }else if offsetY < 0{ 109 | offsetY = 0 110 | } 111 | } 112 | self.bgScrollView.setContentOffset(CGPoint(x: offsetX, y: offsetY), animated: false) 113 | } 114 | 115 | self.photoImageView.center = CGPoint(x:xCenter,y:yCenter) 116 | } 117 | 118 | 119 | //处理设置图片 120 | func handler() { 121 | 122 | self.indicatorView.stopAnimating() 123 | 124 | if let item = photoBrowserItem{ 125 | 126 | if let closure = item.thumbnailClosure{ 127 | self.indicatorView.startAnimating() 128 | closure(item.index, photoImageView, { (thumbnail) in 129 | 130 | item.thumbnail = thumbnail 131 | 132 | self.hanlderPhotoImageView(image: thumbnail, isThumbnail: true) 133 | 134 | 135 | //设置大图 136 | if let bigImageClosure = item.bigImageClosure{ 137 | 138 | self.indicatorView.startAnimating() 139 | 140 | bigImageClosure(item.index, self.photoImageView, { (bigImage) in 141 | self.hanlderPhotoImageView(image: bigImage, isThumbnail: false) 142 | }) 143 | } 144 | 145 | //设置成缩略图 146 | if let tb = thumbnail{ 147 | self.photoImageView.image = tb 148 | } 149 | }) 150 | } 151 | } 152 | } 153 | 154 | 155 | 156 | //处理图片 157 | private func hanlderPhotoImageView(image:UIImage?,isThumbnail:Bool) { 158 | 159 | if isThumbnail{ 160 | 161 | var imageW = bgScrollView.bounds.width 162 | var imageH = imageW 163 | 164 | if let thumbnail = image{ 165 | 166 | self.indicatorView.stopAnimating() 167 | 168 | self.photoImageView.image = thumbnail 169 | 170 | let imageSize = thumbnail.size 171 | 172 | imageW = bgScrollView.bounds.width 173 | imageH = imageW / imageSize.width * imageSize.height 174 | } 175 | 176 | //图片高度比bgScrollView高 177 | if imageH > bgScrollView.bounds.height{ 178 | 179 | bgScrollView.contentSize = CGSize(width: imageW, height: imageH) 180 | bgScrollView.contentOffset = CGPoint(x: 0, y: (imageH - bgScrollView.bounds.height)/2) 181 | //设置x 182 | self.photoImageView.frame = CGRect(x:(bgScrollView.bounds.width - imageW)/2, y: 0, width: imageW, height: imageH) 183 | }else{ 184 | bgScrollView.contentSize = bgScrollView.bounds.size 185 | bgScrollView.contentOffset = CGPoint.zero 186 | //设置x 187 | self.photoImageView.frame = CGRect(x: (bgScrollView.bounds.width - imageW)/2, y: (bgScrollView.bounds.height - imageH)/2, width: imageW, height: imageH) 188 | } 189 | 190 | 191 | }else{ 192 | 193 | var imageW = bgScrollView.bounds.width 194 | var imageH = imageW 195 | 196 | 197 | if let bigImage = image{ 198 | 199 | self.photoImageView.image = bigImage 200 | 201 | self.indicatorView.stopAnimating() 202 | 203 | let imageSize = bigImage.size 204 | 205 | imageW = bgScrollView.bounds.width 206 | imageH = imageW / imageSize.width * imageSize.height 207 | } 208 | 209 | //图片高度比bgScrollView高 210 | if imageH > bgScrollView.bounds.height{ 211 | bgScrollView.contentSize = CGSize(width: imageW, height: imageH) 212 | bgScrollView.contentOffset = CGPoint(x: 0, y: (imageH - bgScrollView.bounds.height)/2) 213 | //设置x 214 | self.photoImageView.frame = CGRect(x:(bgScrollView.bounds.width - imageW)/2, y: 0, width: imageW, height: imageH) 215 | }else{ 216 | bgScrollView.contentSize = bgScrollView.bounds.size 217 | bgScrollView.contentOffset = CGPoint.zero 218 | //设置x 219 | self.photoImageView.frame = CGRect(x: (bgScrollView.bounds.width - imageW)/2, y: (bgScrollView.bounds.height - imageH)/2, width: imageW, height: imageH) 220 | } 221 | } 222 | self.bgScrollView.setZoomScale(bgScrollView.minimumZoomScale, animated: false) 223 | self.currentScale = bgScrollView.minimumZoomScale 224 | } 225 | 226 | 227 | //初始化 228 | override init(frame: CGRect) { 229 | super.init(frame: frame) 230 | 231 | //底部的scrollView 232 | self.contentView.addSubview(bgScrollView) 233 | self.bgScrollView.backgroundColor = UIColor.clear 234 | self.bgScrollView.addSubview(photoImageView) 235 | self.bgScrollView.maximumZoomScale = 3 236 | self.bgScrollView.minimumZoomScale = 1 237 | self.bgScrollView.delegate = self 238 | 239 | self.currentScale = 1 240 | 241 | 242 | //加载中 243 | indicatorView.hidesWhenStopped = true 244 | indicatorView.tintColor = UIColor.orange 245 | self.contentView.addSubview(indicatorView) 246 | 247 | 248 | self.photoImageView.isUserInteractionEnabled = true 249 | self.photoImageView.contentMode = UIViewContentMode.scaleAspectFit 250 | self.photoImageView.clipsToBounds = true 251 | self.doubleTapReg = UITapGestureRecognizer(target: self, action: #selector(JWPhotoBrowserCell.handlerDoubleTapReg(reg:))) 252 | self.doubleTapReg.numberOfTapsRequired = 2 253 | self.doubleTapReg.numberOfTouchesRequired = 1 254 | self.photoImageView.addGestureRecognizer(self.doubleTapReg) 255 | 256 | 257 | 258 | self.contentView.layer.borderWidth = 5 259 | self.contentView.layer.borderColor = UIColor.clear.cgColor 260 | 261 | let singleTap:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(JWPhotoBrowserCell.handlerTap(tapReg:))) 262 | self.contentView.addGestureRecognizer(singleTap) 263 | 264 | singleTap.require(toFail: self.doubleTapReg) 265 | } 266 | 267 | 268 | var dismissClosure:((JWPhotoBrowerItem,UIView)->Void)? 269 | func handlerTap(tapReg:UITapGestureRecognizer) { 270 | if let closure = dismissClosure{ 271 | if let item = self.photoBrowserItem{ 272 | closure(item,self.photoImageView) 273 | } 274 | } 275 | } 276 | 277 | override func layoutSubviews() { 278 | super.layoutSubviews() 279 | 280 | self.bgScrollView.frame = self.bounds 281 | 282 | self.indicatorView.frame = CGRect(x: 0, y: 0, width: 80, height: 80) 283 | self.indicatorView.center = self.bgScrollView.center 284 | 285 | handler() 286 | } 287 | 288 | 289 | required init?(coder aDecoder: NSCoder) { 290 | fatalError("init(coder:) has not been implemented") 291 | } 292 | 293 | } 294 | -------------------------------------------------------------------------------- /JavenKit/JWProgressHUD/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JWProgressHUD/.DS_Store -------------------------------------------------------------------------------- /JavenKit/JWProgressHUD/JWProgressHUD.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWProgressHUD.swift 3 | // CarServer 4 | // 5 | // Created by 朱建伟 on 2016/11/13. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public enum JWProgressHUDType:Int{ 12 | case loading//加载中 13 | case message//提示 14 | case success//成功 15 | case error//失败 16 | case dismiss//隐藏 17 | } 18 | 19 | public 20 | class JWProgressHUD: UIView,CAAnimationDelegate { 21 | 22 | private let duration:TimeInterval = 0.25 23 | 24 | private let selfWidth :CGFloat = UIScreen.main.bounds.width 25 | private let selfHeight:CGFloat = UIScreen.main.bounds.height 26 | 27 | 28 | private var currentProgressType:JWProgressHUDType = .dismiss 29 | private var nextProgressExcuteType:JWProgressHUDType? 30 | 31 | private var isDismissDelay:Bool = false 32 | 33 | //背景层View 34 | private let bgCoverView:UIButton = UIButton() 35 | 36 | //弹出模块 37 | private let containerView:UIView = UIView() 38 | 39 | //提示文字 40 | private let promptLabel:UILabel = UILabel() 41 | 42 | //加载中的View 43 | private let loadingView:JWProgressHUDLoadingView = JWProgressHUDLoadingView() 44 | 45 | 46 | 47 | public override init(frame: CGRect) { 48 | super.init(frame: frame) 49 | 50 | //背景 51 | let rgb:CGFloat = 60.0/255.0 52 | bgCoverView.backgroundColor = UIColor(red: rgb, green: rgb, blue: rgb, alpha: 0.5) 53 | 54 | bgCoverView.addTarget(self, action: #selector(JWProgressHUD.bgBtnClick(bgBtn:)), for: UIControlEvents.touchUpInside) 55 | addSubview(bgCoverView) 56 | 57 | //弹出模块 58 | containerView.backgroundColor = UIColor.white 59 | containerView.layer.cornerRadius = 5.0 60 | containerView.layer.masksToBounds = true 61 | // containerView.layer.borderColor = UIColor.orange.cgColor 62 | // containerView.layer.borderWidth = 0.5 63 | addSubview(containerView) 64 | 65 | //提示文字 66 | promptLabel.font = UIFont.systemFont(ofSize: 16) 67 | promptLabel.numberOfLines = 0 68 | promptLabel.textColor = UIColor.darkGray 69 | promptLabel.textAlignment = NSTextAlignment.center 70 | containerView.addSubview(promptLabel) 71 | 72 | //加载View 73 | containerView.addSubview(loadingView) 74 | 75 | // self.isHidden = true 76 | } 77 | 78 | required public init?(coder aDecoder: NSCoder) { 79 | fatalError("init(coder:) has not been implemented") 80 | } 81 | 82 | //dismiss 83 | internal func bgBtnClick(bgBtn:UIButton) { 84 | if self.currentProgressType == .dismiss{ 85 | self.showMessage(message: "", type: JWProgressHUDType.dismiss,complectionClosure: { }) 86 | }else { 87 | if bgBtn.tag == 1 { 88 | bgBtn.tag = 0 89 | self.showMessage(message: "", type: JWProgressHUDType.dismiss,complectionClosure: {}) 90 | }else{ 91 | bgBtn.tag = 1 92 | } 93 | 94 | } 95 | } 96 | 97 | 98 | private var complectionClosure:(()->Void)? 99 | 100 | //展示弹出层 101 | public func showMessage(message:String?,type:JWProgressHUDType){ 102 | 103 | //展示弹出层 104 | self.showMessage(message:message,type:type,complectionClosure: {}) 105 | } 106 | 107 | 108 | //展示弹出层 109 | public func showMessage(message:String?,type:JWProgressHUDType,complectionClosure:@escaping (()->Void)){ 110 | 111 | if self.currentProgressType == type{ 112 | return 113 | } 114 | 115 | if let closure = self.complectionClosure{ 116 | closure() 117 | } 118 | 119 | self.complectionClosure = complectionClosure 120 | 121 | let lastWinidow = UIApplication.shared.keyWindow 122 | lastWinidow?.addSubview(self) 123 | 124 | //几个状态 125 | var tempLoadingViewHidden:Bool = true 126 | 127 | var tempPromptFrame:CGRect = CGRect() 128 | 129 | var tempPromptHidden:Bool = false 130 | 131 | var tempContainerViewFrame:CGRect = CGRect() 132 | //结状状态 133 | 134 | 135 | let loadingViewW:CGFloat = 80 136 | let loadingViewH:CGFloat = 80 137 | 138 | 139 | let edge:UIEdgeInsets = UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15) 140 | 141 | //loadingView 142 | loadingView.frame = CGRect(x:edge.left, y: edge.top, width:loadingViewW, height: loadingViewH) 143 | 144 | 145 | //提示文字的最大高度 146 | var promptLabelMaxW:CGFloat = 0 147 | var promptLabelY:CGFloat = 0 148 | 149 | 150 | if type == .loading { 151 | tempLoadingViewHidden = false 152 | promptLabelMaxW = loadingViewW 153 | promptLabelY = loadingView.frame.maxY + 10 154 | }else if(type == .message){ 155 | promptLabelMaxW = selfWidth * 0.8 - edge.left - edge.right 156 | promptLabelY = edge.top 157 | }else{ 158 | tempLoadingViewHidden = false 159 | promptLabelMaxW = loadingViewW + edge.left + edge.right 160 | promptLabelY = loadingView.frame.maxY 161 | } 162 | 163 | 164 | tempPromptHidden = true 165 | 166 | //有值则布局 167 | if let msg = message{ 168 | //不为“”则展示 169 | if msg.compare("") != ComparisonResult.orderedSame{ 170 | //高度 171 | let promptSize:CGSize = NSString(string: msg).boundingRect(with: CGSize(width:promptLabelMaxW,height:selfHeight), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName:promptLabel.font], context: nil).size 172 | 173 | var promptLabelH:CGFloat = promptSize.height + 2 174 | 175 | //当行高度 176 | if promptLabelH < 20 { 177 | promptLabelH = 20 178 | } 179 | 180 | var promptLabelW:CGFloat = promptSize.width 181 | 182 | if promptLabelW < loadingViewW 183 | { 184 | promptLabelW = loadingViewW 185 | 186 | } 187 | 188 | //提示文字 189 | tempPromptFrame = CGRect(x:edge.left, y: promptLabelY, width: promptLabelW, height: promptLabelH) 190 | 191 | tempPromptHidden = false 192 | } 193 | } 194 | 195 | 196 | //计算container宽度 197 | let containerW:CGFloat = tempPromptHidden == false ? (tempPromptFrame.width + edge.left + edge.right):(loadingView.bounds.width + edge.left + edge.right) 198 | //计算container高度 199 | let containerH:CGFloat = ( tempPromptHidden ? loadingView.frame.maxY : tempPromptFrame.maxY ) + edge.bottom 200 | 201 | tempContainerViewFrame = CGRect(x: (selfWidth - containerW)*0.5, y: ((selfHeight) - containerH)*0.5, width: containerW, height: containerH) 202 | 203 | 204 | 205 | let animation:CAKeyframeAnimation = CAKeyframeAnimation(keyPath: "transform.scale") 206 | animation.delegate = self 207 | animation.duration = self.duration 208 | animation.fillMode = kCAFillModeForwards 209 | animation.isRemovedOnCompletion = false 210 | animation.autoreverses = false 211 | 212 | let opacityAnim:CABasicAnimation = CABasicAnimation(keyPath: "opacity") 213 | opacityAnim.fillMode = kCAFillModeForwards 214 | opacityAnim.isRemovedOnCompletion = false 215 | opacityAnim.autoreverses = false 216 | 217 | 218 | //清空 219 | self.nextProgressExcuteType = nil 220 | self.isDismissDelay = false 221 | 222 | 223 | //如果是dismiss 224 | if type == .dismiss && self.currentProgressType != .dismiss{ 225 | 226 | if self.currentProgressType == .loading{ 227 | //缓解dimss的问题 228 | self.isDismissDelay = true 229 | 230 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3, execute: {//loading 延时消失 231 | 232 | if self.isDismissDelay{ 233 | self.containerView.layer.removeAllAnimations() 234 | //执行弹出 235 | animation.values = [1.0,1.2,1.0,0.5] 236 | self.containerView.layer.add(animation, forKey: String(format:"%zd",type.rawValue)) 237 | 238 | //隐藏 239 | opacityAnim.toValue = 0 240 | self.bgCoverView.layer.add(opacityAnim, forKey: "opacity") 241 | 242 | self.currentProgressType = .dismiss 243 | } 244 | }) 245 | return; 246 | } 247 | 248 | self.containerView.layer.removeAllAnimations() 249 | 250 | //执行弹出 251 | animation.values = [1.0,1.2,1.0,0.5] 252 | self.containerView.layer.add(animation, forKey: String(format:"%zd",type.rawValue)) 253 | 254 | //隐藏 255 | opacityAnim.toValue = 0 256 | self.bgCoverView.layer.add(opacityAnim, forKey: "opacity") 257 | 258 | 259 | self.currentProgressType = .dismiss 260 | return 261 | 262 | } 263 | 264 | //根据当前的类型选择动画 265 | switch self.currentProgressType { 266 | case .loading: 267 | //如果将要弹出状态不是加载状态 268 | if type != .loading{ 269 | //如果是dismiss则隐藏 270 | if type == .dismiss{ 271 | //缓解dimss的问题 272 | self.isDismissDelay = true 273 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3, execute: {//loading延时消失 274 | if self.isDismissDelay{ 275 | self.showMessage(message: "消失", type: JWProgressHUDType.dismiss) 276 | } 277 | }) 278 | return 279 | }else{ 280 | 281 | //延时消失 282 | self.isDismissDelay = true 283 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3, execute: {//loading延时消失 284 | //如果当前动画还是dismiss 则执行 285 | if self.isDismissDelay { 286 | //通知 loadingView 停止loading动画 并切换状态 287 | self.loadingView.loadingType = type 288 | 289 | self.containerView.layer.removeAllAnimations() 290 | self.promptLabel.text = !tempPromptHidden ? message ?? "" : "" 291 | 292 | self.isHidden = false 293 | UIView.animate(withDuration: self.duration, animations: { 294 | self.loadingView.isHidden = tempLoadingViewHidden 295 | self.promptLabel.frame = tempPromptFrame 296 | self.promptLabel.isHidden = tempPromptHidden 297 | self.containerView.frame = tempContainerViewFrame 298 | }, completion: { (finished) in 299 | 300 | //如果直接结束的时候出现新的动画 则不能开启dimiss 301 | if let nextExcuteType = self.nextProgressExcuteType 302 | { 303 | self.showMessage(message: message, type: nextExcuteType) 304 | return 305 | }else{ 306 | var delay:TimeInterval = 1.2; 307 | if let msg = message{ 308 | delay = TimeInterval((msg.characters.count)) * 0.04 309 | } 310 | //延时消失 311 | self.isDismissDelay = true 312 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + self.duration + delay, execute: {//message延时消失 313 | //如果当前动画还是dismiss 则执行 314 | if self.isDismissDelay { 315 | self.showMessage(message: "消失", type: JWProgressHUDType.dismiss) 316 | } 317 | }) 318 | } 319 | }) 320 | } 321 | }) 322 | } 323 | break 324 | }else{ 325 | self.isDismissDelay = false 326 | fallthrough 327 | } 328 | case .success: 329 | fallthrough 330 | case .error: 331 | fallthrough 332 | case .message: 333 | //如果将要弹出状态不是加载状态 334 | if type == .loading || type == .success || type == .error{ 335 | 336 | promptLabel.text = !tempPromptHidden ? message ?? "" : "" 337 | self.loadingView.loadingType = type 338 | 339 | 340 | //开启延时隐藏 341 | if type != .loading{ 342 | 343 | //延时消失 344 | self.isDismissDelay = true 345 | var delay:TimeInterval = 1.2; 346 | if let msg = message{ 347 | delay = TimeInterval((msg.characters.count)) * 0.04 348 | } 349 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + self.duration + delay, execute: {//message延时消失 350 | 351 | //如果当前动画还是dismiss 则执行 352 | if self.isDismissDelay { 353 | 354 | self.isHidden = false 355 | self.containerView.layer.removeAllAnimations() 356 | 357 | UIView.animate(withDuration: self.duration, animations: { 358 | self.loadingView.isHidden = tempLoadingViewHidden 359 | self.promptLabel.frame = tempPromptFrame 360 | self.promptLabel.isHidden = tempPromptHidden 361 | self.containerView.frame = tempContainerViewFrame 362 | }, completion: { (finished) in 363 | //若果直接结束的时候出现新的动画 则不能开启dimiss 364 | if let nextExcuteType = self.nextProgressExcuteType 365 | { 366 | self.showMessage(message: message, type: nextExcuteType) 367 | return 368 | }else{ 369 | //通知 loadingView 开启loading动画 并切换状态 370 | self.loadingView.loadingType = type 371 | } 372 | }) 373 | } 374 | }) 375 | return 376 | } 377 | 378 | self.isDismissDelay = false 379 | self.containerView.layer.removeAllAnimations() 380 | 381 | //弹出错误或者成功 382 | self.isHidden = false 383 | UIView.animate(withDuration: duration, animations: { 384 | self.loadingView.isHidden = tempLoadingViewHidden 385 | self.promptLabel.frame = tempPromptFrame 386 | self.promptLabel.isHidden = tempPromptHidden 387 | self.containerView.frame = tempContainerViewFrame 388 | }, completion: { (finished) in 389 | //若果直接结束的时候出现新的动画 则不能开启dimiss 390 | if let nextExcuteType = self.nextProgressExcuteType 391 | { 392 | self.showMessage(message: message, type: nextExcuteType) 393 | return 394 | }else{ 395 | //通知 loadingView 开启loading动画 并切换状态 396 | self.loadingView.loadingType = type 397 | } 398 | 399 | //开启延时隐藏 400 | self.isDismissDelay = true 401 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + self.duration + 1, execute: {//成功失败延时消失 402 | //如果当前动画还是dismiss 则执行 403 | if self.isDismissDelay { 404 | self.showMessage(message: "消失", type: JWProgressHUDType.dismiss) 405 | } 406 | }) 407 | }) 408 | }else if type == .dismiss{ 409 | //如果是dismiss 不用操作,自动隐藏 410 | 411 | //移除所有的动画 412 | self.containerView.layer.removeAllAnimations() 413 | 414 | 415 | promptLabel.text = !tempPromptHidden ? message ?? "" : "" 416 | self.loadingView.loadingType = type 417 | 418 | //执行弹出 419 | animation.values = [1.0,1.2,1.0,0.5] 420 | containerView.layer.add(animation, forKey: String(format:"%zd",type.rawValue)) 421 | 422 | //隐藏 423 | // opacityAnim.fromValue = 1 424 | opacityAnim.toValue = 0 425 | self.bgCoverView.layer.add(opacityAnim, forKey: "opacity") 426 | 427 | return 428 | }else{ //message 429 | //设置好控件的状态 430 | self.loadingView.isHidden = tempLoadingViewHidden 431 | self.promptLabel.frame = tempPromptFrame 432 | self.promptLabel.isHidden = tempPromptHidden 433 | self.containerView.frame = tempContainerViewFrame 434 | 435 | promptLabel.text = !tempPromptHidden ? message ?? "" : "" 436 | self.loadingView.loadingType = type 437 | 438 | self.containerView.layer.removeAllAnimations() 439 | 440 | //执行弹出 441 | animation.values = [0.5,1.2,0.8,1.0] 442 | containerView.layer.add(animation, forKey: String(format:"%zd",type.rawValue)) 443 | 444 | //隐藏 445 | // opacityAnim.fromValue = 0 446 | opacityAnim.toValue = 1 447 | self.bgCoverView.layer.add(opacityAnim, forKey: "opacity") 448 | 449 | 450 | 451 | self.isDismissDelay = true 452 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + duration + 1, execute: {//成功失败延时消失 453 | //如果当前动画还是dismiss 则执行 454 | if self.isDismissDelay { 455 | self.showMessage(message: "消失", type: JWProgressHUDType.dismiss) 456 | } 457 | }) 458 | 459 | } 460 | break 461 | case .dismiss://dismiss状态下,直接弹出提示 462 | if type != .dismiss{ 463 | //设置好控件的状态 464 | self.loadingView.isHidden = tempLoadingViewHidden 465 | self.promptLabel.frame = tempPromptFrame 466 | self.promptLabel.isHidden = tempPromptHidden 467 | self.containerView.frame = tempContainerViewFrame 468 | 469 | promptLabel.text = !tempPromptHidden ? message ?? "" : "" 470 | self.loadingView.loadingType = type 471 | 472 | self.containerView.layer.removeAllAnimations() 473 | 474 | //动画切换 475 | animation.values = [0.5,1.2,0.8,1.0] 476 | containerView.layer.add(animation, forKey: String(format:"%zd",type.rawValue)) 477 | 478 | //隐藏 479 | // opacityAnim.fromValue = 0 480 | opacityAnim.toValue = 1 481 | self.bgCoverView.layer.add(opacityAnim, forKey: "opacity") 482 | 483 | //如果不等于loading 延时消失 484 | if type != .loading{ 485 | //延时消失 486 | self.isDismissDelay = true 487 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + duration + 1, execute: {//成功失败延时消失 488 | //如果当前动画还是dismiss 则执行 489 | if self.isDismissDelay { 490 | self.showMessage(message: "消失", type: JWProgressHUDType.dismiss) 491 | return 492 | } 493 | }) 494 | }else{ 495 | self.isDismissDelay = false 496 | } 497 | } 498 | break 499 | } 500 | 501 | self.currentProgressType = type 502 | } 503 | 504 | 505 | public func animationDidStart(_ anim: CAAnimation) { 506 | self.isHidden = false 507 | } 508 | 509 | 510 | public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { 511 | if self.currentProgressType == .dismiss { 512 | self.isHidden = true 513 | if let closure = self.complectionClosure{ 514 | closure() 515 | } 516 | }else{ 517 | self.isHidden = false 518 | } 519 | } 520 | 521 | //布局 522 | override public func layoutSubviews() { 523 | super.layoutSubviews() 524 | 525 | let x:CGFloat = 0 526 | let y:CGFloat = 0 527 | let w:CGFloat = selfWidth 528 | let h:CGFloat = selfHeight 529 | 530 | let rect:CGRect = CGRect(x: x, y: y, width: w, height: h) 531 | self.frame = rect//UIScreen.main.bounds 532 | 533 | bgCoverView.frame = rect// self.bounds 534 | 535 | } 536 | 537 | } 538 | 539 | public let JavenHUD:JWProgressHUD = JWProgressHUD() 540 | -------------------------------------------------------------------------------- /JavenKit/JWProgressHUD/JWProgressHUDLoadingView.swift: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // LoadingView.swift 4 | // CarServer 5 | // 6 | // Created by 朱建伟 on 2016/11/13. 7 | // Copyright © 2016年 zhujianwei. All rights reserved. 8 | // 9 | 10 | import UIKit 11 | 12 | public class JWProgressHUDLoadingView: UIView { 13 | 14 | //当前类型 15 | var loadingType:JWProgressHUDType?{ 16 | willSet{ 17 | if let type = newValue{ 18 | if type == .loading{ 19 | self.imageView.startAnimating() 20 | }else if type == .success{ 21 | self.imageView.stopAnimating() 22 | imageView.image = Bundle.image(named:"success")! 23 | }else if type == .error{ 24 | self.imageView.stopAnimating() 25 | imageView.image = Bundle.image(named:"error")! 26 | }else{ 27 | self.imageView.stopAnimating() 28 | } 29 | }else{ 30 | 31 | self.imageView.stopAnimating() 32 | } 33 | } 34 | } 35 | 36 | //imageView 37 | private var imageView:UIImageView = UIImageView() 38 | 39 | 40 | 41 | override init(frame: CGRect) { 42 | super.init(frame: frame) 43 | 44 | addSubview(imageView) 45 | 46 | imageView.animationImages = [Bundle.image(named:"loading1")!,Bundle.image(named:"loading2")!,Bundle.image(named:"loading3")!] 47 | imageView.animationDuration = 0.3 48 | imageView.animationRepeatCount = 10000 49 | 50 | } 51 | 52 | required public init?(coder aDecoder: NSCoder) { 53 | fatalError("init(coder:) has not been implemented") 54 | } 55 | 56 | override public func layoutSubviews() { 57 | super.layoutSubviews() 58 | 59 | let imageWH:CGFloat = self.bounds.width > self.bounds.height ? self.bounds.height : self.bounds.height 60 | 61 | let imageX:CGFloat = (self.bounds.width - imageWH)/2 62 | let imageY:CGFloat = (self.bounds.height - imageWH)/2 63 | imageView.frame = CGRect(x: imageX, y: imageY, width: imageWH, height: imageWH) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /JavenKit/JWWebBrowser/JWWebViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWWebViewController.swift 3 | // CarServer 4 | // 5 | // Created by 朱建伟 on 2016/10/12. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | import WebKit 9 | import UIKit 10 | 11 | 12 | public class JWWebViewController: UIViewController,WKNavigationDelegate,WKUIDelegate,WKScriptMessageHandler { 13 | 14 | 15 | public var urlString:String? 16 | 17 | public var filePathString:String? 18 | 19 | 20 | 21 | 22 | //加载 23 | public func reload() { 24 | if let urlStr = urlString{ 25 | let handlerData = handlerWebViewRequest(urlString: urlStr) 26 | if handlerData.0{ 27 | if let request = handlerData.1 { 28 | self.webView.load(request) 29 | } 30 | } 31 | return 32 | } 33 | 34 | if let filePathStr = filePathString { 35 | let handlerData = handlerWebViewRequest(urlString: filePathStr) 36 | if handlerData.0{ 37 | if let request = handlerData.1 { 38 | self.webView.load(request) 39 | } 40 | } 41 | } 42 | } 43 | 44 | 45 | //覆盖父类的重新加载 46 | public func refresh(item: UIBarButtonItem) { 47 | //iOS 7 48 | URLCache.shared.removeAllCachedResponses() 49 | 50 | if #available(iOS 9.0, *) { 51 | let types = WKWebsiteDataStore.allWebsiteDataTypes() 52 | let dateFrom = Date(timeIntervalSince1970: 0.0) 53 | WKWebsiteDataStore.default().removeData(ofTypes: types, modifiedSince: dateFrom, completionHandler: { 54 | self.reload() 55 | }) 56 | } else if #available(iOS 8.0, *) { 57 | var Paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.libraryDirectory, FileManager.SearchPathDomainMask.userDomainMask, true) 58 | 59 | let libraryPath:String = Paths[0] 60 | 61 | let bundleId:String = Bundle.main.object(forInfoDictionaryKey: "CFBundleIdentifier") as! String 62 | 63 | let webkitFolderInLib = String(format:"%@/WebKit",libraryPath) 64 | 65 | let webKitFolderInCaches = String(format:"%@/Caches/%@/WebKit",libraryPath,bundleId) 66 | 67 | let webKitFolderInCachesfs = String(format:"%@/Caches/%@/fsCachedData",libraryPath,bundleId) 68 | 69 | do{ 70 | try FileManager.default.removeItem(atPath: webKitFolderInCaches) 71 | try FileManager.default.removeItem(atPath: webkitFolderInLib) 72 | try FileManager.default.removeItem(atPath: webKitFolderInCachesfs) 73 | }catch{ 74 | 75 | } 76 | 77 | self.webView.reload() 78 | } 79 | 80 | } 81 | 82 | 83 | //加工一下请求 84 | private func handlerWebViewRequest(urlString:String) -> (Bool,URLRequest?) { 85 | 86 | if let url = URL(string: urlString){ 87 | let request:URLRequest = URLRequest(url:url) 88 | return (true,request) 89 | } 90 | 91 | return (false,nil) 92 | 93 | } 94 | 95 | 96 | 97 | 98 | 99 | public var navigationActionClosure:((String)->Bool)? 100 | 101 | 102 | private let estimatedProgressKey = "estimatedProgress" 103 | private let loadingKey = "loading" 104 | private let titleKey = "title" 105 | 106 | //进度 107 | lazy var progressView:JWWebViewProgressView = { 108 | let progressView:JWWebViewProgressView = JWWebViewProgressView(frame: CGRect(x: 0, y: 64, width: UIScreen.main.bounds.width, height: 3)) 109 | progressView.isHidden = true 110 | return progressView 111 | }() 112 | 113 | 114 | 115 | public lazy var webView:WKWebView = { 116 | 117 | //配置 118 | let config:WKWebViewConfiguration = WKWebViewConfiguration() 119 | 120 | //偏好设置 121 | let preference = WKPreferences() 122 | preference.javaScriptEnabled = true//启用javascript 123 | preference.javaScriptCanOpenWindowsAutomatically = false//javascript 是否可以打开新窗口 124 | preference.minimumFontSize = 12 //最小文字大小 125 | config.preferences = preference 126 | 127 | //内容处理池 128 | let processPool = WKProcessPool() 129 | config.processPool = processPool 130 | 131 | //js与webView交互 132 | let userContent : WKUserContentController = WKUserContentController() 133 | config.userContentController = userContent 134 | 135 | 136 | let webView:WKWebView = WKWebView(frame:CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height), configuration: config) 137 | webView.allowsBackForwardNavigationGestures = true 138 | 139 | return webView 140 | }() 141 | 142 | 143 | 144 | 145 | private var addObserverFlag:Bool = false 146 | 147 | override public func viewDidLoad() { 148 | super.viewDidLoad() 149 | 150 | 151 | view.addSubview(webView) 152 | 153 | let refreshItem:UIBarButtonItem = UIBarButtonItem(title: "刷新", style: UIBarButtonItemStyle.done, target: self, action: #selector(JWWebViewController.refresh(item:))) 154 | let closeItem:UIBarButtonItem = UIBarButtonItem(title: "关闭", style: UIBarButtonItemStyle.done, target: self, action: #selector(JWWebViewController.close(item:))) 155 | 156 | navigationItem.rightBarButtonItems = [closeItem,refreshItem] 157 | 158 | 159 | webView.uiDelegate = self 160 | webView.navigationDelegate = self 161 | 162 | 163 | addObserverFlag = true 164 | 165 | //通过监听estimatedProgress可以获取它的加载进度 还可以监听它的title ,URL, loading 166 | webView.addObserver(self, forKeyPath: estimatedProgressKey, options: NSKeyValueObservingOptions.new, context: nil) 167 | 168 | //加载 169 | webView.addObserver(self, forKeyPath: loadingKey, options: NSKeyValueObservingOptions.new, context: nil) 170 | 171 | //标题 172 | webView.addObserver(self, forKeyPath: titleKey, options: NSKeyValueObservingOptions.new, context: nil) 173 | 174 | 175 | webView.configuration.userContentController.add(self, name: "defaultHandler") 176 | 177 | 178 | //刷新 179 | self.reload() 180 | } 181 | 182 | func close(item:UIBarButtonItem) { 183 | if let nav = navigationController{ 184 | nav.popViewController(animated: true) 185 | } 186 | } 187 | 188 | 189 | public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { 190 | 191 | } 192 | 193 | override public func viewWillAppear(_ animated: Bool) { 194 | super.viewWillAppear(animated) 195 | 196 | view.addSubview(progressView) 197 | 198 | } 199 | 200 | override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 201 | 202 | //进度 203 | if (keyPath?.compare(estimatedProgressKey) == ComparisonResult.orderedSame) { 204 | progressView.progress = CGFloat(webView.estimatedProgress) 205 | }else if(keyPath?.compare(loadingKey) == ComparisonResult.orderedSame) 206 | { 207 | progressView.isHidden = !webView.isLoading 208 | if webView.isLoading 209 | { 210 | progressView.startAnimation() 211 | }else 212 | { 213 | progressView.progress = 0.0//改回0 214 | } 215 | 216 | }else if(keyPath?.compare(titleKey) == ComparisonResult.orderedSame) 217 | { 218 | navigationItem.title = webView.title 219 | } 220 | } 221 | 222 | 223 | //MARK:UI代理 224 | 225 | //网页中在新窗口弹出的交互功能 target = __bank 226 | public func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? { 227 | 228 | if let frameInfo = navigationAction.targetFrame { 229 | if frameInfo.isMainFrame { 230 | webView .load(navigationAction.request) 231 | } 232 | }else{ 233 | 234 | } 235 | 236 | return nil 237 | } 238 | 239 | //弹出 alert 240 | public func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) { 241 | 242 | 243 | let alertController:JWAlertController = JWAlertController(title: "提示", message: message, preferredStyle: JWAlertControllerStyle.alert) 244 | 245 | let confirmAction:JWAlertAction = JWAlertAction(title: "知道了", style:JWAlertActionStyle.default) { (action) in 246 | //点击确定 247 | completionHandler() 248 | } 249 | 250 | alertController.addAction(action: confirmAction) 251 | 252 | present(alertController, animated: true) { 253 | //完成了 254 | 255 | } 256 | } 257 | 258 | //弹出 取消 确定 面板 259 | public func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) { 260 | 261 | 262 | 263 | let alertController:JWAlertController = JWAlertController(title: "提示", message: message, preferredStyle: JWAlertControllerStyle.alert) 264 | 265 | let confirmAction:JWAlertAction = JWAlertAction(title: "确定", style:JWAlertActionStyle.default) { (action) in 266 | //点击确定 267 | completionHandler(true) 268 | } 269 | 270 | let cancelAction:JWAlertAction = JWAlertAction(title: "取消", style:JWAlertActionStyle.default) { (action) in 271 | //点击确定 272 | completionHandler(false) 273 | } 274 | 275 | alertController.addAction(action:confirmAction) 276 | alertController.addAction(action:cancelAction) 277 | 278 | present(alertController, animated: true) { 279 | //完成了 280 | } 281 | } 282 | 283 | //js 弹出输入框 284 | public func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) { 285 | 286 | 287 | let alertController:UIAlertController = UIAlertController(title: "", message:prompt , preferredStyle: UIAlertControllerStyle.alert) 288 | 289 | 290 | //弹出输入框 291 | alertController.addTextField { (textField) in 292 | textField.placeholder = prompt 293 | } 294 | 295 | let confirmAction:UIAlertAction = UIAlertAction(title: "确定", style:UIAlertActionStyle.default) { (action) in 296 | //点击确定 297 | completionHandler(alertController.textFields?.last?.text) 298 | } 299 | 300 | 301 | let cancelAction:UIAlertAction = UIAlertAction(title: "取消", style:UIAlertActionStyle.default) { (action) in 302 | //点击确定 303 | completionHandler(defaultText) 304 | } 305 | 306 | alertController.addAction(confirmAction) 307 | alertController.addAction(cancelAction) 308 | 309 | present(alertController, animated: true) { 310 | //完成了 311 | } 312 | } 313 | 314 | 315 | //MARK:navigation代理 316 | 317 | 318 | // 内容处理中断 319 | public func webViewWebContentProcessDidTerminate(_ webView: WKWebView) { 320 | 321 | } 322 | 323 | 324 | //请求准备发起 决定是否发起 325 | public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { 326 | //print("decidePolicyFor navigationAction") 327 | 328 | 329 | if let closure = self.navigationActionClosure{ 330 | if let url = navigationAction.request.url{ 331 | if closure(url.absoluteString){ 332 | decisionHandler(WKNavigationActionPolicy.allow) 333 | }else{ 334 | decisionHandler(WKNavigationActionPolicy.cancel) 335 | } 336 | } 337 | }else{ 338 | decisionHandler(WKNavigationActionPolicy.allow) 339 | } 340 | } 341 | 342 | 343 | //请求发起收到响应 决定是否跳转 344 | public func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) { 345 | //print("decidePolicyFor") 346 | decisionHandler(WKNavigationResponsePolicy.allow) 347 | } 348 | 349 | 350 | 351 | //导航失败 352 | public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { 353 | //刷新状态 354 | //print("导航失败") 355 | } 356 | 357 | //请求完成 358 | public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { 359 | navigationItem.title = webView.title 360 | //刷新状态 361 | //print("didFinish") 362 | } 363 | 364 | //开始返回内容 365 | public func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { 366 | //print("didCommit") 367 | } 368 | 369 | //页面开始加载 370 | public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { 371 | //print("didStartProvisionalNavigation") 372 | } 373 | 374 | //收到重定向 375 | public func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) { 376 | //print("重定向") 377 | } 378 | 379 | 380 | //页面加载失败 381 | public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { 382 | 383 | //print("didFailProvisionalNavigation") 384 | } 385 | 386 | 387 | //https 授权 388 | public func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { 389 | //按默认处理 390 | completionHandler(URLSession.AuthChallengeDisposition.performDefaultHandling, nil) 391 | } 392 | 393 | 394 | 395 | //销毁 396 | deinit { 397 | if addObserverFlag{ 398 | webView.removeObserver(self, forKeyPath: estimatedProgressKey) 399 | webView.removeObserver(self, forKeyPath: loadingKey) 400 | webView.removeObserver(self, forKeyPath: titleKey) 401 | } 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /JavenKit/JWWebBrowser/JWWebViewProgressView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWWebViewProgress.swift 3 | // CarServer 4 | // 5 | // Created by 朱建伟 on 2016/10/12. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class JWWebViewProgressView: UIView,CAAnimationDelegate{ 12 | 13 | let maskLayer:CALayer = CALayer() 14 | 15 | lazy var progressLayer:CAGradientLayer = { 16 | let progressLayer:CAGradientLayer = CAGradientLayer() 17 | progressLayer.startPoint = CGPoint(x: 0.0, y: 0.5) 18 | progressLayer.endPoint = CGPoint(x: 1.0, y: 0.5) 19 | 20 | var colors = [CGColor]() 21 | var locations = [NSNumber]() 22 | 23 | for var i in 0...360 24 | { 25 | if i%5 == 0 { 26 | let hue = CGFloat(i)/360.0 27 | var color:CGColor = UIColor(hue: hue, saturation: 1.0, brightness: 1.0, alpha: 1.0).cgColor 28 | 29 | colors.append(color) 30 | } 31 | } 32 | 33 | progressLayer.colors = colors 34 | return progressLayer 35 | }() 36 | 37 | //进度条 38 | var progress:CGFloat? = 0 39 | { 40 | 41 | willSet{ 42 | if let value = newValue { 43 | let width = bounds.width * value 44 | 45 | maskLayer.frame = CGRect(x: 0, y: 0, width: width, height: bounds.height) 46 | progressLayer.mask = self.maskLayer 47 | } 48 | } 49 | } 50 | 51 | 52 | override init(frame: CGRect) { 53 | super.init(frame: frame) 54 | 55 | 56 | //maskLayer 设置 57 | maskLayer.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height) 58 | maskLayer.backgroundColor = UIColor.white.cgColor 59 | progressLayer.mask = maskLayer 60 | progressLayer.backgroundColor = UIColor.blue.cgColor 61 | layer.addSublayer(progressLayer) 62 | } 63 | 64 | required public init?(coder aDecoder: NSCoder) { 65 | fatalError("init(coder:) has not been implemented") 66 | } 67 | 68 | 69 | //更新进度颜色 70 | func startAnimation() { 71 | if let colors = progressLayer.colors 72 | { 73 | var newColors:[CGColor] = colors as! [CGColor] 74 | let lastColor = newColors.last 75 | newColors.removeLast() 76 | newColors.insert(lastColor!, at: 0) 77 | 78 | progressLayer.colors = newColors 79 | 80 | let animation:CABasicAnimation = CABasicAnimation(keyPath: "colors") 81 | animation.toValue = newColors 82 | animation.duration = 1.0 83 | animation.isRemovedOnCompletion = true 84 | animation.fillMode = kCAFillModeForwards 85 | animation.delegate = self 86 | progressLayer.add(animation, forKey: "animateGradient") 87 | } 88 | } 89 | 90 | 91 | 92 | public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { 93 | //加载中 and 未隐藏 94 | if progress! < CGFloat(1.0) && !isHidden 95 | { 96 | startAnimation() 97 | } 98 | } 99 | 100 | 101 | override public func layoutSubviews() { 102 | super.layoutSubviews() 103 | 104 | progressLayer.frame = bounds 105 | 106 | } 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /JavenKit/JavenKit.bundle/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JavenKit.bundle/.DS_Store -------------------------------------------------------------------------------- /JavenKit/JavenKit.bundle/Bom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JavenKit.bundle/Bom.png -------------------------------------------------------------------------------- /JavenKit/JavenKit.bundle/JWMessage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JavenKit.bundle/JWMessage.png -------------------------------------------------------------------------------- /JavenKit/JavenKit.bundle/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JavenKit.bundle/error.png -------------------------------------------------------------------------------- /JavenKit/JavenKit.bundle/highlightBg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JavenKit.bundle/highlightBg.png -------------------------------------------------------------------------------- /JavenKit/JavenKit.bundle/loading1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JavenKit.bundle/loading1.png -------------------------------------------------------------------------------- /JavenKit/JavenKit.bundle/loading2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JavenKit.bundle/loading2.png -------------------------------------------------------------------------------- /JavenKit/JavenKit.bundle/loading3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JavenKit.bundle/loading3.png -------------------------------------------------------------------------------- /JavenKit/JavenKit.bundle/normalBg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JavenKit.bundle/normalBg.png -------------------------------------------------------------------------------- /JavenKit/JavenKit.bundle/normalBlackBg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JavenKit.bundle/normalBlackBg.png -------------------------------------------------------------------------------- /JavenKit/JavenKit.bundle/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/JavenKit/JavenKit.bundle/success.png -------------------------------------------------------------------------------- /KitDemo.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/KitDemo.zip -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Javen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavenKit 2 | 做的第一个Swift项目,好多控件不想用别人的所以自己就一个一个写,积累了一点点,以后会不断的添加 3 | 4 | # Installation with CocoaPods 5 | ``` 6 | pod 'JavenKit' 7 | ``` 8 | ## 图片浏览(仿Twitter) 9 | ![](https://github.com/GitHubOfJW/JWKit/blob/master/showGif/JWPhotoBrowserViewController.gif) 10 | ## 日期选择控制器(2种样式) 11 | 1. #### 普通样式 12 | ![](https://github.com/GitHubOfJW/JWKit/blob/master/showGif/JWCalendarViewController1.gif) 13 | 14 | 2. #### 带网格样式 15 | ![](https://github.com/GitHubOfJW/JWKit/blob/master/showGif/JWCalendarViewController2.gif) 16 | ## 日期选择键盘(10种模式) 17 | ![](https://github.com/GitHubOfJW/JWKit/blob/master/showGif/JWPickerView.gif) 18 | ## 自动轮播(UIScrollView) 19 | ![](https://github.com/GitHubOfJW/JWKit/blob/master/showGif/JWAutoScrollView.gif) 20 | 21 | ## 弹出提示(JWAlertController) 22 | ![](https://github.com/GitHubOfJW/JWKit/blob/master/showGif/JWAlertController.gif) 23 | ## 加载提示(简洁大方) 24 | ![](https://github.com/GitHubOfJW/JWKit/blob/master/showGif/JWProgressHUD.gif) 25 | ## WebViewController(彩色进度条) 26 | ![](https://github.com/GitHubOfJW/JWKit/blob/master/showGif/JWWebView.gif) 27 | ## MessageView(消息拖拽效果) 28 | ![](https://github.com/GitHubOfJW/JWKit/blob/master/showGif/JWMessageView.gif) 29 | ## 学习交流 30 | #### 觉得还凑活的朋友给个星,一起学习交流 QQ:969271828 31 | 32 | -------------------------------------------------------------------------------- /showGif/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/showGif/.DS_Store -------------------------------------------------------------------------------- /showGif/JWAlertController.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/showGif/JWAlertController.gif -------------------------------------------------------------------------------- /showGif/JWAutoScrollView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/showGif/JWAutoScrollView.gif -------------------------------------------------------------------------------- /showGif/JWCalendarViewController1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/showGif/JWCalendarViewController1.gif -------------------------------------------------------------------------------- /showGif/JWCalendarViewController2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/showGif/JWCalendarViewController2.gif -------------------------------------------------------------------------------- /showGif/JWMessageView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/showGif/JWMessageView.gif -------------------------------------------------------------------------------- /showGif/JWPhotoBrowserViewController.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/showGif/JWPhotoBrowserViewController.gif -------------------------------------------------------------------------------- /showGif/JWPickerView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/showGif/JWPickerView.gif -------------------------------------------------------------------------------- /showGif/JWProgressHUD.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/showGif/JWProgressHUD.gif -------------------------------------------------------------------------------- /showGif/JWWebView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JavenKit/6d8f1860bef3b4ccc939900d410a1e1591400970/showGif/JWWebView.gif --------------------------------------------------------------------------------