From 4190c3b2619b4daa3823d8470dee829450beb6f0 Mon Sep 17 00:00:00 2001 From: Nathan Kot Date: Mon, 10 Oct 2016 21:37:35 +0900 Subject: [PATCH] Upgrade to Swift 3.0 Xcode 8 requires that a DevelopmentTeam be set, I've chosen my personal one but you may want to update this with your own :) --- Hermes.xcodeproj/project.pbxproj | 15 ++ Hermes/Hermes.swift | 104 +++++------ Hermes/HermesBulletinView.swift | 204 ++++++++++----------- Hermes/HermesDefaultNotificationView.swift | 82 ++++----- Hermes/HermesNotification.swift | 26 +-- Hermes/HermesNotificationView.swift | 8 +- HermesApp/AppDelegate.swift | 4 +- HermesApp/ViewController.swift | 51 +++--- HermesTests/HermesTests.swift | 2 +- 9 files changed, 253 insertions(+), 243 deletions(-) diff --git a/Hermes.xcodeproj/project.pbxproj b/Hermes.xcodeproj/project.pbxproj index 73b849c..57cf46a 100644 --- a/Hermes.xcodeproj/project.pbxproj +++ b/Hermes.xcodeproj/project.pbxproj @@ -285,12 +285,17 @@ TargetAttributes = { 40B186151B20C88100636EEE = { CreatedOnToolsVersion = 6.3.2; + DevelopmentTeam = U7299KCYL8; + LastSwiftMigration = 0800; + ProvisioningStyle = Automatic; }; 40B186201B20C88100636EEE = { CreatedOnToolsVersion = 6.3.2; + LastSwiftMigration = 0800; }; 40B186351B20C88E00636EEE = { CreatedOnToolsVersion = 6.3.2; + LastSwiftMigration = 0800; }; }; }; @@ -502,7 +507,9 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = U7299KCYL8; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -517,6 +524,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -525,7 +533,9 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = U7299KCYL8; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -539,6 +549,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.imgur.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -557,6 +568,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.imgur.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -571,6 +583,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.imgur.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -588,6 +601,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.imgur.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = HermesApp; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 1; }; name = Debug; @@ -602,6 +616,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.imgur.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = HermesApp; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 1; }; name = Release; diff --git a/Hermes/Hermes.swift b/Hermes/Hermes.swift index 02ce392..7d6da7a 100644 --- a/Hermes/Hermes.swift +++ b/Hermes/Hermes.swift @@ -7,14 +7,14 @@ import AVFoundation :param: notification the notification being made :returns: the notification view, or nil to use HermesDefaultNotificationView */ - optional func hermesNotificationViewForNotification(hermes hermes: Hermes, notification: HermesNotification) -> HermesNotificationView? - + @objc optional func hermesNotificationViewForNotification(hermes: Hermes, notification: HermesNotification) -> HermesNotificationView? + /** :param: hermes the Hermes instance :param: explicit is true if the user closed the bulletin with their finger, instead of relying on autoclose :param: notification the notification that was showing when Hermes was closed */ - optional func hermesDidClose(hermes: Hermes, explicit: Bool, notification: HermesNotification) + @objc optional func hermesDidClose(_ hermes: Hermes, explicit: Bool, notification: HermesNotification) } /** @@ -33,135 +33,135 @@ bugs and still needs to be handled. */ @objc public enum HermesStyle : Int { - case Dark, Light + case dark, light } -public class Hermes: NSObject, HermesBulletinViewDelegate { +open class Hermes: NSObject, HermesBulletinViewDelegate { // MARK: - Public variables // MARK: - Singleton /** You typically will never need to use more than one instance of Hermes */ - public static let sharedInstance = Hermes() - public var style: HermesStyle = .Dark - + open static let sharedInstance = Hermes() + open var style: HermesStyle = .dark + // MARK: - - weak public var delegate: HermesDelegate? - + weak open var delegate: HermesDelegate? + // MARK: - private variables - private var bulletinView: HermesBulletinView? - private var notifications = [HermesNotification]() - + fileprivate var bulletinView: HermesBulletinView? + fileprivate var notifications = [HermesNotification]() + var audioPlayer: AVAudioPlayer? /** When Hermes is waiting, he will collect all of your notifications. Use wait() and go() to tell Hermes when to collect and when to deliver notifications */ - private var waiting = false { + fileprivate var waiting = false { didSet { if !waiting { - showNotifications() + showNotifications() } } } - + // MARK: - Public methods - + /** Give Hermes one notification to post. If waiting == false, you'll see this notification right away - + :param: notification The notification you want Hermes to post */ - public func postNotification(notification: HermesNotification) { + open func postNotification(_ notification: HermesNotification) { postNotifications([notification]) } - + /** Give Hermes an array of notifications to post. If waiting == false, you'll see these notifications right away - + :param: notifications The notifications you want Hermes to post */ - public func postNotifications(notifications: [HermesNotification]) { + open func postNotifications(_ notifications: [HermesNotification]) { self.notifications += notifications - + if let firstNotification = self.notifications.first { if firstNotification.soundPath != nil { prepareSound(path: firstNotification.soundPath!) } } - + showNotifications() } - + /** Tell Hermes to wait and you can queue up multiple notifications */ - public func wait() { + open func wait() { waiting = true } - + /** Done queuing up those notifications? Tell Hermes to go! */ - public func go() { + open func go() { waiting = false showNotifications() } - - public func close() { + + open func close() { bulletinView?.close(explicit: false) } - - public func containsNotification(notification: HermesNotification) -> Bool{ + + open func containsNotification(_ notification: HermesNotification) -> Bool{ if let bulletinView = self.bulletinView { return bulletinView.notifications.contains(notification) } return false } - + // MARK: - private methods - + /** - This method will attempt to show all currently queued up notifications. If Hermes has waiting set to true, + This method will attempt to show all currently queued up notifications. If Hermes has waiting set to true, or if there are not notifications, this method will do nothing */ - private func showNotifications() { + fileprivate func showNotifications() { if waiting || notifications.count == 0 || bulletinView != nil { return } - + bulletinView = HermesBulletinView() - + switch style { - case .Dark: - bulletinView!.style = .Dark - case .Light: - bulletinView!.style = .Light + case .dark: + bulletinView!.style = .dark + case .light: + bulletinView!.style = .light } bulletinView!.delegate = self bulletinView!.notifications = notifications bulletinView!.show() audioPlayer?.play() - - notifications.removeAll(keepCapacity: true) + + notifications.removeAll(keepingCapacity: true) } // Initial setup - func prepareSound(path path: String) { - let sound = NSURL(fileURLWithPath: path) - audioPlayer = try! AVAudioPlayer(contentsOfURL: sound) + func prepareSound(path: String) { + let sound = URL(fileURLWithPath: path) + audioPlayer = try? AVAudioPlayer(contentsOf: sound) audioPlayer!.prepareToPlay() } - + // MARK: - HermesBulletinViewDelegate - - func bulletinViewDidClose(bulletinView: HermesBulletinView, explicit: Bool) { + + func bulletinViewDidClose(_ bulletinView: HermesBulletinView, explicit: Bool) { delegate?.hermesDidClose?(self, explicit: explicit, notification: bulletinView.currentNotification) self.bulletinView = nil showNotifications() } - - func bulletinViewNotificationViewForNotification(notification: HermesNotification) -> HermesNotificationView? { + + func bulletinViewNotificationViewForNotification(_ notification: HermesNotification) -> HermesNotificationView? { return delegate?.hermesNotificationViewForNotification?(hermes: self, notification: notification) } } diff --git a/Hermes/HermesBulletinView.swift b/Hermes/HermesBulletinView.swift index e391b2d..bd49bb5 100644 --- a/Hermes/HermesBulletinView.swift +++ b/Hermes/HermesBulletinView.swift @@ -1,8 +1,8 @@ import UIKit protocol HermesBulletinViewDelegate: class { - func bulletinViewDidClose(bulletinView: HermesBulletinView, explicit: Bool) - func bulletinViewNotificationViewForNotification(notification: HermesNotification) -> HermesNotificationView? + func bulletinViewDidClose(_ bulletinView: HermesBulletinView, explicit: Bool) + func bulletinViewNotificationViewForNotification(_ notification: HermesNotification) -> HermesNotificationView? } let kMargin: CGFloat = 8 @@ -10,20 +10,20 @@ let kNotificationHeight: CGFloat = 98 class HermesBulletinView: UIView, UIScrollViewDelegate, HermesNotificationDelegate { weak var delegate: HermesBulletinViewDelegate? - + var currentNotification: HermesNotification { get { let page = pageFromOffset(scrollView.contentOffset) return notifications[page] } } - + let scrollView = UIScrollView() var backgroundView: UIVisualEffectView? - + var currentPage: Int = 0 - var timer: NSTimer? - + var timer: Timer? + var notifications = [HermesNotification]() { didSet { for notification in notifications { @@ -32,22 +32,22 @@ class HermesBulletinView: UIView, UIScrollViewDelegate, HermesNotificationDelega layoutNotifications() } } - + var tabView = UIView() - var style: HermesStyle = .Dark { + var style: HermesStyle = .dark { didSet { switch style { - case .Light: - blurEffectStyle = .ExtraLight - case .Dark: - blurEffectStyle = .Dark + case .light: + blurEffectStyle = .extraLight + case .dark: + blurEffectStyle = .dark } } } - private var blurEffectStyle: UIBlurEffectStyle = .Dark { + fileprivate var blurEffectStyle: UIBlurEffectStyle = .dark { didSet { backgroundView?.removeFromSuperview() - if blurEffectStyle == .Dark { + if blurEffectStyle == .dark { tabView.backgroundColor = UIColor(white: 1, alpha: 0.6) } else { tabView.backgroundColor = UIColor(white: 0, alpha: 0.1) @@ -55,39 +55,39 @@ class HermesBulletinView: UIView, UIScrollViewDelegate, HermesNotificationDelega remakeBackgroundView() } } - + required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override init(frame: CGRect) { super.init(frame: frame) - + let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(HermesBulletinView.pan(_:))) addGestureRecognizer(panGestureRecognizer) - + remakeBackgroundView() - + scrollView.contentInset = UIEdgeInsetsMake(0, kMargin, 0, kMargin) scrollView.delegate = self - + addSubview(scrollView) addSubview(tabView) } - - private func remakeBackgroundView() { + + fileprivate func remakeBackgroundView() { let blurEffect = UIBlurEffect(style: blurEffectStyle) backgroundView = UIVisualEffectView(effect: blurEffect) - backgroundView?.autoresizingMask = [.FlexibleWidth, .FlexibleTopMargin] - insertSubview(backgroundView!, atIndex: 0) + backgroundView?.autoresizingMask = [.flexibleWidth, .flexibleTopMargin] + insertSubview(backgroundView!, at: 0) } - - func pan(gesture: UIPanGestureRecognizer) { + + func pan(_ gesture: UIPanGestureRecognizer) { timer?.invalidate() - var pan = gesture.translationInView(gesture.view!.superview!) + var pan = gesture.translation(in: gesture.view!.superview!) let startFrame = bulletinFrameInView(gesture.view!.superview!) var frame = startFrame - + var dy: CGFloat = 0 let height = gesture.view?.superview!.bounds.size.height let k = height! * 0.2 @@ -97,13 +97,13 @@ class HermesBulletinView: UIView, UIScrollViewDelegate, HermesNotificationDelega } else { dy = pan.y } - + frame.origin.y += dy self.frame = frame - - if gesture.state == .Ended { + + if gesture.state == .ended { let layoutViewFrame = layoutViewFrameInView(gesture.view!.superview!) - let velocity = gesture.velocityInView(gesture.view!.superview!) + let velocity = gesture.velocity(in: gesture.view!.superview!) if dy > layoutViewFrame.size.height * 0.5 || velocity.y > 500{ close(explicit: true) } else { @@ -111,60 +111,60 @@ class HermesBulletinView: UIView, UIScrollViewDelegate, HermesNotificationDelega } } } - + func animateIn() { let bulletinFrame = bulletinFrameInView(self.superview!) - - UIView.animateWithDuration(0.4, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: UIViewAnimationOptions.BeginFromCurrentState, animations: { + + UIView.animate(withDuration: 0.4, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: UIViewAnimationOptions.beginFromCurrentState, animations: { self.frame = bulletinFrame }, completion: { completed in self.scheduleCloseTimer() }) - + } - - func show(view: UIView = (UIApplication.sharedApplication().windows[0]), animated: Bool = true) { + + func show(_ view: UIView = UIApplication.shared.windows[0], animated: Bool = true) { // Add to main queue in case the view loaded but wasn't added to the window yet. This seems to happen in my storyboard test app - dispatch_async(dispatch_get_main_queue(),{ + DispatchQueue.main.async(execute: { view.addSubview(self) - + let bulletinFrame = self.bulletinFrameInView(self.superview!) var startFrame = bulletinFrame startFrame.origin.y += self.superview!.bounds.size.height - + self.frame = startFrame self.animateIn() }) } - + func scheduleCloseTimer() { if currentNotification.autoClose { - timer = NSTimer.scheduledTimerWithTimeInterval(currentNotification.autoCloseTimeInterval, target: self, selector: #selector(HermesBulletinView.nextPageOrClose), userInfo: nil, repeats: false) + timer = Timer.scheduledTimer(timeInterval: currentNotification.autoCloseTimeInterval, target: self, selector: #selector(HermesBulletinView.nextPageOrClose), userInfo: nil, repeats: false) } } - - func close(explicit explicit: Bool) { + + func close(explicit: Bool) { timer?.invalidate() - - userInteractionEnabled = false - + + isUserInteractionEnabled = false + let startFrame = bulletinFrameInView(superview!) var offScreenFrame = startFrame offScreenFrame.origin.y = superview!.bounds.size.height - - UIView.animateWithDuration(0.2, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: UIViewAnimationOptions.BeginFromCurrentState, animations: { + + UIView.animate(withDuration: 0.2, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: UIViewAnimationOptions.beginFromCurrentState, animations: { self.frame = offScreenFrame }, completion: { completed in self.removeFromSuperview() - self.userInteractionEnabled = true + self.isUserInteractionEnabled = true self.delegate?.bulletinViewDidClose(self, explicit: explicit) }) } - - func contentOffsetForPage(page: Int) -> CGPoint { + + func contentOffsetForPage(_ page: Int) -> CGPoint { let boundsWidth = scrollView.bounds.size.width let pageWidth = boundsWidth - 2 * kMargin - var contentOffset = CGPointMake(pageWidth * CGFloat(page) - scrollView.contentInset.left, scrollView.contentOffset.y) + var contentOffset = CGPoint(x: pageWidth * CGFloat(page) - scrollView.contentInset.left, y: scrollView.contentOffset.y) if contentOffset.x < -scrollView.contentInset.left { contentOffset.x = -scrollView.contentInset.left } else if contentOffset.x + scrollView.contentInset.right + scrollView.bounds.size.width > scrollView.contentSize.width { @@ -172,13 +172,13 @@ class HermesBulletinView: UIView, UIScrollViewDelegate, HermesNotificationDelega } return contentOffset } - + func nextPageOrClose() { currentPage = pageFromOffset(scrollView.contentOffset) let boundsWidth = scrollView.bounds.size.width let pageWidth = boundsWidth - 2 * kMargin let totalPages = Int(scrollView.contentSize.width / pageWidth) - + if currentPage + 1 >= totalPages { close(explicit: false) } else { @@ -191,20 +191,20 @@ class HermesBulletinView: UIView, UIScrollViewDelegate, HermesNotificationDelega CATransaction.commit() } } - - func bulletinFrameInView(view: UIView) -> CGRect { - var bulletinFrame = CGRectMake(0, 0, view.bounds.size.width, view.bounds.size.height * 0.5) + + func bulletinFrameInView(_ view: UIView) -> CGRect { + var bulletinFrame = CGRect(x: 0, y: 0, width: view.bounds.size.width, height: view.bounds.size.height * 0.5) let notificationViewFrame = layoutViewFrameInView(view) - - bulletinFrame.origin = CGPointMake(0, view.bounds.size.height - notificationViewFrame.size.height) + + bulletinFrame.origin = CGPoint(x: 0, y: view.bounds.size.height - notificationViewFrame.size.height) return bulletinFrame } - - func layoutViewFrameInView(view: UIView) -> CGRect { - return CGRectMake(0, 0, view.bounds.size.width, kNotificationHeight) + + func layoutViewFrameInView(_ view: UIView) -> CGRect { + return CGRect(x: 0, y: 0, width: view.bounds.size.width, height: kNotificationHeight) } - - func notificationViewFrameInView(view: UIView) -> CGRect { + + func notificationViewFrameInView(_ view: UIView) -> CGRect { var frame = layoutViewFrameInView(view) frame.origin.x += kMargin frame.size.width -= kMargin * 2 @@ -212,18 +212,18 @@ class HermesBulletinView: UIView, UIScrollViewDelegate, HermesNotificationDelega frame.size.height -= 9 // TODO: configurable return frame } - + func layoutNotifications() { // TODO: handle a relayout -- relaying out this view right now adds duplicate notificationViews if superview == nil { return } - + var notificationViewFrame = notificationViewFrameInView(superview!) - - for (i, notification) in notifications.enumerate() { + + for (i, notification) in notifications.enumerated() { notificationViewFrame.origin.x = CGFloat(i) * notificationViewFrame.size.width - + var notificationView = delegate?.bulletinViewNotificationViewForNotification(notification) if notificationView == nil { notificationView = HermesDefaultNotificationView() @@ -231,78 +231,78 @@ class HermesBulletinView: UIView, UIScrollViewDelegate, HermesNotificationDelega } notificationView!.frame = notificationViewFrame notificationView!.notification = notification - + if notification.action != nil { let tapGesture = UITapGestureRecognizer(target: self, action: #selector(HermesBulletinView.action(_:))) notificationView?.addGestureRecognizer(tapGesture) } - + scrollView.addSubview(notificationView!) } - - scrollView.contentSize = CGSizeMake(CGRectGetMaxX(notificationViewFrame), scrollView.bounds.size.height) + + scrollView.contentSize = CGSize(width: notificationViewFrame.maxX, height: scrollView.bounds.size.height) } - - func action(tapGesture: UITapGestureRecognizer) { + + func action(_ tapGesture: UITapGestureRecognizer) { let notificationView = tapGesture.view as! HermesNotificationView if let notification = notificationView.notification { notification.invokeAction() } } - + override func layoutSubviews() { - backgroundView?.frame = CGRectMake(0, 0, bounds.size.width, superview!.bounds.size.height) - + backgroundView?.frame = CGRect(x: 0, y: 0, width: bounds.size.width, height: superview!.bounds.size.height) + scrollView.frame = bounds - - let tabViewFrame = CGRectMake(0, 9, 32, 5) + + let tabViewFrame = CGRect(x: 0, y: 9, width: 32, height: 5) tabView.frame = tabViewFrame - tabView.center = CGPointMake(bounds.size.width / 2, tabView.center.y) - + tabView.center = CGPoint(x: bounds.size.width / 2, y: tabView.center.y) + layoutNotifications() } - - func pageFromOffset(offset: CGPoint) -> Int { + + func pageFromOffset(_ offset: CGPoint) -> Int { let boundsWidth = scrollView.bounds.size.width let pageWidth = boundsWidth return Int((offset.x + pageWidth * 0.5) / pageWidth) } - + // MARK: - Overrides - override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool { + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { var rect = bounds rect.origin.y -= 44 rect.size.height += 44 - return CGRectContainsPoint(rect, point) + return rect.contains(point) } - + // MARK: - UIScrollViewDelegate - func scrollViewWillBeginDragging(scrollView: UIScrollView) { + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { timer?.invalidate() currentPage = pageFromOffset(scrollView.contentOffset) } - - func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { + + func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { let currentPage = self.currentPage - let targetOffset = targetContentOffset.memory + let targetOffset = targetContentOffset.pointee let targetPage = pageFromOffset(targetOffset) - + var newPage = currentPage if targetPage > currentPage { newPage += 1 } else if targetPage < currentPage { newPage -= 1 } - - targetContentOffset.initialize(self.contentOffsetForPage(newPage)) + + targetContentOffset.initialize(to: self.contentOffsetForPage(newPage)) } - - func scrollViewDidEndDecelerating(scrollView: UIScrollView) { + + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { scheduleCloseTimer() } - + // MARK: - HermesNotificationDelegate - func notificationDidChangeAutoClose(notification: HermesNotification) { + func notificationDidChangeAutoClose(_ notification: HermesNotification) { if notification == currentNotification { if notification.autoClose { scheduleCloseTimer() diff --git a/Hermes/HermesDefaultNotificationView.swift b/Hermes/HermesDefaultNotificationView.swift index f803b54..3c8f53a 100644 --- a/Hermes/HermesDefaultNotificationView.swift +++ b/Hermes/HermesDefaultNotificationView.swift @@ -1,24 +1,24 @@ import UIKit extension UIImageView { - func h_setImage(url url: NSURL) { + func h_setImage(url: URL) { // Using NSURLSession API to fetch image // TODO: maybe change NSURLConfiguration to add things like timeouts and cellular configuration - let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() - let session = NSURLSession(configuration: configuration) - + let configuration = URLSessionConfiguration.default + let session = URLSession(configuration: configuration) + // NSURLRequest Object - let request = NSURLRequest(URL: url) - - let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in + let request = URLRequest(url: url) + + let dataTask = session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: NSError?) -> Void in if error == nil { // Set whatever image attribute to the returned data self.image = UIImage(data: data!)! } else { print(error) } - }) - + } as! (Data?, URLResponse?, Error?) -> Void) + // Start the data task dataTask.resume() } @@ -32,68 +32,66 @@ class HermesDefaultNotificationView: HermesNotificationView { colorView.backgroundColor = notification?.color imageView.image = notification?.image if let imageURL = notification?.imageURL { - imageView.h_setImage(url: imageURL) + imageView.h_setImage(url: imageURL as URL) } layoutSubviews() } } - - var style: HermesStyle = .Dark { + + var style: HermesStyle = .dark { didSet { - textLabel.textColor = style == .Light ? .darkGrayColor() : .whiteColor() + textLabel.textColor = style == .light ? .darkGray : .white } } - - private var imageView = UIImageView() - private var textLabel = UILabel() - private var colorView = UIView() - + + fileprivate var imageView = UIImageView() + fileprivate var textLabel = UILabel() + fileprivate var colorView = UIView() + required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override init(frame: CGRect) { super.init(frame: frame) - imageView.contentMode = .Center + imageView.contentMode = .center imageView.clipsToBounds = true imageView.layer.cornerRadius = 5 - textLabel.textColor = .whiteColor() + textLabel.textColor = .white textLabel.font = UIFont(name: "HelveticaNeue", size: 14) textLabel.numberOfLines = 3 addSubview(imageView) addSubview(textLabel) addSubview(colorView) } - + convenience init(notification: HermesNotification) { - self.init(frame: CGRectZero) + self.init(frame: CGRect.zero) self.notification = notification } - + override func layoutSubviews() { let margin: CGFloat = 4 let colorHeight: CGFloat = 4 - - imageView.hidden = notification?.image == nil && notification?.imageURL == nil - imageView.frame = CGRectMake(margin * 2, 0, 34, 34) - imageView.center.y = CGRectGetMidY(bounds) - colorHeight - - var leftRect = CGRect() - var rightRect = CGRect() - CGRectDivide(bounds, &leftRect, &rightRect, CGRectGetMaxX(imageView.frame), .MinXEdge) - + + imageView.isHidden = notification?.image == nil && notification?.imageURL == nil + imageView.frame = CGRect(x: margin * 2, y: 0, width: 34, height: 34) + imageView.center.y = bounds.midY - colorHeight + + let (_, rightRect) = bounds.divided(atDistance: imageView.frame.maxX, from: .minXEdge) + let space: CGFloat = 20 - let constrainedSize = CGRectInset(rightRect, (space + margin) * 0.5, 0).size - + let constrainedSize = rightRect.insetBy(dx: (space + margin) * 0.5, dy: 0).size + textLabel.frame.size = textLabel.sizeThatFits(constrainedSize) - textLabel.frame.origin.x = CGRectGetMaxX(imageView.frame) + space - textLabel.center.y = CGRectGetMidY(bounds) - colorHeight - - colorView.frame = CGRectMake(margin, bounds.size.height - colorHeight, bounds.size.width - 2 * margin, colorHeight) - + textLabel.frame.origin.x = imageView.frame.maxX + space + textLabel.center.y = bounds.midY - colorHeight + + colorView.frame = CGRect(x: margin, y: bounds.size.height - colorHeight, width: bounds.size.width - 2 * margin, height: colorHeight) + // This centers the text across the whole view, unless that would cause it to block the imageView - textLabel.center.x = CGRectGetMidX(bounds) - let leftBound = CGRectGetMaxX(imageView.frame) + space + textLabel.center.x = bounds.midX + let leftBound = imageView.frame.maxX + space if textLabel.frame.origin.x < leftBound { textLabel.frame.origin.x = leftBound } diff --git a/Hermes/HermesNotification.swift b/Hermes/HermesNotification.swift index 02cc31e..a50abac 100644 --- a/Hermes/HermesNotification.swift +++ b/Hermes/HermesNotification.swift @@ -1,14 +1,14 @@ import UIKit protocol HermesNotificationDelegate: class { - func notificationDidChangeAutoClose(notification: HermesNotification) + func notificationDidChangeAutoClose(_ notification: HermesNotification) } -public class HermesNotification: NSObject { +open class HermesNotification: NSObject { weak var delegate: HermesNotificationDelegate? /// The text of the notification - public var text: String? { + open var text: String? { set(text) { if (text != nil) { attributedText = NSAttributedString(string: text!) @@ -22,26 +22,26 @@ public class HermesNotification: NSObject { } /// Text, but with fancy attributes instead of a regular `String` - public var attributedText: NSAttributedString? + open var attributedText: NSAttributedString? /// The color of the bottom bar on the notification - public var color: UIColor? + open var color: UIColor? /// The image to be displayed along with the notification - public var image: UIImage? + open var image: UIImage? /// The URL to the image - public var imageURL: NSURL? - public var tag: String? + open var imageURL: URL? + open var tag: String? /// The path to the sound to be played along with the notification - public var soundPath: String? + open var soundPath: String? /// The code that should be executed when the notification is tapped on - public var action: ((HermesNotification) -> Void)? = nil + open var action: ((HermesNotification) -> Void)? = nil /// Should the notification close automatically? - public var autoClose = true { + open var autoClose = true { didSet { if oldValue != autoClose { delegate?.notificationDidChangeAutoClose(self) @@ -50,10 +50,10 @@ public class HermesNotification: NSObject { } /// The time interval that the notification should stay on screen before self-dismissing - public var autoCloseTimeInterval: NSTimeInterval = 3 + open var autoCloseTimeInterval: TimeInterval = 3 /// Force the notification to perform it's action - public func invokeAction() { + open func invokeAction() { if action != nil { action!(self) } diff --git a/Hermes/HermesNotificationView.swift b/Hermes/HermesNotificationView.swift index 3e25d1f..e13313f 100644 --- a/Hermes/HermesNotificationView.swift +++ b/Hermes/HermesNotificationView.swift @@ -1,7 +1,7 @@ import UIKit -public class HermesNotificationView: UIView { - public var notification: HermesNotification? +open class HermesNotificationView: UIView { + open var notification: HermesNotification? let contentView = UIView() required public init(coder aDecoder: NSCoder) { @@ -13,8 +13,8 @@ public class HermesNotificationView: UIView { addSubview(contentView) } - public override func layoutSubviews() { + open override func layoutSubviews() { let margin: CGFloat = 8 - contentView.frame = CGRectMake(margin, 0, bounds.size.width - 2 * margin, bounds.size.height - margin) + contentView.frame = CGRect(x: margin, y: 0, width: bounds.size.width - 2 * margin, height: bounds.size.height - margin) } } diff --git a/HermesApp/AppDelegate.swift b/HermesApp/AppDelegate.swift index de19bc0..c3d2ea8 100644 --- a/HermesApp/AppDelegate.swift +++ b/HermesApp/AppDelegate.swift @@ -4,8 +4,8 @@ import UIKit class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - application.statusBarHidden = true + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + application.isStatusBarHidden = true return true } } diff --git a/HermesApp/ViewController.swift b/HermesApp/ViewController.swift index 95c617a..e1bb544 100644 --- a/HermesApp/ViewController.swift +++ b/HermesApp/ViewController.swift @@ -6,59 +6,56 @@ class ViewController: UIViewController, HermesDelegate { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = .whiteColor() + view.backgroundColor = .white hermes.delegate = self - + let notification1 = HermesNotification() notification1.text = "Upload complete! Tap here to show an alert!" notification1.image = UIImage(named: "logo") - notification1.color = .greenColor() + notification1.color = .green notification1.action = { notification in let alert = UIAlertView(title: "Success", message: "Hermes notifications are actionable", delegate: nil, cancelButtonTitle: "Close") alert.show() } - notification1.soundPath = NSBundle.mainBundle().pathForResource("notify", ofType: "wav") - + notification1.soundPath = Bundle.main.path(forResource: "notify", ofType: "wav") + let notification2 = HermesNotification() - + let attributedText = NSMutableAttributedString(string: "Alan ") - attributedText.addAttribute(NSForegroundColorAttributeName, value: UIColor.lightGrayColor(), range: NSMakeRange(0, attributedText.length)) + attributedText.addAttribute(NSForegroundColorAttributeName, value: UIColor.lightGray, range: NSMakeRange(0, attributedText.length)) attributedText.addAttribute(NSFontAttributeName , value: UIFont(name: "Helvetica-Bold", size: 14)!, range: NSMakeRange(0, attributedText.length)) - attributedText.appendAttributedString(NSAttributedString(string: "commented on your ")) - + attributedText.append(NSAttributedString(string: "commented on your ")) + let imageText = NSMutableAttributedString(string: "image") - imageText.addAttribute(NSForegroundColorAttributeName, value: UIColor.greenColor(), range: NSMakeRange(0, imageText.length)) + imageText.addAttribute(NSForegroundColorAttributeName, value: UIColor.green, range: NSMakeRange(0, imageText.length)) imageText.addAttribute(NSFontAttributeName, value: UIFont(name: "Helvetica-Bold", size: 15)!, range: NSMakeRange(0, imageText.length)) - - attributedText.appendAttributedString(imageText) - + + attributedText.append(imageText) + notification2.attributedText = attributedText notification2.image = UIImage(named: "logo") - notification2.color = .redColor() - + notification2.color = .red + let notification3 = HermesNotification() notification3.text = "ATTN: There is a major update to your app! Please go to the app store now and download it! Also, this message is purposely really long." notification3.image = UIImage(named: "logo") - notification3.color = .yellowColor() + notification3.color = .yellow + - - var delayTime = dispatch_time(DISPATCH_TIME_NOW, - Int64(1 * Double(NSEC_PER_SEC))) - dispatch_after(delayTime, dispatch_get_main_queue()) { + var delayTime = DispatchTime.now() + Double(Int64(1 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC) + DispatchQueue.main.asyncAfter(deadline: delayTime) { self.hermes.postNotifications([notification1, notification2, notification3, notification1, notification2, notification3]) } - - delayTime = dispatch_time(DISPATCH_TIME_NOW, - Int64(4 * Double(NSEC_PER_SEC))) - dispatch_after(delayTime, dispatch_get_main_queue()) { + + delayTime = DispatchTime.now() + Double(Int64(4 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC) + DispatchQueue.main.asyncAfter(deadline: delayTime) { self.hermes.postNotifications([notification1, notification2, notification3, notification1, notification2, notification3]) } } - + // MARK: - HermesDelegate - func hermesNotificationViewForNotification(hermes hermes: Hermes, notification: HermesNotification) -> HermesNotificationView? { + func hermesNotificationViewForNotification(hermes: Hermes, notification: HermesNotification) -> HermesNotificationView? { // You can create your own HermesNotificationView subclass and return it here :D (or return nil for the default notification view) return nil } } - diff --git a/HermesTests/HermesTests.swift b/HermesTests/HermesTests.swift index 6e53f71..f962e95 100644 --- a/HermesTests/HermesTests.swift +++ b/HermesTests/HermesTests.swift @@ -20,7 +20,7 @@ class HermesTests: XCTestCase { func testPerformanceExample() { // This is an example of a performance test case. - self.measureBlock() { + self.measure() { // Put the code you want to measure the time of here. } }