From 6140c132f795c18e3eccc12f01254e4ad57034ff Mon Sep 17 00:00:00 2001 From: Pavan Welihinda Date: Fri, 20 Dec 2019 18:55:11 +0530 Subject: [PATCH 1/3] updated to Swift 5 --- MFCard/Card /Helper Class/LBZSpinner.swift | 12 ++++---- MFCard/Card /MFCardView.swift | 36 +++++++++++----------- MFCard/Card /Toast/Toast.swift | 4 +-- MFCardDemo.xcodeproj/project.pbxproj | 18 +++++------ MFCardDemo/AppDelegate.swift | 2 +- MFCardDemo/ViewController.swift | 4 +-- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/MFCard/Card /Helper Class/LBZSpinner.swift b/MFCard/Card /Helper Class/LBZSpinner.swift index 7ab151b..1cc2166 100755 --- a/MFCard/Card /Helper Class/LBZSpinner.swift +++ b/MFCard/Card /Helper Class/LBZSpinner.swift @@ -68,7 +68,7 @@ import UIKit fileprivate func initCustomView() { backgroundColor = UIColor.clear // clear black background - NotificationCenter.default.addObserver(self, selector: #selector(LBZSpinner.orientationChanged), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(LBZSpinner.orientationChanged), name: UIDevice.orientationDidChangeNotification, object: nil) //Open spinner click let gesture = UITapGestureRecognizer(target: self, action: #selector(LBZSpinner.openSpinner(_:))) @@ -150,7 +150,7 @@ import UIKit viewChooseDisable = UIView(frame: parentView.frame) // view back click if(dDLblurEnable) { // with blur effect - let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.dark) + let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.dark) blurEffectView = UIVisualEffectView(effect: blurEffect) blurEffectView.effect = nil blurEffectView.backgroundColor = UIColor.black.withAlphaComponent(0) @@ -192,7 +192,7 @@ import UIKit UIView.animate(withDuration: 0.3, delay: 0.0, - options: UIViewAnimationOptions.transitionFlipFromBottom, + options: UIView.AnimationOptions.transitionFlipFromBottom, animations: { self.tableviewChoose.frame.size.height = self.heightTableview self.tableviewChooseShadow.frame.size.height = self.heightTableview @@ -214,7 +214,7 @@ import UIKit UIView.animate(withDuration: 0.3, delay: 0.0, - options: UIViewAnimationOptions.transitionFlipFromBottom, + options: UIView.AnimationOptions.transitionFlipFromBottom, animations: { self.tableviewChoose.frame.origin.y = globalPoint.y-self.heightTableview+self.frame.height self.tableviewChoose.frame.size.height = self.heightTableview @@ -240,7 +240,7 @@ import UIKit tableviewChoose.isUserInteractionEnabled = true tableviewChoose.showsHorizontalScrollIndicator = false tableviewChoose.showsVerticalScrollIndicator = false - tableviewChoose.separatorStyle = UITableViewCellSeparatorStyle.none + tableviewChoose.separatorStyle = UITableViewCell.SeparatorStyle.none tableviewChoose.layer.cornerRadius = 5 //Show stroke @@ -277,7 +277,7 @@ import UIKit if(tableviewChoose != nil) { UIView.animate(withDuration: 0.3, delay: 0.0, - options: UIViewAnimationOptions.transitionFlipFromBottom, + options: UIView.AnimationOptions.transitionFlipFromBottom, animations: { self.tableviewChoose.alpha = 0.0 self.tableviewChooseShadow.alpha = 0.0 diff --git a/MFCard/Card /MFCardView.swift b/MFCard/Card /MFCardView.swift index bfd1b17..49ccd7e 100644 --- a/MFCard/Card /MFCardView.swift +++ b/MFCard/Card /MFCardView.swift @@ -78,9 +78,9 @@ extension MFCardDelegate{ public var toast = true public var topDistance = 100 - public var blurStyle:UIBlurEffect = UIBlurEffect(style: UIBlurEffectStyle.dark) + public var blurStyle:UIBlurEffect = UIBlurEffect(style: UIBlurEffect.Style.dark) - public var cardTypeAnimation :UIViewAnimationOptions = [.transitionFlipFromBottom, .curveLinear] + public var cardTypeAnimation :UIView.AnimationOptions = [.transitionFlipFromBottom, .curveLinear] //MARK: //MARK: initialization public init(withViewController:UIViewController) { @@ -116,7 +116,7 @@ extension MFCardDelegate{ self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view]|", options: [], metrics: nil, views: ["view": self.view])) self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view]|", options: [], metrics: nil, views: ["view": self.view])) //Orientation Observer - NotificationCenter.default.addObserver(self, selector: #selector(self.orientationDidChange), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.orientationDidChange), name: UIDevice.orientationDidChangeNotification, object: nil) self.layoutIfNeeded() self.updateConstraintsIfNeeded() @@ -153,11 +153,11 @@ extension MFCardDelegate{ txtCardNoP4.delegate = self txtCvc.delegate = self - txtCardNoP1.addTarget(self, action: #selector(MFCardView.textFieldDidChange(_:)), for: UIControlEvents.editingChanged) - txtCardNoP2.addTarget(self, action: #selector(MFCardView.textFieldDidChange(_:)), for: UIControlEvents.editingChanged) - txtCardNoP3.addTarget(self, action: #selector(MFCardView.textFieldDidChange(_:)), for: UIControlEvents.editingChanged) - txtCardNoP4.addTarget(self, action: #selector(MFCardView.textFieldDidChange(_:)), for: UIControlEvents.editingChanged) - txtCvc.addTarget(self, action: #selector(MFCardView.textFieldDidChange(_:)), for: UIControlEvents.editingChanged) + txtCardNoP1.addTarget(self, action: #selector(MFCardView.textFieldDidChange(_:)), for: UIControl.Event.editingChanged) + txtCardNoP2.addTarget(self, action: #selector(MFCardView.textFieldDidChange(_:)), for: UIControl.Event.editingChanged) + txtCardNoP3.addTarget(self, action: #selector(MFCardView.textFieldDidChange(_:)), for: UIControl.Event.editingChanged) + txtCardNoP4.addTarget(self, action: #selector(MFCardView.textFieldDidChange(_:)), for: UIControl.Event.editingChanged) + txtCvc.addTarget(self, action: #selector(MFCardView.textFieldDidChange(_:)), for: UIControl.Event.editingChanged) let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(self.flip)) let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(self.flip)) @@ -193,17 +193,17 @@ extension MFCardDelegate{ rootViewController.view.addSubview(view) // center view horizontally in rootViewController.view - rootViewController.view.addConstraint(NSLayoutConstraint(item: view, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: rootViewController.view, attribute: NSLayoutAttribute.centerX, multiplier: 1.0, constant: 0.0)); + rootViewController.view.addConstraint(NSLayoutConstraint(item: view, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: rootViewController.view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1.0, constant: 0.0)); // align view from the top - topConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:|-\(topDistance)-[view]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view": view]) + topConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:|-\(topDistance)-[view]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: ["view": view]) rootViewController.view.addConstraints(topConstraints!); // width constraint - rootViewController.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[view(==300)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view": view])); + rootViewController.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[view(==300)]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: ["view": view])); // height constraint - rootViewController.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[view(==240)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view": view])); + rootViewController.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[view(==240)]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: ["view": view])); animateCard() // Animate it in @@ -391,14 +391,14 @@ extension MFCardDelegate{ } @objc fileprivate func orientationDidChange(){ - if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) { + if UIDevice.current.orientation.isLandscape { if (topConstraints != nil){ topConstraints?[0].constant = 10 } } - if UIDeviceOrientationIsPortrait(UIDevice.current.orientation) { + if UIDevice.current.orientation.isPortrait { if (topConstraints != nil){ topConstraints?[0].constant = CGFloat(topDistance) } @@ -484,8 +484,8 @@ extension MFCardDelegate{ } if (cardFrontView.isHidden == false) { - let transitionOptions: UIViewAnimationOptions = [.transitionFlipFromRight, .showHideTransitionViews] - btnCvc.setTitle("CARD", for: UIControlState()) + let transitionOptions: UIView.AnimationOptions = [.transitionFlipFromRight, .showHideTransitionViews] + btnCvc.setTitle("CARD", for: UIControl.State()) UIView.transition(with: cardFrontView, duration: 1.0, options: transitionOptions, animations: { self.cardFrontView.isHidden = true }, completion: nil) @@ -496,8 +496,8 @@ extension MFCardDelegate{ }, completion: nil) } else { - let transitionOptions: UIViewAnimationOptions = [.transitionFlipFromLeft, .showHideTransitionViews] - btnCvc.setTitle("CVC", for: UIControlState()) + let transitionOptions: UIView.AnimationOptions = [.transitionFlipFromLeft, .showHideTransitionViews] + btnCvc.setTitle("CVC", for: UIControl.State()) UIView.transition(with: cardBackView, duration: 1.0, options: transitionOptions, animations: { self.cardBackView.isHidden = true }, completion: nil) diff --git a/MFCard/Card /Toast/Toast.swift b/MFCard/Card /Toast/Toast.swift index d548a0f..7307bc9 100755 --- a/MFCard/Card /Toast/Toast.swift +++ b/MFCard/Card /Toast/Toast.swift @@ -348,7 +348,7 @@ public extension UIView { activityView.layer.shadowOffset = style.shadowOffset } - let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge) + let activityIndicatorView = UIActivityIndicatorView(style: .whiteLarge) activityIndicatorView.center = CGPoint(x: activityView.bounds.size.width / 2.0, y: activityView.bounds.size.height / 2.0) activityView.addSubview(activityIndicatorView) activityIndicatorView.startAnimating() @@ -377,7 +377,7 @@ public extension UIView { toast.alpha = 1.0 }) { (finished) -> Void in let timer = Timer(timeInterval: duration, target: self, selector: #selector(UIView.toastTimerDidFinish(_:)), userInfo: toast, repeats: false) - RunLoop.main.add(timer, forMode: RunLoopMode.commonModes) + RunLoop.main.add(timer, forMode: RunLoop.Mode.common) objc_setAssociatedObject(toast, &ToastKeys.Timer, timer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } diff --git a/MFCardDemo.xcodeproj/project.pbxproj b/MFCardDemo.xcodeproj/project.pbxproj index ea34b4c..524b30e 100644 --- a/MFCardDemo.xcodeproj/project.pbxproj +++ b/MFCardDemo.xcodeproj/project.pbxproj @@ -244,20 +244,20 @@ 6B24472F1DF84E62003A8285 = { CreatedOnToolsVersion = 8.1; DevelopmentTeam = 536JDUMES6; - LastSwiftMigration = 0900; + LastSwiftMigration = 1120; ProvisioningStyle = Automatic; }; 6B24473B1DF84E82003A8285 = { CreatedOnToolsVersion = 8.1; DevelopmentTeam = 536JDUMES6; - LastSwiftMigration = 0900; + LastSwiftMigration = 1120; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 6B6CF8161DCB53DA00A29C16 /* Build configuration list for PBXProject "MFCardDemo" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -369,7 +369,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -394,7 +394,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -414,7 +414,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.mobilefirst.MFCardDemo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -431,7 +431,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.mobilefirst.MFCardDemo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -489,7 +489,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -539,7 +539,7 @@ SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; VALIDATE_PRODUCT = YES; }; name = Release; diff --git a/MFCardDemo/AppDelegate.swift b/MFCardDemo/AppDelegate.swift index d851ad4..1c765d6 100644 --- a/MFCardDemo/AppDelegate.swift +++ b/MFCardDemo/AppDelegate.swift @@ -14,7 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } diff --git a/MFCardDemo/ViewController.swift b/MFCardDemo/ViewController.swift index 9c679cc..33dc5c8 100644 --- a/MFCardDemo/ViewController.swift +++ b/MFCardDemo/ViewController.swift @@ -26,7 +26,7 @@ class ViewController: UIViewController,MFCardDelegate { myCard.delegate = self myCard.autoDismiss = true myCard.toast = true - myCard.blurStyle = UIBlurEffect(style: UIBlurEffectStyle.extraLight) + myCard.blurStyle = UIBlurEffect(style: UIBlurEffect.Style.extraLight) let demoCard :Card? = Card(holderName: "Rahul Chandnani", number: "6552552665526625", month: Month.Dec, year: "2019", cvc: "234", paymentType: Card.PaymentType.bank, cardType: CardType.Discover, userId: 0) myCard.showCardWithCardDetails(card: demoCard!) } @@ -37,7 +37,7 @@ class ViewController: UIViewController,MFCardDelegate { myCard.delegate = self myCard.autoDismiss = true myCard.toast = true - myCard.blurStyle = UIBlurEffect(style: UIBlurEffectStyle.light) + myCard.blurStyle = UIBlurEffect(style: UIBlurEffect.Style.light) myCard.showCard() } From ab73296ef3777fc1601404da91ed0f32deed6e50 Mon Sep 17 00:00:00 2001 From: Pavan Welihinda Date: Sat, 21 Dec 2019 14:07:31 +0530 Subject: [PATCH 2/3] Updated project and Toast to Swift 5 --- MFCard/Card /Toast/Toast.swift | 454 +++++++++--------- .../xcshareddata/IDEWorkspaceChecks.plist | 8 + 2 files changed, 239 insertions(+), 223 deletions(-) create mode 100644 MFCardDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/MFCard/Card /Toast/Toast.swift b/MFCard/Card /Toast/Toast.swift index 7307bc9..471d09e 100755 --- a/MFCard/Card /Toast/Toast.swift +++ b/MFCard/Card /Toast/Toast.swift @@ -2,7 +2,7 @@ // Toast.swift // Toast-Swift // -// Copyright (c) 2015 Charles Scalesse. +// Copyright (c) 2015-2019 Charles Scalesse. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the @@ -22,19 +22,12 @@ // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - import UIKit import ObjectiveC -public enum ToastPosition { - case top - case center - case bottom -} - /** Toast is a Swift extension that adds toast notifications to the `UIView` object class. - It is intended to be simple, lightweight, and easy to use. Most toast notifications + It is intended to be simple, lightweight, and easy to use. Most toast notifications can be triggered with a single line of code. The `makeToast` methods create a new view and then display it as toast. @@ -48,13 +41,13 @@ public extension UIView { Keys used for associated objects. */ private struct ToastKeys { - static var Timer = "CSToastTimerKey" - static var Duration = "CSToastDurationKey" - static var Position = "CSToastPositionKey" - static var Completion = "CSToastCompletionKey" - static var ActiveToast = "CSToastActiveToastKey" - static var ActivityView = "CSToastActivityViewKey" - static var Queue = "CSToastQueueKey" + static var timer = "com.toast-swift.timer" + static var duration = "com.toast-swift.duration" + static var point = "com.toast-swift.point" + static var completion = "com.toast-swift.completion" + static var activeToasts = "com.toast-swift.activeToasts" + static var activityView = "com.toast-swift.activityView" + static var queue = "com.toast-swift.queue" } /** @@ -63,7 +56,7 @@ public extension UIView { class that can be used with associated objects. */ private class ToastCompletionWrapper { - var completion: ((Bool) -> Void)? + let completion: ((Bool) -> Void)? init(_ completion: ((Bool) -> Void)?) { self.completion = completion @@ -71,16 +64,28 @@ public extension UIView { } private enum ToastError: Error { - case insufficientData + case missingParameters + } + + private var activeToasts: NSMutableArray { + get { + if let activeToasts = objc_getAssociatedObject(self, &ToastKeys.activeToasts) as? NSMutableArray { + return activeToasts + } else { + let activeToasts = NSMutableArray() + objc_setAssociatedObject(self, &ToastKeys.activeToasts, activeToasts, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + return activeToasts + } + } } private var queue: NSMutableArray { get { - if let queue = objc_getAssociatedObject(self, &ToastKeys.Queue) as? NSMutableArray { + if let queue = objc_getAssociatedObject(self, &ToastKeys.queue) as? NSMutableArray { return queue } else { let queue = NSMutableArray() - objc_setAssociatedObject(self, &ToastKeys.Queue, queue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(self, &ToastKeys.queue, queue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) return queue } } @@ -89,70 +94,7 @@ public extension UIView { // MARK: - Make Toast Methods /** - Creates and presents a new toast view with a message and displays it with the - default duration and position. Styled using the shared style. - - @param message The message to be displayed - */ - public func makeToast(_ message: String) { - self.makeToast(message, duration: ToastManager.shared.duration, position: ToastManager.shared.position) - } - - /** - Creates and presents a new toast view with a message. Duration and position - can be set explicitly. Styled using the shared style. - - @param message The message to be displayed - @param duration The toast duration - @param position The toast's position - */ - public func makeToast(_ message: String, duration: TimeInterval, position: ToastPosition) { - self.makeToast(message, duration: duration, position: position, style: nil) - } - - /** - Creates and presents a new toast view with a message. Duration and position - can be set explicitly. Styled using the shared style. - - @param message The message to be displayed - @param duration The toast duration - @param position The toast's center point - */ - public func makeToast(_ message: String, duration: TimeInterval, position: CGPoint) { - self.makeToast(message, duration: duration, position: position, style: nil) - } - - /** - Creates and presents a new toast view with a message. Duration, position, and - style can be set explicitly. - - @param message The message to be displayed - @param duration The toast duration - @param position The toast's position - @param style The style. The shared style will be used when nil - */ - public func makeToast(_ message: String, duration: TimeInterval, position: ToastPosition, style: ToastStyle?) { - self.makeToast(message, duration: duration, position: position, title: nil, image: nil, style: style, completion: nil) - } - - /** - Creates and presents a new toast view with a message. Duration, position, and - style can be set explicitly. - - @param message The message to be displayed - @param duration The toast duration - @param position The toast's center point - @param style The style. The shared style will be used when nil - */ - public func makeToast(_ message: String, duration: TimeInterval, position: CGPoint, style: ToastStyle?) { - self.makeToast(message, duration: duration, position: position, title: nil, image: nil, style: style, completion: nil) - } - - /** - Creates and presents a new toast view with a message, title, and image. Duration, - position, and style can be set explicitly. The completion closure executes when the - toast completes presentation. `didTap` will be `true` if the toast view was dismissed - from a tap. + Creates and presents a new toast view. @param message The message to be displayed @param duration The toast duration @@ -163,60 +105,38 @@ public extension UIView { @param completion The completion closure, executed after the toast view disappears. didTap will be `true` if the toast view was dismissed from a tap. */ - public func makeToast(_ message: String?, duration: TimeInterval, position: ToastPosition, title: String?, image: UIImage?, style: ToastStyle?, completion: ((_ didTap: Bool) -> Void)?) { - var toastStyle = ToastManager.shared.style - if let style = style { - toastStyle = style - } - + func makeToast(_ message: String?, duration: TimeInterval = ToastManager.shared.duration, position: ToastPosition = ToastManager.shared.position, title: String? = nil, image: UIImage? = nil, style: ToastStyle = ToastManager.shared.style, completion: ((_ didTap: Bool) -> Void)? = nil) { do { - let toast = try self.toastViewForMessage(message, title: title, image: image, style: toastStyle) - self.showToast(toast, duration: duration, position: position, completion: completion) - } catch ToastError.insufficientData { + let toast = try toastViewForMessage(message, title: title, image: image, style: style) + showToast(toast, duration: duration, position: position, completion: completion) + } catch ToastError.missingParameters { print("Error: message, title, and image are all nil") } catch {} } /** - Creates and presents a new toast view with a message, title, and image. Duration, - position, and style can be set explicitly. The completion closure executes when the - toast completes presentation. `didTap` will be `true` if the toast view was dismissed - from a tap. + Creates a new toast view and presents it at a given center point. @param message The message to be displayed @param duration The toast duration - @param position The toast's center point + @param point The toast's center point @param title The title @param image The image @param style The style. The shared style will be used when nil @param completion The completion closure, executed after the toast view disappears. didTap will be `true` if the toast view was dismissed from a tap. */ - public func makeToast(_ message: String?, duration: TimeInterval, position: CGPoint, title: String?, image: UIImage?, style: ToastStyle?, completion: ((_ didTap: Bool) -> Void)?) { - var toastStyle = ToastManager.shared.style - if let style = style { - toastStyle = style - } - + func makeToast(_ message: String?, duration: TimeInterval = ToastManager.shared.duration, point: CGPoint, title: String?, image: UIImage?, style: ToastStyle = ToastManager.shared.style, completion: ((_ didTap: Bool) -> Void)?) { do { - let toast = try self.toastViewForMessage(message, title: title, image: image, style: toastStyle) - self.showToast(toast, duration: duration, position: position, completion: completion) - } catch ToastError.insufficientData { + let toast = try toastViewForMessage(message, title: title, image: image, style: style) + showToast(toast, duration: duration, point: point, completion: completion) + } catch ToastError.missingParameters { print("Error: message, title, and image cannot all be nil") } catch {} } // MARK: - Show Toast Methods - /** - Displays any view as toast using the default duration and position. - - @param toast The view to be displayed as toast - */ - public func showToast(_ toast: UIView) { - self.showToast(toast, duration: ToastManager.shared.duration, position: ToastManager.shared.position, completion: nil) - } - /** Displays any view as toast at a provided position and duration. The completion closure executes when the toast view completes. `didTap` will be `true` if the toast view was @@ -228,35 +148,93 @@ public extension UIView { @param completion The completion block, executed after the toast view disappears. didTap will be `true` if the toast view was dismissed from a tap. */ - public func showToast(_ toast: UIView, duration: TimeInterval, position: ToastPosition, completion: ((_ didTap: Bool) -> Void)?) { - let point = self.centerPointForPosition(position, toast: toast) - self.showToast(toast, duration: duration, position: point, completion: completion) + func showToast(_ toast: UIView, duration: TimeInterval = ToastManager.shared.duration, position: ToastPosition = ToastManager.shared.position, completion: ((_ didTap: Bool) -> Void)? = nil) { + let point = position.centerPoint(forToast: toast, inSuperview: self) + showToast(toast, duration: duration, point: point, completion: completion) } /** - Displays any view as toast at a provided position and duration. The completion closure + Displays any view as toast at a provided center point and duration. The completion closure executes when the toast view completes. `didTap` will be `true` if the toast view was dismissed from a tap. @param toast The view to be displayed as toast @param duration The notification duration - @param position The toast's center point + @param point The toast's center point @param completion The completion block, executed after the toast view disappears. didTap will be `true` if the toast view was dismissed from a tap. */ - public func showToast(_ toast: UIView, duration: TimeInterval, position: CGPoint, completion: ((_ didTap: Bool) -> Void)?) { - objc_setAssociatedObject(toast, &ToastKeys.Completion, ToastCompletionWrapper(completion), .OBJC_ASSOCIATION_RETAIN_NONATOMIC); + func showToast(_ toast: UIView, duration: TimeInterval = ToastManager.shared.duration, point: CGPoint, completion: ((_ didTap: Bool) -> Void)? = nil) { + objc_setAssociatedObject(toast, &ToastKeys.completion, ToastCompletionWrapper(completion), .OBJC_ASSOCIATION_RETAIN_NONATOMIC); - if let _ = objc_getAssociatedObject(self, &ToastKeys.ActiveToast) as? UIView, ToastManager.shared.queueEnabled { - objc_setAssociatedObject(toast, &ToastKeys.Duration, NSNumber(value: duration), .OBJC_ASSOCIATION_RETAIN_NONATOMIC); - objc_setAssociatedObject(toast, &ToastKeys.Position, NSValue(cgPoint: position), .OBJC_ASSOCIATION_RETAIN_NONATOMIC); + if ToastManager.shared.isQueueEnabled, activeToasts.count > 0 { + objc_setAssociatedObject(toast, &ToastKeys.duration, NSNumber(value: duration), .OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(toast, &ToastKeys.point, NSValue(cgPoint: point), .OBJC_ASSOCIATION_RETAIN_NONATOMIC); - self.queue.add(toast) + queue.add(toast) } else { - self.showToast(toast, duration: duration, position: position) + showToast(toast, duration: duration, point: point) + } + } + + // MARK: - Hide Toast Methods + + /** + Hides the active toast. If there are multiple toasts active in a view, this method + hides the oldest toast (the first of the toasts to have been presented). + + @see `hideAllToasts()` to remove all active toasts from a view. + + @warning This method has no effect on activity toasts. Use `hideToastActivity` to + hide activity toasts. + + */ + func hideToast() { + guard let activeToast = activeToasts.firstObject as? UIView else { return } + hideToast(activeToast) + } + + /** + Hides an active toast. + + @param toast The active toast view to dismiss. Any toast that is currently being displayed + on the screen is considered active. + + @warning this does not clear a toast view that is currently waiting in the queue. + */ + func hideToast(_ toast: UIView) { + guard activeToasts.contains(toast) else { return } + hideToast(toast, fromTap: false) + } + + /** + Hides all toast views. + + @param includeActivity If `true`, toast activity will also be hidden. Default is `false`. + @param clearQueue If `true`, removes all toast views from the queue. Default is `true`. + */ + func hideAllToasts(includeActivity: Bool = false, clearQueue: Bool = true) { + if clearQueue { + clearToastQueue() + } + + activeToasts.compactMap { $0 as? UIView } + .forEach { hideToast($0) } + + if includeActivity { + hideToastActivity() } } + /** + Removes all toast views from the queue. This has no effect on toast views that are + active. Use `hideAllToasts(clearQueue:)` to hide the active toasts views and clear + the queue. + */ + func clearToastQueue() { + queue.removeAllObjects() + } + // MARK: - Activity Methods /** @@ -271,15 +249,13 @@ public extension UIView { @param position The toast's position */ - public func makeToastActivity(_ position: ToastPosition) { + func makeToastActivity(_ position: ToastPosition) { // sanity - if let _ = objc_getAssociatedObject(self, &ToastKeys.ActivityView) as? UIView { - return - } + guard objc_getAssociatedObject(self, &ToastKeys.activityView) as? UIView == nil else { return } - let toast = self.createToastActivityView() - let point = self.centerPointForPosition(position, toast: toast) - self.makeToastActivity(toast, position: point) + let toast = createToastActivityView() + let point = position.centerPoint(forToast: toast, inSuperview: self) + makeToastActivity(toast, point: point) } /** @@ -292,52 +268,50 @@ public extension UIView { activity views can be presented and dismissed while toast views are being displayed. `makeToastActivity(position:)` has no effect on the queueing behavior of the `showToast` methods. - @param position The toast's center point + @param point The toast's center point */ - public func makeToastActivity(_ position: CGPoint) { + func makeToastActivity(_ point: CGPoint) { // sanity - if let _ = objc_getAssociatedObject(self, &ToastKeys.ActivityView) as? UIView { - return - } + guard objc_getAssociatedObject(self, &ToastKeys.activityView) as? UIView == nil else { return } - let toast = self.createToastActivityView() - self.makeToastActivity(toast, position: position) + let toast = createToastActivityView() + makeToastActivity(toast, point: point) } /** Dismisses the active toast activity indicator view. */ - public func hideToastActivity() { - if let toast = objc_getAssociatedObject(self, &ToastKeys.ActivityView) as? UIView { - UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.curveEaseIn, .beginFromCurrentState], animations: { () -> Void in + func hideToastActivity() { + if let toast = objc_getAssociatedObject(self, &ToastKeys.activityView) as? UIView { + UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.curveEaseIn, .beginFromCurrentState], animations: { toast.alpha = 0.0 - }, completion: { (finished: Bool) -> Void in + }) { _ in toast.removeFromSuperview() - objc_setAssociatedObject(self, &ToastKeys.ActivityView, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - }) + objc_setAssociatedObject(self, &ToastKeys.activityView, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } } } // MARK: - Private Activity Methods - private func makeToastActivity(_ toast: UIView, position: CGPoint) { + private func makeToastActivity(_ toast: UIView, point: CGPoint) { toast.alpha = 0.0 - toast.center = position + toast.center = point - objc_setAssociatedObject(self, &ToastKeys.ActivityView, toast, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(self, &ToastKeys.activityView, toast, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) self.addSubview(toast) - UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: .curveEaseOut, animations: { () -> Void in + UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: .curveEaseOut, animations: { toast.alpha = 1.0 - }, completion: nil) + }) } private func createToastActivityView() -> UIView { let style = ToastManager.shared.style let activityView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: style.activitySize.width, height: style.activitySize.height)) - activityView.backgroundColor = style.backgroundColor + activityView.backgroundColor = style.activityBackgroundColor activityView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, .flexibleBottomMargin] activityView.layer.cornerRadius = style.cornerRadius @@ -351,6 +325,7 @@ public extension UIView { let activityIndicatorView = UIActivityIndicatorView(style: .whiteLarge) activityIndicatorView.center = CGPoint(x: activityView.bounds.size.width / 2.0, y: activityView.bounds.size.height / 2.0) activityView.addSubview(activityIndicatorView) + activityIndicatorView.color = style.activityIndicatorColor activityIndicatorView.startAnimating() return activityView @@ -358,67 +333,63 @@ public extension UIView { // MARK: - Private Show/Hide Methods - private func showToast(_ toast: UIView, duration: TimeInterval, position: CGPoint) { - toast.center = position + private func showToast(_ toast: UIView, duration: TimeInterval, point: CGPoint) { + toast.center = point toast.alpha = 0.0 - if ToastManager.shared.tapToDismissEnabled { + if ToastManager.shared.isTapToDismissEnabled { let recognizer = UITapGestureRecognizer(target: self, action: #selector(UIView.handleToastTapped(_:))) toast.addGestureRecognizer(recognizer) toast.isUserInteractionEnabled = true toast.isExclusiveTouch = true } - objc_setAssociatedObject(self, &ToastKeys.ActiveToast, toast, .OBJC_ASSOCIATION_RETAIN_NONATOMIC); - + activeToasts.add(toast) self.addSubview(toast) - UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.curveEaseOut, .allowUserInteraction], animations: { () -> Void in + UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.curveEaseOut, .allowUserInteraction], animations: { toast.alpha = 1.0 - }) { (finished) -> Void in + }) { _ in let timer = Timer(timeInterval: duration, target: self, selector: #selector(UIView.toastTimerDidFinish(_:)), userInfo: toast, repeats: false) - RunLoop.main.add(timer, forMode: RunLoop.Mode.common) - objc_setAssociatedObject(toast, &ToastKeys.Timer, timer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + RunLoop.main.add(timer, forMode: .common) + objc_setAssociatedObject(toast, &ToastKeys.timer, timer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } - private func hideToast(_ toast: UIView) { - self.hideToast(toast, fromTap: false) - } - private func hideToast(_ toast: UIView, fromTap: Bool) { + if let timer = objc_getAssociatedObject(toast, &ToastKeys.timer) as? Timer { + timer.invalidate() + } - UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.curveEaseIn, .beginFromCurrentState], animations: { () -> Void in + UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.curveEaseIn, .beginFromCurrentState], animations: { toast.alpha = 0.0 - }) { (didFinish: Bool) -> Void in + }) { _ in toast.removeFromSuperview() + self.activeToasts.remove(toast) - objc_setAssociatedObject(self, &ToastKeys.ActiveToast, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - if let wrapper = objc_getAssociatedObject(toast, &ToastKeys.Completion) as? ToastCompletionWrapper, let completion = wrapper.completion { + if let wrapper = objc_getAssociatedObject(toast, &ToastKeys.completion) as? ToastCompletionWrapper, let completion = wrapper.completion { completion(fromTap) } - if let nextToast = self.queue.firstObject as? UIView, let duration = objc_getAssociatedObject(nextToast, &ToastKeys.Duration) as? NSNumber, let position = objc_getAssociatedObject(nextToast, &ToastKeys.Position) as? NSValue { + if let nextToast = self.queue.firstObject as? UIView, let duration = objc_getAssociatedObject(nextToast, &ToastKeys.duration) as? NSNumber, let point = objc_getAssociatedObject(nextToast, &ToastKeys.point) as? NSValue { self.queue.removeObject(at: 0) - self.showToast(nextToast, duration: duration.doubleValue, position: position.cgPointValue) + self.showToast(nextToast, duration: duration.doubleValue, point: point.cgPointValue) } } } // MARK: - Events - @objc func handleToastTapped(_ recognizer: UITapGestureRecognizer) { - if let toast = recognizer.view, let timer = objc_getAssociatedObject(toast, &ToastKeys.Timer) as? Timer { - timer.invalidate() - self.hideToast(toast, fromTap: true) - } + @objc + private func handleToastTapped(_ recognizer: UITapGestureRecognizer) { + guard let toast = recognizer.view else { return } + hideToast(toast, fromTap: true) } - @objc func toastTimerDidFinish(_ timer: Timer) { - if let toast = timer.userInfo as? UIView { - self.hideToast(toast) - } + @objc + private func toastTimerDidFinish(_ timer: Timer) { + guard let toast = timer.userInfo as? UIView else { return } + hideToast(toast) } // MARK: - Toast Construction @@ -430,19 +401,19 @@ public extension UIView { methods must be used to present the resulting view. @warning if message, title, and image are all nil, this method will throw - `ToastError.InsufficientData` + `ToastError.missingParameters` @param message The message to be displayed @param title The title @param image The image @param style The style. The shared style will be used when nil - @throws `ToastError.InsufficientData` when message, title, and image are all nil + @throws `ToastError.missingParameters` when message, title, and image are all nil @return The newly created toast view */ - public func toastViewForMessage(_ message: String?, title: String?, image: UIImage?, style: ToastStyle) throws -> UIView { + func toastViewForMessage(_ message: String?, title: String?, image: UIImage?, style: ToastStyle) throws -> UIView { // sanity - if message == nil && title == nil && image == nil { - throw ToastError.insufficientData + guard message != nil || title != nil || image != nil else { + throw ToastError.missingParameters } var messageLabel: UILabel? @@ -538,11 +509,13 @@ public extension UIView { wrapperView.frame = CGRect(x: 0.0, y: 0.0, width: wrapperWidth, height: wrapperHeight) if let titleLabel = titleLabel { + titleRect.size.width = longerWidth titleLabel.frame = titleRect wrapperView.addSubview(titleLabel) } if let messageLabel = messageLabel { + messageRect.size.width = longerWidth messageLabel.frame = messageRect wrapperView.addSubview(messageLabel) } @@ -554,29 +527,13 @@ public extension UIView { return wrapperView } - // MARK: - Helpers - - private func centerPointForPosition(_ position: ToastPosition, toast: UIView) -> CGPoint { - let padding: CGFloat = ToastManager.shared.style.verticalPadding - - switch(position) { - case .top: - return CGPoint(x: self.bounds.size.width / 2.0, y: (toast.frame.size.height / 2.0) + padding) - case .center: - return CGPoint(x: self.bounds.size.width / 2.0, y: self.bounds.size.height / 2.0) - case .bottom: - return CGPoint(x: self.bounds.size.width / 2.0, y: (self.bounds.size.height - (toast.frame.size.height / 2.0)) - padding) - } - } } // MARK: - Toast Style - /** `ToastStyle` instances define the look and feel for toast views created via the `makeToast` methods as well for toast views created directly with `toastViewForMessage(message:title:image:style:)`. - @warning `ToastStyle` offers relatively simple styling options for the default toast view. If you require a toast view with more complex UI, it probably makes more sense to create your own custom UIView subclass and present it with the `showToast` @@ -587,19 +544,19 @@ public struct ToastStyle { public init() {} /** - The background color. Default is `UIColor.blackColor()` at 80% opacity. + The background color. Default is `.black` at 80% opacity. */ - public var backgroundColor = UIColor.black.withAlphaComponent(0.8) + public var backgroundColor: UIColor = UIColor.black.withAlphaComponent(0.8) /** The title color. Default is `UIColor.whiteColor()`. */ - public var titleColor = UIColor.white + public var titleColor: UIColor = .white /** - The message color. Default is `UIColor.whiteColor()`. + The message color. Default is `.white`. */ - public var messageColor = UIColor.white + public var messageColor: UIColor = .white /** A percentage value from 0.0 to 1.0, representing the maximum width of the toast @@ -625,13 +582,15 @@ public struct ToastStyle { The spacing from the horizontal edge of the toast view to the content. When an image is present, this is also used as the padding between the image and the text. Default is 10.0. + */ public var horizontalPadding: CGFloat = 10.0 /** The spacing from the vertical edge of the toast view to the content. When a title is present, this is also used as the padding between the title and the message. - Default is 10.0. + Default is 10.0. On iOS11+, this value is added added to the `safeAreaInset.top` + and `safeAreaInsets.bottom`. */ public var verticalPadding: CGFloat = 10.0 @@ -641,24 +600,24 @@ public struct ToastStyle { public var cornerRadius: CGFloat = 10.0; /** - The title font. Default is `UIFont.boldSystemFontOfSize(16.0)`. + The title font. Default is `.boldSystemFont(16.0)`. */ - public var titleFont = UIFont.boldSystemFont(ofSize: 16.0) + public var titleFont: UIFont = .boldSystemFont(ofSize: 16.0) /** - The message font. Default is `UIFont.systemFontOfSize(16.0)`. + The message font. Default is `.systemFont(ofSize: 16.0)`. */ - public var messageFont = UIFont.systemFont(ofSize: 16.0) + public var messageFont: UIFont = .systemFont(ofSize: 16.0) /** The title text alignment. Default is `NSTextAlignment.Left`. */ - public var titleAlignment = NSTextAlignment.center + public var titleAlignment: NSTextAlignment = .left /** The message text alignment. Default is `NSTextAlignment.Left`. */ - public var messageAlignment = NSTextAlignment.left + public var messageAlignment: NSTextAlignment = .left /** The maximum number of lines for the title. The default is 0 (no limit). @@ -676,9 +635,9 @@ public struct ToastStyle { public var displayShadow = false /** - The shadow color. Default is `UIColor.blackColor()`. + The shadow color. Default is `.black`. */ - public var shadowColor = UIColor.black + public var shadowColor: UIColor = .black /** A value from 0.0 to 1.0, representing the opacity of the shadow. @@ -716,10 +675,19 @@ public struct ToastStyle { */ public var fadeDuration: TimeInterval = 0.2 + /** + Activity indicator color. Default is `.white`. + */ + public var activityIndicatorColor: UIColor = .white + + /** + Activity background color. Default is `.black` at 80% opacity. + */ + public var activityBackgroundColor: UIColor = UIColor.black.withAlphaComponent(0.8) + } // MARK: - Toast Manager - /** `ToastManager` provides general configuration options for all toast notifications. Backed by a singleton instance. @@ -728,33 +696,38 @@ public class ToastManager { /** The `ToastManager` singleton instance. + */ public static let shared = ToastManager() /** The shared style. Used whenever toastViewForMessage(message:title:image:style:) is called with with a nil style. + */ public var style = ToastStyle() /** Enables or disables tap to dismiss on toast views. Default is `true`. + */ - public var tapToDismissEnabled = true + public var isTapToDismissEnabled = true /** Enables or disables queueing behavior for toast views. When `true`, toast views will appear one after the other. When `false`, multiple toast views will appear at the same time (potentially overlapping depending on their positions). This has no effect on the toast activity view, - which operates independently of normal toast views. Default is `true`. + which operates independently of normal toast views. Default is `false`. + */ - public var queueEnabled = true + public var isQueueEnabled = false /** The default duration. Used for the `makeToast` and `showToast` methods that don't require an explicit duration. Default is 3.0. + */ public var duration: TimeInterval = 3.0 @@ -762,7 +735,42 @@ public class ToastManager { Sets the default position. Used for the `makeToast` and `showToast` methods that don't require an explicit position. Default is `ToastPosition.Bottom`. + */ - public var position = ToastPosition.bottom + public var position: ToastPosition = .bottom + +} + +// MARK: - ToastPosition +public enum ToastPosition { + case top + case center + case bottom + + fileprivate func centerPoint(forToast toast: UIView, inSuperview superview: UIView) -> CGPoint { + let topPadding: CGFloat = ToastManager.shared.style.verticalPadding + superview.csSafeAreaInsets.top + let bottomPadding: CGFloat = ToastManager.shared.style.verticalPadding + superview.csSafeAreaInsets.bottom + + switch self { + case .top: + return CGPoint(x: superview.bounds.size.width / 2.0, y: (toast.frame.size.height / 2.0) + topPadding) + case .center: + return CGPoint(x: superview.bounds.size.width / 2.0, y: superview.bounds.size.height / 2.0) + case .bottom: + return CGPoint(x: superview.bounds.size.width / 2.0, y: (superview.bounds.size.height - (toast.frame.size.height / 2.0)) - bottomPadding) + } + } +} + +// MARK: - Private UIView Extensions +private extension UIView { + + var csSafeAreaInsets: UIEdgeInsets { + if #available(iOS 11.0, *) { + return self.safeAreaInsets + } else { + return .zero + } + } } diff --git a/MFCardDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/MFCardDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/MFCardDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + From 06e9c9de1b8919b0313451b12ba76de32a900e8c Mon Sep 17 00:00:00 2001 From: Pavan Welihinda Date: Sat, 21 Dec 2019 14:55:48 +0530 Subject: [PATCH 3/3] updated readme file --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 635637c..8961305 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Platform iOS Swift 3 compatible Swift 4 compatible +Swift 5 compatible License MIT CocoaPods compatible