diff --git a/SwiftMessageBar.podspec b/SwiftMessageBar.podspec index e094f21..d98b428 100644 --- a/SwiftMessageBar.podspec +++ b/SwiftMessageBar.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "SwiftMessageBar" - s.version = "1.0.5" + s.version = "1.0.6" s.summary = "A Swift Message Bar" s.description = <<-DESC diff --git a/SwiftMessageBar.xcodeproj/project.pbxproj b/SwiftMessageBar.xcodeproj/project.pbxproj index e978975..b6c68c6 100644 --- a/SwiftMessageBar.xcodeproj/project.pbxproj +++ b/SwiftMessageBar.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + F22ADD2E1B8CB23F00EBA839 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = F22ADD2D1B8CB23F00EBA839 /* Message.swift */; }; F2AB58E81B28B33C001DCC74 /* SwiftMessageBar.h in Headers */ = {isa = PBXBuildFile; fileRef = F2AB58E71B28B33C001DCC74 /* SwiftMessageBar.h */; settings = {ATTRIBUTES = (Public, ); }; }; F2AB58EE1B28B33D001DCC74 /* SwiftMessageBar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F2AB58E21B28B33C001DCC74 /* SwiftMessageBar.framework */; }; F2AB58F51B28B33D001DCC74 /* SwiftMessageBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2AB58F41B28B33D001DCC74 /* SwiftMessageBarTests.swift */; }; @@ -33,6 +34,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + F22ADD2D1B8CB23F00EBA839 /* Message.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = ""; }; F2AB58E21B28B33C001DCC74 /* SwiftMessageBar.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftMessageBar.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F2AB58E61B28B33C001DCC74 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F2AB58E71B28B33C001DCC74 /* SwiftMessageBar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftMessageBar.h; sourceTree = ""; }; @@ -94,6 +96,7 @@ F2AB58E71B28B33C001DCC74 /* SwiftMessageBar.h */, F2AB58E51B28B33C001DCC74 /* Supporting Files */, F2AB59391B28B45A001DCC74 /* SwiftMessageBar.swift */, + F22ADD2D1B8CB23F00EBA839 /* Message.swift */, ); path = SwiftMessageBar; sourceTree = ""; @@ -249,6 +252,7 @@ buildActionMask = 2147483647; files = ( F2AB593A1B28B45A001DCC74 /* SwiftMessageBar.swift in Sources */, + F22ADD2E1B8CB23F00EBA839 /* Message.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/SwiftMessageBar/Message.swift b/SwiftMessageBar/Message.swift new file mode 100644 index 0000000..a38b88e --- /dev/null +++ b/SwiftMessageBar/Message.swift @@ -0,0 +1,207 @@ +// +// Created by Jan Gorman on 25/08/15. +// Copyright (c) 2015 Schnaub. All rights reserved. +// + +import UIKit + +internal protocol Identifiable { + + func id() -> NSUUID + +} + +internal final class Message: UIView, Identifiable { + + private static let Padding: CGFloat = 10 + private static let MessageOffset: CGFloat = 2 + private static let IconSize: CGFloat = 36 + + private let uuid = NSUUID() + private var title: String? + private var message: String? + private var titleFontColor: UIColor! + private var messageFontColor: UIColor! + private var icon: UIImage? + internal var isHit: Bool = false + internal private(set) var callback: Callback? + internal private(set) var duration: NSTimeInterval! + internal private(set) var dismiss: Bool = true + + private var titleFont = UIFont.boldSystemFontOfSize(16) + private var messageFont = UIFont.systemFontOfSize(14) + + private var paragraphStyle: NSMutableParagraphStyle { + let paragraphStyle = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle + paragraphStyle.alignment = .Left + return paragraphStyle + } + + init(title: String?, message: String?, backgroundColor: UIColor, titleFontColor: UIColor, messageFontColor: UIColor, + icon: UIImage?, duration: NSTimeInterval, dismiss: Bool = true, callback: Callback?) { + self.title = title + self.message = message + self.duration = duration + self.callback = callback + self.titleFontColor = titleFontColor + self.messageFontColor = messageFontColor + self.icon = icon + self.dismiss = dismiss + + super.init(frame: CGRectZero) + + self.backgroundColor = backgroundColor + usesAutoLayout(true) + initSubviews() + } + + private func initSubviews() { + let iconImageView = initIcon() + let titleLabel = initTitle() + let messageLabel = initMessage() + + let views = ["icon": iconImageView, "title": titleLabel, "message": messageLabel] + let metrics = [ + "iconTop": Message.Padding, + "titleTop": Message.Padding, + "right": Message.Padding, + "bottom": Message.Padding, + "messageLeft": Message.Padding + Message.MessageOffset, + "iconLeft": Message.Padding, + + "padding": Message.MessageOffset, + "width": Message.IconSize, + "height": Message.IconSize + ] + + addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[icon(==width)]", options: .allZeros, + metrics: metrics, views: views)) + addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[icon(==height)]", options: .allZeros, + metrics: metrics, views: views)) + + addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-iconLeft-[icon]-messageLeft-[title]-right-|", + options: .allZeros, metrics: metrics, views: views)) + addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-iconLeft-[icon]-messageLeft-[message]-right-|", + options: .allZeros, metrics: metrics, views: views)) + addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-titleTop-[title]-padding-[message]-bottom-|", + options: .allZeros, metrics: metrics, views: views)) + addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-iconTop-[icon]", + options: .allZeros, metrics: metrics, views: views)) + } + + private func initIcon() -> UIImageView { + let iconImageView = UIImageView() + iconImageView.image = icon + iconImageView.usesAutoLayout(true) + addSubview(iconImageView) + return iconImageView + } + + private func initTitle() -> UILabel { + let titleLabel = UILabel() + titleLabel.numberOfLines = 0 + titleLabel.usesAutoLayout(true) + addSubview(titleLabel) + + if let title = title { + let attributes = [ + NSFontAttributeName : titleFont, + NSForegroundColorAttributeName: titleFontColor, + NSParagraphStyleAttributeName: paragraphStyle + ] + titleLabel.attributedText = NSAttributedString(string: title, attributes: attributes) + } + return titleLabel + } + + private func initMessage() -> UILabel { + let messageLabel = UILabel() + messageLabel.numberOfLines = 0 + messageLabel.usesAutoLayout(true) + addSubview(messageLabel) + + if let message = message { + let attributes = [ + NSFontAttributeName : messageFont, + NSForegroundColorAttributeName: messageFontColor, + NSParagraphStyleAttributeName: paragraphStyle + ] + messageLabel.attributedText = NSAttributedString(string: message, attributes: attributes) + } + return messageLabel + } + + required init(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override func updateConstraints() { + superview?.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[view]-0-|", options: .allZeros, + metrics: nil, views: ["view": self])) + super.updateConstraints() + } + + override func intrinsicContentSize() -> CGSize { + return CGSize(width: statusBarFrame.width, height: estimatedHeight) + } + + var estimatedHeight: CGFloat { + if icon != nil { + return max(Message.Padding * 2 + titleSize.height + messageSize.height + statusBarOffset, Message.Padding * 2 + Message.IconSize + statusBarOffset) + + } else { + return Message.Padding * 2 + titleSize.height + messageSize.height + statusBarOffset + } + } + + var titleSize: CGSize { + let boundedSize = CGSize(width: availableWidth, height: CGFloat.max) + let titleFontAttributes = [NSFontAttributeName: titleFont] + if let size = title?.boundingRectWithSize(boundedSize, options: .TruncatesLastVisibleLine | .UsesLineFragmentOrigin, attributes: titleFontAttributes, context: nil).size { + return CGSize(width: ceil(size.width), height: ceil(size.height)) + } + return CGSizeZero + } + + var messageSize: CGSize { + let boundedSize = CGSize(width: availableWidth, height: CGFloat.max) + let titleFontAttributes = [NSFontAttributeName: messageFont] + if let size = message?.boundingRectWithSize(boundedSize, options: .TruncatesLastVisibleLine | .UsesLineFragmentOrigin, attributes: titleFontAttributes, context: nil).size { + return CGSize(width: ceil(size.width), height: ceil(size.height)) + } + return CGSizeZero + } + + var statusBarOffset: CGFloat { + return statusBarFrame.height + } + + var width: CGFloat { + return statusBarFrame.width + } + + var statusBarFrame: CGRect { + let windowFrame = UIApplication.sharedApplication().keyWindow!.frame + let statusFrame = UIApplication.sharedApplication().statusBarFrame + return CGRect(x: windowFrame.minX, y: windowFrame.minY, width: windowFrame.width, height: statusFrame.height) + } + + var availableWidth: CGFloat { + return width - Message.Padding * 2 - Message.IconSize + } + + // MARK: Identifiable + + internal func id() -> NSUUID { + return uuid + } + +} + +extension UIView { + + func usesAutoLayout(usesAutoLayout: Bool) { + setTranslatesAutoresizingMaskIntoConstraints(!usesAutoLayout) + } + +} \ No newline at end of file diff --git a/SwiftMessageBar/SwiftMessageBar.swift b/SwiftMessageBar/SwiftMessageBar.swift index 37049af..1149859 100644 --- a/SwiftMessageBar/SwiftMessageBar.swift +++ b/SwiftMessageBar/SwiftMessageBar.swift @@ -29,6 +29,7 @@ public struct MessageBarConfig { self.infoIcon = infoIcon ?? UIImage(named: "icon-info", inBundle: bundle, compatibleWithTraitCollection: nil) self.errorIcon = errorIcon ?? UIImage(named: "icon-error", inBundle: bundle, compatibleWithTraitCollection: nil) } + } public typealias Callback = () -> Void @@ -102,16 +103,16 @@ public final class SwiftMessageBar { } public static func showMessageWithTitle(_ title: String? = nil, message: String? = nil, type: MessageType, - duration: NSTimeInterval = 3, dismiss: Bool = true, callback: Callback? = nil) -> NSUUID { - return SharedMessageBar.showMessageWithTitle(title, message: message, type: type, duration: duration, dismiss: dismiss, callback: callback) + duration: NSTimeInterval = 3, dismiss: Bool = true, callback: Callback? = nil) -> NSUUID { + return SharedMessageBar.showMessageWithTitle(title, message: message, type: type, duration: duration, dismiss: dismiss, callback: callback) } public func showMessageWithTitle(_ title: String? = nil, message: String? = nil, type: MessageType, - duration: NSTimeInterval = 3, dismiss: Bool = true, callback: Callback? = nil) -> NSUUID { - let message = Message(title: title, message: message, backgroundColor: type.backgroundColor(fromConfig: config), titleFontColor: config.titleColor, messageFontColor: config.messageColor, icon: type.image(fromConfig: config), duration: duration, dismiss: dismiss, callback: callback) - messageQueue.enqueue(message) - if !isMessageVisible { - dequeueNextMessage() + duration: NSTimeInterval = 3, dismiss: Bool = true, callback: Callback? = nil) -> NSUUID { + let message = Message(title: title, message: message, backgroundColor: type.backgroundColor(fromConfig: config), titleFontColor: config.titleColor, messageFontColor: config.messageColor, icon: type.image(fromConfig: config), duration: duration, dismiss: dismiss, callback: callback) + messageQueue.enqueue(message) + if !isMessageVisible { + dequeueNextMessage() } return message.id() } @@ -139,7 +140,7 @@ public final class SwiftMessageBar { messageBarView.addSubview(message) messageBarView.bringSubviewToFront(message) isMessageVisible = true - message.frame = CGRect(x: 0, y: -message.height, width: message.width, height: message.height) + message.frame = CGRect(x: 0, y: -message.estimatedHeight, width: message.width, height: message.estimatedHeight) message.hidden = false message.setNeedsUpdateConstraints() @@ -150,7 +151,8 @@ public final class SwiftMessageBar { delay: 0, options: .CurveEaseInOut, animations: { - message.frame = CGRect(x: message.frame.minX, y: message.frame.minY + message.height, width: message.width, height: message.height) + message.frame = CGRect(x: message.frame.minX, y: message.frame.minY + message.estimatedHeight, + width: message.width, height: message.estimatedHeight) }, completion: nil) if message.dismiss { @@ -179,7 +181,8 @@ public final class SwiftMessageBar { delay: 0, options: .CurveEaseInOut, animations: { - message.frame = CGRect(x: message.frame.minX, y: message.frame.minY - message.height, width: message.width, height: message.height) + message.frame = CGRect(x: message.frame.minX, y: message.frame.minY - message.estimatedHeight, + width: message.width, height: message.estimatedHeight) }, completion: { [weak self] _ in @@ -237,243 +240,14 @@ private class MessageBarController: UIViewController { } -private protocol Identifiable { - - func id() -> NSUUID - -} - -private class Message: UIView, Identifiable { - - private static let Padding: CGFloat = 10 - private static let MessageOffset: CGFloat = 2 - private static let IconSize: CGFloat = 36 - - private let uuid = NSUUID() - var title: String? - var message: String? - var duration: NSTimeInterval! - var titleFontColor: UIColor! - var messageFontColor: UIColor! - var icon: UIImage? - var callback: Callback? - var isHit: Bool = false - var dismiss: Bool = true - - var titleFont: UIFont! - var messageFont: UIFont! - - private var iconImageView: UIImageView! - private var titleLabel: UILabel! - private var messageLabel: UILabel! - - private var paragraphStyle: NSMutableParagraphStyle { - let paragraphStyle = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle - paragraphStyle.alignment = .Left - return paragraphStyle - } - - init(title: String?, message: String?, backgroundColor: UIColor, titleFontColor: UIColor, messageFontColor: UIColor, - icon: UIImage?, duration: NSTimeInterval, dismiss: Bool = true, callback: Callback?) { - self.title = title - self.message = message - self.duration = duration - self.callback = callback - self.titleFontColor = titleFontColor - self.messageFontColor = messageFontColor - self.icon = icon - self.dismiss = dismiss - titleFont = UIFont.boldSystemFontOfSize(16) - messageFont = UIFont.systemFontOfSize(14) - - super.init(frame: CGRectZero) - - self.backgroundColor = backgroundColor - usesAutoLayout(true) - initSubviews() - - NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("didChangeOrientation:"), name: UIDeviceOrientationDidChangeNotification, object: nil) - } - - private func initSubviews() { - iconImageView = UIImageView() - iconImageView.image = icon - iconImageView.usesAutoLayout(true) - addSubview(iconImageView) - - titleLabel = UILabel() - titleLabel.numberOfLines = 0 - titleLabel.usesAutoLayout(true) - addSubview(titleLabel) - - messageLabel = UILabel() - messageLabel.numberOfLines = 0 - messageLabel.usesAutoLayout(true) - addSubview(messageLabel) - } - - required init(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - deinit { - NSNotificationCenter.defaultCenter().removeObserver(self) - } - - @objc func didChangeOrientation(notification: NSNotification) { - invalidateIntrinsicContentSize() - setNeedsUpdateConstraints() - } - - override func updateConstraints() { - updateFrameConstraints() - updateIconConstraints() - updateTitleConstraints() - updateMessageConstraints() - super.updateConstraints() - } - - override func intrinsicContentSize() -> CGSize { - return CGSize(width: statusBarFrame.width, height: height) - } - - private func updateFrameConstraints() { - superview?.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[view]-0-|", options: .allZeros, - metrics: nil, views: ["view": self])) - } - - private func updateIconConstraints() { - let views = ["icon": iconImageView] - let metrics = [ - "top": Message.Padding + statusBarOffset, - "left": Message.Padding, - "width": Message.IconSize, - "height": Message.IconSize - ] - - addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[icon(==width)]", options: .allZeros, - metrics: metrics, views: views)) - addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[icon(==height)]", options: .allZeros, - metrics: metrics, views: views)) - addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-top-[icon]", options: .allZeros, - metrics: metrics, views:views)) - addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-left-[icon]", options: .allZeros, - metrics: metrics, views:views)) - } - - private func updateTitleConstraints() { - let views = ["icon": iconImageView, "title": titleLabel] - let metrics = [ - "top": Message.Padding + statusBarOffset - Message.MessageOffset, - "left": Message.Padding + Message.MessageOffset, - "iconLeft": Message.Padding, - "right": Message.Padding - ] - - addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-iconLeft-[icon]-left-[title]-right-|", - options: .allZeros, metrics: metrics, views: views)) - addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-top-[title]", options: .allZeros, - metrics: metrics, views: views)) - - if let title = title { - let attributes = [ - NSFontAttributeName : titleFont, - NSForegroundColorAttributeName: titleFontColor, - NSParagraphStyleAttributeName: paragraphStyle - ] - let attributedTitle = NSAttributedString(string: title, attributes: attributes) - titleLabel.attributedText = attributedTitle - } - } - - private func updateMessageConstraints() { - if let message = message { - let attributes = [ - NSFontAttributeName : messageFont, - NSForegroundColorAttributeName: messageFontColor, - NSParagraphStyleAttributeName: paragraphStyle - ] - let attributedMessage = NSAttributedString(string: message, attributes: attributes) - messageLabel.attributedText = attributedMessage - - let views = ["icon": iconImageView, "title": titleLabel, "message": messageLabel] - let metrics = [ - "top": Message.MessageOffset, - "titleTop": Message.Padding + statusBarOffset - Message.MessageOffset, - "left": Message.Padding + Message.MessageOffset, - "iconLeft": Message.Padding, - "right": Message.Padding, - "bottom": Message.Padding - ] - - addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-iconLeft-[icon]-left-[message]-right-|", - options: .allZeros, metrics: metrics, views: views)) - addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-titleTop-[title]-top-[message]-bottom-|", - options: .allZeros, metrics: metrics, views: views)) - } - } - - var height: CGFloat { - if icon != nil { - return max(Message.Padding * 2 + titleSize.height + messageSize.height + statusBarOffset, Message.Padding * 2 + Message.IconSize + statusBarOffset) - - } else { - return Message.Padding * 2 + titleSize.height + messageSize.height + statusBarOffset - } - } - - var titleSize: CGSize { - let boundedSize = CGSize(width: availableWidth, height: CGFloat.max) - let titleFontAttributes = [NSFontAttributeName: titleFont] - if let size = title?.boundingRectWithSize(boundedSize, options: .TruncatesLastVisibleLine | .UsesLineFragmentOrigin, attributes: titleFontAttributes, context: nil).size { - return CGSize(width: ceil(size.width), height: ceil(size.height)) - } - return CGSizeZero - } - - var messageSize: CGSize { - let boundedSize = CGSize(width: availableWidth, height: CGFloat.max) - let titleFontAttributes = [NSFontAttributeName: messageFont] - if let size = message?.boundingRectWithSize(boundedSize, options: .TruncatesLastVisibleLine | .UsesLineFragmentOrigin, attributes: titleFontAttributes, context: nil).size { - return CGSize(width: ceil(size.width), height: ceil(size.height)) - } - return CGSizeZero - } - - var statusBarOffset: CGFloat { - return statusBarFrame.height - } - - var statusBarFrame: CGRect { - let windowFrame = UIApplication.sharedApplication().keyWindow!.frame - let statusFrame = UIApplication.sharedApplication().statusBarFrame - return CGRect(x: windowFrame.minX, y: windowFrame.minY, width: windowFrame.width, height: statusFrame.height) - } - - var width: CGFloat { - return statusBarFrame.width - } - - var availableWidth: CGFloat { - return width - Message.Padding * 2 - Message.IconSize - } - - // MARK: Identifiable - - private func id() -> NSUUID { - return uuid - } - -} - private struct Queue { - + private var queue = [T]() - + mutating func dequeue() -> T? { return !queue.isEmpty ? queue.removeAtIndex(0) : nil } - + mutating func enqueue(newElement: T) { queue.append(newElement) } @@ -500,13 +274,5 @@ private struct Queue { } return nil } - -} - -extension UIView { - - func usesAutoLayout(usesAutoLayout: Bool) { - setTranslatesAutoresizingMaskIntoConstraints(!usesAutoLayout) - } } \ No newline at end of file