mirror of
https://github.com/HackPlan/TTGSnackbar.git
synced 2026-04-26 13:04:56 +08:00
Added: Animation types.
This commit is contained in:
@@ -15,6 +15,14 @@ public enum TTGSnackbarDuration: NSTimeInterval {
|
||||
case TTGSnackbarDurationForever = 9999999999.0 // Must dismiss manually.
|
||||
}
|
||||
|
||||
public enum TTGSnackbarAnimationType {
|
||||
case TTGSnackbarAnimationFadeInFadeOut
|
||||
case TTGSnackbarAnimationSlideFromBottomToTop
|
||||
case TTGSnackbarAnimationSlideFromBottomBackToBottom
|
||||
case TTGSnackbarAnimationSlideFromLeftToRight
|
||||
case TTGSnackbarAnimationSlideFromRightToLeft
|
||||
}
|
||||
|
||||
public class TTGSnackbar: UIView {
|
||||
// Current instance
|
||||
private static var currentInstance: TTGSnackbar? = nil
|
||||
@@ -24,7 +32,7 @@ public class TTGSnackbar: UIView {
|
||||
private static let snackbarBottomMargin: CGFloat = 4
|
||||
private static let snackbarHorizonMargin: CGFloat = 4
|
||||
private static let snackbarActionButtonWidth: CGFloat = 64
|
||||
|
||||
|
||||
// Callback block type
|
||||
public typealias TTGActionBlock = (snackbar: TTGSnackbar) -> Void
|
||||
public typealias TTGDismissBlock = (snackbar: TTGSnackbar) -> Void
|
||||
@@ -33,6 +41,7 @@ public class TTGSnackbar: UIView {
|
||||
public var actionBlock: TTGActionBlock? = nil
|
||||
public var dismissBlock: TTGDismissBlock? = nil
|
||||
public var duration: TTGSnackbarDuration = TTGSnackbarDuration.TTGSnackbarDurationShort
|
||||
public var animationType: TTGSnackbarAnimationType = TTGSnackbarAnimationType.TTGSnackbarAnimationSlideFromBottomBackToBottom
|
||||
public var message: String = "" {
|
||||
didSet {
|
||||
self.messageLabel.text = message
|
||||
@@ -60,9 +69,11 @@ public class TTGSnackbar: UIView {
|
||||
private var actionButton: UIButton!
|
||||
private var activityIndicatorView: UIActivityIndicatorView!
|
||||
private var dismissTimer: NSTimer? = nil
|
||||
private var bottomMarginConstraint: NSLayoutConstraint? = nil // For snackbar show/Hide animation
|
||||
|
||||
private var centerXConstraint: NSLayoutConstraint? = nil
|
||||
private var bottomMarginConstraint: NSLayoutConstraint? = nil
|
||||
private var actionButtonWidthConstraint: NSLayoutConstraint? = nil // Action button width
|
||||
|
||||
|
||||
// --Public init--
|
||||
public init(message: String, duration: TTGSnackbarDuration) {
|
||||
super.init(frame: CGRectMake(0, 0, 0, 0))
|
||||
@@ -70,7 +81,7 @@ public class TTGSnackbar: UIView {
|
||||
self.message = message
|
||||
configure()
|
||||
}
|
||||
|
||||
|
||||
public init(message: String, duration: TTGSnackbarDuration, actionText: String, actionBlock: TTGActionBlock) {
|
||||
super.init(frame: CGRectMake(0, 0, 0, 0))
|
||||
self.duration = duration
|
||||
@@ -83,76 +94,70 @@ public class TTGSnackbar: UIView {
|
||||
public required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
|
||||
// --Public methods--
|
||||
public func show() {
|
||||
// Only show once
|
||||
if self.superview != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Dismiss last bar
|
||||
TTGSnackbar.currentInstance?.dismissAnimated(false)
|
||||
|
||||
|
||||
// Save current show instance
|
||||
TTGSnackbar.currentInstance = self
|
||||
|
||||
|
||||
// Create dismiss timer
|
||||
dismissTimer = NSTimer.scheduledTimerWithTimeInterval(duration.rawValue, target: self, selector: "dismiss", userInfo: nil, repeats: false)
|
||||
|
||||
|
||||
// Show or hide action button
|
||||
actionButton.hidden = actionText.isEmpty || actionBlock == nil
|
||||
seperateView.hidden = actionButton.hidden
|
||||
actionButtonWidthConstraint?.constant = actionButton.hidden ? 0 : TTGSnackbar.snackbarActionButtonWidth
|
||||
|
||||
|
||||
// Find current stop viewcontroller
|
||||
if let topController = getTopViewController() {
|
||||
let superView: UIView = topController.view
|
||||
superView.addSubview(self)
|
||||
|
||||
// Horizontal constraints
|
||||
let hConstraints: [NSLayoutConstraint] =
|
||||
NSLayoutConstraint.constraintsWithVisualFormat(
|
||||
"H:|-snackbarHorizonMargin-[self]-snackbarHorizonMargin-|",
|
||||
options: NSLayoutFormatOptions(rawValue: 0),
|
||||
metrics: ["snackbarHorizonMargin": TTGSnackbar.snackbarHorizonMargin],
|
||||
views: ["self": self])
|
||||
|
||||
|
||||
// Snackbar height constraint
|
||||
let heightConstraint: NSLayoutConstraint = NSLayoutConstraint.init(item: self, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: TTGSnackbar.snackbarHeight)
|
||||
|
||||
// Bottom margin constraint for animation
|
||||
bottomMarginConstraint = NSLayoutConstraint.init(item: self, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: superview, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: TTGSnackbar.snackbarHeight)
|
||||
|
||||
|
||||
// Snackbar width constraint
|
||||
let widthConstraint: NSLayoutConstraint = NSLayoutConstraint.init(item: self, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: superView.bounds.width - 2 * TTGSnackbar.snackbarHorizonMargin)
|
||||
|
||||
// Center X constraint
|
||||
centerXConstraint = NSLayoutConstraint.init(item: self, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0)
|
||||
|
||||
// Bottom margin constraint
|
||||
bottomMarginConstraint = NSLayoutConstraint.init(item: self, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: superview, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 0)
|
||||
|
||||
// Add constraints
|
||||
self.addConstraint(heightConstraint)
|
||||
self.addConstraint(widthConstraint)
|
||||
superView.addConstraint(bottomMarginConstraint!)
|
||||
superView.addConstraints(hConstraints)
|
||||
superView.layoutIfNeeded()
|
||||
|
||||
// Show with animation
|
||||
bottomMarginConstraint?.constant = -TTGSnackbar.snackbarBottomMargin
|
||||
self.setNeedsLayout()
|
||||
UIView.animateWithDuration(TTGSnackbar.snackbarAnimationDuration, animations: { () -> Void in
|
||||
self.layoutIfNeeded()
|
||||
}, completion: nil)
|
||||
superView.addConstraint(centerXConstraint!)
|
||||
|
||||
// Show
|
||||
showWithAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func dismiss() {
|
||||
// On main thread
|
||||
dispatch_async(dispatch_get_main_queue()) { () -> Void in
|
||||
self.dismissAnimated(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --Private methods--
|
||||
private func configure() {
|
||||
self.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.backgroundColor = UIColor.init(white: 0, alpha: 0.8)
|
||||
self.layer.cornerRadius = 4
|
||||
self.layer.masksToBounds = true
|
||||
|
||||
|
||||
messageLabel = UILabel()
|
||||
messageLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
messageLabel.textColor = UIColor.whiteColor()
|
||||
@@ -161,7 +166,7 @@ public class TTGSnackbar: UIView {
|
||||
messageLabel.textAlignment = NSTextAlignment.Left
|
||||
messageLabel.text = message
|
||||
self.addSubview(messageLabel)
|
||||
|
||||
|
||||
actionButton = UIButton()
|
||||
actionButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
actionButton.backgroundColor = UIColor.clearColor()
|
||||
@@ -170,58 +175,58 @@ public class TTGSnackbar: UIView {
|
||||
actionButton.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
|
||||
actionButton.addTarget(self, action: Selector("doAction"), forControlEvents: UIControlEvents.TouchUpInside)
|
||||
self.addSubview(actionButton)
|
||||
|
||||
|
||||
seperateView = UIView()
|
||||
seperateView.translatesAutoresizingMaskIntoConstraints = false
|
||||
seperateView.backgroundColor = UIColor.grayColor()
|
||||
self.addSubview(seperateView)
|
||||
|
||||
|
||||
activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.White)
|
||||
activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
|
||||
activityIndicatorView.stopAnimating()
|
||||
self.addSubview(activityIndicatorView)
|
||||
|
||||
|
||||
// Add constraints
|
||||
let hConstraints: [NSLayoutConstraint] = NSLayoutConstraint.constraintsWithVisualFormat(
|
||||
"H:|-snackbarHorizonMargin-[messageLabel]-[seperateView(1)]-[actionButton]-snackbarHorizonMargin-|",
|
||||
options: NSLayoutFormatOptions(rawValue: 0),
|
||||
metrics: ["snackbarHorizonMargin": TTGSnackbar.snackbarHorizonMargin],
|
||||
views: ["messageLabel": messageLabel, "seperateView": seperateView, "actionButton": actionButton])
|
||||
|
||||
|
||||
let vConstraintsForMessageLabel: [NSLayoutConstraint] = NSLayoutConstraint.constraintsWithVisualFormat(
|
||||
"V:|-[messageLabel]-|",
|
||||
options: NSLayoutFormatOptions(rawValue: 0),
|
||||
metrics: nil,
|
||||
views: ["messageLabel": messageLabel])
|
||||
|
||||
|
||||
let vConstraintsForSeperateView: [NSLayoutConstraint] = NSLayoutConstraint.constraintsWithVisualFormat(
|
||||
"V:|-[seperateView]-|",
|
||||
options: NSLayoutFormatOptions(rawValue: 0),
|
||||
metrics: nil,
|
||||
views: ["seperateView": seperateView])
|
||||
|
||||
|
||||
let vConstraintsForActionButton: [NSLayoutConstraint] = NSLayoutConstraint.constraintsWithVisualFormat(
|
||||
"V:|-[actionButton]-|",
|
||||
options: NSLayoutFormatOptions(rawValue: 0),
|
||||
metrics: nil,
|
||||
views: ["actionButton": actionButton])
|
||||
|
||||
|
||||
actionButtonWidthConstraint = NSLayoutConstraint.init(
|
||||
item: actionButton, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal,
|
||||
toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: TTGSnackbar.snackbarActionButtonWidth)
|
||||
|
||||
|
||||
let vConstraintsForActivityIndicatorView: [NSLayoutConstraint] = NSLayoutConstraint.constraintsWithVisualFormat(
|
||||
"V:|-(2)-[activityIndicatorView]-(2)-|",
|
||||
options: NSLayoutFormatOptions(rawValue: 0),
|
||||
metrics: nil,
|
||||
views: ["activityIndicatorView": activityIndicatorView])
|
||||
|
||||
|
||||
let hConstraintsForActivityIndicatorView: [NSLayoutConstraint] = NSLayoutConstraint.constraintsWithVisualFormat(
|
||||
"H:[activityIndicatorView(activityIndicatorWidth)]-(2)-|",
|
||||
options: NSLayoutFormatOptions(rawValue: 0),
|
||||
metrics: ["activityIndicatorWidth": TTGSnackbar.snackbarHeight - 4],
|
||||
views: ["activityIndicatorView": activityIndicatorView])
|
||||
|
||||
|
||||
self.addConstraints(hConstraints)
|
||||
self.addConstraints(vConstraintsForMessageLabel)
|
||||
self.addConstraints(vConstraintsForSeperateView)
|
||||
@@ -230,12 +235,12 @@ public class TTGSnackbar: UIView {
|
||||
self.addConstraints(hConstraintsForActivityIndicatorView)
|
||||
actionButton.addConstraint(actionButtonWidthConstraint!)
|
||||
}
|
||||
|
||||
|
||||
private func invalidDismissTimer() {
|
||||
dismissTimer?.invalidate()
|
||||
dismissTimer = nil
|
||||
}
|
||||
|
||||
|
||||
private func getTopViewController() -> UIViewController? {
|
||||
var topController: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController
|
||||
while topController?.presentedViewController != nil {
|
||||
@@ -243,32 +248,92 @@ public class TTGSnackbar: UIView {
|
||||
}
|
||||
return topController
|
||||
}
|
||||
|
||||
|
||||
private func dismissAnimated(animated: Bool) {
|
||||
invalidDismissTimer()
|
||||
activityIndicatorView.stopAnimating()
|
||||
TTGSnackbar.currentInstance = nil
|
||||
|
||||
if animated {
|
||||
bottomMarginConstraint?.constant = TTGSnackbar.snackbarHeight
|
||||
self.setNeedsLayout()
|
||||
UIView.animateWithDuration(TTGSnackbar.snackbarAnimationDuration, animations: { () -> Void in
|
||||
self.layoutIfNeeded()
|
||||
}, completion: { (Bool finished) -> Void in
|
||||
self.dismissBlock?(snackbar: self)
|
||||
self.removeFromSuperview()
|
||||
})
|
||||
} else {
|
||||
|
||||
if !animated {
|
||||
dismissBlock?(snackbar: self)
|
||||
self.removeFromSuperview()
|
||||
return
|
||||
}
|
||||
|
||||
var animationBlock: (() -> Void)? = nil
|
||||
|
||||
switch animationType {
|
||||
case .TTGSnackbarAnimationFadeInFadeOut:
|
||||
animationBlock = {self.alpha = 0.0}
|
||||
case .TTGSnackbarAnimationSlideFromBottomBackToBottom:
|
||||
bottomMarginConstraint?.constant = TTGSnackbar.snackbarHeight
|
||||
case .TTGSnackbarAnimationSlideFromBottomToTop:
|
||||
animationBlock = {self.alpha = 0.0}
|
||||
bottomMarginConstraint?.constant = -TTGSnackbar.snackbarHeight - TTGSnackbar.snackbarBottomMargin
|
||||
case .TTGSnackbarAnimationSlideFromLeftToRight:
|
||||
centerXConstraint?.constant = superview!.bounds.width
|
||||
case .TTGSnackbarAnimationSlideFromRightToLeft:
|
||||
centerXConstraint?.constant = -superview!.bounds.width
|
||||
}
|
||||
|
||||
self.setNeedsLayout()
|
||||
UIView.animateWithDuration(TTGSnackbar.snackbarAnimationDuration,
|
||||
delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.2, options: UIViewAnimationOptions.CurveEaseIn,
|
||||
animations: { () -> Void in
|
||||
animationBlock?()
|
||||
self.layoutIfNeeded()
|
||||
}) { (finished) -> Void in
|
||||
self.dismissBlock?(snackbar: self)
|
||||
self.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func showWithAnimation() {
|
||||
var animationBlock: (() -> Void)? = nil
|
||||
|
||||
switch animationType {
|
||||
case .TTGSnackbarAnimationFadeInFadeOut:
|
||||
bottomMarginConstraint?.constant = -TTGSnackbar.snackbarBottomMargin
|
||||
self.layoutIfNeeded()
|
||||
self.alpha = 0.0
|
||||
animationBlock = {self.alpha = 1.0}
|
||||
case .TTGSnackbarAnimationSlideFromBottomBackToBottom, .TTGSnackbarAnimationSlideFromBottomToTop:
|
||||
// Init
|
||||
bottomMarginConstraint?.constant = TTGSnackbar.snackbarHeight
|
||||
self.layoutIfNeeded()
|
||||
// Animation
|
||||
bottomMarginConstraint?.constant = -TTGSnackbar.snackbarBottomMargin
|
||||
case .TTGSnackbarAnimationSlideFromLeftToRight:
|
||||
// Init
|
||||
centerXConstraint?.constant = -superview!.bounds.width
|
||||
bottomMarginConstraint?.constant = -TTGSnackbar.snackbarBottomMargin
|
||||
self.layoutIfNeeded()
|
||||
// Animation
|
||||
centerXConstraint?.constant = 0
|
||||
case .TTGSnackbarAnimationSlideFromRightToLeft:
|
||||
// Init
|
||||
centerXConstraint?.constant = superview!.bounds.width
|
||||
bottomMarginConstraint?.constant = -TTGSnackbar.snackbarBottomMargin
|
||||
self.layoutIfNeeded()
|
||||
// Animation
|
||||
centerXConstraint?.constant = 0
|
||||
}
|
||||
|
||||
self.setNeedsLayout()
|
||||
|
||||
UIView.animateWithDuration(TTGSnackbar.snackbarAnimationDuration,
|
||||
delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: UIViewAnimationOptions.CurveEaseInOut,
|
||||
animations: { () -> Void in
|
||||
animationBlock?()
|
||||
self.layoutIfNeeded()
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
// Button action
|
||||
func doAction() {
|
||||
// Call action block first
|
||||
actionBlock?(snackbar: self)
|
||||
|
||||
|
||||
// Show activity indicator
|
||||
if duration == TTGSnackbarDuration.TTGSnackbarDurationForever && actionButton.hidden == false{
|
||||
actionButton.hidden = true
|
||||
|
||||
Reference in New Issue
Block a user