├── extensions ├── UITextField+Emptiness.swift ├── UIScreen+Dimensions.swift ├── UINavigationController+Extensions.swift ├── Array+Remove.swift ├── UITableView+ReuseCell.swift ├── UIWindow+Visibility.swift ├── String+Extensions.swift ├── UIImage+Color.swift ├── UICollectionView+ReuseCell.swift └── UIColor+HexString.swift ├── README.md ├── swift-tweaks.podspec ├── BasicOperation.swift ├── ErrorWithAlertExtension.swift ├── LogService.swift └── SwiftTweaks.swift /extensions/UITextField+Emptiness.swift: -------------------------------------------------------------------------------- 1 | public extension UITextField { 2 | 3 | public var isEmpty: Bool { 4 | return text?.isEmpty ?? true 5 | } 6 | 7 | public var isNotEmpty: Bool { 8 | return !isEmpty 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iOS-SwiftTweaks 2 | 3 | -- 4 | ### IMPORTANT: 5 | 6 | **This project is no longer in active development or maintanance.** Check out [pNre/ExSwift](https://github.com/pNre/ExSwift) or [goktugyil/EZSwiftExtensions](https://github.com/goktugyil/EZSwiftExtensions) instead ;-) 7 | -- 8 | 9 | -------------------------------------------------------------------------------- /extensions/UIScreen+Dimensions.swift: -------------------------------------------------------------------------------- 1 | public extension UIScreen { 2 | public class func screenWidth() -> CGFloat! { 3 | return UIScreen.mainScreen().bounds.size.width 4 | } 5 | 6 | public class func screenHeight() -> CGFloat! { 7 | return UIScreen.mainScreen().bounds.size.height 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /extensions/UINavigationController+Extensions.swift: -------------------------------------------------------------------------------- 1 | public extension UINavigationController { 2 | 3 | public var rootViewController: UIViewController { 4 | return self.viewControllers.first! 5 | } 6 | 7 | 8 | public func setTransparentNavigationBar() { 9 | self.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: .Default) 10 | self.navigationBar.shadowImage = UIImage() 11 | self.navigationBar.translucent = true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /swift-tweaks.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'SwiftTweaks' 3 | s.version = '3.0.1' 4 | s.license = 'MIT' 5 | s.summary = 'Set of helper classes and functions for every Xcode project.' 6 | s.homepage = 'https://github.com/VojtaStavik/iOS-SwiftTweaks' 7 | s.social_media_url = 'http://twitter.com/VojtaStavik' 8 | s.authors = { "Vojta Stavik" => "stavik@outlook.com" } 9 | s.source = { :git => 'https://github.com/VojtaStavik/iOS-SwiftTweaks', :tag => s.version } 10 | s.ios.deployment_target = '7.0' 11 | s.source_files = '*.swift', 'extensions/*.swift' 12 | s.frameworks = 'UIKit' 13 | s.requires_arc = true 14 | end 15 | -------------------------------------------------------------------------------- /extensions/Array+Remove.swift: -------------------------------------------------------------------------------- 1 | public extension Array { 2 | 3 | public var isEmpty : Bool { 4 | return count == 0 5 | } 6 | 7 | public var isNotEmpty : Bool { 8 | return !isEmpty 9 | } 10 | 11 | public mutating func removeObject(object: U) { 12 | var index: Int? 13 | 14 | for (idx, objectToCompare) in self.enumerate() { 15 | if let to = objectToCompare as? U { 16 | if object == to { 17 | index = idx 18 | } 19 | } 20 | } 21 | 22 | if let index = index { 23 | self.removeAtIndex(index) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /extensions/UITableView+ReuseCell.swift: -------------------------------------------------------------------------------- 1 | public extension UITableViewCell { 2 | static public var autoReuseIdentifier : String { 3 | 4 | return NSStringFromClass(self) + "AutogeneratedIdentifier" 5 | } 6 | } 7 | 8 | public extension UITableView { 9 | 10 | public func dequeue(cell cell: T.Type, indexPath: NSIndexPath) -> T? { 11 | return dequeueReusableCellWithIdentifier(T.autoReuseIdentifier, forIndexPath: indexPath) as? T 12 | } 13 | 14 | public func registerCell(cell : T.Type) { 15 | registerNib(nibFromClass(cell), forCellReuseIdentifier: cell.autoReuseIdentifier) 16 | } 17 | 18 | // Private 19 | 20 | private func nibFromClass(type: UITableViewCell.Type) -> UINib? { 21 | guard let nibName = NSStringFromClass(type).componentsSeparatedByString(".").last else { 22 | return nil 23 | } 24 | 25 | return UINib(nibName: nibName, bundle: nil) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /extensions/UIWindow+Visibility.swift: -------------------------------------------------------------------------------- 1 | public extension UIWindow { 2 | 3 | public func visibleViewController() -> UIViewController? { 4 | 5 | if let rootViewController: UIViewController = self.rootViewController { 6 | return UIWindow.getVisibleViewControllerFrom(rootViewController) 7 | } 8 | 9 | return nil 10 | } 11 | 12 | public class func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController { 13 | 14 | if let navigationController = vc as? UINavigationController { 15 | 16 | return UIWindow.getVisibleViewControllerFrom(navigationController.visibleViewController!) 17 | 18 | } else if let tabBarController = vc as? UITabBarController { 19 | 20 | return UIWindow.getVisibleViewControllerFrom(tabBarController.selectedViewController!) 21 | 22 | } else if let pageViewController = vc as? UIPageViewController, currentVC = pageViewController.viewControllers?.first { 23 | 24 | return UIWindow.getVisibleViewControllerFrom(currentVC) 25 | 26 | } else { 27 | 28 | if let presentedViewController = vc.presentedViewController { 29 | 30 | return UIWindow.getVisibleViewControllerFrom(presentedViewController) 31 | 32 | } else { 33 | 34 | return vc 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /extensions/String+Extensions.swift: -------------------------------------------------------------------------------- 1 | public extension String { 2 | 3 | public var urlEncodeString: String? { 4 | get { 5 | let allowedCharacters = NSCharacterSet.URLQueryAllowedCharacterSet().mutableCopy() as! NSMutableCharacterSet 6 | allowedCharacters.removeCharactersInString("+/=") 7 | 8 | return self.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacters) 9 | } 10 | } 11 | 12 | public subscript (i: Int) -> Character { 13 | return self[self.startIndex.advancedBy(i) ] 14 | } 15 | 16 | public subscript (i: Int) -> String { 17 | return String(self[i] as Character) 18 | } 19 | 20 | public subscript (r: Range) -> String { 21 | return substringWithRange(Range(start: startIndex.advancedBy(r.startIndex), end: startIndex.advancedBy(r.endIndex))) 22 | } 23 | 24 | public func isValidEmail() -> Bool { 25 | 26 | let regex = try? NSRegularExpression(pattern: "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$", options: [NSRegularExpressionOptions.CaseInsensitive]) 27 | 28 | return regex?.firstMatchInString(self, options: [], range: NSMakeRange(0, self.characters.count)) != nil 29 | } 30 | 31 | public func isLongerThen(input: Int) -> Bool { 32 | return self.characters.count > input 33 | } 34 | 35 | public var isNotEmpty: Bool { 36 | return !isEmpty 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /extensions/UIImage+Color.swift: -------------------------------------------------------------------------------- 1 | public extension UIImage { 2 | 3 | /** 4 | Returns image with size 1x1px of certain color. 5 | */ 6 | public class func imageWithColor(color : UIColor) -> UIImage { 7 | let rect = CGRectMake(0.0, 0.0, 1.0, 1.0) 8 | UIGraphicsBeginImageContext(rect.size) 9 | let context = UIGraphicsGetCurrentContext() 10 | 11 | CGContextSetFillColorWithColor(context, color.CGColor) 12 | CGContextFillRect(context, rect) 13 | 14 | let image = UIGraphicsGetImageFromCurrentImageContext() 15 | UIGraphicsEndImageContext() 16 | 17 | return image 18 | } 19 | 20 | /** 21 | Returns current image colored to certain color. 22 | */ 23 | @available(*, deprecated, message="Use similar build-in XCAssetCatalog functionality.") 24 | public func imageWithColor(color: UIColor) -> UIImage { 25 | UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale) 26 | 27 | let context = UIGraphicsGetCurrentContext() 28 | CGContextTranslateCTM(context, 0, self.size.height) 29 | CGContextScaleCTM(context, 1.0, -1.0) 30 | 31 | 32 | CGContextSetBlendMode(context, .Normal) 33 | 34 | let rect = CGRectMake(0, 0, self.size.width, self.size.height) 35 | CGContextClipToMask(context, rect, self.CGImage) 36 | color.setFill() 37 | CGContextFillRect(context, rect) 38 | 39 | let newImage = UIGraphicsGetImageFromCurrentImageContext() 40 | UIGraphicsEndImageContext() 41 | 42 | return newImage; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /BasicOperation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Martin Prusa on 13/10/15. 3 | // Copyright © 2015 STRV. All rights reserved. 4 | // 5 | 6 | import Foundation 7 | 8 | public class BasicOperation: NSOperation { 9 | 10 | private var _finished : Bool = false 11 | private var _executing : Bool = false 12 | 13 | override public var asynchronous : Bool { 14 | return true 15 | } 16 | 17 | override public var executing : Bool { 18 | 19 | get { return _executing } 20 | 21 | set { 22 | if newValue == true { 23 | willChangeValueForKey("isExecuting") 24 | _executing = newValue 25 | didChangeValueForKey("isExecuting") 26 | } else { 27 | willChangeValueForKey("isExecuting") 28 | _executing = newValue 29 | didChangeValueForKey("isExecuting") 30 | } 31 | } 32 | } 33 | 34 | override public var finished : Bool { 35 | 36 | get { return _finished } 37 | 38 | set { 39 | willChangeValueForKey("isFinished") 40 | _finished = newValue 41 | didChangeValueForKey("isFinished") 42 | } 43 | } 44 | 45 | override public func start() { 46 | 47 | // Always check for cancellation before launching the task. 48 | if self.cancelled { 49 | self.finish() 50 | return 51 | } 52 | 53 | executing = true 54 | self.main() 55 | } 56 | 57 | public func finish() { 58 | executing = false 59 | finished = true 60 | } 61 | 62 | override public func main(){ 63 | // do your implementation 64 | self.finish() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ErrorWithAlertExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorExtension.swift 3 | // Matic 4 | // 5 | // Created by Vojta Stavik on 16/04/15. 6 | // Copyright (c) 2015 STRV. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // If you want to use swift tweaks with extension, set the EXTENSION flag to TRUE 12 | 13 | #if !EXTENSION 14 | 15 | @available(iOS 8.0, *) 16 | public extension LogService { 17 | 18 | public func errorWithAlert(text: String, error: NSError?, _ file: String = __FILE__, _ function: String = __FUNCTION__, _ line: Int = __LINE__) { 19 | showAlert("ERROR", text: error?.localizedDescription ?? text) 20 | self.error(text, error: error, file, function, line) 21 | } 22 | 23 | public func showAlert(title: String, text: String, alert: UIAlertController? = nil) -> UIAlertController { 24 | 25 | let alertToShow : UIAlertController 26 | 27 | if let alert = alert { 28 | alertToShow = alert 29 | } else { 30 | let alert = UIAlertController(title: title, message: text, preferredStyle: .Alert) 31 | alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil)) 32 | alertToShow = alert 33 | } 34 | 35 | delay(0.1) { [weak self] in 36 | if let topVC = UIApplication.sharedApplication().keyWindow?.visibleViewController() where topVC.isBeingDismissed() == false { 37 | topVC.presentViewController(alertToShow, animated: true, completion: nil) 38 | } else { 39 | self?.showAlert(title, text: text, alert: alertToShow) 40 | } 41 | } 42 | 43 | return alertToShow 44 | } 45 | } 46 | 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /extensions/UICollectionView+ReuseCell.swift: -------------------------------------------------------------------------------- 1 | public extension UICollectionReusableView { 2 | 3 | static public var autoReuseIdentifier : String { 4 | return NSStringFromClass(self) + "AutogenerateIdentifier" 5 | } 6 | } 7 | 8 | public extension UICollectionView { 9 | 10 | public var currentPageNumber: Int { 11 | return Int(ceil(self.contentOffset.x / self.frame.size.width)) 12 | } 13 | 14 | public func dequeue(cell cell: T.Type, indexPath: NSIndexPath) -> T? { 15 | return dequeueReusableCellWithReuseIdentifier(T.autoReuseIdentifier, forIndexPath: indexPath) as? T 16 | } 17 | 18 | public func dequeue(header header: T.Type, indexPath: NSIndexPath) -> T? { 19 | return dequeueReusableSupplementaryViewOfKind( 20 | UICollectionElementKindSectionHeader, 21 | withReuseIdentifier: T.autoReuseIdentifier, 22 | forIndexPath: indexPath) as? T 23 | } 24 | 25 | public func dequeue(footer footer: T.Type, indexPath: NSIndexPath) -> T? { 26 | return dequeueReusableSupplementaryViewOfKind( 27 | UICollectionElementKindSectionFooter, 28 | withReuseIdentifier: T.autoReuseIdentifier, 29 | forIndexPath: indexPath) as? T 30 | } 31 | 32 | public func registerCell(cell : T.Type) { 33 | registerNib(nibFromClass(cell), forCellWithReuseIdentifier: cell.autoReuseIdentifier) 34 | } 35 | 36 | public func registerSectionHeader(header : T.Type) { 37 | registerNib(nibFromClass(header), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, 38 | withReuseIdentifier: header.autoReuseIdentifier) 39 | } 40 | 41 | public func registerSectionFooter(footer : T.Type) { 42 | registerNib(nibFromClass(footer), forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, 43 | withReuseIdentifier: footer.autoReuseIdentifier) 44 | } 45 | 46 | // Private 47 | 48 | private func nibFromClass(type: UICollectionReusableView.Type) -> UINib? { 49 | guard let nibName = NSStringFromClass(type).componentsSeparatedByString(".").last else { 50 | return nil 51 | } 52 | 53 | return UINib(nibName: nibName, bundle: nil) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /extensions/UIColor+HexString.swift: -------------------------------------------------------------------------------- 1 | public extension UIColor { 2 | 3 | public convenience init(hexString: String) { 4 | 5 | var red: CGFloat = 0.0 6 | var green: CGFloat = 0.0 7 | var blue: CGFloat = 0.0 8 | var alpha: CGFloat = 1.0 9 | 10 | if hexString.hasPrefix("#") { 11 | let index = hexString.startIndex.advancedBy(1) 12 | let hex = hexString.substringFromIndex(index) 13 | let scanner = NSScanner(string: hex) 14 | var hexValue: CUnsignedLongLong = 0 15 | 16 | if scanner.scanHexLongLong(&hexValue) { 17 | switch (hex.characters.count) { 18 | case 3: 19 | red = CGFloat((hexValue & 0xF00) >> 8) / 15.0 20 | green = CGFloat((hexValue & 0x0F0) >> 4) / 15.0 21 | blue = CGFloat(hexValue & 0x00F) / 15.0 22 | 23 | case 4: 24 | red = CGFloat((hexValue & 0xF000) >> 12) / 15.0 25 | green = CGFloat((hexValue & 0x0F00) >> 8) / 15.0 26 | blue = CGFloat((hexValue & 0x00F0) >> 4) / 15.0 27 | alpha = CGFloat(hexValue & 0x000F) / 15.0 28 | 29 | case 6: 30 | red = CGFloat((hexValue & 0xFF0000) >> 16) / 255.0 31 | green = CGFloat((hexValue & 0x00FF00) >> 8) / 255.0 32 | blue = CGFloat(hexValue & 0x0000FF) / 255.0 33 | 34 | case 8: 35 | red = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0 36 | green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0 37 | blue = CGFloat((hexValue & 0x0000FF00) >> 8) / 255.0 38 | alpha = CGFloat(hexValue & 0x000000FF) / 255.0 39 | 40 | default: 41 | print("Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8") 42 | 43 | } 44 | 45 | } else { 46 | 47 | print("Scan hex error") 48 | } 49 | 50 | } else { 51 | 52 | print("Invalid RGB string, missing '#' as prefix") 53 | } 54 | 55 | self.init(red:red, green:green, blue:blue, alpha:alpha) 56 | } 57 | 58 | public class func colorWithHexString (hex:String) -> UIColor 59 | { 60 | return UIColor(hexString: hex) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /LogService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LogService.swift 3 | // 4 | // Created by Vojta Stavik 5 | // Copyright (c) 2015 STRV. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | public typealias ExternalLogAction = (text: String) -> Void 11 | 12 | public class LogService { 13 | 14 | public static let sharedService = LogService() 15 | 16 | public enum GeneralLogLevel { 17 | 18 | case Debug 19 | case Production 20 | case ProductionWithCrashlogs 21 | } 22 | 23 | 24 | // We use this because of nice readible syntax : 25 | // log.message("Test remote")(.RemoteLogging) 26 | // log.message("Text local") 27 | 28 | public enum ExternalLogActions { 29 | 30 | case RemoteLogging 31 | case None 32 | } 33 | 34 | public var crashLogAction: ExternalLogAction? = nil 35 | public var messageLogAction: ExternalLogAction? = nil 36 | public var errorLogAction: ExternalLogAction? = nil 37 | 38 | public var logLevel : GeneralLogLevel = .Debug 39 | 40 | 41 | typealias Message = String -> String 42 | 43 | private func detailedMessage(file: String = __FILE__, _ function: String = __FUNCTION__, _ line: Int = __LINE__) -> Message { 44 | 45 | return { text -> String in 46 | 47 | let filename = NSURL(string: file)?.URLByDeletingPathExtension?.lastPathComponent 48 | 49 | let messageText = "\n===============" + " \(filename).\(function)[\(line)]: \n " + text + "\n===============" 50 | 51 | return messageText 52 | } 53 | } 54 | 55 | public func message(text: String, _ file: String = __FILE__, _ function: String = __FUNCTION__, _ line: Int = __LINE__) -> (ExternalLogActions -> Void)! { 56 | 57 | let message = detailedMessage(file, function, line) 58 | 59 | 60 | if logLevel == .Debug { 61 | 62 | print(message(text)) 63 | } 64 | 65 | 66 | if logLevel == .ProductionWithCrashlogs { 67 | 68 | crashLogAction?(text: message(text)) 69 | } 70 | 71 | 72 | return { externalLogAction -> Void in 73 | 74 | if externalLogAction == .RemoteLogging { self.messageLogAction?(text: message(text)) } 75 | } 76 | } 77 | 78 | public func error(text: String, error: NSError?, _ file: String = __FILE__, _ function: String = __FUNCTION__, _ line: Int = __LINE__) -> (ExternalLogActions -> Void)! { 79 | 80 | let errorText = error?.description ?? "No NSError object" 81 | 82 | let messageText = "ERROR! \n Message: \(text) \n Error object: \(errorText)" 83 | 84 | message(messageText, file, function, line) 85 | 86 | return { externalLogAction -> Void in 87 | 88 | if externalLogAction == .RemoteLogging { self.errorLogAction?(text: self.detailedMessage(file, function, line)(messageText)) } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /SwiftTweaks.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftTweaks.swift 3 | // 4 | // Created by Vojta Stavik 5 | // Copyright (c) 2015 Vojtech Stavik. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | public let kNavigationBarHeight : CGFloat = 64 12 | 13 | 14 | // MARK: - 15 | 16 | public func delay(delay:Double,closure: ()->()) { 17 | dispatch_after( 18 | 19 | dispatch_time( 20 | 21 | DISPATCH_TIME_NOW, 22 | Int64(delay * Double(NSEC_PER_SEC)) 23 | ), 24 | 25 | dispatch_get_main_queue(), closure) 26 | } 27 | 28 | public func backgroundQueue(closure: ()->()) { 29 | dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), closure) 30 | } 31 | 32 | public func mainQueue(closure: ()->()) { 33 | dispatch_async(dispatch_get_main_queue(), closure) 34 | } 35 | 36 | public extension dispatch_once_t { 37 | 38 | public mutating func once(closure: () -> ()) { 39 | 40 | dispatch_once(&self) { 41 | 42 | closure() 43 | } 44 | } 45 | } 46 | 47 | 48 | // MARK: - 49 | 50 | public func removeNotificationObserver(observer: AnyObject?) { 51 | 52 | if let observer = observer { 53 | 54 | NSNotificationCenter.defaultCenter().removeObserver(observer) 55 | } 56 | } 57 | 58 | 59 | // MARK: - 60 | 61 | public func cycle(times: Int, closure: () -> ()) { 62 | for _ in 0..(minimum: T, maximum: T, value: T) -> T { 68 | return min( max(minimum, value) , maximum) 69 | } 70 | 71 | public func degreesToRadians(degrees:Double) -> CGFloat { 72 | return CGFloat((degrees * M_PI) / 180.0) 73 | } 74 | 75 | 76 | // MARK: - 77 | 78 | public func randomStringWithLength (len : Int) -> String 79 | { 80 | let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 81 | 82 | var randomString = "" 83 | 84 | cycle(len) 85 | { 86 | let rand = Int(arc4random_uniform(UInt32(letters.characters.count))) 87 | randomString.append(letters[rand]) 88 | } 89 | 90 | return randomString 91 | } 92 | 93 | public func printAllAvailableFonts() { 94 | 95 | let fontFamilyNames = UIFont.familyNames() 96 | 97 | for familyName in fontFamilyNames { 98 | print("------------------------------") 99 | print("Font Family Name = [\(familyName)]") 100 | let names = UIFont.fontNamesForFamilyName(familyName ) 101 | print("Font Names = [\(names)]") 102 | } 103 | } 104 | 105 | public func RGB(red: CGFloat,_ green: CGFloat,_ blue: CGFloat) -> UIColor! { 106 | return RGBA(red, green, blue, 1) 107 | } 108 | 109 | public func RGBA(red: CGFloat,_ green: CGFloat,_ blue: CGFloat,_ alpha: CGFloat) -> UIColor! { 110 | return UIColor(red: red/255.0, green: green/255.0, blue: blue/255.0, alpha: alpha) 111 | } 112 | --------------------------------------------------------------------------------