├── GBToolboxUnitTests ├── en.lproj │ └── InfoPlist.strings ├── GBToolboxUnitTests-Prefix.pch ├── GBToolboxUnitTests.h └── GBToolboxUnitTests-Info.plist ├── GBToolbox.xcodeproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── GBToolbox ├── GBAddress.m ├── GBTestView.h ├── GBTypes_OSX.h ├── GBToolbox-Prefix.pch ├── NSData+GBToolbox.h ├── NSImage+GBToolbox.h ├── UISearchBar+GBToolbox.h ├── GBEnumWrapper.h ├── GBSolidColorView.h ├── GBConstants_Common.h ├── GBFrameChangedProtocol.h ├── CALayer+GBToolbox.h ├── GBMessageInterceptor.h ├── UIColor+GBToolbox.h ├── GBUtility_OSX.h ├── UIScreen+GBToolbox.h ├── GBResizableImageView.h ├── UIControl+GBToolbox.h ├── GBTypes_iOS.h ├── GBGlowingImageButtonCell.h ├── GBConstants_Common.m ├── GBDebuggableInstanceVendor.h ├── UISearchBar+GBToolbox.m ├── GBCustomNavigationBar.h ├── NSDate+GBToolbox.h ├── UINavigationBar+GBToolbox.h ├── GBControlAggregator.h ├── NSMutableDictionary+GBToolbox.h ├── UIView+GBOwningViewController.h ├── NSView+GBToolbox.h ├── GBTextView.h ├── GBUtility_OSX.m ├── GBCache.h ├── UIColor+GBToolbox.m ├── NSTimer+GBToolbox.h ├── UITableViewCell+AdditionalViews.h ├── GBGradientView.h ├── UIScrollView+GBToolbox.h ├── CALayer+GBToolbox.m ├── NSInvocation+GBToolbox.h ├── GBAddress.h ├── GBCustomImageButtonCell.h ├── GBRadialGradientView.h ├── GBEnumWrapper.m ├── GBButton.h ├── GBMacros_OSX.h ├── GBToggleControl.h ├── GBSolidColorView.m ├── UIScreen+GBToolbox.m ├── UITableView+GBToolbox.h ├── GBCustomViewButtonCell.h ├── NSImage+GBToolbox.m ├── UINavigationBar+GBToolbox.m ├── NSMutableDictionary+GBToolbox.m ├── GBColoredBarView.h ├── MKMapView+GBToolbox.h ├── UIImageView+GBToolbox.h ├── NSData+GBToolbox.m ├── GBCAAnimationDelegateHandler.h ├── GBTestView.m ├── GBCAAnimationDelegateHandler.m ├── GBCache.m ├── GBMessageInterceptor.m ├── GBCompletable.h ├── GBAsyncOperation.h ├── NSSet+GBToolbox.h ├── GBRoundBadgeView.h ├── GBToolbox-Info.plist ├── GBTableViewController.h ├── GBRemoteDebugMessages.h ├── NSView+GBToolbox.m ├── UIScrollView+GBToolbox.m ├── GBCustomNavigationBar.m ├── NSMapTable+GBToolbox.h ├── NSSet+GBToolbox.m ├── GBToggleControlRadioGroupManager.h ├── NSMapTable+GBToolbox.m ├── NSOrderedSet+GBToolbox.h ├── GBHighPrecisionTimer.h ├── GBKeyboardListener.h ├── NSMutableArray+GBToolbox.h ├── UIImage+GBToolbox.h ├── UITableView+GBToolbox.m ├── NSTimer+GBToolbox.m ├── NSInvocation+GBToolbox.m ├── UIView+GBOwningViewController.m ├── UIViewController+GBToolbox.h ├── GBAlertBadge.h ├── GBGradientView.m ├── UIView+GBPopUp.h ├── NSObject+GBToolbox.h ├── NSOrderedSet+GBToolbox.m ├── UIImageView+GBToolbox.m ├── NSDictionary+GBToolbox.h ├── GBAlertBadgeView.h ├── GBRadialGradientView.m ├── GBTextField.h ├── GBGlowingImageButtonCell.m ├── NSDate+GBToolbox.m ├── MKMapView+GBToolbox.m ├── GBTextBox.h ├── GBAsyncOperation.m ├── GBMultiStateButton.h ├── GBCustomImageButtonCell.m ├── UIControl+GBToolbox.m ├── NSDictionary+GBToolbox.m ├── NSObject+GBToolbox.m ├── UIView+GBFloatingPopoverView.h ├── NSMutableURLRequest+GBToolbox.h ├── NSMutableArray+GBToolbox.m ├── GBToggleControl.m ├── GBCompletable.m ├── GBButton.m ├── GBHighPrecisionTimer.m ├── GBTextView.m ├── UITableViewCell+AdditionalViews.m ├── UIViewController+GBToolbox.m ├── GBFastArray.h ├── GBMacros_iOS.h ├── NSString+GBToolbox.h ├── GBToolbox.h ├── GBToggleControlRadioGroupManager.m ├── GBTypes_Common.h ├── GBKeyboardListener.m ├── UIView+GBToolbox.h ├── GBUtility_iOS.h ├── NSArray+GBToolbox.h ├── GBFastArray.m ├── GBRemoteDebugMessages.m ├── GBRoundBadgeView.m └── GBColoredBarView.m ├── README.md └── .gitignore /GBToolboxUnitTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /GBToolbox.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GBToolbox/GBAddress.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBAddress.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 07/06/2014. 6 | // Copyright (c) 2014 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBAddress.h" 10 | 11 | @implementation GBAddress 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /GBToolboxUnitTests/GBToolboxUnitTests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'GBToolboxUnitTests' target in the 'GBToolboxUnitTests' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #endif 9 | -------------------------------------------------------------------------------- /GBToolbox/GBTestView.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBTestView.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 12/07/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBTestView : UIView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /GBToolbox/GBTypes_OSX.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBTypes_OSX.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 11/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #ifndef GBToolbox_GBTypes_OSX_h 10 | #define GBToolbox_GBTypes_OSX_h 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /GBToolbox/GBToolbox-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'GBToolbox' target in the 'GBToolbox' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #if IPHONE 7 | #import 8 | #import 9 | #else 10 | #import 11 | #endif 12 | #endif 13 | -------------------------------------------------------------------------------- /GBToolbox/NSData+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSData (GBToolbox) 12 | 13 | //md5 hash 14 | -(NSString *)md5; 15 | 16 | @end -------------------------------------------------------------------------------- /GBToolbox/NSImage+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSImage+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSImage (GBToolbox) 12 | 13 | -(void)saveAsJpegWithName:(NSString *)fileName; 14 | 15 | @end -------------------------------------------------------------------------------- /GBToolboxUnitTests/GBToolboxUnitTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBToolboxUnitTests.h 3 | // GBToolboxUnitTests 4 | // 5 | // Created by Luka Mirosevic on 23/11/2012. 6 | // Copyright (c) 2012 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBToolboxUnitTests : SenTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /GBToolbox/UISearchBar+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // UISearchBar+GBToolbox.h 3 | // Pods 4 | // 5 | // Created by Luka Mirosevic on 18/10/2016. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface UISearchBar (GBToolbox) 12 | 13 | /** 14 | Returns the UITextField inside the UISearchBar. 15 | */ 16 | - (nullable UITextField *)textField; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /GBToolbox/GBEnumWrapper.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBEnumWrapper.h 3 | // World Cup Survival Guide 4 | // 5 | // Created by Luka Mirosevic on 06/06/2014. 6 | // Copyright (c) 2014 Goonbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBEnumWrapper : NSObject 12 | 13 | -(id)initWithInt:(int)value; 14 | -(int)intValue; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /GBToolbox/GBSolidColorView.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBSolidColorView.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 13/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBSolidColorView : NSView 12 | 13 | @property (strong, nonatomic) NSColor *color; 14 | 15 | -(id)initWithColor:(NSColor *)color; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /GBToolbox/GBConstants_Common.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBConstants_Common.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 15/05/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | //Exceptions 10 | extern NSString * const GBTypeMismatchException; 11 | extern NSString * const GBUnexpectedMessageException; 12 | 13 | //Constants 14 | extern NSUInteger const kGBSearchResultNotFound; -------------------------------------------------------------------------------- /GBToolbox/GBFrameChangedProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // FrameChangedProtocol.h 3 | // Russia 4 | // 5 | // Created by Luka Mirosevic on 02/07/2013. 6 | // Copyright (c) 2013 Goonbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol GBFrameChangedProtocol 12 | @required 13 | 14 | -(void)viewController:(id)viewController didChangeFrameTo:(CGRect)frame; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /GBToolbox/CALayer+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 11/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CALayer (GBToolbox) 12 | 13 | //enumarate all sublayers depth first 14 | -(void)enumerateSublayersWithBlock:(void(^)(CALayer *layer))block; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /GBToolbox/GBMessageInterceptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBMessageInterceptor.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 21/06/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBMessageInterceptor : NSObject 12 | 13 | @property (nonatomic, assign) id receiver; 14 | @property (nonatomic, assign) id middleMan; 15 | 16 | @end -------------------------------------------------------------------------------- /GBToolbox/UIColor+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 28/08/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIColor (GBToolbox) 12 | 13 | //returns a random color (random hue with brightness: 0.5, saturation: 0.5, alpha: 1.0) 14 | +(UIColor *)randomColor; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /GBToolbox/GBUtility_OSX.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBUtility_OSX.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBTypes_Common.h" 10 | #import "GBTypes_OSX.h" 11 | 12 | #import "GBUtility_Common.h" 13 | 14 | @interface GBToolbox (OSX) 15 | 16 | #pragma mark - App Store redirect 17 | 18 | void RedirectToAppStore(NSString *appID); 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /GBToolbox/UIScreen+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIScreen+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 23/02/16. 6 | // Copyright © 2016 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIScreen (GBToolbox) 12 | 13 | /** 14 | Returns the bounds of the display, irrespective of the rotation. 15 | */ 16 | @property (assign, nonatomic, readonly) CGRect fixedBounds; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /GBToolbox/GBResizableImageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBResizableImageView.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 04/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "GBToolbox.h" 12 | 13 | @interface GBResizableImageView : NSImageView 14 | 15 | @property (strong, atomic) NSImage *image; 16 | @property (assign, nonatomic) GBEdgeInsets capInsets; 17 | 18 | @end -------------------------------------------------------------------------------- /GBToolbox/UIControl+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 20/02/2016. 6 | // Copyright © 2016 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void(^GBActionBlock)(id sender, UIEvent *event); 12 | 13 | @interface UIControl (GBToolbox) 14 | 15 | - (void)addTargetActionForControlEvents:(UIControlEvents)controlEvents withBlock:(GBActionBlock)block; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /GBToolbox/GBTypes_iOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBTypes_iOS.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 11/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #ifndef GBToolbox_GBTypes_iOS_h 10 | #define GBToolbox_GBTypes_iOS_h 11 | 12 | #pragma mark - Device stuff 13 | 14 | typedef enum { 15 | Phone, 16 | Pad 17 | } GBDevice; 18 | 19 | typedef enum { 20 | Portrait, 21 | Landscape 22 | } GBOrientation; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /GBToolbox/GBGlowingImageButtonCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBGlowingImageButtonCell.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 13/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBCustomImageButtonCell.h" 10 | 11 | @interface GBGlowingImageButtonCell : GBCustomImageButtonCell 12 | 13 | @property (assign, nonatomic) BOOL isGlowing; 14 | @property (assign, nonatomic) GBEdgeInsets glowEdgeInsets; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /GBToolbox/GBConstants_Common.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBConstants_Common.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 15/05/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBConstants_Common.h" 10 | 11 | NSString * const GBTypeMismatchException = @"GBTypeMismatchException"; 12 | NSString * const GBUnexpectedMessageException = @"GBUnexpectedMessageException"; 13 | 14 | NSUInteger const kGBSearchResultNotFound = NSNotFound; -------------------------------------------------------------------------------- /GBToolbox/GBDebuggableInstanceVendor.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBDebuggableInstanceVendor.h 3 | // BindSafe 4 | // 5 | // Created by Luka Mirosevic on 28/03/16. 6 | // Copyright © 2016 Goonbee e.U. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol GBDebuggableInstanceVendor 12 | 13 | @required 14 | 15 | /** 16 | Convenience method for creating a new instance for debugging purposes. 17 | */ 18 | + (instancetype)debuggingInstance; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /GBToolbox/UISearchBar+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // UISearchBar+GBToolbox.m 3 | // Pods 4 | // 5 | // Created by Luka Mirosevic on 18/10/2016. 6 | // 7 | // 8 | 9 | #import "UISearchBar+GBToolbox.h" 10 | 11 | @implementation UISearchBar (GBToolbox) 12 | 13 | - (nullable UITextField *)textField { 14 | if (@available(iOS 13, *)) { 15 | return [self searchTextField]; 16 | } else { 17 | return (UITextField *)[self valueForKey:@"searchField"]; 18 | } 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /GBToolbox/GBCustomNavigationBar.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBCustomNavigationBar.h 3 | // Xmas List 4 | // 5 | // Created by Luka Mirosevic on 29/11/2012. 6 | // Copyright (c) 2012 Goonbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBCustomNavigationBar : UINavigationBar 12 | 13 | @property (assign, nonatomic, readonly) CGFloat navBarHeight; 14 | 15 | +(Class)navigationBarClassWithHeight:(CGFloat)height; 16 | -(id)initWithNavBarHeight:(CGFloat)height; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /GBToolbox/NSDate+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 21/06/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSDate (GBToolbox) 12 | 13 | +(NSDate *)dateWithISO8601String:(NSString *)dateString; 14 | -(NSString *)iso8601String; 15 | -(BOOL)isInPast; 16 | -(BOOL)isDateBetweenStartDate:(NSDate*)beginDate andEndDate:(NSDate*)endDate; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /GBToolbox/UINavigationBar+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // UINavigationBar+GBToolbox.h 3 | // Pods 4 | // 5 | // Created by Luka Mirosevic on 18/09/2016. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface UINavigationBar (GBToolbox) 12 | 13 | /** 14 | Set the navigation bar background color with a more sane behaviour than the standard Cocoa Touch methods. 15 | 16 | If color is nil, the bar will be transparent. 17 | */ 18 | - (void)styleWithColor:(nullable UIColor *)color; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /GBToolbox/GBControlAggregator.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBControlAggregator.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 20/02/2016. 6 | // Copyright © 2016 Goonbee e.U. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBControlAggregator : UIControl 12 | 13 | @property (strong, nonatomic, readonly) NSArray<__kindof UIControl *> *controls; 14 | 15 | - (instancetype)initWithControls:(NSArray *)controls axis:(UILayoutConstraintAxis)axis; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /GBToolbox/NSMutableDictionary+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableDictionary+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 06/06/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSMutableDictionary (GBToolbox) 12 | 13 | #pragma mark - Pruning 14 | 15 | /** 16 | Removes all intances of NSNull from this dictionary (shallow search only). 17 | */ 18 | - (void)pruneNulls; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /GBToolbox/UIView+GBOwningViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+GBOwningViewController.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 08/06/2017. 6 | // Copyright (c) 2017 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIView (GBOwningViewController) 12 | 13 | /** 14 | The view controller that currently has this view set as its `view` property. 15 | */ 16 | @property (weak, nonatomic, nullable, readonly) UIViewController *GBOwningViewController; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /GBToolbox/NSView+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSView+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSView (GBToolbox) 12 | 13 | //returns the view's frame in global coordinates 14 | @property (assign, nonatomic, readonly) NSRect globalFrame; 15 | 16 | //enumarate all subviews depth first 17 | -(void)enumerateSubviewsWithBlock:(void(^)(NSView *view))block; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /GBToolbox/GBTextView.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBTextView.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 28/06/2013. 6 | // Copyright (c) 2013 Goonbee. All rights reserved. 7 | // 8 | // Original code from SO: http://stackoverflow.com/a/1704469/399772 9 | 10 | #import 11 | 12 | @interface GBTextView : UITextView 13 | 14 | @property (nonatomic, retain) NSString *placeholder; 15 | @property (nonatomic, retain) UIColor *placeholderColor; 16 | 17 | -(void)textChanged:(NSNotification*)notification; 18 | 19 | @end -------------------------------------------------------------------------------- /GBToolbox/GBUtility_OSX.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBUtility_OSX.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBUtility_OSX.h" 10 | 11 | @implementation GBToolbox (OSX) 12 | 13 | #pragma mark - App Store redirect 14 | 15 | void RedirectToAppStore(NSString *appID) { 16 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"macappstore://itunes.apple.com/app/id%@?mt=12", appID]]]; 17 | } 18 | 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /GBToolbox/GBCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBCache.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 10/04/2014. 6 | // Copyright (c) 2014 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSUInteger const kGBCacheUnlimitedCacheSize; 12 | 13 | @interface GBCache : NSCache 14 | 15 | @property (assign, nonatomic) NSUInteger maxCacheSize;//default: kGBCacheUnlimitedCacheSize 16 | 17 | -(id)objectForKeyedSubscript:(id)key; 18 | -(void)setObject:(id)obj forKeyedSubscript:(id)key; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /GBToolbox/UIColor+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 28/08/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "UIColor+GBToolbox.h" 10 | 11 | #define ARC4RANDOM_MAX 0x100000000 12 | 13 | @implementation UIColor (GBToolbox) 14 | 15 | +(UIColor *)randomColor { 16 | CGFloat randomHue = ((double)arc4random() / ARC4RANDOM_MAX); 17 | return [UIColor colorWithHue:randomHue saturation:0.5 brightness:0.5 alpha:1]; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /GBToolbox/NSTimer+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimer+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSTimer (GBToolbox) 12 | 13 | //blocks 14 | +(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats withBlock:(void(^)(void))block; 15 | +(NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats withBlock:(void(^)(void))block; 16 | 17 | @end -------------------------------------------------------------------------------- /GBToolbox/UITableViewCell+AdditionalViews.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewCell+AdditionalViews.h 3 | // Russia 4 | // 5 | // Created by Luka Mirosevic on 27/06/2013. 6 | // Copyright (c) 2013 Goonbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UITableViewCell (AdditionalViews) 12 | 13 | @property (strong, nonatomic) UIView *leftView; 14 | @property (assign, nonatomic) CGRect leftViewFrame; 15 | @property (strong, nonatomic) UIView *rightView; 16 | @property (assign, nonatomic) CGRect rightViewFrame; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /GBToolbox/GBGradientView.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBGradientView.h 3 | // Pods 4 | // 5 | // Created by Luka Mirosevic on 13/11/2014. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface GBGradientView : UIView 12 | 13 | typedef NS_ENUM(NSInteger, GBGradientDimension) { 14 | GBGradientDimensionVertical, 15 | GBGradientDimensionHorizontal, 16 | }; 17 | 18 | /** 19 | Creates a GBGradientView with it's background set to a gradient. 20 | */ 21 | + (instancetype)gradientViewWithColors:(NSArray *)colors dimension:(GBGradientDimension)dimension; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /GBToolbox/UIScrollView+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 27/06/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIScrollView (GBToolbox) 12 | 13 | //Scrolling 14 | -(void)scrollToOriginAnimated:(BOOL)animated; 15 | -(void)scrollToTopAnimated:(BOOL)animated; 16 | -(void)scrollToBottomAnimated:(BOOL)animated; 17 | -(void)scrollToLeftAnimated:(BOOL)animated; 18 | -(void)scrollToRightAnimated:(BOOL)animated; 19 | 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /GBToolbox/CALayer+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 11/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "CALayer+GBToolbox.h" 10 | 11 | @implementation CALayer (GBToolbox) 12 | 13 | -(void)enumerateSublayersWithBlock:(void(^)(CALayer *layer))block { 14 | //itself 15 | block(self); 16 | 17 | //then all its subviews 18 | for (CALayer *sublayer in self.sublayers) { 19 | [sublayer enumerateSublayersWithBlock:block]; 20 | } 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /GBToolbox/NSInvocation+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSInvocation+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 30/04/2014. 6 | // Copyright (c) 2014 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSInvocation (GBToolbox) 12 | 13 | +(NSInvocation *)invocationWithTarget:(id)target selector:(SEL)selector arguments:(void *)argument, ... NS_REQUIRES_NIL_TERMINATION; 14 | +(NSInvocation *)invocationWithTarget:(id)target selector:(SEL)selector argument:(void *)argument argumentList:(va_list)argumentList; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /GBToolbox/GBAddress.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBAddress.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 07/06/2014. 6 | // Copyright (c) 2014 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBAddress : NSObject 12 | 13 | @property (copy, nonatomic) NSString *street; 14 | @property (copy, nonatomic) NSString *city; 15 | @property (copy, nonatomic) NSString *postCode; 16 | @property (copy, nonatomic) NSString *country; 17 | @property (copy, nonatomic) NSString *countryCode; 18 | @property (copy, nonatomic) NSString *state; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /GBToolbox/GBCustomImageButtonCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBCustomImageButtonCell.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 13/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBCustomViewButtonCell.h" 10 | 11 | #import "GBTypes_Common.h" 12 | 13 | @interface GBCustomImageButtonCell : GBCustomViewButtonCell 14 | 15 | @property (strong, nonatomic) NSImage *backgroundImage; 16 | @property (assign, nonatomic) GBEdgeInsets backgroundImageCapInsets; 17 | @property (strong, nonatomic) NSImage *foregroundImage; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /GBToolbox/GBRadialGradientView.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBRadialGradientView.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 11/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "GBToolbox.h" 12 | 13 | @interface GBRadialGradientView : NSView 14 | 15 | @property (strong, nonatomic) NSColor *innerColor; 16 | @property (strong, nonatomic) NSColor *outerColor; 17 | @property (assign, nonatomic) CGFloat cornerRadius; 18 | @property (assign, nonatomic) GBEdgeInsets edgeInsets; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /GBToolbox/GBEnumWrapper.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBEnumWrapper.m 3 | // World Cup Survival Guide 4 | // 5 | // Created by Luka Mirosevic on 06/06/2014. 6 | // Copyright (c) 2014 Goonbee. All rights reserved. 7 | // 8 | 9 | #import "GBEnumWrapper.h" 10 | 11 | @interface GBEnumWrapper () 12 | 13 | @property (assign, nonatomic) int value; 14 | 15 | @end 16 | 17 | @implementation GBEnumWrapper 18 | 19 | -(id)initWithInt:(int)value { 20 | if (self = [super init]) { 21 | self.value = value; 22 | } 23 | 24 | return self; 25 | } 26 | 27 | -(int)intValue { 28 | return self.value; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /GBToolbox/GBButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBButton.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 28/06/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBButton : UIButton 12 | 13 | @property (strong, nonatomic) UIView *leftView; 14 | @property (assign, nonatomic) CGFloat leftViewLeftOffset; 15 | @property (assign, nonatomic) CGFloat leftViewVerticalOffset; 16 | @property (strong, nonatomic) UIView *rightView; 17 | @property (assign, nonatomic) CGFloat rightViewRightOffset; 18 | @property (assign, nonatomic) CGFloat rightViewVerticalOffset; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /GBToolbox/GBMacros_OSX.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBMacros_OSX.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #ifndef GBToolbox_GBMacros_OSX_h 10 | #define GBToolbox_GBMacros_OSX_h 11 | 12 | #import 13 | 14 | //View controller instantiation 15 | static inline id InstantiateViewControllerWithXib(NSString *xibName) { 16 | NSString *className = [xibName stringByAppendingString:@"ViewController"]; 17 | return [([NSClassFromString(className) alloc]) initWithNibName:className bundle:nil]; 18 | } 19 | #define vc(xibName) InstantiateViewControllerWithXib(xibName) 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /GBToolbox/GBToggleControl.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBToggleControl.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 28/06/2013. 6 | // Copyright (c) 2013 Goonbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBToggleControl : UIControl 12 | 13 | @property (strong, nonatomic) IBInspectable UIImage *imageWhenOff; 14 | @property (strong, nonatomic) IBInspectable UIImage *imageWhenOn; 15 | @property (strong, nonatomic) IBInspectable UIImage *backgroundImageWhenOff; 16 | @property (strong, nonatomic) IBInspectable UIImage *backgroundImageWhenOn; 17 | 18 | @property (assign, nonatomic) IBInspectable BOOL isOn; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /GBToolbox/GBSolidColorView.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBSolidColorView.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 13/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBSolidColorView.h" 10 | 11 | @implementation GBSolidColorView 12 | 13 | -(id)initWithColor:(NSColor *)color { 14 | if (self = [super init]) { 15 | self.color = color; 16 | } 17 | 18 | return self; 19 | } 20 | 21 | -(void)setColor:(NSColor *)color { 22 | _color = color; 23 | 24 | [self setNeedsDisplay:YES]; 25 | } 26 | 27 | - (void)drawRect:(NSRect)dirtyRect { 28 | [self.color setFill]; 29 | NSRectFill(dirtyRect); 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /GBToolbox/UIScreen+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIScreen+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 23/02/16. 6 | // Copyright © 2016 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "UIScreen+GBToolbox.h" 10 | 11 | @implementation UIScreen (GBToolbox) 12 | 13 | - (CGRect)fixedBounds { 14 | // iOS 8+ 15 | if ([[UIScreen mainScreen] respondsToSelector:@selector(fixedCoordinateSpace)]) { 16 | return [[UIScreen mainScreen].coordinateSpace convertRect:[UIScreen mainScreen].bounds toCoordinateSpace:[UIScreen mainScreen].fixedCoordinateSpace]; 17 | } 18 | // iOS 7 and prior 19 | else { 20 | return [UIScreen mainScreen].bounds; 21 | } 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /GBToolbox/UITableView+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UITableView (GBToolbox) 12 | 13 | //detecting when the tableview is scrolled fully down 14 | @property (nonatomic, readonly) BOOL isScrolledToBottom; 15 | -(BOOL)isScrolledToBottomWithTolerance:(CGFloat)tolerance; 16 | 17 | //updates the tableview header height 18 | -(void)recalculateHeaderHeightAnimated:(BOOL)animated; 19 | 20 | //lets you change the height of the header view 21 | -(void)updateHeaderHeightTo:(CGFloat)newHeight animated:(BOOL)animated; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /GBToolbox/GBCustomViewButtonCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBCustomViewButtonCell.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 13/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBCustomViewButtonCell : NSButtonCell 12 | 13 | @property (strong, nonatomic) NSView *customView; 14 | @property (assign, nonatomic) BOOL shouldDarkenOnTouch; 15 | @property (copy, nonatomic) NSString *text; 16 | @property (copy, nonatomic) NSAttributedString *attributedText; 17 | @property (strong, nonatomic) NSColor *textColor; 18 | @property (strong, atomic) NSFont *font; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /GBToolbox/NSImage+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSImage+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "NSImage+GBToolbox.h" 10 | 11 | @implementation NSImage (GBToolbox) 12 | 13 | -(void)saveAsJpegWithName:(NSString*)fileName { 14 | NSData *imageData = [self TIFFRepresentation]; 15 | NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData]; 16 | NSDictionary *imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:1.0] forKey:NSImageCompressionFactor]; 17 | imageData = [imageRep representationUsingType:NSJPEGFileType properties:imageProps]; 18 | [imageData writeToFile:fileName atomically:NO]; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /GBToolboxUnitTests/GBToolboxUnitTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.goonbee.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /GBToolbox/UINavigationBar+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // UINavigationBar+GBToolbox.m 3 | // Pods 4 | // 5 | // Created by Luka Mirosevic on 18/09/2016. 6 | // 7 | // 8 | 9 | #import "UINavigationBar+GBToolbox.h" 10 | 11 | @implementation UINavigationBar (GBToolbox) 12 | 13 | - (void)styleWithColor:(nullable UIColor *)color { 14 | // an actual color 15 | if (color && ![color isEqual:[UIColor clearColor]]) { 16 | [self setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault]; 17 | self.shadowImage = nil; 18 | self.barTintColor = color; 19 | } 20 | // no color -> seethrough bar 21 | else { 22 | [self setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; 23 | self.shadowImage = [UIImage new]; 24 | self.barTintColor = nil; 25 | } 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /GBToolbox/NSMutableDictionary+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableDictionary+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 06/06/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "NSMutableDictionary+GBToolbox.h" 10 | 11 | #import "NSDictionary+GBToolbox.h" 12 | 13 | @implementation NSMutableDictionary (GBToolbox) 14 | 15 | #pragma mark - pruning 16 | 17 | - (void)pruneNulls { 18 | // create a pruned copy 19 | NSDictionary *prunedVersion = [NSDictionary dictionaryByPruningNullsInDictionary:self]; 20 | 21 | // now reassing everything in so that this object retains it's identity 22 | [self removeAllObjects]; 23 | for (id key in prunedVersion) { 24 | [self setObject:[prunedVersion objectForKey:key] forKey:key]; 25 | } 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /GBToolbox/GBColoredBarView.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBColoredBarView.h 3 | // Russia 4 | // 5 | // Created by Luka Mirosevic on 27/06/2013. 6 | // Copyright (c) 2013 Goonbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBColoredBarView : UIView 12 | 13 | @property (strong, nonatomic) NSArray *colors; 14 | @property (strong, nonatomic) NSArray *fractions; 15 | @property (strong, nonatomic) UIColor *backgroundColorWhenEmpty; 16 | @property (strong, nonatomic) UIColor *backgroundColorWhenFull; 17 | @property (strong, nonatomic) UIColor *borderColor; 18 | @property (assign, nonatomic) CGFloat borderThickness; 19 | @property (assign, nonatomic) BOOL showBorderWhenFull; 20 | @property (assign, nonatomic) BOOL showBorderWhenEmpty; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /GBToolbox/MKMapView+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // MKMapView+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 10/07/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MKMapView (GBToolbox) 12 | 13 | //pans and zooms the map to include the points in the locationsArray, which should be CLLocation objects. second method lets you zoom out a little so the contained points arent right on the edges 14 | -(void)moveToRegionIncludingLocations:(NSArray *)locationsArray animated:(BOOL)animated; 15 | -(void)moveToRegionIncludingLocations:(NSArray *)locationsArray withPaddingFactor:(CGFloat)paddingFactor animated:(BOOL)animated; 16 | 17 | //radius in meters 18 | -(void)moveToLocation:(CLLocation *)location showingRadius:(CGFloat)radius animated:(BOOL)animated; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /GBToolbox/UIImageView+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 28/06/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIImageView (GBToolbox) 12 | 13 | /** 14 | Returns a UIImageView with its image set to image, and its frame size set to the image size 15 | */ 16 | + (UIImageView *)imageViewWithImage:(UIImage *)image; 17 | 18 | /** 19 | Returns a UIImageView with its image set to the image with name imageName, and its frame size set to the image size. 20 | */ 21 | + (UIImageView *)imageViewWithImageNamed:(NSString *)imageName; 22 | 23 | /** 24 | Returns the image bounds of the displayed image inside the receiver. Useful when the contentMode is aspect fit. 25 | */ 26 | - (CGRect)displayedImageBounds; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /GBToolbox/NSData+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "NSData+GBToolbox.h" 10 | 11 | #import 12 | 13 | @implementation NSData (NSData_GBUtil) 14 | 15 | //md5 hash 16 | -(NSString *)md5 { 17 | unsigned char result[16]; 18 | CC_MD5(self.bytes, (CC_LONG)self.length, result); 19 | 20 | return [NSString stringWithFormat: @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 21 | result[0], result[1], result[2], result[3], 22 | result[4], result[5], result[6], result[7], 23 | result[8], result[9], result[10], result[11], 24 | result[12], result[13], result[14], result[15] 25 | ]; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /GBToolbox/GBCAAnimationDelegateHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBCAAnimationDelegateHandler.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 16/09/2016. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | 12 | typedef void(^GBCAAnimationDidStartBlock)(CAAnimation * _Nonnull animation); 13 | typedef void(^GBCAAnimationDidStopBlock)(CAAnimation * _Nonnull animation, BOOL finished); 14 | 15 | @interface GBCAAnimationDelegateHandler : NSObject 16 | 17 | + (nonnull instancetype)delegateWithDidStart:(nullable GBCAAnimationDidStartBlock)didStart didStop:(nullable GBCAAnimationDidStopBlock)didStop; 18 | 19 | @end 20 | 21 | @interface GBCAAnimationDelegateHandler () 22 | 23 | @property (copy, nonatomic, nullable) GBCAAnimationDidStartBlock didStart; 24 | @property (copy, nonatomic, nullable) GBCAAnimationDidStopBlock didStop; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /GBToolbox/GBTestView.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBTestView.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 12/07/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBTestView.h" 10 | 11 | #import "GBToolbox.h" 12 | 13 | @implementation GBTestView 14 | 15 | - (id)initWithFrame:(CGRect)frame { 16 | self = [super initWithFrame:frame]; 17 | if (self) { 18 | // Initialization code 19 | } 20 | return self; 21 | } 22 | 23 | /* 24 | // Only override drawRect: if you perform custom drawing. 25 | // An empty implementation adversely affects performance during animation. 26 | - (void)drawRect:(CGRect)rect 27 | { 28 | // Drawing code 29 | } 30 | */ 31 | 32 | - (void)setFrame:(CGRect)frame { 33 | // _lRect(frame); 34 | [super setFrame:frame]; 35 | } 36 | 37 | - (void)dealloc { 38 | NSLog(@"deallocating GBTestView instance %@", self); 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /GBToolbox/GBCAAnimationDelegateHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBCAAnimationDelegateHandler.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 16/09/2016. 6 | // 7 | // 8 | 9 | #import "GBCAAnimationDelegateHandler.h" 10 | 11 | #pragma mark - API 12 | 13 | @implementation GBCAAnimationDelegateHandler 14 | 15 | + (nonnull instancetype)delegateWithDidStart:(nullable GBCAAnimationDidStartBlock)didStart didStop:(nullable GBCAAnimationDidStopBlock)didStop { 16 | GBCAAnimationDelegateHandler *delegate = [self.class new]; 17 | delegate.didStart = didStart; 18 | delegate.didStop = didStop; 19 | 20 | return delegate; 21 | } 22 | 23 | #pragma mark - CAAnimationDelegate 24 | 25 | - (void)animationDidStart:(CAAnimation *)animation { 26 | if (self.didStart) self.didStart(animation); 27 | } 28 | 29 | - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag { 30 | if (self.didStop) self.didStop(animation, flag); 31 | } 32 | 33 | @end 34 | 35 | -------------------------------------------------------------------------------- /GBToolbox/GBCache.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBCache.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 10/04/2014. 6 | // Copyright (c) 2014 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBCache.h" 10 | 11 | #import 12 | 13 | NSUInteger const kGBCacheUnlimitedCacheSize = 0; 14 | 15 | @implementation GBCache 16 | 17 | #pragma mark - CA 18 | 19 | -(void)setMaxCacheSize:(NSUInteger)maxCacheSize { 20 | self.totalCostLimit = maxCacheSize; 21 | } 22 | 23 | -(NSUInteger)maxCacheSize { 24 | return self.totalCostLimit; 25 | } 26 | 27 | #pragma mark - Life 28 | 29 | -(id)init { 30 | if (self = [super init]) { 31 | self.maxCacheSize = kGBCacheUnlimitedCacheSize; 32 | } 33 | 34 | return self; 35 | } 36 | 37 | #pragma mark - API 38 | 39 | -(id)objectForKeyedSubscript:(id)key { 40 | return [self objectForKey:key]; 41 | } 42 | 43 | -(void)setObject:(id)obj forKeyedSubscript:(id)key { 44 | [self setObject:obj forKey:key]; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /GBToolbox/GBMessageInterceptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBMessageInterceptor.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 21/06/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | // Originally from this SO answer: http://stackoverflow.com/a/3862591/399772 9 | 10 | 11 | #import "GBMessageInterceptor.h" 12 | 13 | @implementation GBMessageInterceptor 14 | 15 | - (id)forwardingTargetForSelector:(SEL)aSelector { 16 | if ([_middleMan respondsToSelector:aSelector]) { return _middleMan; } 17 | if ([_receiver respondsToSelector:aSelector]) { return _receiver; } 18 | return [super forwardingTargetForSelector:aSelector]; 19 | } 20 | 21 | - (BOOL)respondsToSelector:(SEL)aSelector { 22 | if ([[_middleMan superclass] instancesRespondToSelector:aSelector]) return NO; 23 | if ([_middleMan respondsToSelector:aSelector]) return YES; 24 | if ([_receiver respondsToSelector:aSelector]) return YES; 25 | return [super respondsToSelector:aSelector]; 26 | } 27 | 28 | @end -------------------------------------------------------------------------------- /GBToolbox/GBCompletable.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBCompletable.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 15/07/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "GBToolbox.h" 12 | 13 | typedef enum { 14 | GBCompletableStateUnresolved, 15 | GBCompletableStateSuccess, 16 | GBCompletableStateFailed, 17 | } GBCompletableState; 18 | 19 | @interface GBCompletable : NSObject 20 | 21 | @property (assign, nonatomic, readonly) BOOL isResolved; 22 | @property (assign, nonatomic, readonly) BOOL isCompleted; 23 | @property (assign, nonatomic, readonly) BOOL isFailed; 24 | @property (assign, nonatomic, readonly) GBCompletableState state; 25 | 26 | //provider uses the 27 | +(GBCompletable *)completableWithSuccess:(VoidBlock)success fail:(VoidBlock)fail resolve:(VoidBlock)resolve; 28 | 29 | //client uses this 30 | -(void)success; 31 | -(void)fail; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /GBToolbox/GBAsyncOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBAsyncOperation.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 22/11/16. 6 | // Copyright © 2016 Goonbee e.U.. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | @interface GBAsyncOperation : NSOperation 12 | 13 | /** 14 | Init. 15 | */ 16 | - (nonnull instancetype)init NS_DESIGNATED_INITIALIZER; 17 | 18 | /** 19 | The method that is called for subclasses to do their work. You must override this method WITHOUT calling super. When your work is completed, you must call `completeAsyncOperation`. This method is called on the same thread as start was called for the operation. 20 | */ 21 | - (void)main; 22 | 23 | /** 24 | You must also override cancel, and call `completeAsyncOperation` somewhere in your implementation. You SHOULD call super first. 25 | */ 26 | - (void)cancel; 27 | 28 | /** 29 | Marks this operation as completed and sends all the right KVO notifications as per the NSOperation subsclassing contract. 30 | */ 31 | - (void)completeAsyncOperation; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /GBToolbox/NSSet+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSSet+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 28/03/16. 6 | // Copyright © 2016 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSSet<__covariant ObjectType> (GBToolbox) 12 | 13 | /** 14 | Returns YES if any the function returns YES for at least one object in the set. 15 | */ 16 | - (BOOL)contains:(BOOL(^ _Nonnull)(ObjectType _Nonnull object))function; 17 | 18 | /** 19 | Returns a copy of the array containing only the objects for which function returns YES. 20 | */ 21 | - (nonnull NSSet *)filter:(BOOL(^ _Nonnull)(ObjectType _Nonnull object))function; 22 | 23 | /** 24 | Returns the first object for which the function returns YES, nil otherwise. 25 | 26 | First might be undeterministic for an NSSet, so this is really only useful for when you know that only 1 object in the set will return YES for the function. 27 | */ 28 | - (nullable ObjectType)first:(BOOL(^ _Nonnull)(ObjectType _Nonnull object))function; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /GBToolbox/GBRoundBadgeView.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBRoundBadgeView.h 3 | // Russia 4 | // 5 | // Created by Luka Mirosevic on 26/06/2013. 6 | // Copyright (c) 2013 Goonbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | A round badge view, that is composed of a background image that is aspect filled and a foreground image whose content mode can be freely specified. 13 | 14 | It clips the edges to create a circle. 15 | 16 | Useful for rounded profile pics, with an optional background image. 17 | */ 18 | @interface GBRoundBadgeView : UIView 19 | 20 | @property (strong, nonatomic) UIImage *backgroundImage; 21 | @property (strong, nonatomic) UIImage *foregroundImage; 22 | @property (assign, nonatomic) UIViewContentMode foregroundImageContentMode; 23 | @property (assign, nonatomic) UIEdgeInsets clippingMargin; 24 | @property (assign, nonatomic) UIEdgeInsets backgroundImageMargin; 25 | @property (assign, nonatomic) UIEdgeInsets foregroundImageMargin; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /GBToolbox/GBToolbox-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.goonbee.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSHumanReadableCopyright 26 | Copyright © 2013 Luka Mirosevic. All rights reserved. 27 | NSPrincipalClass 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /GBToolbox/GBTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBTableViewController.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 14/07/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBTableViewController : UIViewController 12 | 13 | @property (strong, nonatomic) UITableView *tableView; 14 | @property (assign, nonatomic) BOOL clearsSelectionOnViewWillAppear;// defaults to YES. If YES, any selection is cleared in viewWillAppear: 15 | @property (strong, nonatomic) UIView *emptyView; 16 | 17 | -(id)initWithStyle:(UITableViewStyle)style; 18 | 19 | -(void)clearEmpty;//don't show the empty (e.g. if you have an error condition and you want to show an error message instead) 20 | -(void)handleEmpty; 21 | 22 | //for subclasses to override 23 | -(Class)classForTableView; 24 | -(void)tableView:(UITableView *)tableView didBeginFullyDisplayingCellForRowAtIndexPath:(NSIndexPath *)indexPath; 25 | -(void)tableView:(UITableView *)tableView didEndFullyDisplayingCellForRowAtIndexPath:(NSIndexPath *)indexPath; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /GBToolbox/GBRemoteDebugMessages.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBRemoteDebugMessages.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 08/11/2014. 6 | // Copyright (c) 2014 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBRemoteDebugMessages : NSObject 12 | 13 | @property (assign, nonatomic) BOOL shouldLogLocallyAsWell;// default: NO 14 | 15 | + (instancetype)sharedMessages; 16 | - (id)init; 17 | 18 | /** 19 | Sends a debug message via the network for remote debugging. To listen for messages run `nc -l -k 10000` on the target host, where 10000 is the default port to which messages are sent if you don't specify a custom route. If you specify a port using `RouteRemoteDebugMessagesToServerOnPort` then you should listen on that port. 20 | */ 21 | - (void)sendRemoteDebugMessage:(NSString *)message, ...; 22 | 23 | /** 24 | Determines where to remote debug messages will be sent. 25 | */ 26 | - (void)routeRemoteDebugMessagesToServer:(NSString *)server onPort:(UInt32)port; 27 | 28 | @end 29 | 30 | // Shorthands 31 | void SendRemoteDebugMessage(NSString *message, ...); 32 | void RouteRemoteDebugMessagesToServerOnPort(NSString *server, UInt32 port); 33 | -------------------------------------------------------------------------------- /GBToolbox/NSView+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSView+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "NSView+GBToolbox.h" 10 | 11 | #import "GBToolbox.h" 12 | 13 | @implementation NSView (GBToolbox) 14 | 15 | //returns the view's frame in global coordinates 16 | -(NSRect)globalFrame { 17 | if ([self.window respondsToSelector:@selector(convertRectToScreen:)]) {//10.7 18 | return [self.window convertRectToScreen:self.frame]; 19 | } 20 | else { 21 | #pragma clang diagnostic push 22 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 23 | NSRect frame = [self frame]; 24 | frame.origin = [self.window convertBaseToScreen:frame.origin]; 25 | return frame; 26 | #pragma clang diagnostic pop 27 | } 28 | } 29 | 30 | //enumarate all subviews depth first 31 | -(void)enumerateSubviewsWithBlock:(void(^)(NSView *view))block { 32 | //itself 33 | block(self); 34 | 35 | //then all its subviews 36 | for (NSView *subview in self.subviews) { 37 | [subview enumerateSubviewsWithBlock:block]; 38 | } 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /GBToolbox/UIScrollView+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 27/06/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "UIScrollView+GBToolbox.h" 10 | 11 | @implementation UIScrollView (GBToolbox) 12 | 13 | -(void)scrollToOriginAnimated:(BOOL)animated { 14 | [self setContentOffset:CGPointZero animated:animated]; 15 | } 16 | 17 | -(void)scrollToTopAnimated:(BOOL)animated { 18 | [self setContentOffset:CGPointMake(self.contentOffset.x, 0 - self.contentInset.top) animated:animated]; 19 | } 20 | 21 | -(void)scrollToBottomAnimated:(BOOL)animated { 22 | [self setContentOffset:CGPointMake(self.contentOffset.x, MAX(-self.contentInset.top, self.contentSize.height - self.bounds.size.height + self.contentInset.bottom)) animated:animated]; 23 | } 24 | 25 | -(void)scrollToLeftAnimated:(BOOL)animated { 26 | self.contentOffset = CGPointMake(0 - self.contentInset.left, self.contentOffset.y); 27 | } 28 | 29 | -(void)scrollToRightAnimated:(BOOL)animated { 30 | [self setContentOffset:CGPointMake(MAX(-self.contentInset.right, self.contentSize.width - self.bounds.size.width + self.contentInset.right), self.contentOffset.y) animated:animated]; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /GBToolbox/GBCustomNavigationBar.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBCustomNavigationBar.m 3 | // Xmas List 4 | // 5 | // Created by Luka Mirosevic on 29/11/2012. 6 | // Copyright (c) 2012 Goonbee. All rights reserved. 7 | // 8 | 9 | #import "GBCustomNavigationBar.h" 10 | 11 | @interface GBCustomNavigationBar () 12 | 13 | @property (assign, nonatomic, readwrite) CGFloat navBarHeight; 14 | 15 | @end 16 | 17 | @implementation GBCustomNavigationBar 18 | 19 | #pragma mark - life 20 | 21 | static CGFloat _defaultNavBarHeightForClass = 44; 22 | 23 | +(Class)navigationBarClassWithHeight:(CGFloat)height { 24 | _defaultNavBarHeightForClass = height; 25 | 26 | return [self class]; 27 | } 28 | 29 | -(id)initWithFrame:(CGRect)frame { 30 | if (self = [super initWithFrame:frame]) { 31 | self.navBarHeight = _defaultNavBarHeightForClass; 32 | } 33 | 34 | return self; 35 | } 36 | 37 | -(id)initWithNavBarHeight:(CGFloat)height { 38 | if (self = [self init]) { 39 | self.navBarHeight = height; 40 | } 41 | 42 | return self; 43 | } 44 | 45 | #pragma mark - customizations 46 | 47 | -(CGSize)sizeThatFits:(CGSize)size { 48 | CGRect frame = [UIScreen mainScreen].bounds; 49 | 50 | return CGSizeMake(frame.size.width , self.navBarHeight); 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /GBToolbox/NSMapTable+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMapTable+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 01/06/2014. 6 | // Copyright (c) 2014 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSMapTable (GBToolbox) 12 | 13 | /** 14 | Returns the object for a key, assuming both object and key are objects. 15 | */ 16 | - (nullable ObjectType)objectForKeyedSubscript:(nullable KeyType)key; 17 | 18 | /** 19 | Stores an object into the map table for the given key. Assumes both object and key are objects. 20 | */ 21 | - (void)setObject:(nullable ObjectType)object forKeyedSubscript:(nullable KeyType)key; 22 | 23 | /** 24 | Returns an array holding all the keys. 25 | */ 26 | @property (strong, nonatomic, nonnull, readonly) NSArray *allKeys; 27 | 28 | /** 29 | Returns an array holding all the objects. 30 | */ 31 | @property (strong, nonatomic, nonnull, readonly) NSArray *allObjects; 32 | 33 | /** 34 | Checks whether the map table contains the object. 35 | */ 36 | - (BOOL)containsObject:(nullable ObjectType)object; 37 | 38 | /** 39 | Returns the first matching key for the object. 40 | */ 41 | - (nullable KeyType)keyForObject:(nullable ObjectType)object; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /GBToolbox/NSSet+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSSet+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 28/03/16. 6 | // Copyright © 2016 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "NSSet+GBToolbox.h" 10 | 11 | @implementation NSSet (GBToolbox) 12 | 13 | - (BOOL)contains:(BOOL(^)(id object))function { 14 | for (id object in self) { 15 | if (function(object)) { 16 | return YES; 17 | } 18 | } 19 | 20 | return NO; 21 | } 22 | 23 | - (NSSet *)filter:(BOOL(^)(id object))function { 24 | // creates a results set in which to store results, sets the capacity for faster writes 25 | NSMutableSet *resultsSet = [[NSMutableSet alloc] initWithCapacity:self.count]; 26 | 27 | // apply the function to each object in self and store the results in the new set 28 | for (id object in self) { 29 | if (function(object)) { 30 | [resultsSet addObject:object]; 31 | } 32 | } 33 | 34 | // return an immutable copy 35 | return [resultsSet copy]; 36 | } 37 | 38 | - (id)first:(BOOL(^)(id object))function { 39 | for (id object in self) { 40 | if (function(object)) { 41 | return object; 42 | } 43 | } 44 | 45 | return nil; 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /GBToolbox/GBToggleControlRadioGroupManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBToggleControlRadioGroupManager.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 08/07/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class GBToggleControl; 12 | 13 | @protocol GBToggleControlRadioGroupManagerDelegate; 14 | 15 | @interface GBToggleControlRadioGroupManager : NSObject 16 | 17 | @property (weak, nonatomic) id delegate; 18 | 19 | @property (strong, nonatomic) NSArray *toggleControls; 20 | 21 | @property (assign, nonatomic) NSInteger selectedControlIndex; 22 | @property (strong, nonatomic) GBToggleControl *selectedControl; 23 | 24 | -(void)deselectAllControls; 25 | 26 | @end 27 | 28 | @protocol GBToggleControlRadioGroupManagerDelegate 29 | @optional 30 | 31 | -(void)toggleControlRadioGroupManager:(GBToggleControlRadioGroupManager *)toggleControlRadioGroupManager didSelectToggleControl:(GBToggleControl *)toggleControl withIndex:(NSInteger)index; 32 | 33 | -(void)toggleControlRadioGroupManagerDidResetSelection:(GBToggleControlRadioGroupManager *)toggleControlRadioGroupManager; 34 | 35 | @end -------------------------------------------------------------------------------- /GBToolbox/NSMapTable+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMapTable+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 01/06/2014. 6 | // Copyright (c) 2014 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "NSMapTable+GBToolbox.h" 10 | 11 | #import "NSArray+GBToolbox.h" 12 | 13 | @implementation NSMapTable (GBToolbox) 14 | 15 | - (id)objectForKeyedSubscript:(id)key { 16 | return [self objectForKey:key]; 17 | } 18 | 19 | - (void)setObject:(id)object forKeyedSubscript:(id)key { 20 | [self setObject:object forKey:key]; 21 | } 22 | 23 | - (NSArray *)allKeys { 24 | NSMutableArray *allKeys = [[NSMutableArray alloc] initWithCapacity:self.count]; 25 | 26 | for (id key in self) { 27 | [allKeys addObject:key]; 28 | } 29 | 30 | return allKeys; 31 | } 32 | 33 | - (NSArray *)allObjects { 34 | return [self.allKeys map:^id(id key) { 35 | return [self objectForKey:key]; 36 | }]; 37 | } 38 | 39 | - (BOOL)containsObject:(id)object { 40 | return [self.allObjects containsObject:object]; 41 | } 42 | 43 | - (id)keyForObject:(id)object { 44 | for (id key in self) { 45 | if ([self[key] isEqual:object]) { 46 | return key; 47 | } 48 | } 49 | 50 | // fallback in case nothing is found 51 | return nil; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /GBToolbox/NSOrderedSet+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSOrderedSet+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 28/03/16. 6 | // Copyright © 2016 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSOrderedSet<__covariant ObjectType> (GBToolbox) 12 | 13 | /** 14 | Returns the index of the object that is identical to anObject, or NSNotFound if the set doesn't contain it. 15 | */ 16 | - (NSUInteger)indexOfObjectIdenticalTo:(nonnull id)anObject; 17 | 18 | /** 19 | Returns a new set with the elements from the receiver transformed by function. 20 | */ 21 | - (nonnull NSOrderedSet *)map:(id _Nonnull(^ _Nonnull)(ObjectType _Nonnull object))function; 22 | 23 | /** 24 | Fold Left 25 | */ 26 | - (nonnull ObjectType)foldLeft:(ObjectType _Nonnull(^ _Nonnull)(ObjectType _Nonnull objectA, ObjectType _Nonnull objectB))function lastObject:(nonnull ObjectType)lastObject; 27 | 28 | /** 29 | Fold Right. 30 | */ 31 | - (nonnull ObjectType)foldRight:(ObjectType _Nonnull(^ _Nonnull)(ObjectType _Nonnull objectA, ObjectType _Nonnull objectB))function initialObject:(nonnull ObjectType)initialObject; 32 | 33 | /** 34 | Synonym for foldLeft 35 | */ 36 | - (nonnull ObjectType)reduce:(ObjectType _Nonnull(^ _Nonnull)(ObjectType _Nonnull objectA, ObjectType _Nonnull objectB))function lastObject:(nonnull ObjectType)lastObject; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /GBToolbox/GBHighPrecisionTimer.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBHighPrecisionTimer.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 13/12/2016. 6 | // Copyright © 2016 Goonbee e.U.. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | @interface GBHighPrecisionTimer : NSObject 12 | 13 | /** 14 | Initialises a new timer, that will call `block` every `period`. 15 | 16 | Warning: The object will retain itself for as long as the timer doesn't get stopped. The object will store a copy of the block and release it when the timer is stopped. `block` will be called on a background thread. 17 | */ 18 | - (nonnull instancetype)initWithPeriod:(NSTimeInterval)period repeats:(BOOL)repeats block:(void(^ _Nonnull)(void))block NS_DESIGNATED_INITIALIZER; 19 | 20 | /** 21 | Initialises a new timer, that will call the `selector` on `target` every `period`. 22 | 23 | Warning: The object will retain itself for as long as the timer doesn't get stopped. `selector` will be called from a background thread. 24 | */ 25 | - (nonnull instancetype)initWithPeriod:(NSTimeInterval)period repeats:(BOOL)repeats taget:(nonnull id)target selector:(nonnull SEL)selector; 26 | 27 | - (nonnull instancetype)init NS_UNAVAILABLE; 28 | 29 | /** 30 | Fires the timer immediately. Will be called on a background thread. 31 | */ 32 | - (void)fire; 33 | 34 | /** 35 | Stops the timer from firing again. 36 | */ 37 | - (void)stop; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /GBToolbox/GBKeyboardListener.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBKeyboardListener.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 22/05/2017. 6 | // Copyright © 2017 Goonbee e.U. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | The block that is called when the keyboard moves. 13 | */ 14 | typedef void(^GBKeyboardWillMoveBlock)(BOOL willShow, CGRect beginFrame, CGRect endFrame, NSTimeInterval animationDuration, UIViewAnimationOptions animationCurve); 15 | 16 | /** 17 | A class for listening on changes to the keyboard. 18 | */ 19 | @interface GBKeyboardListener : NSObject 20 | 21 | + (nonnull GBKeyboardListener *)sharedListener; 22 | 23 | /** 24 | Whether or not the keyboard is currently visible. 25 | */ 26 | @property (assign, nonatomic, readonly, getter=isVisible) BOOL visible; 27 | 28 | /** 29 | The frame of the keyboard, in screen coordinates. 30 | */ 31 | @property (assign, nonatomic, readonly) CGRect keyboardFrame; 32 | 33 | /** 34 | Returns the rect of the keyboard, translated to view's coordinates. 35 | */ 36 | - (CGRect)frameInCoordinatesOfView:(nonnull UIView *)view; 37 | 38 | /** 39 | Adds a block in a context. 40 | */ 41 | - (void)addListenerForContext:(nonnull id)context block:(nonnull GBKeyboardWillMoveBlock)block; 42 | 43 | /** 44 | Removes all notification blocks in a certain context. 45 | */ 46 | - (void)removeListenersForContext:(nonnull id)context; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /GBToolbox/NSMutableArray+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 17/05/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSMutableArray (GBToolbox) 12 | 13 | #pragma mark - Array padding 14 | //These are all non-destructive and leave existing elements in, only when extendin the array do we actually pad it 15 | 16 | /** 17 | Pads the array out so the last index is the one that's passed in, with [NSNull null]. 18 | */ 19 | - (void)padToIndex:(NSUInteger)index; 20 | 21 | /** 22 | Pads the array out so the last index is the one that's passed in, with any non nil object 23 | */ 24 | - (void)padToIndex:(NSUInteger)index withObject:(nonnull id)object; 25 | 26 | /** 27 | Pads the array out to the desired size with [NSNull null] objects. 28 | */ 29 | - (void)padToSize:(NSUInteger)count; 30 | 31 | /** 32 | Pads the array out to the desired size with any object you supply. 33 | */ 34 | - (void)padToSize:(NSUInteger)count withObject:(nonnull id)object; 35 | 36 | #pragma mark - Deleting 37 | 38 | /** 39 | Removes an object from the array by searching using pointer equality, rather than sending the isEqual: message. 40 | */ 41 | - (void)removeObjectByIdentity:(nonnull id)object; 42 | 43 | #pragma mark - Moving 44 | 45 | /** 46 | Moves an object from one index to another. toIndex is the final index *after* the move. 47 | */ 48 | - (void)moveObjectAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /GBToolbox/UIImage+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIImage (GBToolbox) 12 | 13 | -(UIImage *)cropToRect:(CGRect)rect; 14 | -(UIImage *)resizedImage:(CGSize)newSize interpolationQuality:(CGInterpolationQuality)quality; 15 | -(UIImage *)resizedImageWithContentMode:(UIViewContentMode)contentMode bounds:(CGSize)bounds interpolationQuality:(CGInterpolationQuality)quality; 16 | -(UIImage *)downscaledImageWithMaxResolution:(CGFloat)maxResolution; 17 | 18 | #pragma mark - Programmatic images 19 | 20 | + (UIImage *)imageWithSolidColor:(UIColor *)color size:(CGSize)size; 21 | + (UIImage *)imageWithSolidColor:(UIColor *)color size:(CGSize)size capInsets:(UIEdgeInsets)capInsets; 22 | 23 | #pragma mark - Apple UIImage+ImageEffects 24 | 25 | -(UIImage *)applyLightEffect; 26 | -(UIImage *)applyExtraLightEffect; 27 | -(UIImage *)applyDarkEffect; 28 | -(UIImage *)applyTintEffectWithColor:(UIColor *)tintColor; 29 | 30 | -(UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage; 31 | 32 | #pragma mark - Tint 33 | 34 | - (UIImage *)tintedImage:(UIColor *)tintColor; 35 | 36 | #pragma mark - Blending 37 | 38 | /** 39 | Blends multiple images together, by drawing each successive image in the array on top of the previous one. 40 | */ 41 | + (UIImage *)imageByBlending:(NSArray *)images; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /GBToolbox/UITableView+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "UITableView+GBToolbox.h" 10 | 11 | @implementation UITableView (GBToolbox) 12 | 13 | -(BOOL)isScrolledToBottomWithTolerance:(CGFloat)tolerance { 14 | if (self.contentSize.height - self.bounds.size.height - self.contentOffset.y <= tolerance) { 15 | return YES; 16 | } 17 | else { 18 | return NO; 19 | } 20 | } 21 | 22 | -(BOOL)isScrolledToBottom { 23 | return [self isScrolledToBottomWithTolerance:0]; 24 | } 25 | 26 | -(void)recalculateHeaderHeightAnimated:(BOOL)animated { 27 | if (animated) [self beginUpdates]; 28 | self.tableHeaderView = self.tableHeaderView;//causes it to refresh the height 29 | if (animated) [self endUpdates]; 30 | } 31 | 32 | -(void)updateHeaderHeightTo:(CGFloat)newHeight animated:(BOOL)animated { 33 | if (animated) { 34 | [UIView animateWithDuration:0.25 animations:^{ 35 | self.tableHeaderView.frame = CGRectMake(self.tableHeaderView.frame.origin.x, 36 | self.tableHeaderView.frame.origin.y, 37 | self.tableHeaderView.frame.size.width, 38 | newHeight); 39 | 40 | self.tableHeaderView = self.tableHeaderView;//causes it to reconsider the height 41 | 42 | } completion:nil]; 43 | } 44 | } 45 | 46 | @end -------------------------------------------------------------------------------- /GBToolbox/NSTimer+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimer+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "NSTimer+GBToolbox.h" 10 | 11 | @implementation NSTimer (GBToolbox) 12 | 13 | //blocks 14 | +(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats withBlock:(void(^)(void))block { 15 | return [self _timerFactory:interval repeats:repeats withBlock:block shouldSchedule:YES]; 16 | } 17 | 18 | +(NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats withBlock:(void(^)(void))block { 19 | return [self _timerFactory:interval repeats:repeats withBlock:block shouldSchedule:NO]; 20 | } 21 | 22 | +(NSTimer *)_timerFactory:(NSTimeInterval)interval repeats:(BOOL)repeats withBlock:(void(^)(void))block shouldSchedule:(BOOL)shouldSchedule { 23 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self instanceMethodSignatureForSelector:@selector(callBlock:)]]; 24 | NSTimer *timer = shouldSchedule ? 25 | [NSTimer scheduledTimerWithTimeInterval:interval invocation:invocation repeats:repeats] : 26 | [NSTimer timerWithTimeInterval:interval invocation:invocation repeats:repeats]; 27 | 28 | [invocation setTarget:timer]; 29 | [invocation setSelector:@selector(callBlock:)]; 30 | 31 | void(^copy)(void) = [block copy]; 32 | [invocation setArgument:© atIndex:2]; 33 | 34 | return timer; 35 | } 36 | 37 | -(void)callBlock:(void(^)(void))block { 38 | block(); 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /GBToolbox/NSInvocation+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSInvocation+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 30/04/2014. 6 | // Copyright (c) 2014 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "NSInvocation+GBToolbox.h" 10 | 11 | @implementation NSInvocation (GBToolbox) 12 | 13 | +(NSInvocation *)invocationWithTarget:(id)target selector:(SEL)selector arguments:(void *)argument, ... NS_REQUIRES_NIL_TERMINATION { 14 | va_list argumentList; 15 | va_start(argumentList, argument); 16 | NSInvocation *invocation = [self invocationWithTarget:target selector:selector argument:argument argumentList:argumentList]; 17 | va_end(argumentList); 18 | 19 | return invocation; 20 | } 21 | 22 | +(NSInvocation *)invocationWithTarget:(id)target selector:(SEL)selector argument:(void *)argument argumentList:(va_list)argumentList { 23 | // prepare the invocation 24 | NSMethodSignature *methodSignature = [[target class] instanceMethodSignatureForSelector:selector]; 25 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; 26 | [invocation setTarget:target]; 27 | [invocation setSelector:selector]; 28 | void *anArgumentPointer; 29 | 30 | if (argument) { 31 | [invocation setArgument:argument atIndex:2]; 32 | NSUInteger counter = 3; 33 | while ((anArgumentPointer = va_arg(argumentList, void *))) { 34 | [invocation setArgument:anArgumentPointer atIndex:counter]; 35 | counter += 1; 36 | } 37 | } 38 | [invocation retainArguments]; 39 | 40 | return invocation; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /GBToolbox/UIView+GBOwningViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+GBOwningViewController.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 08/06/2017. 6 | // Copyright (c) 2017 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "UIView+GBToolbox.h" 10 | 11 | #import "GBUtility_Common.h" 12 | #import 13 | 14 | @interface UIView () 15 | 16 | @property (weak, nonatomic, nullable, readwrite) UIViewController *GBOwningViewController; 17 | 18 | @end 19 | 20 | @implementation UIView (GBOwningViewController) 21 | 22 | #pragma mark - CA 23 | 24 | static char gbOwningViewController; 25 | 26 | - (void)setGBOwningViewController:(UIViewController *)owningViewController { 27 | objc_setAssociatedObject(self, &gbOwningViewController, owningViewController, OBJC_ASSOCIATION_ASSIGN); 28 | } 29 | 30 | - (UIViewController *)GBOwningViewController { 31 | return objc_getAssociatedObject(self, &gbOwningViewController); 32 | } 33 | 34 | @end 35 | 36 | @interface UIViewController (GBOwningViewController) 37 | 38 | @end 39 | 40 | @implementation UIViewController (GBOwningViewController) 41 | 42 | #pragma mark - Overrides 43 | 44 | + (void)load { 45 | SwizzleInstanceMethodsInClass(self, @selector(setView:), @selector(_swizz_setView:)); 46 | } 47 | 48 | - (void)_swizz_setView:(UIView *)view { 49 | // deassociate self from old view 50 | if (self.isViewLoaded) { 51 | self.view.GBOwningViewController = nil; 52 | } 53 | 54 | // call default implementation 55 | [self _swizz_setView:view]; 56 | 57 | // associate self to new view 58 | view.GBOwningViewController = self; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /GBToolbox/UIViewController+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIViewController (GBToolbox) 12 | 13 | /** 14 | Returns YES when the view controller is visible (in between viewWillAppear: and viewDidDisappear:) 15 | */ 16 | @property (assign, nonatomic) BOOL isVisible; 17 | 18 | /** 19 | Returns YES when the view controller is visible (in between viewWillAppear: and viewWillDisappear:) 20 | */ 21 | @property (assign, nonatomic) BOOL isVisibleCurrently; 22 | 23 | /** 24 | Makes sure that the view is loaded 25 | */ 26 | - (void)ensureViewIsLoaded; 27 | 28 | /** 29 | Returns the preferred status bar style of the view controller that's currently shown. 30 | */ 31 | @property (assign, nonatomic, readonly) UIStatusBarStyle inheritedPreferredStatusBarStyle; 32 | 33 | /** 34 | Set this viewcontroller's navigation bar background color with a more sane behaviour than the standard Cocoa Touch methods. 35 | 36 | If color is nil, the bar will be transparent. 37 | */ 38 | - (void)styleNavigationBarWithColor:(nullable UIColor *)color; 39 | 40 | /** 41 | Hides the title next to the back button, showing only the chevron. 42 | */ 43 | - (void)hideBackButtonTitle; 44 | 45 | /** 46 | Returns the view controller that is modally presenting the receiver, even if the receiver is embedded inside a tab bar or navigation controller 47 | */ 48 | @property (strong, nonatomic, readonly, nullable) UIViewController *presentingViewControllerAcrossParents; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GBToolbox ![Version](https://img.shields.io/cocoapods/v/GBToolbox.svg?style=flat) ![License](https://img.shields.io/badge/license-Apache_2-green.svg?style=flat) 2 | 3 | Goonbee's iOS & Mac development toolbox 4 | 5 | Categories 6 | ------------ 7 | 8 | * NSString 9 | * UIImage 10 | * NSData 11 | * NSTimer 12 | * NSObject 13 | * UIViewController 14 | * UITableView 15 | 16 | Functions 17 | ------------ 18 | 19 | * Math 20 | * UI 21 | * Timing 22 | * Convenience 23 | 24 | Macros 25 | ------------ 26 | 27 | * Logging 28 | * Localisation 29 | * Universal app device detection 30 | * View controller instantiation (Storyboards) 31 | * Object instantiation 32 | * Rotation 33 | * Lazy instantiation 34 | * Set 35 | * Resource Bundles 36 | * Singleton 37 | * Debugging 38 | * Strings 39 | 40 | Feature list 41 | ------------ 42 | 43 | Look inside header files. 44 | 45 | Dependencies 46 | ------------ 47 | 48 | * [JRSwizzle](https://github.com/rentzsch/jrswizzle) 49 | 50 | Copyright & License 51 | ------------ 52 | 53 | Copyright 2017 Luka Mirosevic 54 | 55 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License in the LICENSE file, or at: 56 | 57 | http://www.apache.org/licenses/LICENSE-2.0 58 | 59 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 60 | 61 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lmirosevic/gbtoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 62 | -------------------------------------------------------------------------------- /GBToolbox/GBAlertBadge.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBAlertBadge.h 3 | // GBToolBox 4 | // 5 | // Created by Luka Mirosevic on 10/10/2016. 6 | // Copyright © 2016 Goonbee e.U. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | The classic alert badge with a number/string inside it. 13 | 14 | Useful for signalling things that have unread notifications or similar. 15 | */ 16 | @interface GBAlertBadge : UIView 17 | 18 | @property (copy, nonatomic, nullable) NSString *badgeText; 19 | @property (assign, nonatomic) NSInteger badgeCount; 20 | @property (strong, nonatomic, nonnull) UIFont *font; 21 | @property (strong, nonatomic, nonnull) UIColor *textColor; 22 | @property (strong, nonatomic, nullable) UIImage *backgroundImage; 23 | @property (assign, nonatomic) UIEdgeInsets padding; 24 | @property (assign, nonatomic) BOOL hidesWhenCountZero;//default: NO 25 | @property (assign, nonatomic) BOOL clipsToPillShape;//default: YES 26 | 27 | /** 28 | Creates a badge with a background color and clipped to a pill shape. 29 | */ 30 | + (nonnull instancetype)badgeWithFont:(nonnull UIFont *)font textColor:(nonnull UIColor *)textColor backgroundColor:(nonnull UIColor *)backgroundColor padding:(UIEdgeInsets)padding; 31 | 32 | /** 33 | Creates a badge with a background image. 34 | */ 35 | + (nonnull instancetype)badgeWithFont:(nonnull UIFont *)font textColor:(nonnull UIColor *)textColor backgroundImage:(nullable UIImage *)backgroundImage padding:(UIEdgeInsets)padding; 36 | - (nonnull instancetype)initWithFont:(nonnull UIFont *)font textColor:(nonnull UIColor *)textColor backgroundImage:(nullable UIImage *)backgroundImage padding:(UIEdgeInsets)padding; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /GBToolbox/GBGradientView.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBGradientView.m 3 | // Pods 4 | // 5 | // Created by Luka Mirosevic on 13/11/2014. 6 | // 7 | // 8 | 9 | #import "GBGradientView.h" 10 | 11 | #import "NSArray+GBToolbox.h" 12 | 13 | @interface GBGradientView () 14 | 15 | @property (strong, nonatomic) CAGradientLayer *myGradientLayer; 16 | 17 | @end 18 | 19 | @implementation GBGradientView 20 | 21 | #pragma mark - API 22 | 23 | + (instancetype)gradientViewWithColors:(NSArray *)colors dimension:(GBGradientDimension)dimension { 24 | // configure my view 25 | GBGradientView *view = [[self alloc] initWithFrame:CGRectMake(0., 0., 0., 0.)]; 26 | CAGradientLayer *gradientLayer = [CAGradientLayer layer]; 27 | 28 | switch (dimension) { 29 | case GBGradientDimensionHorizontal: { 30 | gradientLayer.startPoint = CGPointMake(0.0, 0.5); 31 | gradientLayer.endPoint = CGPointMake(1.0, 0.5); 32 | } break; 33 | 34 | case GBGradientDimensionVertical: { 35 | gradientLayer.startPoint = CGPointMake(0.5, 0.0); 36 | gradientLayer.endPoint = CGPointMake(0.5, 1.0); 37 | } break; 38 | } 39 | 40 | gradientLayer.frame = view.bounds; 41 | gradientLayer.colors = [colors map:^id(id object) { 42 | return (id)[object CGColor]; 43 | }]; 44 | [view.layer insertSublayer:gradientLayer atIndex:0]; 45 | 46 | // store the layer 47 | view.myGradientLayer = gradientLayer; 48 | 49 | return view; 50 | } 51 | 52 | #pragma mark - Overrides 53 | 54 | - (void)layoutSubviews { 55 | [super layoutSubviews]; 56 | 57 | // resize the layer when the view changes size 58 | self.myGradientLayer.frame = self.bounds; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /GBToolbox/UIView+GBPopUp.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+GBPopUp.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 08/07/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum { 12 | GBPopUpAnimationFlyUp, 13 | GBPopUpAnimationFadeAway, 14 | } GBPopUpAnimation; 15 | 16 | typedef void(^GBPopUpAutolayoutInstallationBlock)(UIView *superview, UIView *view); 17 | 18 | @interface UIView (GBPopUp) 19 | 20 | @property (strong, nonatomic) UIColor *popUpBackgroundColor; 21 | @property (assign, nonatomic, readonly) BOOL isPresentedAsPopUp; 22 | 23 | /** 24 | Presents this view directly onto the key window. 25 | */ 26 | - (void)presentAsPopUpOnWindowAnimated:(BOOL)animated; 27 | 28 | /** 29 | Presents this view directly onto the key window, with an optional block for installing autolayout constraints. 30 | */ 31 | - (void)presentAsPopUpOnWindowAnimated:(BOOL)animated installingAutolayout:(GBPopUpAutolayoutInstallationBlock)autolayoutInstallation; 32 | 33 | /** 34 | Presents this view onto the desired view. 35 | */ 36 | - (void)presentAsPopUpOnView:(UIView *)targetView animated:(BOOL)animated; 37 | 38 | /** 39 | Presents this view onto the desired view, with an optional block for installing autolayout constraints. 40 | */ 41 | - (void)presentAsPopUpOnView:(UIView *)targetView animated:(BOOL)animated installingAutolayout:(GBPopUpAutolayoutInstallationBlock)autolayoutInstallation; 42 | 43 | /** 44 | Dismisses the popup with the default fade animation. 45 | */ 46 | - (void)dismissPopUpAnimated:(BOOL)animated; 47 | 48 | /** 49 | Dismisses the popup with a specific animation style. 50 | */ 51 | - (void)dismissPopUpWithAnimation:(GBPopUpAnimation)animationType; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /GBToolbox/NSObject+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSObject (GBToolbox) 12 | 13 | /** 14 | An easy way to associate an arbitrary payload with an object. e.g. when you create a button which pertains to changing some model, you can attach the model to the button and then get to it in the button action method. 15 | */ 16 | @property (strong, nonatomic, nullable) id GBPayload; 17 | 18 | /** 19 | An easy way to associate arbitrary key value paris to an object. e.g. when you create a button which pertains to changing some model, you can attach the model to the button and then get to it in the button action method. 20 | 21 | Keys and values are strongly retained. Keys are not copied in. 22 | */ 23 | @property (strong, nonatomic, nonnull, readonly) NSMapTable *GBPayloadMap; 24 | 25 | /** 26 | Returns the pointer address of an object as a string. 27 | */ 28 | @property (copy, nonatomic, readonly, nonnull) NSString *pointerAddress; 29 | 30 | /** 31 | Yields self to the block, and then returns self. The primary purpose of this method is to “tap into” a method chain, in order to perform operations on intermediate results within the chain. 32 | */ 33 | - (nonnull instancetype)tap:(void (^ _Nonnull)(id _Nonnull object))block; 34 | 35 | /** 36 | Returns the getter selector for a property with a name. 37 | */ 38 | + (nullable SEL)getterForPropertyWithName:(nonnull NSString *)name; 39 | 40 | /** 41 | Returns the setter selector for a property with a name. 42 | */ 43 | + (nullable SEL)setterForPropertyWithName:(nonnull NSString *)name; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /GBToolbox/NSOrderedSet+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSOrderedSet+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 28/03/16. 6 | // Copyright © 2016 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "NSOrderedSet+GBToolbox.h" 10 | 11 | @implementation NSOrderedSet (GBToolbox) 12 | 13 | - (NSUInteger)indexOfObjectIdenticalTo:(id)anObject { 14 | for (NSUInteger i = 0; i < self.count; i++) { 15 | if (self[i] == anObject) { 16 | return i; 17 | } 18 | } 19 | 20 | return NSNotFound; 21 | } 22 | 23 | - (NSOrderedSet *)map:(id(^)(id object))function { 24 | NSMutableOrderedSet *results = [[NSMutableOrderedSet alloc] initWithCapacity:self.count]; 25 | 26 | for (id object in self) { 27 | id transformed = function(object); 28 | if (transformed) { 29 | [results addObject:transformed]; 30 | } else { 31 | @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Mapping function must return a non-nil object" userInfo:nil]; 32 | } 33 | } 34 | 35 | return [results copy]; 36 | } 37 | 38 | - (id)foldLeft:(id(^)(id objectA, id objectB))function lastObject:(id)accumulator { 39 | for (id object in self) { 40 | accumulator = function(accumulator, object); 41 | } 42 | 43 | return accumulator; 44 | } 45 | 46 | - (id)foldRight:(id(^)(id objectA, id objectB))function initialObject:(id)accumulator { 47 | for (id object in [self reverseObjectEnumerator]) { 48 | accumulator = function(accumulator, object); 49 | } 50 | 51 | return accumulator; 52 | } 53 | 54 | - (id)reduce:(id(^)(id objectA, id objectB))function lastObject:(id)lastObject { 55 | return [self foldLeft:function lastObject:lastObject]; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /GBToolbox/UIImageView+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 28/06/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "UIImageView+GBToolbox.h" 10 | 11 | @implementation UIImageView (GBToolbox) 12 | 13 | + (UIImageView *)imageViewWithImage:(UIImage *)image { 14 | UIImageView *imageView = [[UIImageView new] initWithImage:image]; 15 | imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height); 16 | return imageView; 17 | } 18 | 19 | + (UIImageView *)imageViewWithImageNamed:(NSString *)imageName { 20 | return [self imageViewWithImage:[UIImage imageNamed:imageName]]; 21 | } 22 | 23 | // based on http://stackoverflow.com/a/15447942/399772 24 | - (CGRect)displayedImageBounds { 25 | UIImage *image = [self image]; 26 | if (self.contentMode != UIViewContentModeScaleAspectFit || !image) { 27 | return self.bounds; 28 | } 29 | 30 | CGFloat boundsWidth = self.bounds.size.width, 31 | boundsHeight = self.bounds.size.height; 32 | 33 | CGSize imageSize = image.size; 34 | CGFloat imageRatio = imageSize.width / imageSize.height; 35 | CGFloat viewRatio = boundsWidth / boundsHeight; 36 | 37 | if (imageRatio < viewRatio) { 38 | CGFloat scale = boundsHeight / imageSize.height; 39 | CGFloat width = scale * imageSize.width; 40 | CGFloat topLeftX = (boundsWidth - width) * 0.5; 41 | return CGRectMake(topLeftX, 0, width, boundsHeight); 42 | } 43 | 44 | CGFloat scale = boundsWidth / imageSize.width; 45 | CGFloat height = scale * imageSize.height; 46 | CGFloat topLeftY = (boundsHeight - height) * 0.5; 47 | 48 | return CGRectMake(0, topLeftY, boundsWidth, height); 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /GBToolbox/NSDictionary+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 06/06/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSDictionary<__covariant KeyType, __covariant ObjectType> (GBToolbox) 12 | 13 | #pragma mark - Funtional Programming 14 | 15 | /** 16 | Classic functional filter. 17 | */ 18 | - (nonnull NSDictionary *)filter:(nonnull BOOL(^)(KeyType _Nonnull key, ObjectType _Nonnull object))function; 19 | 20 | /** 21 | Returns a random key for which the function returns YES. 22 | */ 23 | - (nullable KeyType)aKey:(BOOL(^ _Nonnull)(KeyType _Nonnull key, ObjectType _Nonnull object))function; 24 | 25 | /** 26 | Returns a random object for which the function return YES. 27 | */ 28 | - (nullable ObjectType)anObject:(BOOL(^ _Nonnull)(KeyType _Nonnull key, ObjectType _Nonnull object))function; 29 | 30 | /** 31 | Returns a new dictionary with all key/value pairs from dictionary removed whose values was an instance of NSNull. Works recursively. 32 | */ 33 | + (nonnull NSDictionary *)dictionaryByPruningNullsInDictionary:(nullable NSDictionary *)dictionary; 34 | 35 | /** 36 | Returns a copy of the receiver with all key/value pairs removed whose values was an instance of NSNull. Works recursively. 37 | */ 38 | - (nonnull NSDictionary *)dictionaryByPruningNulls; 39 | 40 | /** 41 | Returns a new dictionary (shallow copy) that contains both key/value pairs from the receiver and dictionary. In case of duplicate keys, dictionary's values will override those of the receiver. 42 | */ 43 | - (nonnull NSDictionary *)dictionaryByMergingWithDictionary:(nullable NSDictionary *)dictionary; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /GBToolbox/GBAlertBadgeView.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBAlertBadgeView.h 3 | // Russia 4 | // 5 | // Created by Luka Mirosevic on 01/07/2013. 6 | // Copyright (c) 2013 Goonbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | The classic alert badge with a number/string inside it. 13 | 14 | Useful for signalling things that have unread notifications or similar. 15 | */ 16 | __deprecated_msg("Deprecated since 21.7. Use GBAlertBadgeView2 instead.") 17 | @interface GBAlertBadgeView : UIView 18 | 19 | @property (copy, nonatomic) NSString *badgeText NS_AVAILABLE_IOS(7_0); 20 | @property (assign, nonatomic) NSInteger badgeCount NS_AVAILABLE_IOS(7_0); 21 | @property (copy, nonatomic) UIFont *font NS_AVAILABLE_IOS(7_0); 22 | @property (copy, nonatomic) UIColor *textColor NS_AVAILABLE_IOS(7_0); 23 | @property (strong, nonatomic) UIImage *backgroundImage NS_AVAILABLE_IOS(7_0); 24 | @property (assign, nonatomic) CGFloat height NS_AVAILABLE_IOS(7_0); 25 | @property (assign, nonatomic) CGFloat horizontalPadding NS_AVAILABLE_IOS(7_0); 26 | @property (assign, nonatomic) BOOL hidesWhenCountZero NS_AVAILABLE_IOS(7_0);//default: NO 27 | 28 | + (GBAlertBadgeView *)badgeWithHeight:(CGFloat)height font:(UIFont *)font textColor:(UIColor *)textColor backgroundImage:(UIImage *)backgroundImage horizontalPadding:(CGFloat)horizontalPadding NS_AVAILABLE_IOS(7_0); 29 | - (id)initWithHeight:(CGFloat)height font:(UIFont *)font textColor:(UIColor *)textColor backgroundImage:(UIImage *)backgroundImage horizontalPadding:(CGFloat)horizontalPadding NS_AVAILABLE_IOS(7_0); 30 | 31 | - (void)syncFrameWithView:(UIView *)view offset:(CGPoint)offset NS_AVAILABLE_IOS(7_0) __deprecated_msg("Deprecated since 21.7. Use Autolayout instead."); 32 | - (void)stopSyncingFrame NS_AVAILABLE_IOS(7_0) __deprecated_msg("Deprecated since 21.7. When using Autolayout this should no longer be called."); 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /GBToolbox/GBRadialGradientView.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBRadialGradientView.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 11/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBRadialGradientView.h" 10 | 11 | @implementation GBRadialGradientView 12 | 13 | #pragma mark - init 14 | 15 | -(id)initWithFrame:(NSRect)frameRect { 16 | if (self = [super initWithFrame:frameRect]) { 17 | self.innerColor = [[NSColor whiteColor] colorWithAlphaComponent:.12]; 18 | self.outerColor = [NSColor clearColor]; 19 | self.cornerRadius = 4; 20 | } 21 | 22 | return self; 23 | } 24 | 25 | #pragma mark - custom properties 26 | 27 | -(void)setEdgeInsets:(GBEdgeInsets)edgeInsets { 28 | _edgeInsets = edgeInsets; 29 | 30 | [self setNeedsDisplay:YES]; 31 | } 32 | 33 | -(void)setInnerColor:(NSColor *)innerColor { 34 | _innerColor = innerColor; 35 | 36 | [self setNeedsDisplay:YES]; 37 | } 38 | 39 | -(void)setOuterColor:(NSColor *)outerColor { 40 | _outerColor = outerColor; 41 | 42 | [self setNeedsDisplay:YES]; 43 | } 44 | 45 | -(void)setCornerRadius:(CGFloat)cornerRadius { 46 | _cornerRadius = cornerRadius; 47 | 48 | [self setNeedsDisplay:YES]; 49 | } 50 | 51 | #pragma mark - drawing 52 | 53 | - (void)drawRect:(NSRect)dirtyRect { 54 | CGRect preliminaryClipRect = CGRectMake(self.edgeInsets.left, self.edgeInsets.bottom, self.bounds.size.width - self.edgeInsets.left - self.edgeInsets.right, self.bounds.size.height - self.edgeInsets.bottom - self.edgeInsets.top); 55 | NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:preliminaryClipRect xRadius:self.cornerRadius yRadius:self.cornerRadius]; 56 | NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:self.innerColor endingColor:self.outerColor]; 57 | [gradient drawInBezierPath:path relativeCenterPosition:NSMakePoint(0, 0)]; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /GBToolbox/GBTextField.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBTextField.h 3 | // Business cards 4 | // 5 | // Created by Luka Mirosevic on 28/09/2012. 6 | // Copyright (c) 2012 Luka Mirosevic. 7 | // 8 | // Licensed under the Apache License, Version 2.0 (the "License"); 9 | // you may not use this file except in compliance with the License. 10 | // You may obtain a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, software 15 | // distributed under the License is distributed on an "AS IS" BASIS, 16 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | // See the License for the specific language governing permissions and 18 | // limitations under the License. 19 | 20 | #import 21 | 22 | #import "GBTypes_Common.h" 23 | 24 | @protocol GBTextFieldDelegate; 25 | 26 | @interface GBTextField : UITextField 27 | 28 | @property (assign, nonatomic) UIEdgeInsets padding; 29 | @property (assign, nonatomic, readonly) BOOL isDirty; 30 | @property (assign, nonatomic) CGFloat leftViewLeftOffset; 31 | @property (assign, nonatomic) CGFloat rightViewRightOffset; 32 | @property (assign, nonatomic) CGRect leftViewFrame; 33 | @property (assign, nonatomic) CGRect rightViewFrame; 34 | 35 | @property (assign, nonatomic) BOOL invalidatesIntrinsicContentSizeDuringEditing;// default: YES 36 | 37 | //This is an augmented protocol. Adds 2 new methods, see below. 38 | -(id)delegate; 39 | -(void)setDelegate:(id)delegate; 40 | 41 | @end 42 | 43 | @protocol GBTextFieldDelegate 44 | @optional 45 | 46 | -(void)textField:(GBTextField *)textField keyPressed:(NSString *)string; 47 | -(void)textField:(GBTextField *)textField specialKeyPressed:(GBSpecialKey)specialKey; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /GBToolbox/GBGlowingImageButtonCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBGlowingImageButtonCell.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 13/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBGlowingImageButtonCell.h" 10 | 11 | #import "GBToolbox.h" 12 | 13 | 14 | @interface GBGlowingImageButtonCell () 15 | 16 | @property (strong, nonatomic) GBRadialGradientView *glowView; 17 | 18 | @end 19 | 20 | 21 | @implementation GBGlowingImageButtonCell 22 | 23 | 24 | #pragma mark - lazy 25 | 26 | -(GBRadialGradientView *)glowView { 27 | if (!_glowView) { 28 | _glowView = [[GBRadialGradientView alloc] initWithFrame:self.controlView.bounds]; 29 | _glowView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; 30 | _glowView.innerColor = [[NSColor whiteColor] colorWithAlphaComponent:.07]; 31 | _glowView.outerColor = [NSColor clearColor]; 32 | _glowView.cornerRadius = 4; 33 | _glowView.edgeInsets = GBEdgeInsetsMake(0, 0, 0, 0); 34 | } 35 | 36 | return _glowView; 37 | } 38 | 39 | #pragma mark - custom accessor 40 | 41 | -(void)setGlowEdgeInsets:(GBEdgeInsets)glowEdgeInsets { 42 | self.glowView.edgeInsets = glowEdgeInsets; 43 | } 44 | 45 | -(GBEdgeInsets)glowEdgeInsets { 46 | return self.glowView.edgeInsets; 47 | } 48 | 49 | -(void)setIsGlowing:(BOOL)isGlowing { 50 | //if theres a change 51 | if (isGlowing != _isGlowing) { 52 | if (isGlowing) { 53 | //add glow on top 54 | [self.controlView addSubview:self.glowView positioned:NSWindowAbove relativeTo:self.controlView.subviews.lastObject]; 55 | } 56 | else { 57 | //remove the glow 58 | [self.glowView removeFromSuperview]; 59 | } 60 | } 61 | 62 | //save ivar 63 | _isGlowing = isGlowing; 64 | } 65 | 66 | #pragma mark - init 67 | 68 | -(void)awakeFromNib { 69 | [super awakeFromNib]; 70 | 71 | self.isGlowing = YES; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /GBToolbox/NSDate+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 21/06/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "NSDate+GBToolbox.h" 10 | 11 | @implementation NSDate (GBToolbox) 12 | 13 | static NSDateFormatter *outDateFormatter; 14 | static NSArray *inDateFormatters; 15 | 16 | +(void)load { 17 | static dispatch_once_t onceToken; 18 | dispatch_once(&onceToken, ^{ 19 | outDateFormatter = [NSDateFormatter new]; 20 | outDateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZZ"; 21 | outDateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; 22 | 23 | NSDateFormatter *inDateFormatter1 = [NSDateFormatter new]; 24 | inDateFormatter1.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZZ"; 25 | NSDateFormatter *inDateFormatter2 = [NSDateFormatter new]; 26 | inDateFormatter2.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZZ"; 27 | inDateFormatters = @[inDateFormatter1, inDateFormatter2]; 28 | }); 29 | } 30 | 31 | +(NSDate *)dateWithISO8601String:(NSString *)dateString { 32 | if (!dateString) return nil; 33 | if ([dateString hasSuffix:@"Z"]) dateString = [[dateString substringToIndex:(dateString.length-1)] stringByAppendingString:@"+0000"]; 34 | 35 | NSDate *date; 36 | for (NSDateFormatter *dateFormatter in inDateFormatters) { 37 | date = [dateFormatter dateFromString:dateString]; 38 | if (date) break; 39 | } 40 | 41 | return date; 42 | } 43 | 44 | -(NSString *)iso8601String { 45 | return [outDateFormatter stringFromDate:self]; 46 | } 47 | 48 | -(BOOL)isInPast { 49 | return ([self timeIntervalSinceNow] < 0.); 50 | } 51 | 52 | -(BOOL)isDateBetweenStartDate:(NSDate*)beginDate andEndDate:(NSDate*)endDate { 53 | if ([self compare:beginDate] == NSOrderedAscending) { 54 | return NO; 55 | } 56 | else if ([self compare:endDate] == NSOrderedDescending) { 57 | return NO; 58 | } 59 | else { 60 | return YES; 61 | } 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /GBToolbox/MKMapView+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // MKMapView+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 10/07/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "MKMapView+GBToolbox.h" 10 | 11 | @implementation MKMapView (GBToolbox) 12 | 13 | -(void)moveToRegionIncludingLocations:(NSArray *)locationsArray animated:(BOOL)animated { 14 | [self moveToRegionIncludingLocations:locationsArray withPaddingFactor:1.0 animated:animated]; 15 | } 16 | 17 | -(void)moveToRegionIncludingLocations:(NSArray *)locationsArray withPaddingFactor:(CGFloat)paddingFactor animated:(BOOL)animated { 18 | MKMapRect zoomRect = MKMapRectNull; 19 | for (CLLocation *location in locationsArray) { 20 | MKMapPoint annotationPoint = MKMapPointForCoordinate(location.coordinate); 21 | MKMapRect pointRect = MKMapRectMake(annotationPoint.x-5, annotationPoint.y-5, 10, 10); 22 | zoomRect = MKMapRectUnion(zoomRect, pointRect); 23 | } 24 | 25 | CGSize sizeDelta = CGSizeMake(zoomRect.size.width*(paddingFactor - 1.0), 26 | zoomRect.size.height*(paddingFactor - 1.0)); 27 | 28 | MKMapRect paddedMapRect = MKMapRectMake(zoomRect.origin.x - (sizeDelta.width / 2.), 29 | zoomRect.origin.y - (sizeDelta.height / 2.), 30 | zoomRect.size.width + sizeDelta.width, 31 | zoomRect.size.height + sizeDelta.height); 32 | 33 | [self setVisibleMapRect:paddedMapRect animated:animated]; 34 | } 35 | 36 | -(void)moveToLocation:(CLLocation *)location showingRadius:(CGFloat)radius animated:(BOOL)animated { 37 | //111111m = 1deg lat 38 | //111111m * cos(lat) = 1deg lng 39 | CGFloat dlat = radius / 111111.; 40 | CGFloat dlng = radius*cos(dlat) / 111111.; 41 | 42 | MKCoordinateSpan span = MKCoordinateSpanMake(dlat, dlng); 43 | MKCoordinateRegion region = MKCoordinateRegionMake(location.coordinate, span); 44 | 45 | [self setRegion:region animated:animated]; 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /GBToolbox/GBTextBox.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBTextBox.h 3 | // Russia 4 | // 5 | // Created by Luka Mirosevic on 05/07/2013. 6 | // Copyright (c) 2013 Goonbee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum { 12 | GBTextBoxFlexibleDimensionHeight, 13 | GBTextBoxFlexibleDimensionWidth 14 | } GBTextBoxFlexibleDimension; 15 | 16 | @protocol GBTextBoxDelegate; 17 | 18 | @interface GBTextBox : UIView 19 | 20 | @property (weak, nonatomic) id delegate; 21 | 22 | @property (assign, nonatomic) GBTextBoxFlexibleDimension flexibleDimension;//defaults to height 23 | @property (strong, nonatomic) UIImage *icon; 24 | @property (assign, nonatomic) CGFloat iconLeftMargin; 25 | @property (assign, nonatomic) CGFloat iconVerticalOffset; 26 | @property (strong, nonatomic) UIImage *rightIcon; 27 | @property (assign, nonatomic) CGFloat rightIconRightMargin; 28 | @property (assign, nonatomic) CGFloat rightIconVerticalOffset; 29 | @property (strong, nonatomic) UIImage *backgroundImage; 30 | @property (strong, nonatomic) NSString *text; 31 | @property (strong, nonatomic) UIColor *textColor; 32 | @property (strong, nonatomic) UIColor *textShadowColor; 33 | @property (assign, nonatomic) CGSize textShadowOffset; 34 | @property (strong, nonatomic) UIFont *font; 35 | @property (assign, nonatomic) NSTextAlignment textAlignment; 36 | @property (assign, nonatomic) UIEdgeInsets textPadding; 37 | 38 | //forces it to recalculate the height based on the current text and it's properties. you would call this after you set the frame of this view so that it recalculated it's own height 39 | -(void)recalculateHeight; 40 | 41 | @end 42 | 43 | @protocol GBTextBoxDelegate 44 | @optional 45 | 46 | -(void)textBox:(GBTextBox *)textBox didChangeSizeFrom:(CGSize)oldSize to:(CGSize)newSize; 47 | 48 | @end -------------------------------------------------------------------------------- /GBToolbox/GBAsyncOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBAsyncOperation.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 22/11/16. 6 | // Copyright © 2016 Goonbee e.U.. All rights reserved. 7 | // 8 | 9 | #import "GBAsyncOperation.h" 10 | 11 | @implementation GBAsyncOperation { 12 | BOOL _executing; 13 | BOOL _finished; 14 | } 15 | 16 | #pragma mark - API 17 | 18 | - (instancetype)init { 19 | if (self = [super init]) { 20 | _executing = NO; 21 | _finished = NO; 22 | } 23 | 24 | return self; 25 | } 26 | 27 | - (void)completeAsyncOperation { 28 | [self willChangeValueForKey:@"isFinished"]; 29 | [self willChangeValueForKey:@"isExecuting"]; 30 | 31 | _executing = NO; 32 | _finished = YES; 33 | 34 | [self didChangeValueForKey:@"isExecuting"]; 35 | [self didChangeValueForKey:@"isFinished"]; 36 | } 37 | 38 | #pragma mark - Overrides 39 | 40 | - (void)main { 41 | @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"You must override main in your NSOperation subclass." userInfo:nil]; 42 | } 43 | 44 | - (void)start { 45 | // Always check for cancellation before launching the task. 46 | if (self.isCancelled) { 47 | // Must move the operation to the finished state if it is canceled. 48 | [self willChangeValueForKey:@"isFinished"]; 49 | _finished = YES; 50 | [self didChangeValueForKey:@"isFinished"]; 51 | 52 | return; 53 | } 54 | 55 | // If the operation is not canceled, begin executing the task. 56 | [self willChangeValueForKey:@"isExecuting"]; 57 | [self performSelector:@selector(main)];// using performSelector to avoid NS_UNAVAILABLE compiler warning 58 | _executing = YES; 59 | [self didChangeValueForKey:@"isExecuting"]; 60 | } 61 | 62 | - (void)cancel { 63 | // we implement this only so that we can highlight it in the interface, and to keep the compiler happy 64 | [super cancel]; 65 | } 66 | 67 | - (BOOL)isConcurrent { 68 | return YES; 69 | } 70 | 71 | - (BOOL)isExecuting { 72 | return _executing; 73 | } 74 | 75 | - (BOOL)isFinished { 76 | return _finished; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /GBToolbox/GBMultiStateButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBMultiStateButton.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 20/02/2016. 6 | // Copyright © 2016 Goonbee e.U. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class GBMultiStateButtonState; 12 | 13 | @interface GBMultiStateButton : UIControl 14 | 15 | @property (strong, nonatomic, readonly) NSArray *states; 16 | @property (strong, nonatomic) GBMultiStateButtonState *activeState; 17 | @property (strong, nonatomic) id activeStateIdentifier; 18 | @property (assign, nonatomic) BOOL adjustsImageWhenHighlighted; 19 | 20 | - (instancetype)initWithStates:(NSArray *)states; 21 | 22 | @end 23 | 24 | @interface GBMultiStateButtonState : NSObject 25 | 26 | @property (strong, nonatomic) id stateIdentifier; 27 | @property (strong, nonatomic) UIImage *image; 28 | @property (strong, nonatomic) UIImage *backgroundImage; 29 | @property (strong, nonatomic) UIImage *imageWhenHighlighted; 30 | @property (strong, nonatomic) UIImage *backgroundImageWhenHighlighted; 31 | @property (assign, nonatomic, getter=isUserSelectable) BOOL userSelectable; 32 | 33 | + (instancetype)stateWithIdentifier:(id)identifier image:(UIImage *)image; 34 | + (instancetype)stateWithIdentifier:(id)identifier image:(UIImage *)image backgroundImage:(UIImage *)backgroundImage; 35 | + (instancetype)stateWithIdentifier:(id)identifier image:(UIImage *)image backgroundImage:(UIImage *)backgroundImage imageWhenHighlighted:(UIImage *)imageWhenHighlighted backgroundImageWhenHighlighted:(UIImage *)backgroundImageWhenHighlighted; 36 | + (instancetype)stateWithIdentifier:(id)identifier image:(UIImage *)image backgroundImage:(UIImage *)backgroundImage imageWhenHighlighted:(UIImage *)imageWhenHighlighted backgroundImageWhenHighlighted:(UIImage *)backgroundImageWhenHighlighted userSelectable:(BOOL)userSelectable; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /GBToolbox/GBCustomImageButtonCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBCustomImageButtonCell.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 13/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBCustomImageButtonCell.h" 10 | 11 | #import "GBToolbox.h" 12 | 13 | 14 | @interface GBCustomImageButtonCell () 15 | 16 | @property (strong, nonatomic) GBResizableImageView *resizableImageView; 17 | @property (strong, nonatomic) NSImageView *foregroundImageView; 18 | 19 | @end 20 | 21 | 22 | @implementation GBCustomImageButtonCell 23 | 24 | #pragma mark - lazy 25 | 26 | -(GBResizableImageView *)resizableImageView { 27 | if (!_resizableImageView) { 28 | _resizableImageView = [[GBResizableImageView alloc] initWithFrame:self.controlView.bounds]; 29 | _resizableImageView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; 30 | } 31 | 32 | return _resizableImageView; 33 | } 34 | 35 | -(NSImageView *)foregroundImageView { 36 | if (!_foregroundImageView) { 37 | _foregroundImageView = [[NSImageView alloc] initWithFrame:self.controlView.bounds]; 38 | _foregroundImageView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; 39 | //set scaling and alignment to just center the image without resizing 40 | _foregroundImageView.imageAlignment = NSImageAlignCenter; 41 | _foregroundImageView.imageScaling = NSImageScaleNone; 42 | } 43 | 44 | return _foregroundImageView; 45 | } 46 | 47 | #pragma mark - custom setter 48 | 49 | -(void)setForegroundImage:(NSImage *)foregroundImage { 50 | _foregroundImage = foregroundImage; 51 | 52 | self.foregroundImageView.image = foregroundImage; 53 | } 54 | 55 | -(void)setBackgroundImage:(NSImage *)backgroundImage { 56 | self.resizableImageView.image = backgroundImage; 57 | } 58 | 59 | -(NSImage *)backgroundImage { 60 | return self.resizableImageView.image; 61 | } 62 | 63 | -(void)setBackgroundImageCapInsets:(GBEdgeInsets)backgroundImageCapInsets { 64 | self.resizableImageView.capInsets = backgroundImageCapInsets; 65 | } 66 | 67 | -(GBEdgeInsets)backgroundImageCapInsets { 68 | return self.resizableImageView.capInsets; 69 | } 70 | 71 | #pragma mark - init 72 | 73 | -(void)awakeFromNib { 74 | [super awakeFromNib]; 75 | 76 | self.customView = self.resizableImageView; 77 | [self.customView addSubview:self.foregroundImageView]; 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /GBToolbox/UIControl+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 20/02/2016. 6 | // Copyright © 2016 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "UIControl+GBToolbox.h" 10 | 11 | #import "GBMacros_Common.h" 12 | #import 13 | 14 | @interface GBControlTargetActionTarget : NSObject 15 | 16 | @property (copy, nonatomic) GBActionBlock block; 17 | 18 | @end 19 | 20 | @implementation GBControlTargetActionTarget 21 | 22 | #pragma mark - API 23 | 24 | + (instancetype)targetActionTargetWithBlock:(GBActionBlock)block { 25 | GBControlTargetActionTarget *targetActionTarget = [self new]; 26 | targetActionTarget.block = block; 27 | 28 | return targetActionTarget; 29 | } 30 | 31 | - (void)fireForSender:(id)sender event:(UIEvent *)event { 32 | if (self.block) self.block(sender, event); 33 | } 34 | 35 | @end 36 | 37 | @interface UIControl () 38 | 39 | @property (strong, nonatomic, readonly) NSHashTable *targetActionTargets; 40 | 41 | @end 42 | 43 | @implementation UIControl (GBToolbox) 44 | 45 | #pragma mark - CA 46 | 47 | static void *AssociatedTargetActionsKey = &AssociatedTargetActionsKey; 48 | 49 | - (NSHashTable *)targetActionTargets { 50 | NSHashTable *targetActionTargets = objc_getAssociatedObject(self, &AssociatedTargetActionsKey); 51 | 52 | // lazily create out hashTable 53 | if (!targetActionTargets) { 54 | targetActionTargets = [NSHashTable hashTableWithOptions:NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality]; 55 | objc_setAssociatedObject(self, &AssociatedTargetActionsKey, targetActionTargets, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 56 | } 57 | 58 | return targetActionTargets; 59 | } 60 | 61 | #pragma mark - API 62 | 63 | - (void)addTargetActionForControlEvents:(UIControlEvents)controlEvents withBlock:(GBActionBlock)block { 64 | // Create the target that will store and trigger the block, exposed by a method that we can pass to the target/action mechanism 65 | GBControlTargetActionTarget *target = [GBControlTargetActionTarget targetActionTargetWithBlock:block]; 66 | 67 | // Retain our target, so it lives for as long as this object lives 68 | [self.targetActionTargets addObject:target]; 69 | 70 | // Add the target using normal target/action, setting the action to our fire trigger 71 | [self addTarget:target action:@selector(fireForSender:event:) forControlEvents:controlEvents]; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /GBToolbox/NSDictionary+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 06/06/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "NSDictionary+GBToolbox.h" 10 | 11 | @implementation NSDictionary (GBToolbox) 12 | 13 | #pragma mark - Funtional Programming 14 | 15 | - (NSDictionary *)filter:(BOOL(^)(id key, id object))function { 16 | // creates a results array in which to store results, sets the capacity for faster writes 17 | NSUInteger count = self.count; 18 | NSMutableDictionary *resultsDictionary = [[NSMutableDictionary alloc] initWithCapacity:count]; 19 | 20 | // applies the function to each item and stores the result in the new dictionary 21 | for (id key in self) { 22 | if (function(key, self[key])) { 23 | resultsDictionary[key] = self[key]; 24 | } 25 | } 26 | 27 | // returns an immutable copy 28 | return [resultsDictionary copy]; 29 | } 30 | 31 | - (id)aKey:(BOOL(^)(id key, id object))function { 32 | for (id key in self) { 33 | if (function(key, self[key])) { 34 | return key; 35 | } 36 | } 37 | 38 | return nil; 39 | } 40 | 41 | - (id)anObject:(BOOL(^)(id key, id object))function { 42 | id key = [self aKey:function]; 43 | if (key) { 44 | return self[key]; 45 | } 46 | else { 47 | return nil; 48 | } 49 | } 50 | 51 | #pragma mark - pruning 52 | 53 | + (NSDictionary *)dictionaryByPruningNullsInDictionary:(NSDictionary *)dictionary { 54 | // fallback for nil dictionaries. 55 | if (!dictionary) dictionary = @{}; 56 | 57 | NSMutableDictionary *temp = [[NSMutableDictionary alloc] initWithCapacity:dictionary.count]; 58 | 59 | for (id key in dictionary) { 60 | id value = [dictionary objectForKey:key]; 61 | 62 | if ([value isKindOfClass:[NSDictionary class]]) { 63 | [temp setObject:[NSDictionary dictionaryByPruningNullsInDictionary:value] forKey:key]; 64 | } 65 | else if ([value isKindOfClass:[NSNull class]]) { 66 | continue; 67 | } 68 | else { 69 | [temp setObject:value forKey:key]; 70 | } 71 | } 72 | 73 | return temp; 74 | } 75 | 76 | - (nonnull NSDictionary *)dictionaryByPruningNulls { 77 | return [self.class dictionaryByPruningNullsInDictionary:self]; 78 | } 79 | 80 | - (nonnull NSDictionary *)dictionaryByMergingWithDictionary:(nullable NSDictionary *)dictionary { 81 | NSMutableDictionary *newDictionary = [self mutableCopy]; 82 | 83 | for (id key in dictionary) { 84 | newDictionary[key] = dictionary[key]; 85 | } 86 | 87 | return newDictionary; 88 | } 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /GBToolbox/NSObject+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "NSObject+GBToolbox.h" 10 | 11 | #import "GBMacros_Common.h" 12 | 13 | @interface NSObject () 14 | 15 | @property (strong, nonatomic, readwrite) NSMapTable *GBPayloadMap_storage; 16 | 17 | @end 18 | 19 | @implementation NSObject (GBToolbox) 20 | 21 | _associatedObject(strong, nonatomic, id, GBPayload, setGBPayload) 22 | 23 | _associatedObject(strong, nonatomic, NSMapTable *, GBPayloadMap_storage, setGBPayloadMap_storage) 24 | 25 | - (NSMapTable *)GBPayloadMap { 26 | if (!self.GBPayloadMap_storage) { 27 | self.GBPayloadMap_storage = [NSMapTable strongToStrongObjectsMapTable]; 28 | } 29 | 30 | return self.GBPayloadMap_storage; 31 | } 32 | 33 | - (NSString *)pointerAddress { 34 | return [NSString stringWithFormat:@"%p", self]; 35 | } 36 | 37 | - (instancetype)tap:(void (^)(id object))block { 38 | if (block) block(self); 39 | 40 | return self; 41 | } 42 | 43 | // http://stackoverflow.com/a/38759825/399772 44 | + (SEL)getterForPropertyWithName:(NSString *)name { 45 | const char *propertyName = [name cStringUsingEncoding:NSASCIIStringEncoding]; 46 | objc_property_t prop = class_getProperty(self, propertyName); 47 | 48 | const char *selectorName = property_copyAttributeValue(prop, "G"); 49 | if (selectorName == NULL) { 50 | selectorName = [name cStringUsingEncoding:NSASCIIStringEncoding]; 51 | } 52 | NSString *selectorString = [NSString stringWithCString:selectorName encoding:NSASCIIStringEncoding]; 53 | return NSSelectorFromString(selectorString); 54 | } 55 | 56 | // http://stackoverflow.com/a/38759825/399772 57 | + (SEL)setterForPropertyWithName:(NSString *)name { 58 | const char *propertyName = [name cStringUsingEncoding:NSASCIIStringEncoding]; 59 | objc_property_t prop = class_getProperty(self, propertyName); 60 | 61 | char *selectorName = property_copyAttributeValue(prop, "S"); 62 | NSString *selectorString; 63 | if (selectorName == NULL) { 64 | char firstChar = (char)toupper(propertyName[0]); 65 | NSString *capitalLetter = [NSString stringWithFormat:@"%c", firstChar]; 66 | NSString *reminder = [NSString stringWithCString: propertyName+1 67 | encoding: NSASCIIStringEncoding]; 68 | selectorString = [@[@"set", capitalLetter, reminder, @":"] componentsJoinedByString:@""]; 69 | } else { 70 | selectorString = [NSString stringWithCString:selectorName encoding:NSASCIIStringEncoding]; 71 | } 72 | 73 | return NSSelectorFromString(selectorString); 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /GBToolbox/UIView+GBFloatingPopoverView.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+GBFloatingPopoverView.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 16/09/16. 6 | // Copyright © 2016 Goonbee e.U. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | FOUNDATION_EXPORT NSTimeInterval const kGBFloatingPopoverShowForever; 12 | 13 | typedef void(^GBFloatingPopoverAutolayoutConfigurationBlock)(UIView * _Nonnull view); 14 | 15 | @interface UIView (GBFloatingPopoverView) 16 | 17 | /** 18 | Shows receiver on the targetView. `layoutBlock` is called synchronously as soon as the view has been added to targetView (unless if the view is already a subview of targetView. Handles intelligent nesting of views within a context. 19 | */ 20 | - (void)floatOnView:(nonnull UIView *)targetView animated:(BOOL)animated context:(nonnull id)context layoutConfigurationBlock:(nullable GBFloatingPopoverAutolayoutConfigurationBlock)layoutBlock; 21 | 22 | /** 23 | Shows receiver on the targetView. `layoutBlock` is called synchronously as soon as the view has been added to targetView (unless if the view is already a subview of targetView. Handles intelligent nesting of views within a context. If autoDismiss is set to YES, this view will show for a while and then dismiss itself. 24 | */ 25 | - (void)floatOnView:(nonnull UIView *)targetView animated:(BOOL)animated autoDismiss:(BOOL)autoDismiss context:(nonnull id)context layoutConfigurationBlock:(nullable GBFloatingPopoverAutolayoutConfigurationBlock)layoutBlock; 26 | 27 | /** 28 | Shows receiver on the targetView. `layoutBlock` is called synchronously as soon as the view has been added to targetView (unless if the view is already a subview of targetView. Handles intelligent nesting of views within a context. If you do not want the view to dismiss, pass a `kGBFloatingPopoverShowForever` as the `showDuration.` 29 | 30 | Note: If you pass a non-nil layoutConfigurationBlock, the `view` will have its `translatesAutoresizingMaskIntoConstraints` property set to NO. 31 | 32 | Warning: Passing different durations for a particular context, will have undefined behaviour if another view is already being shown in this context. 33 | */ 34 | - (void)floatOnView:(nonnull UIView *)targetView context:(nonnull id)context fadeInDuration:(NSTimeInterval)fadeInDuration showDuration:(NSTimeInterval)showDuration fadeOutDuration:(NSTimeInterval)fadeOutDuration layoutConfigurationBlock:(nullable GBFloatingPopoverAutolayoutConfigurationBlock)layoutBlock; 35 | 36 | /** 37 | Dismisses the receiver. 38 | */ 39 | - (void)floatingViewDismissForContext:(nonnull id)context animated:(BOOL)animated; 40 | 41 | /** 42 | Dismisses the receiver with a desired animation duration. 43 | */ 44 | - (void)floatingViewDismissForContext:(nonnull id)context fadeOutDuration:(NSTimeInterval)fadeOutDuration; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /GBToolbox/NSMutableURLRequest+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableURLRequest+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 13/06/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GBFormPayload : NSObject 12 | 13 | @property (copy, nonatomic) NSString *contentType; 14 | @property (copy, nonatomic) NSString *name; 15 | @property (copy, nonatomic) NSString *filename; 16 | @property (strong, nonatomic) NSData *data; 17 | 18 | +(GBFormPayload *)formPayloadWithName:(NSString *)name filename:(NSString *)filename contentType:(NSString *)contentType data:(NSData *)data; 19 | +(GBFormPayload *)formPayloadWithName:(NSString *)name contentType:(NSString *)contentType data:(NSData *)data; 20 | -(id)initWithName:(NSString *)name filename:(NSString *)filename contentType:(NSString *)contentType data:(NSData *)data; 21 | -(id)initWithName:(NSString *)name contentType:(NSString *)contentType data:(NSData *)data; 22 | 23 | -(NSData *)representationInMultipartForm; 24 | 25 | @end 26 | 27 | @interface NSMutableURLRequest (GBToolbox) 28 | 29 | +(NSMutableURLRequest *)postRequestWithURLString:(NSString *)urlString jsonPayload:(NSData *)data; 30 | +(NSMutableURLRequest *)postRequestWithURLString:(NSString *)urlString jsonPayload:(NSData *)data headers:(NSDictionary *)headers; 31 | +(NSMutableURLRequest *)postRequestWithURLString:(NSString *)urlString jsonPayload:(NSData *)data headers:(NSDictionary *)headers timeout:(NSTimeInterval)timeout; 32 | 33 | +(NSMutableURLRequest *)postRequestWithURLString:(NSString *)urlString payloadType:(NSString *)contentType payloadData:(NSData *)data; 34 | +(NSMutableURLRequest *)postRequestWithURLString:(NSString *)urlString payloadType:(NSString *)contentType payloadData:(NSData *)data headers:(NSDictionary *)headers; 35 | +(NSMutableURLRequest *)postRequestWithURLString:(NSString *)urlString payloadType:(NSString *)contentType payloadData:(NSData *)data headers:(NSDictionary *)headers timeout:(NSTimeInterval)timeout; 36 | 37 | +(NSMutableURLRequest *)multipartPostRequestWithURLString:(NSString *)urlString payloads:(NSArray *)payloads; 38 | +(NSMutableURLRequest *)multipartPostRequestWithURLString:(NSString *)urlString payloads:(NSArray *)payloads headers:(NSDictionary *)headers; 39 | +(NSMutableURLRequest *)multipartPostRequestWithURLString:(NSString *)urlString payloads:(NSArray *)payloads headers:(NSDictionary *)headers timeout:(NSTimeInterval)timeout; 40 | 41 | +(NSMutableURLRequest *)requestWithURLString:(NSString *)urlString method:(NSString *)method body:(NSData *)body headers:(NSDictionary *)headers; 42 | +(NSMutableURLRequest *)requestWithURLString:(NSString *)urlString method:(NSString *)method body:(NSData *)body headers:(NSDictionary *)headers timeout:(NSTimeInterval)timeout; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /GBToolbox/NSMutableArray+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 17/05/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "NSMutableArray+GBToolbox.h" 10 | 11 | #import "GBConstants_Common.h" 12 | 13 | @implementation NSMutableArray (GBToolbox) 14 | 15 | #pragma mark - Array padding 16 | //These are all non-destructive and leave existing elements in, only when extendin the array do we actually pad it 17 | 18 | //Pads the array out so the last index is the one that's passed in, with [NSNull null] 19 | -(void)padToIndex:(NSUInteger)index { 20 | [self padToIndex:index withObject:[NSNull null]]; 21 | } 22 | 23 | //Pads the array out so the last index is the one that's passed in, with any non nil object 24 | -(void)padToIndex:(NSUInteger)index withObject:(id)object { 25 | [self padToSize:index+1 withObject:object]; 26 | } 27 | 28 | //Pads the array out to the desired size with [NSNull null] objects 29 | -(void)padToSize:(NSUInteger)count { 30 | [self padToSize:count withObject:[NSNull null]]; 31 | } 32 | 33 | //Pads the array out to the desired size with any object you supply 34 | -(void)padToSize:(NSUInteger)count withObject:(id)object { 35 | //make sure it's not nil 36 | if (object) { 37 | //if the size is already bigger or equal then we're done 38 | if (self.count >= count) { 39 | //noop 40 | } 41 | else { 42 | //enumerate from the last element+1 and assign until we are at the desired length 43 | for (NSUInteger i=self.count; i 0 ) 77 | { 78 | if (_placeHolderLabel == nil ) 79 | { 80 | _placeHolderLabel = [[UILabel alloc] initWithFrame:CGRectMake(8,8,self.bounds.size.width - 16,0)]; 81 | _placeHolderLabel.lineBreakMode = NSLineBreakByWordWrapping; 82 | _placeHolderLabel.numberOfLines = 0; 83 | _placeHolderLabel.font = self.font; 84 | _placeHolderLabel.backgroundColor = [UIColor clearColor]; 85 | _placeHolderLabel.textColor = self.placeholderColor; 86 | _placeHolderLabel.alpha = 0; 87 | _placeHolderLabel.tag = 999; 88 | [self addSubview:_placeHolderLabel]; 89 | } 90 | 91 | _placeHolderLabel.text = self.placeholder; 92 | [_placeHolderLabel sizeToFit]; 93 | [self sendSubviewToBack:_placeHolderLabel]; 94 | } 95 | 96 | if( [[self text] length] == 0 && [[self placeholder] length] > 0 ) 97 | { 98 | [[self viewWithTag:999] setAlpha:1]; 99 | } 100 | 101 | [super drawRect:rect]; 102 | } 103 | 104 | -(UIColor *)defaultPlaceHolderColor { 105 | if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) { 106 | return [UIColor colorWithRed:0 green:0 blue:0.098039 alpha:0.22]; 107 | } 108 | else { 109 | return [UIColor colorWithWhite:0.7 alpha:1.]; 110 | } 111 | } 112 | 113 | @end -------------------------------------------------------------------------------- /GBToolbox/UITableViewCell+AdditionalViews.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewCell+AdditionalViews.m 3 | // Russia 4 | // 5 | // Created by Luka Mirosevic on 27/06/2013. 6 | // Copyright (c) 2013 Goonbee. All rights reserved. 7 | // 8 | 9 | #import "UITableViewCell+AdditionalViews.h" 10 | 11 | #import "GBMacros_Common.h" 12 | 13 | @interface UITableViewCell () 14 | 15 | @property (strong, nonatomic) UIView *internalLeftView; 16 | @property (assign, nonatomic) NSValue *internalLeftViewFrame; 17 | @property (strong, nonatomic) UIView *internalRightView; 18 | @property (assign, nonatomic) NSValue *internalRightViewFrame; 19 | 20 | @end 21 | 22 | @implementation UITableViewCell (AdditionalViews) 23 | 24 | #pragma mark - mem 25 | 26 | _associatedObject(strong, nonatomic, UIView *, internalLeftView, setInternalLeftView) 27 | _associatedObject(strong, nonatomic, NSValue *, internalLeftViewFrame, setInternalLeftViewFrame) 28 | 29 | _associatedObject(strong, nonatomic, UIView *, internalRightView, setInternalRightView) 30 | _associatedObject(strong, nonatomic, NSValue *, internalRightViewFrame, setInternalRightViewFrame) 31 | 32 | #pragma mark - custom accessors 33 | 34 | -(void)setLeftView:(UIView *)leftView { 35 | if (self.internalLeftView != leftView) { 36 | //remove old one 37 | [self.internalLeftView removeFromSuperview]; 38 | 39 | //remeber it 40 | self.internalLeftView = leftView; 41 | 42 | //show new one 43 | [self.contentView addSubview:leftView]; 44 | 45 | //configure it 46 | [self _layoutLeftView]; 47 | } 48 | } 49 | 50 | -(UIView *)leftView { 51 | return self.internalLeftView; 52 | } 53 | 54 | -(void)setLeftViewFrame:(CGRect)leftViewFrame { 55 | self.internalLeftViewFrame = [NSValue valueWithCGRect:leftViewFrame]; 56 | 57 | [self _layoutLeftView]; 58 | } 59 | 60 | -(CGRect)leftViewFrame { 61 | return [self.internalLeftViewFrame CGRectValue]; 62 | } 63 | 64 | -(void)setRightView:(UIView *)rightView { 65 | if (self.internalRightView != rightView) { 66 | //remove old one 67 | [self.internalRightView removeFromSuperview]; 68 | 69 | //remeber it 70 | self.internalRightView = rightView; 71 | 72 | //show new one 73 | [self.contentView addSubview:rightView]; 74 | 75 | //configure it 76 | [self _layoutRightView]; 77 | } 78 | } 79 | 80 | -(UIView *)rightView { 81 | return self.internalRightView; 82 | } 83 | 84 | -(void)setRightViewFrame:(CGRect)rightViewFrame { 85 | self.internalRightViewFrame = [NSValue valueWithCGRect:rightViewFrame]; 86 | 87 | [self _layoutRightView]; 88 | } 89 | 90 | -(CGRect)rightViewFrame { 91 | return [self.internalRightViewFrame CGRectValue]; 92 | } 93 | 94 | #pragma mark - util 95 | 96 | -(void)_layoutLeftView { 97 | self.internalLeftView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin; 98 | self.internalLeftView.frame = self.leftViewFrame; 99 | } 100 | 101 | -(void)_layoutRightView { 102 | self.internalRightView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin; 103 | self.internalRightView.frame = self.rightViewFrame; 104 | } 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /GBToolbox/UIViewController+GBToolbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+GBToolbox.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "UIViewController+GBToolbox.h" 10 | 11 | #import "GBUtility_Common.h" 12 | #import "UINavigationBar+GBToolbox.h" 13 | #import 14 | 15 | @implementation UIViewController (GBToolbox) 16 | 17 | static char gbIsVisibleKey; 18 | static char gbIsVisibleCurrentlyKey; 19 | 20 | #pragma mark - CA 21 | 22 | - (void)setIsVisible:(BOOL)isVisible { 23 | objc_setAssociatedObject(self, &gbIsVisibleKey, @(isVisible), OBJC_ASSOCIATION_COPY); 24 | } 25 | 26 | - (BOOL)isVisible { 27 | return [objc_getAssociatedObject(self, &gbIsVisibleKey) boolValue]; 28 | } 29 | 30 | - (void)setIsVisibleCurrently:(BOOL)isVisibleCurrently { 31 | objc_setAssociatedObject(self, &gbIsVisibleCurrentlyKey, @(isVisibleCurrently), OBJC_ASSOCIATION_COPY); 32 | } 33 | 34 | - (BOOL)isVisibleCurrently { 35 | return [objc_getAssociatedObject(self, &gbIsVisibleCurrentlyKey) boolValue]; 36 | } 37 | 38 | #pragma mark - Overrides 39 | 40 | + (void)load { 41 | SwizzleInstanceMethodsInClass(self, @selector(viewWillAppear:), @selector(_swizz_viewWillAppear:)); 42 | SwizzleInstanceMethodsInClass(self, @selector(viewWillDisappear:), @selector(_swizz_viewWillDisappear:)); 43 | SwizzleInstanceMethodsInClass(self, @selector(viewDidDisappear:), @selector(_swizz_viewDidDisappear:)); 44 | } 45 | 46 | - (void)_swizz_viewWillAppear:(BOOL)animated { 47 | self.isVisible = YES; 48 | self.isVisibleCurrently = YES; 49 | 50 | [self _swizz_viewWillAppear:animated]; 51 | } 52 | 53 | - (void)_swizz_viewWillDisappear:(BOOL)animated { 54 | self.isVisibleCurrently = NO; 55 | 56 | [self _swizz_viewWillDisappear:animated]; 57 | } 58 | 59 | - (void)_swizz_viewDidDisappear:(BOOL)animated { 60 | self.isVisible = NO; 61 | 62 | [self _swizz_viewDidDisappear:animated]; 63 | } 64 | 65 | #pragma mark - Extensions 66 | 67 | - (void)ensureViewIsLoaded { 68 | [self view];//this causes the view to get loaded 69 | } 70 | 71 | - (UIStatusBarStyle)inheritedPreferredStatusBarStyle { 72 | UIViewController *targetViewController = self.presentingViewController; 73 | if (!targetViewController) { 74 | return UIStatusBarStyleLightContent; 75 | } 76 | 77 | // walk all the way up the presenting chain 78 | while (targetViewController.parentViewController) { 79 | targetViewController = targetViewController.parentViewController; 80 | } 81 | 82 | // walk all the way down the status bar style chain 83 | while (targetViewController.childViewControllerForStatusBarStyle) { 84 | targetViewController = targetViewController.childViewControllerForStatusBarStyle; 85 | } 86 | 87 | return [targetViewController preferredStatusBarStyle]; 88 | } 89 | 90 | - (void)styleNavigationBarWithColor:(nullable UIColor *)color { 91 | [self.navigationController.navigationBar styleWithColor:color]; 92 | } 93 | 94 | - (void)hideBackButtonTitle { 95 | self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:self.navigationItem.backBarButtonItem.style target:nil action:nil]; 96 | } 97 | 98 | - (UIViewController *)presentingViewControllerAcrossParents { 99 | // parent view controller 100 | if (self.parentViewController) { 101 | // -> recurse out at the parent;s level 102 | return self.tabBarController.presentingViewControllerAcrossParents; 103 | } 104 | // base case 105 | else { 106 | return self.presentingViewController; 107 | } 108 | } 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /GBToolbox/GBFastArray.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBFastArray.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 01/05/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "GBConstants_Common.h" 12 | 13 | typedef enum { 14 | GBSearchResultMatch, 15 | GBSearchResultLow, 16 | GBSearchResultHigh, 17 | } GBSearchResult; 18 | 19 | //lambda for guiding binary search algo. there's no param for target item, but no need cuz it will get closed over from your calling context 20 | typedef GBSearchResult(^SearchLambda)(void *candidateItem); 21 | 22 | @interface GBFastArray : NSObject 23 | 24 | @property (assign, nonatomic, readonly) NSUInteger count; 25 | @property (assign, nonatomic, readonly) BOOL isEmpty; 26 | 27 | /** 28 | Initializes the array with for a specific typesize. 29 | 30 | Example: [[GBFastArray alloc] initWithTypeSize:sizeof(MyStruct)]; 31 | */ 32 | - (instancetype)initWithTypeSize:(NSUInteger)typeSize; 33 | 34 | /** 35 | Initializes the array with for a specific typesize. 36 | 37 | Initial capacity is the amount of contiguous memory allocated for the array, and the resizingFactor is the factor used for resizing the array, the new capacity will be oldCapacity * resizingFactor. Tune these values to find the right balance between minimizing the count of resizing operations (which are slow), whilst not overallocating memory (which is wasteful). 38 | 39 | Example: [[GBFastArray alloc] initWithTypeSize:sizeof(MyStruct)]; 40 | */ 41 | - (instancetype)initWithTypeSize:(NSUInteger)typeSize initialCapacity:(NSUInteger)initialCapacity resizingFactor:(CGFloat)resizingFactor; 42 | 43 | /** 44 | Inserts an item into the array. 45 | 46 | The item is dereferenced and copied using memcpy. 47 | The array will resize itself in order to accomodate the new index. If you choose a large index, beware that memory will be allocated for all the intermediary items, that is GBFastArray is not a sparse array. 48 | 49 | Example: [myFastArray insertItem:&myItem atIndex:10]; 50 | */ 51 | - (void)insertItem:(void *)itemAddress atIndex:(NSUInteger)index; 52 | 53 | /** 54 | Appends an item to the end of the array, increasing the length of the array by 1. 55 | 56 | The item is dereferenced and copied using memcpy. 57 | This method does not reallocate memory for the array unless it has to. 58 | */ 59 | - (void)appendItem:(void *)itemAddress; 60 | 61 | /** 62 | Returns a pointer to the item at the specific index. This is the actual item stored, and is not a copy, so beware that when dereferencing and changing, that these changes will persist. 63 | 64 | Example: MyStruct myItem = *(MyStruct *)[myFastArray itemAtIndex:10]; 65 | */ 66 | - (void *)itemAtIndex:(NSUInteger)index; 67 | 68 | /** 69 | Do a binary search through the array using the searchLambda predicate block 70 | */ 71 | - (NSUInteger)binarySearchForIndexWithSearchLambda:(SearchLambda)searchLambda; 72 | 73 | /** 74 | Do a binary search through the array using the searchLambda predicate block. You can specify subarray in which to search using the lowIndex and highIndex. 75 | */ 76 | - (NSUInteger)binarySearchForIndexWithLow:(NSUInteger)lowIndex high:(NSUInteger)highIndex searchLambda:(SearchLambda)searchLambda; 77 | 78 | /** 79 | Return the size in bytes of the type that is being stored in the array. 80 | */ 81 | - (NSUInteger)typeSize; 82 | 83 | /** 84 | Return the length of the array. 85 | */ 86 | - (NSUInteger)currentAllocationSize; 87 | 88 | /** 89 | Reallocate the array to a specific size. You don't need to call this methid as the array will resize itself, however if you know ahead of time that the array will have to resize itself multiple times in order to fulfill some set of operations, you can optimize this down to a single resizing by preemtively calling this method before you start your set of operations which will insert items into the array. 90 | */ 91 | - (void)reallocToSize:(NSUInteger)newSize; 92 | 93 | /** 94 | You can adjust the resizingFactor here at any time. 95 | */ 96 | - (void)setResizingFactor:(CGFloat)resizingFactor; 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /GBToolbox/GBMacros_iOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBMacros_iOS.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #ifndef GBToolbox_GBMacros_iOS_h 10 | #define GBToolbox_GBMacros_iOS_h 11 | 12 | #import 13 | 14 | //Universal app device detection 15 | #define _d ((UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPhone) ? Phone : Pad) 16 | 17 | //View controller instantiation 18 | #define __sb(ARGC, ARGS...) sb_ ## ARGC (ARGS) 19 | #define _sb(ARGC, ARGS...) __sb(ARGC, ARGS) 20 | #define sb(...) _sb(NARGS(__VA_ARGS__), __VA_ARGS__) 21 | #define sb_0(...) (UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPhone) ? @"StoryboardPhone" : @"StoryboardPad" 22 | #define sb_1(ARG, ...) (UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPhone) ? ARG @"Phone" : ARG @"Pad" 23 | 24 | #define vc(storyboardID) [[UIStoryboard storyboardWithName:sb() bundle:nil] instantiateViewControllerWithIdentifier:storyboardID] 25 | #define vcsb(storyboardID, storyboardName) [[UIStoryboard storyboardWithName:sb(storyboardName) bundle:nil] instantiateViewControllerWithIdentifier:storyboardID] 26 | 27 | //View instantiation and controller hookup 28 | #define vSelfLink(nibName) [[NSBundle.mainBundle loadNibNamed:nibName owner:self options:nil] objectAtIndex:0] 29 | #define v(nibName) [[NSBundle.mainBundle loadNibNamed:nibName owner:nil options:nil] objectAtIndex:0] 30 | 31 | //View loading from nib 32 | #define _loadFromNib(nibName) -(id)awakeAfterUsingCoder:(NSCoder *)aDecoder { \ 33 | BOOL needsLoading = (self.subviews.count == 0); \ 34 | if (needsLoading) { \ 35 | UIView *nibView = [[NSBundle.mainBundle loadNibNamed:nibName owner:nil options:nil] objectAtIndex:0]; \ 36 | \ 37 | nibView.frame = self.frame; \ 38 | nibView.autoresizingMask = self.autoresizingMask; \ 39 | nibView.alpha = self.alpha; \ 40 | nibView.opaque = self.opaque; \ 41 | nibView.hidden = self.hidden; \ 42 | nibView.backgroundColor = self.backgroundColor; \ 43 | nibView.userInteractionEnabled = self.userInteractionEnabled; \ 44 | nibView.contentMode = self.contentMode; \ 45 | nibView.clearsContextBeforeDrawing = self.clearsContextBeforeDrawing; \ 46 | nibView.clipsToBounds = self.clipsToBounds; \ 47 | nibView.autoresizesSubviews = self.autoresizesSubviews; \ 48 | nibView.multipleTouchEnabled = self.multipleTouchEnabled; \ 49 | nibView.tag = self.tag; \ 50 | \ 51 | return nibView; \ 52 | } \ 53 | \ 54 | return self; \ 55 | } 56 | 57 | //Rotation 58 | #define _o (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]) ? Portrait : Landscape) 59 | 60 | //Debugging 61 | static inline void _lEdgeInsets(UIEdgeInsets edgeInsets) {NSLog(@"EdgeInsets: %f %f %f %f", edgeInsets.top, edgeInsets.left, edgeInsets.bottom, edgeInsets.right);} 62 | #ifdef DEBUG 63 | #define EnableAutolayoutDebugHotkeyWithCrash(shouldCrashIfAmbiguous) -(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event { AutoLayoutDebugOn(shouldCrashIfAmbiguous); } 64 | #else 65 | #define EnableAutolayoutDebugHotkeyWithCrash(shouldCrashIfAmbiguous) /* noop */ 66 | #endif 67 | 68 | //Animation 69 | static inline UIViewAnimationOptions UIAnimationOptionsWithCurve(UIViewAnimationCurve curve) { 70 | switch (curve) { 71 | case UIViewAnimationCurveEaseInOut: { 72 | return UIViewAnimationOptionCurveEaseInOut; 73 | } break; 74 | 75 | case UIViewAnimationCurveEaseIn: { 76 | return UIViewAnimationOptionCurveEaseIn; 77 | } break; 78 | 79 | case UIViewAnimationCurveEaseOut: { 80 | return UIViewAnimationOptionCurveEaseOut; 81 | } break; 82 | 83 | case UIViewAnimationCurveLinear: { 84 | return UIViewAnimationOptionCurveLinear; 85 | } break; 86 | } 87 | } 88 | 89 | //Views 90 | static inline id AutoLayout(UIView *view) { 91 | [view setTranslatesAutoresizingMaskIntoConstraints:NO]; 92 | return view; 93 | } 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /GBToolbox/NSString+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (GBToolbox) 12 | 13 | #define GBStringUtilsLocalDNSSuffixes @{@"local", @"lan", @"group"} 14 | 15 | /** 16 | Checks if string is an integer. 17 | */ 18 | - (BOOL)isInteger; 19 | 20 | /** 21 | Checks if the receiver contains a substring. 22 | 23 | This check is case sensitive. 24 | */ 25 | - (BOOL)containsSubstring:(nullable NSString *)substring; 26 | 27 | /** 28 | Checks if the receiver contains a substring. 29 | */ 30 | - (BOOL)containsSubstring:(nullable NSString *)substring caseSensitive:(BOOL)isCaseSensitive; 31 | 32 | /** 33 | Returns YES if the receiver equals any of the strings in the strings array. 34 | */ 35 | - (BOOL)isEqualToOneOf:(nonnull NSArray *)strings; 36 | 37 | /** 38 | Returns a new string with the DNS suffix removed from the receiver. 39 | 40 | Requires 10.7+ 41 | */ 42 | - (nonnull NSString *)stringByDeletingDNSSuffix; 43 | 44 | /** 45 | Checks to see if the receiver looks like an IPv4 IP address. 46 | 47 | Requires 10.7+ 48 | */ 49 | - (BOOL)isIP; 50 | 51 | /** 52 | Attempt to get int out of string. 53 | */ 54 | - (int)attemptConversionToInt; 55 | 56 | /** 57 | Attempt to get float out of a string. 58 | */ 59 | - (float)attemptConversionToFloat; 60 | 61 | /** 62 | Attempt to get double out of a string. 63 | */ 64 | - (double)attemptConversionToDouble; 65 | 66 | /** 67 | Return a new string with the leading and trailing whitespace trimmed. 68 | */ 69 | -(nonnull NSString *)stringByTrimmingLeadingAndTrailingWhitespace; 70 | 71 | /** 72 | Return a new string with the leading and trailing whitespace trimmed, and multiple whitespaces coalesced into a single space. 73 | */ 74 | - (nonnull NSString *)stringByCleaningWhitespace; 75 | 76 | /** 77 | Returns a string with all the characters in the set removed from receiver. 78 | */ 79 | - (nonnull NSString *)stringByRemovingCharactersInSet:(nonnull NSCharacterSet *)characterSet; 80 | 81 | /** 82 | Returns a string consisting only of the characters in the characterSet. 83 | */ 84 | - (nonnull NSString *)stringByRemovingCharactersNotInSet:(nonnull NSCharacterSet *)characterSet; 85 | 86 | /** 87 | Converts "Mirosevic" -> "M." and "sloppy" -> "S." 88 | */ 89 | - (nonnull NSString *)stringByAbbreviating; 90 | 91 | /** 92 | Converts "Luka Mirosevic" -> "Luka M." and "Vincent Van Gogh" -> "Vincent V. G." 93 | */ 94 | - (nonnull NSString *)abbreviatedName; 95 | 96 | /** 97 | Return a string with the trailing slash removed from the receiver if there was one. 98 | */ 99 | - (nonnull NSString *)stringByRemovingTrailingSlash; 100 | 101 | /** 102 | Returns a new string with the prefix from the receiver removed if found, otherwise returns the receiver. 103 | */ 104 | - (nonnull NSString *)stringByRemovingPrefix:(nonnull NSString *)prefix; 105 | 106 | /** 107 | Removes a new string with the suffix from the receiver removed if found, otherwise returns the receiver. 108 | */ 109 | - (nonnull NSString *)stringByRemovingSuffix:(nonnull NSString *)suffix; 110 | 111 | /** 112 | Returns an attributed string initialized with self, with the attributes from `attributes`. 113 | */ 114 | - (nonnull NSAttributedString *)attributedStringWithAttributes:(nullable NSDictionary *)attributes; 115 | 116 | /** 117 | Returns a copy of the receiver sanitized by removing all characters which could be unsafe inside a filename. Not just for OS X but across other systems as well. 118 | */ 119 | - (nonnull NSString *)filenameSanitizedString; 120 | 121 | //Hashes 122 | @property (nonatomic, readonly, nonnull) NSString *md5; 123 | @property (nonatomic, readonly, nonnull) NSString *sha1; 124 | @property (nonatomic, readonly, nonnull) NSString *sha224; 125 | @property (nonatomic, readonly, nonnull) NSString *sha256; 126 | @property (nonatomic, readonly, nonnull) NSString *sha384; 127 | @property (nonatomic, readonly, nonnull) NSString *sha512; 128 | 129 | @end 130 | -------------------------------------------------------------------------------- /GBToolbox/GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 28/09/2012. 6 | // Copyright (c) 2012 Luka Mirosevic. All rights reserved. 7 | // 8 | // Licensed under the Apache License, Version 2.0 (the "License"); 9 | // you may not use this file except in compliance with the License. 10 | // You may obtain a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, software 15 | // distributed under the License is distributed on an "AS IS" BASIS, 16 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | // See the License for the specific language governing permissions and 18 | // limitations under the License. 19 | 20 | 21 | /* Common imports */ 22 | 23 | #import "GBTypes_Common.h" 24 | 25 | #import "GBConstants_Common.h" 26 | 27 | #import "GBMacros_Common.h" 28 | #import "GBUtility_Common.h" 29 | 30 | #import "GBFastArray.h" 31 | #import "GBMessageInterceptor.h" 32 | #import "GBCompletable.h" 33 | #import "GBCache.h" 34 | #import "GBEnumWrapper.h" 35 | #import "GBAddress.h" 36 | #import "GBRemoteDebugMessages.h" 37 | #import "GBCAAnimationDelegateHandler.h" 38 | #import "GBAsyncOperation.h" 39 | #import "GBHighPrecisionTimer.h" 40 | 41 | #import "NSObject+GBToolbox.h" 42 | #import "NSTimer+GBToolbox.h" 43 | #import "NSData+GBToolbox.h" 44 | #import "NSString+GBToolbox.h" 45 | #import "CALayer+GBToolbox.h" 46 | #import "NSArray+GBToolbox.h" 47 | #import "NSMutableArray+GBToolbox.h" 48 | #import "NSDictionary+GBToolbox.h" 49 | #import "NSMutableDictionary+GBToolbox.h" 50 | #import "NSMutableURLRequest+GBToolbox.h" 51 | #import "NSDate+GBToolbox.h" 52 | #import "NSInvocation+GBToolbox.h" 53 | #import "NSMapTable+GBToolbox.h" 54 | #import "NSSet+GBToolbox.h" 55 | #import "NSOrderedSet+GBToolbox.h" 56 | 57 | #import "GBFrameChangedProtocol.h" 58 | #import "GBDebuggableInstanceVendor.h" 59 | 60 | 61 | /* iOS imports */ 62 | 63 | #if TARGET_OS_IPHONE 64 | 65 | #import "GBTypes_iOS.h" 66 | 67 | #import "GBMacros_iOS.h" 68 | #import "GBUtility_iOS.h" 69 | 70 | #import "GBCustomNavigationBar.h" 71 | #import "GBTextField.h" 72 | #import "GBTextView.h" 73 | #import "GBButton.h" 74 | #import "GBToggleControl.h" 75 | #import "GBToggleControlRadioGroupManager.h" 76 | #import "GBAlertBadgeView.h" 77 | #import "GBRoundBadgeView.h" 78 | #import "GBColoredBarView.h" 79 | #import "GBTextBox.h" 80 | #import "GBTestView.h" 81 | #import "GBTableViewController.h" 82 | #import "GBGradientView.h" 83 | #import "GBControlAggregator.h" 84 | #import "GBMultiStateButton.h" 85 | #import "GBAlertBadge.h" 86 | #import "GBKeyboardListener.h" 87 | 88 | #import "UITableView+GBToolbox.h" 89 | #import "UIViewController+GBToolbox.h" 90 | #import "UIView+GBToolbox.h" 91 | #import "UIImage+GBToolbox.h" 92 | #import "UIScrollView+GBToolbox.h" 93 | #import "UIImageView+GBToolbox.h" 94 | #import "MKMapView+GBToolbox.h" 95 | #import "UIColor+GBToolbox.h" 96 | #import "UIControl+GBToolbox.h" 97 | #import "UIScreen+GBToolbox.h" 98 | #import "UISearchBar+GBToolbox.h" 99 | 100 | #import "UITableViewCell+AdditionalViews.h" 101 | #import "UIView+GBPopUp.h" 102 | #import "UIView+GBFloatingPopoverView.h" 103 | #import "UIView+GBOwningViewController.h" 104 | #import "UINavigationBar+GBToolbox.h" 105 | 106 | #endif 107 | 108 | 109 | /* OS X imports */ 110 | 111 | #if !TARGET_OS_IPHONE 112 | 113 | #import "GBTypes_OSX.h" 114 | 115 | #import "GBMacros_OSX.h" 116 | #import "GBUtility_OSX.h" 117 | 118 | #import "GBResizableImageView.h" 119 | #import "GBRadialGradientView.h" 120 | #import "GBCustomViewButtonCell.h" 121 | #import "GBCustomImageButtonCell.h" 122 | #import "GBGlowingImageButtonCell.h" 123 | #import "GBSolidColorView.h" 124 | 125 | #import "NSImage+GBToolbox.h" 126 | #import "NSView+GBToolbox.h" 127 | 128 | #endif 129 | 130 | 131 | /* Notes */ 132 | 133 | 134 | //distill the press and hold guy from GBPingInitiatorViewController, look at the following methods: 135 | // 136 | //-(void)touchUp:(id)sender tapHandler:(void(^)(void))tapHandler; 137 | //-(void)touchDown:(id)sender pressAndHoldHandler:(void(^)(void))pressAndHoldHandler; 138 | -------------------------------------------------------------------------------- /GBToolbox/GBToggleControlRadioGroupManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBToggleControlRadioGroupManager.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 08/07/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBToggleControlRadioGroupManager.h" 10 | 11 | #import "GBToggleControl.h" 12 | 13 | @implementation GBToggleControlRadioGroupManager 14 | 15 | #pragma mark - ca 16 | 17 | -(void)dealloc { 18 | //must do this to remove old targets 19 | self.toggleControls = nil; 20 | } 21 | 22 | -(void)setToggleControls:(NSArray *)toggleControls { 23 | //if they're not all toggle controls, then throw exception 24 | for (id object in toggleControls) { 25 | if (![object isKindOfClass:GBToggleControl.class]) { 26 | @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Must pass in an array of GBToggleControls" userInfo:nil]; 27 | } 28 | } 29 | 30 | //make sure to remove self as target from old controls 31 | for (GBToggleControl *toggle in _toggleControls) { 32 | [toggle removeTarget:self action:@selector(toggleAction:) forControlEvents:UIControlEventValueChanged]; 33 | } 34 | 35 | //add self as target for new controls 36 | for (GBToggleControl *toggle in toggleControls) { 37 | [toggle addTarget:self action:@selector(toggleAction:) forControlEvents:UIControlEventValueChanged]; 38 | } 39 | 40 | _toggleControls = toggleControls; 41 | } 42 | 43 | -(void)setSelectedControl:(GBToggleControl *)selectedControl { 44 | [self _selectControl:selectedControl]; 45 | } 46 | 47 | -(void)setSelectedControlIndex:(NSInteger)selectedControlIndex { 48 | [self _selectControlWithIndex:selectedControlIndex]; 49 | } 50 | 51 | #pragma mark - API 52 | 53 | -(void)deselectAllControls { 54 | [self _resetState]; 55 | } 56 | 57 | #pragma mark - util 58 | 59 | -(void)_resetState { 60 | for (GBToggleControl *toggle in self.toggleControls) { 61 | toggle.isOn = NO; 62 | } 63 | 64 | _selectedControlIndex = -1; 65 | _selectedControl = nil; 66 | 67 | //tell delegate 68 | if ([self.delegate respondsToSelector:@selector(toggleControlRadioGroupManagerDidResetSelection:)]) { 69 | [self.delegate toggleControlRadioGroupManagerDidResetSelection:self]; 70 | } 71 | } 72 | 73 | -(void)_selectControl:(GBToggleControl *)control { 74 | //find the index 75 | NSUInteger index = [self.toggleControls indexOfObject:control]; 76 | [self _selectControlWithIndex:index]; 77 | } 78 | 79 | -(void)_selectControlWithIndex:(NSUInteger)index { 80 | if (index == NSNotFound) { 81 | NSLog(@"Warning: trying to select control which has not been added to the GBRadioToggleControlsManager"); 82 | } 83 | else if (index >= self.toggleControls.count) { 84 | NSLog(@"Warning: trying to select control whose index is out of bounds of the controls added to the GBRadioToggleControlsManager"); 85 | } 86 | else { 87 | //select my control and deselect all the other ones 88 | for (NSUInteger i=0; i 13 | #import 14 | 15 | #pragma mark - General 16 | 17 | typedef void(^VoidBlock)(void); 18 | typedef BOOL(^BoolBlock)(void); 19 | typedef void(^VoidBlockObject)(id object); 20 | typedef void(^VoidBlockDictionary)(NSDictionary *dictionary); 21 | typedef void(^VoidBlockBool)(BOOL isTrue); 22 | 23 | #pragma mark - Range 24 | 25 | typedef struct { 26 | CGFloat min; 27 | CGFloat max; 28 | } GBRange; 29 | 30 | #pragma mark - Linear algebra 31 | 32 | typedef struct { 33 | CGFloat x; 34 | CGFloat y; 35 | } GBVector2D; 36 | 37 | #pragma mark - UI 38 | 39 | typedef struct { 40 | NSUInteger rows; 41 | NSUInteger columns; 42 | } GBMatrixGrid; 43 | 44 | typedef struct { 45 | CGFloat top; 46 | CGFloat left; 47 | CGFloat bottom; 48 | CGFloat right; 49 | } GBEdgeInsets; 50 | 51 | #pragma mark - GBBoolean 52 | 53 | typedef enum { 54 | BUndefined, 55 | BYES, 56 | BNO, 57 | } GBBoolean; 58 | 59 | static inline GBBoolean GBBooleanToggle(GBBoolean boolean) { 60 | switch (boolean) { 61 | case BYES: return BNO; 62 | case BNO: return BYES; 63 | case BUndefined: return BUndefined; 64 | } 65 | } 66 | 67 | static inline GBBoolean Bool2GBBoolean(BOOL boolean) { 68 | if (boolean) { 69 | return BYES; 70 | } 71 | else { 72 | return BNO; 73 | } 74 | } 75 | 76 | static inline BOOL IsTruthyGBBoolean(GBBoolean boolean) { 77 | return boolean == BYES; 78 | } 79 | 80 | 81 | static inline GBBoolean GBBooleanNot(GBBoolean boolean) { 82 | switch (boolean) { 83 | case BYES: 84 | return BNO; 85 | 86 | case BNO: 87 | case BUndefined: 88 | return BYES; 89 | } 90 | } 91 | 92 | static inline GBBoolean GBBooleanAnd(GBBoolean booleanA, GBBoolean booleanB) { 93 | return Bool2GBBoolean(IsTruthyGBBoolean(booleanA) && IsTruthyGBBoolean(booleanB)); 94 | } 95 | 96 | static inline GBBoolean GBBooleanOr(GBBoolean booleanA, GBBoolean booleanB) { 97 | return Bool2GBBoolean(IsTruthyGBBoolean(booleanA) || IsTruthyGBBoolean(booleanB)); 98 | } 99 | 100 | static inline GBBoolean Number2GBBoolean(NSNumber *number) { 101 | if ([number isKindOfClass:[NSNumber class]]) { 102 | if ([number boolValue] == YES) { 103 | return BYES; 104 | } 105 | else if ([number boolValue] == NO) { 106 | return BNO; 107 | } 108 | } 109 | 110 | return BUndefined; 111 | } 112 | 113 | static inline NSNumber * GBBoolean2Number(GBBoolean boolean) { 114 | switch (boolean) { 115 | case BYES: 116 | return @(YES); 117 | 118 | case BNO: 119 | return @(NO); 120 | 121 | case BUndefined: 122 | return nil; 123 | } 124 | } 125 | 126 | #pragma mark - Special keys 127 | 128 | typedef enum { 129 | GBSpecialKeyReturn = 1, 130 | GBSpecialKeyBackspace = 2, 131 | GBSpecialKeyF1 = 3, 132 | GBSpecialKeyF2 = 4, 133 | GBSpecialKeyF3 = 5, 134 | GBSpecialKeyF4 = 6, 135 | GBSpecialKeyF5 = 7, 136 | GBSpecialKeyF6 = 8, 137 | GBSpecialKeyF7 = 9, 138 | GBSpecialKeyF8 = 10, 139 | GBSpecialKeyF9 = 11, 140 | GBSpecialKeyF10 = 12, 141 | GBSpecialKeyF11 = 13, 142 | GBSpecialKeyF12 = 14, 143 | GBSpecialKeyEsc = 15, 144 | GBSpecialKeyCmdDown = 16, 145 | GBSpecialKeyCtrlDown = 17, 146 | GBSpecialKeyAltDown = 18, 147 | GBSpecialKeyHome = 19, 148 | GBSpecialKeyPageUp = 20, 149 | GBSpecialKeyEnd = 21, 150 | GBSpecialKeyPageDown = 22, 151 | GBSpecialKeyUp = 23, 152 | GBSpecialKeyLeft = 24, 153 | GBSpecialKeyDown = 25, 154 | GBSpecialKeyRight = 26, 155 | GBSpecialKeyCtrlUp = 27, 156 | GBSpecialKeyAltUp = 28, 157 | GBSpecialKeyCmdUp = 29, 158 | } GBSpecialKey; 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /GBToolbox/GBKeyboardListener.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBKeyboardListener.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 22/05/2017. 6 | // Copyright © 2017 Goonbee e.U. All rights reserved. 7 | // 8 | 9 | #import "GBKeyboardListener.h" 10 | 11 | #import "NSMapTable+GBToolbox.h" 12 | 13 | @interface GBKeyboardListener () 14 | 15 | @property (assign, nonatomic, readwrite, getter=isVisible) BOOL visible; 16 | @property (assign, nonatomic, readwrite) CGRect keyboardFrame; 17 | 18 | @property (strong, nonatomic) NSMapTable *> *contexts; 19 | 20 | @end 21 | 22 | @implementation GBKeyboardListener 23 | 24 | #pragma mark - Life and Setup 25 | 26 | + (void)load { 27 | @autoreleasepool { 28 | [self sharedListener]; 29 | } 30 | } 31 | 32 | - (id)init { 33 | if (self = [super init]) { 34 | _contexts = [NSMapTable weakToStrongObjectsMapTable]; 35 | 36 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_willShowNotification:) name:UIKeyboardWillShowNotification object:nil]; 37 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_willHideNotification:) name:UIKeyboardWillHideNotification object:nil]; 38 | } 39 | 40 | return self; 41 | } 42 | 43 | + (GBKeyboardListener *)sharedListener { 44 | static GBKeyboardListener *_sharedListener; 45 | static dispatch_once_t onceToken; 46 | dispatch_once(&onceToken, ^{ 47 | _sharedListener = [[GBKeyboardListener alloc] init]; 48 | }); 49 | 50 | return _sharedListener; 51 | } 52 | 53 | - (void)dealloc { 54 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 55 | } 56 | 57 | #pragma mark - Notifications 58 | 59 | - (void)_willShowNotification:(NSNotification *)notification { 60 | NSTimeInterval duration = ((NSNumber *)notification.userInfo[UIKeyboardAnimationDurationUserInfoKey]).doubleValue; 61 | UIViewAnimationOptions curve = ((NSNumber *)notification.userInfo[UIKeyboardAnimationCurveUserInfoKey]).integerValue; 62 | CGRect endFrame = ((NSValue *)notification.userInfo[UIKeyboardFrameEndUserInfoKey]).CGRectValue; 63 | CGRect beginFrame = ((NSValue *)notification.userInfo[UIKeyboardFrameBeginUserInfoKey]).CGRectValue; 64 | 65 | self.visible = YES; 66 | self.keyboardFrame = endFrame; 67 | 68 | [self _fireListenersWithShown:YES beginFrame:beginFrame endFrame:endFrame animationDuration:duration animationCurve:curve]; 69 | } 70 | 71 | - (void)_willHideNotification:(NSNotification *)notification { 72 | NSTimeInterval duration = ((NSNumber *)notification.userInfo[UIKeyboardAnimationDurationUserInfoKey]).doubleValue; 73 | UIViewAnimationOptions curve = ((NSNumber *)notification.userInfo[UIKeyboardAnimationCurveUserInfoKey]).integerValue; 74 | CGRect endFrame = ((NSValue *)notification.userInfo[UIKeyboardFrameEndUserInfoKey]).CGRectValue; 75 | CGRect beginFrame = ((NSValue *)notification.userInfo[UIKeyboardFrameBeginUserInfoKey]).CGRectValue; 76 | 77 | self.visible = NO; 78 | self.keyboardFrame = endFrame; 79 | 80 | [self _fireListenersWithShown:NO beginFrame:beginFrame endFrame:endFrame animationDuration:duration animationCurve:curve]; 81 | } 82 | 83 | 84 | #pragma mark - Private 85 | 86 | - (void)_fireListenersWithShown:(BOOL)shown beginFrame:(CGRect)beginFrame endFrame:(CGRect)endFrame animationDuration:(NSTimeInterval)animationDuration animationCurve:(UIViewAnimationOptions)animationCurve { 87 | for (id context in self.contexts) { 88 | for (GBKeyboardWillMoveBlock listener in self.contexts[context]) { 89 | listener(shown, beginFrame, endFrame, animationDuration, animationCurve); 90 | } 91 | } 92 | } 93 | 94 | #pragma mark - API 95 | 96 | - (CGRect)frameInCoordinatesOfView:(UIView *)view { 97 | return [[UIApplication sharedApplication].windows.firstObject convertRect:self.keyboardFrame toView:view]; 98 | } 99 | 100 | - (void)addListenerForContext:(id)context block:(GBKeyboardWillMoveBlock)block { 101 | if (!self.contexts[context]) { 102 | self.contexts[context] = [NSMutableArray new]; 103 | } 104 | 105 | [self.contexts[context] addObject:block]; 106 | } 107 | 108 | - (void)removeListenersForContext:(id)context { 109 | [self.contexts removeObjectForKey:context]; 110 | } 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /GBToolbox/UIView+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 22/05/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol GBViewViewRelatedChangesDelegate; 12 | 13 | @interface UIView (GBToolbox) 14 | 15 | /** 16 | Set this to YES if the background color that you specifed in IB was for design time only and you would like the background to be clearColor when it is unarchived from the NIB. Useful if you want IB to show a different color at design time, such as a placeholder color, but you don't want this visible at runtime. 17 | */ 18 | @property (assign, nonatomic) IBInspectable BOOL designTimeOnlyBackgroundColor; 19 | 20 | /** 21 | Removes all subviews. 22 | */ 23 | - (void)removeAllSubviews; 24 | 25 | /** 26 | Sets the view's frame to the receiver's bounds and adds it as a subview. 27 | */ 28 | - (void)embedView:(UIView *)view; 29 | 30 | /** 31 | Resigns the first responder for any subview of the receiver. Some code from SO: http://stackoverflow.com/a/1823360/399772 32 | */ 33 | - (BOOL)findAndResignFirstResponder; 34 | 35 | /** 36 | Returns a view controller whose view has the receiver added as a subview, laid out in Autolayout to have it's view the same size as the receiver (edges are pinned). 37 | 38 | Warning: Always creates a new instance, every time it is called. 39 | */ 40 | - (UIViewController *)wrappingViewController; 41 | 42 | /** 43 | Returns a view controller whose view has the receiver added as a subview, laid out in Autolayout to have it's view the same size as the receiver (edges are pinned) taking into account the edge margins. 44 | 45 | Warning: Always creates a new instance, every time it is called. 46 | */ 47 | - (UIViewController *)wrappingViewControllerWithMargins:(UIEdgeInsets)margins; 48 | 49 | /** 50 | Returns a view with the receiver added as a subview, laid out in Autolayout to have it's view the same size as the receiver (edges are pinned) taking into account the edge margins. 51 | 52 | Warning: Always creates a new instance, every time it is called. 53 | */ 54 | - (UIView *)wrappingViewWithMargins:(UIEdgeInsets)margins; 55 | 56 | /** 57 | Returns the part of the view rendered into an image inside the rect. 58 | 59 | Setting legacy to YES will use the older (more reliable) method of rendering. Always try with legacy:NO first as it is more efficient in iOS7+. If it doesn't work fallback to legacy:YES. 60 | */ 61 | - (UIImage *)renderToImageForRect:(CGRect)rect legacy:(BOOL)legacy; 62 | 63 | /** 64 | Adds a fade animation to receiver's layer. A useful case is for animating label content changes. 65 | */ 66 | - (void)addCrossFadeAnimationToLayerWithDuration:(NSTimeInterval)duration; 67 | 68 | /** 69 | Setting this to YES disables user interactions on the the receiver, without disabling it for the child views. 70 | 71 | This is in contrast to simply setting userInteractionEnabled to NO, in which case the view will not receive touch input, will however also not forward it to it's children. 72 | 73 | Setting this to YES is useful in cases where one might want to add a container view that has some children, where the container view should not exist for purposes of handling touch input--it should neither receive touch input, not prevent its children from receiving it. 74 | 75 | Defaults to YES. 76 | */ 77 | @property (assign, nonatomic) BOOL shouldParticipateInUserInput; 78 | 79 | /** 80 | Sets a mask to this view's layer that clips the corners by cornerRadius. 81 | 82 | You must call this again if your view resizes. 83 | */ 84 | - (void)roundCorners:(UIRectCorner)corners withRadius:(CGFloat)cornerRadius; 85 | 86 | /** 87 | The delegate that will be notified of view related changes. 88 | */ 89 | @property (weak, nonatomic) id viewRelatedChangesDelegate; 90 | 91 | @end 92 | 93 | /** 94 | Allows getting notified of changes to the view from outside of the view. 95 | */ 96 | @protocol GBViewViewRelatedChangesDelegate 97 | @optional 98 | 99 | - (void)view:(UIView *)view didAddSubview:(UIView *)subview; 100 | - (void)view:(UIView *)view willRemoveSubview:(UIView *)subview; 101 | - (void)view:(UIView *)view willMoveToSuperview:(UIView *)superview; 102 | - (void)view:(UIView *)view didMoveToSuperview:(UIView *)superview; 103 | - (void)view:(UIView *)view willMoveToWindow:(UIWindow *)window; 104 | - (void)view:(UIView *)view didMoveToWindow:(UIWindow *)window; 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /GBToolbox/GBUtility_iOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBUtility_iOS.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 05/02/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBTypes_Common.h" 10 | #import "GBTypes_iOS.h" 11 | 12 | #import "GBUtility_Common.h" 13 | #import "UIControl+GBToolbox.h" 14 | 15 | #import 16 | #import 17 | #import 18 | #import 19 | 20 | @class GBAddress; 21 | 22 | @interface GBToolbox (iOS) 23 | 24 | #pragma mark - UIView 25 | 26 | NSUInteger tagFromUIViewSubclass(_Nullable id sender); 27 | 28 | #pragma mark - View Hierarchy 29 | 30 | UIViewController * _Nullable TopmostViewController(); 31 | UIViewController * _Nullable TopmostViewControllerWithRootViewController(UIViewController * _Nullable rootViewController); 32 | 33 | #pragma mark - Screen Locking 34 | 35 | +(BOOL)isAutoScreenLockingEnabled; 36 | +(void)enableAutoScreenLocking:(BOOL)enable; 37 | 38 | #pragma mark - Torch 39 | 40 | /** 41 | Enables or disables the device's flash/torch if the device has one 42 | */ 43 | void EnableTorch(BOOL enable); 44 | 45 | #pragma mark - App Store redirect 46 | 47 | void RedirectToAppStore(NSString * _Nonnull appID); 48 | 49 | #pragma mark - Images 50 | 51 | UIImage * _Nullable Image(NSString * _Nonnull name); 52 | UIImage * _Nullable ImageResizableWithCapInsets(NSString * _Nonnull name, CGFloat topCap, CGFloat leftCap, CGFloat bottomCap, CGFloat rightCap); 53 | 54 | #pragma mark - Clipping 55 | 56 | CAShapeLayer * _Nonnull RoundClippingMaskInRectWithMargin(CGRect rect, UIEdgeInsets margin); 57 | UIBezierPath * _Nonnull RoundBezierPathForRectWithMargin(CGRect rect, UIEdgeInsets margin); 58 | 59 | #pragma mark - Push Notifications 60 | 61 | BOOL IsPushDisabled(); 62 | 63 | #pragma mark - UITableView 64 | 65 | BOOL IsCellAtIndexPathFullyVisible(NSIndexPath * _Nullable indexPath, UITableView * _Nullable tableView); 66 | 67 | #pragma mark - Keyboard hiding 68 | 69 | void DismissKeyboard(); 70 | 71 | #pragma mark - Twitter 72 | 73 | BOOL IsTwitterAccountAvailable(); 74 | 75 | #pragma mark - Localisation 76 | 77 | NSString * _Nullable UIKitLocalizedString(NSString * _Nullable string); 78 | 79 | #pragma mark - Vibration 80 | 81 | void VibrateDevice(); 82 | 83 | #pragma mark - Colors 84 | 85 | //Returns a random color with a bias towards the more saturated and brighter colors 86 | UIColor * _Nonnull RandomColor(); 87 | 88 | #pragma mark - Auto Layout 89 | 90 | void AutoLayoutDebugOn(BOOL crashOnTrigger); 91 | NSString * _Nonnull AutoLayoutViewPointer(NSObject * _Nonnull object); 92 | NSDictionary * _Nonnull AutoLayoutPointerViewsDictionaryForViews(NSArray * _Nonnull views); 93 | 94 | #pragma mark - Debugging 95 | 96 | void DebugCode(VoidBlock _Nonnull code); 97 | 98 | UIButton * _Nonnull RegisterDebugButton(NSString * _Nonnull title, GBActionBlock _Nonnull action); 99 | 100 | #pragma mark - Geocoding 101 | 102 | void ReverseGeocodeLocation(CLLocation * _Nullable location, void(^_Nullable block)(GBAddress * _Nullable address)); 103 | 104 | #pragma mark - UIViewController containment 105 | 106 | void AddChildViewController(UIViewController * _Nonnull hostViewController, UIViewController * _Nonnull childViewController); 107 | void AddChildViewControllerToView(UIViewController * _Nonnull hostViewController, UIView * _Nonnull hostView, UIViewController * _Nonnull childViewController); 108 | void RemoveChildViewController(UIViewController * _Nonnull childViewController); 109 | 110 | #pragma mark - Actions 111 | 112 | void OpenLinkInSafari(NSString * _Nullable link); 113 | void OpenMap(CLLocation * _Nonnull location, NSString * _Nullable name); 114 | void CallPhone(NSString * _Nonnull number); 115 | void SendSMS(NSString * _Nonnull number); 116 | 117 | #pragma mark - Fonts 118 | 119 | void ListAvailableFonts(); 120 | 121 | #pragma mark - Disk utils 122 | 123 | NSString * _Nonnull DocumentsDirectoryPath(); 124 | NSURL * _Nonnull DocumentsDirectoryURL(); 125 | 126 | #pragma mark - Cookies 127 | 128 | void ClearCookies(); 129 | 130 | #pragma mark - Email 131 | 132 | void ShowContactEmailOnViewController(UIViewController * _Nonnull viewController, NSArray * _Nullable toAdresses, NSString * _Nullable subject, NSString * _Nullable body); 133 | 134 | #pragma mark - Alerts 135 | 136 | void ShowAlert(NSString * _Nullable title, NSString * _Nullable message, NSString * _Nullable dismissButtonTitle); 137 | 138 | #pragma mark - Network 139 | 140 | NSString * _Nullable CurrentWifiNetworkName(); 141 | 142 | @end 143 | 144 | -------------------------------------------------------------------------------- /GBToolbox/NSArray+GBToolbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+GBToolbox.h 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 20/03/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSArray<__covariant ObjectType> (GBToolbox) 12 | 13 | #pragma mark - Helper methods 14 | 15 | /** 16 | Returns a random object from the array, or nil if the array is empty. 17 | */ 18 | @property (strong, nonatomic, readonly, nullable) ObjectType randomObject; 19 | 20 | #pragma mark - Functional Programming 21 | 22 | /** 23 | Returns a new array with the elements from the receiver transformed by function. 24 | */ 25 | - (nonnull NSArray *)map:(id _Nonnull(^ _Nonnull)(ObjectType _Nonnull object))function; 26 | 27 | /** 28 | Returns a new array with the elements from the receiver transformed by function. 29 | */ 30 | - (nonnull NSArray *)mapWithIndex:(id _Nonnull(^ _Nonnull)(ObjectType _Nonnull object, NSUInteger index))function; 31 | 32 | /** 33 | Fold Left. 34 | */ 35 | - (nonnull ObjectType)foldLeft:(ObjectType _Nonnull(^ _Nonnull)(ObjectType _Nonnull objectA, ObjectType _Nonnull objectB))function lastObject:(nonnull ObjectType)lastObject; 36 | 37 | /** 38 | Fold Right. 39 | */ 40 | - (nonnull ObjectType)foldRight:(ObjectType _Nonnull(^ _Nonnull)(ObjectType _Nonnull objectA, ObjectType _Nonnull objectB))function initialObject:(nonnull ObjectType)initialObject; 41 | 42 | /** 43 | Synonym for foldLeft 44 | */ 45 | - (nonnull ObjectType)reduce:(ObjectType _Nonnull(^ _Nonnull)(ObjectType _Nonnull objectA, ObjectType _Nonnull objectB))function lastObject:(nonnull ObjectType)lastObject; 46 | 47 | /** 48 | Returns a copy of the array containing only the objects for which function returns YES. 49 | */ 50 | - (nonnull NSArray *)filter:(BOOL(^ _Nonnull)(ObjectType _Nonnull object))function; 51 | 52 | /** 53 | Returns YES if all of the elements in the receiver return true for the function. 54 | 55 | Returns NO for an empty array. 56 | */ 57 | - (BOOL)all:(BOOL(^ _Nonnull)(ObjectType _Nonnull object))function; 58 | 59 | /** 60 | Returns YES if at least one of the elements in the receiver return true for the function. 61 | 62 | Returns NO for an empty array. 63 | */ 64 | - (BOOL)any:(BOOL(^ _Nonnull)(ObjectType _Nonnull object))function; 65 | 66 | /** 67 | Returns the first object for which the function returns YES, nil otherwise. 68 | */ 69 | - (nullable ObjectType)first:(BOOL(^ _Nonnull)(ObjectType _Nonnull object))function; 70 | 71 | /** 72 | Returns the count of objects for which function returns YES. 73 | */ 74 | - (NSUInteger)count:(BOOL(^ _Nonnull)(ObjectType _Nonnull object))function; 75 | 76 | /** 77 | Returns the index of the first object that returns YES for the block. If no object returns YES, returns NSNotFound 78 | */ 79 | - (NSUInteger)indexOfFirst:(BOOL(^ _Nonnull)(ObjectType _Nonnull object))function; 80 | 81 | /** 82 | Returns YES if any the function returns YES for at least one object in the set. 83 | */ 84 | - (BOOL)contains:(BOOL(^ _Nonnull)(ObjectType _Nonnull object))function; 85 | 86 | #pragma mark - Conveniences 87 | 88 | /** 89 | Returns the first object inside the receiver which is equal to the object (compared using isEqual:). Returns nil if no object is equal. 90 | */ 91 | - (nullable ObjectType)firstObjectEqualToObject:(id _Nonnull)object; 92 | 93 | /** 94 | Returns YES if the received contains the specific passed in object instance. 95 | */ 96 | - (BOOL)containsObjectIdenticalTo:(nonnull id)anObject; 97 | 98 | #pragma mark - Set like operations 99 | 100 | /** 101 | Returns a new array which is a copy of the receiver with all objects removed that are inside `array` 102 | */ 103 | - (nonnull NSArray *)arrayBySubtractingArray:(nonnull NSArray *)array; 104 | 105 | #pragma mark - Description 106 | 107 | /** 108 | Returns the array concatenated by ", " 109 | */ 110 | @property (copy, nonatomic, nonnull, readonly) NSString *shortStringRepresentation; 111 | 112 | #pragma mark - Sorting 113 | 114 | /** 115 | Returns an array sorted by the properties in the objects. 116 | */ 117 | - (nonnull NSArray *)sortedArrayByPropertiesOfObjects:(nonnull NSArray *)properties ascending:(BOOL)ascending; 118 | 119 | /** 120 | Returns an array sorted by the properties in the objects in ascending order. 121 | */ 122 | - (nonnull NSArray *)sortedArrayByPropertiesOfObjects:(nonnull NSArray *)properties; 123 | 124 | #pragma mark - Unique 125 | 126 | /** 127 | Returns a copy of the receiver with all duplicates removed. 128 | */ 129 | - (nonnull NSArray *)uniquedArray; 130 | 131 | #pragma mark - Convenience 132 | 133 | /** 134 | Returns a set initialized with the contents of the receiver. 135 | */ 136 | - (nonnull NSSet *)set; 137 | 138 | /** 139 | Returns an ordered set initialized with the contents of the receiver. 140 | */ 141 | - (nonnull NSOrderedSet *)orderedSet; 142 | 143 | #pragma mark - NSAttributedString 144 | 145 | /** 146 | Returns a new NSAttributedString by concatenating all the NSAttributedString's in the receiver. 147 | */ 148 | - (nonnull NSAttributedString *)concatenatedArrayOfAttributedStrings; 149 | 150 | @end 151 | -------------------------------------------------------------------------------- /GBToolbox/GBFastArray.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBFastArray.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 01/05/2013. 6 | // Copyright (c) 2013 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBFastArray.h" 10 | 11 | static CGFloat const kDefaultResizingFactor = 1.5; 12 | static CGFloat const kDefaultInitialCapacity = 100; 13 | 14 | @implementation GBFastArray { 15 | NSUInteger _allocationSize; 16 | NSUInteger _lastElementIndex; 17 | NSUInteger _typeSize; 18 | CGFloat _resizingFactor; 19 | char *_theArray; 20 | } 21 | 22 | #pragma mark - Public API: Init 23 | 24 | - (instancetype)initWithTypeSize:(NSUInteger)typeSize { 25 | return [self initWithTypeSize:typeSize initialCapacity:kDefaultInitialCapacity resizingFactor:kDefaultResizingFactor]; 26 | } 27 | 28 | - (instancetype)initWithTypeSize:(NSUInteger)typeSize initialCapacity:(NSUInteger)initialCapacity resizingFactor:(CGFloat)resizingFactor { 29 | if (self = [super init]) { 30 | //State 31 | _typeSize = typeSize; 32 | _allocationSize = initialCapacity; 33 | _lastElementIndex = NSUIntegerMax; 34 | [self setResizingFactor:resizingFactor]; 35 | 36 | //Alloc array 37 | _theArray = malloc(_typeSize * _allocationSize); 38 | } 39 | 40 | return self; 41 | } 42 | 43 | - (void)dealloc { 44 | //clean up array 45 | free(_theArray); 46 | } 47 | 48 | #pragma mark - Public API: Properties 49 | 50 | - (NSUInteger)count { 51 | if (_lastElementIndex == NSUIntegerMax) { 52 | return 0; 53 | } 54 | else { 55 | return _lastElementIndex + 1; 56 | } 57 | } 58 | 59 | - (BOOL)isEmpty { 60 | return (_lastElementIndex == NSUIntegerMax); 61 | } 62 | 63 | #pragma mark - Public API: Querying 64 | 65 | - (void)insertItem:(void *)itemAddress atIndex:(NSUInteger)index { 66 | //if its too short, resize the array 67 | if (index >= _allocationSize) { 68 | [self reallocToSize:(_allocationSize * _resizingFactor)]; 69 | } 70 | 71 | //if it stretches the lastElementIndex, set it 72 | if (index > _lastElementIndex || _lastElementIndex == NSUIntegerMax) { 73 | _lastElementIndex = index; 74 | } 75 | 76 | //put it in 77 | memcpy(_theArray + (index * _typeSize), itemAddress, _typeSize); 78 | } 79 | 80 | - (void)appendItem:(void *)itemAddress { 81 | [self insertItem:itemAddress atIndex:_lastElementIndex + 1]; 82 | } 83 | 84 | - (void *)itemAtIndex:(NSUInteger)index { 85 | return _theArray + (index * _typeSize); 86 | } 87 | 88 | #pragma mark - Public API: Searching 89 | 90 | - (NSUInteger)binarySearchForIndexWithSearchLambda:(SearchLambda)searchLambda { 91 | return [self binarySearchForIndexWithLow:0 high:_lastElementIndex searchLambda:searchLambda]; 92 | } 93 | 94 | - (NSUInteger)binarySearchForIndexWithLow:(NSUInteger)lowIndex high:(NSUInteger)highIndex searchLambda:(SearchLambda)searchLambda { 95 | //make sure its well formed 96 | if (searchLambda && lowIndex <= highIndex) { 97 | //prepare 98 | NSUInteger midIndex; 99 | GBSearchResult searchResult; 100 | 101 | //search while the subarray has at least 1 item 102 | while (highIndex >= lowIndex) { 103 | //calculate midpoint (as simple downrounded average) 104 | midIndex = (highIndex+lowIndex)*0.5; 105 | 106 | //eval our search lambda 107 | searchResult = searchLambda([self itemAtIndex:midIndex]); 108 | 109 | switch (searchResult) { 110 | case GBSearchResultMatch: { 111 | return midIndex; 112 | } break; 113 | 114 | case GBSearchResultHigh: { 115 | if (midIndex == 0) return kGBSearchResultNotFound; 116 | 117 | highIndex = midIndex - 1; 118 | } break; 119 | 120 | case GBSearchResultLow: { 121 | if (midIndex == _lastElementIndex) return kGBSearchResultNotFound; 122 | 123 | lowIndex = midIndex + 1; 124 | } break; 125 | } 126 | } 127 | } 128 | 129 | //if we got here it means we didnt find anything, or the params were bad 130 | return kGBSearchResultNotFound; 131 | } 132 | 133 | #pragma mark - Public API: Size management 134 | 135 | - (NSUInteger)typeSize { 136 | return _typeSize; 137 | } 138 | 139 | -( NSUInteger)currentAllocationSize { 140 | return _allocationSize; 141 | } 142 | 143 | - (void)reallocToSize:(NSUInteger)newSize { 144 | _allocationSize = newSize; 145 | _theArray = realloc(_theArray, (newSize * _typeSize)); 146 | 147 | if (newSize < self.count) { 148 | _lastElementIndex = newSize - 1; 149 | } 150 | } 151 | 152 | - (void)setResizingFactor:(CGFloat)resizingFactor { 153 | if (resizingFactor > 1.0) { 154 | _resizingFactor = resizingFactor; 155 | } 156 | else { 157 | @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Resizing factor must be bigger than 1.0" userInfo:@{@"resizingFactor": @(resizingFactor)}]; 158 | } 159 | } 160 | 161 | @end 162 | -------------------------------------------------------------------------------- /GBToolbox/GBRemoteDebugMessages.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBRemoteDebugMessages.m 3 | // GBToolbox 4 | // 5 | // Created by Luka Mirosevic on 08/11/2014. 6 | // Copyright (c) 2014 Luka Mirosevic. All rights reserved. 7 | // 8 | 9 | #import "GBRemoteDebugMessages.h" 10 | 11 | static NSString * const kDefaultServer = @"localhost"; 12 | static UInt32 const kDefaultPort = 10000; 13 | static BOOL const kDefaultShouldLogLocallyAsWell = NO; 14 | 15 | @interface GBRemoteDebugMessages () 16 | 17 | @property (strong, nonatomic) NSMutableString *buffer; 18 | @property (strong, nonatomic) dispatch_queue_t messagesQueue; 19 | @property (strong, nonatomic) NSOutputStream *outputStream; 20 | 21 | @end 22 | 23 | @implementation GBRemoteDebugMessages 24 | 25 | + (instancetype)sharedMessages { 26 | static GBRemoteDebugMessages *sharedMessages; 27 | static dispatch_once_t onceToken; 28 | dispatch_once(&onceToken, ^{ 29 | sharedMessages = [self new]; 30 | }); 31 | return sharedMessages; 32 | } 33 | 34 | - (id)init { 35 | if (self = [super init]) { 36 | self.messagesQueue = dispatch_queue_create("com.goonbee.GBToolbox.GBRemoteDebugMessages.Queue", DISPATCH_QUEUE_SERIAL); 37 | self.shouldLogLocallyAsWell = kDefaultShouldLogLocallyAsWell; 38 | dispatch_async(self.messagesQueue, ^{ 39 | self.buffer = [NSMutableString new]; 40 | }); 41 | } 42 | 43 | return self; 44 | } 45 | 46 | #pragma mark - API 47 | 48 | - (void)sendRemoteDebugMessage:(NSString *)message, ... { 49 | va_list args; 50 | va_start(args, message); 51 | NSString *completeMessage = [[NSString alloc] initWithFormat:message arguments:args]; 52 | va_end(args); 53 | 54 | [[GBRemoteDebugMessages sharedMessages] _sendRemoteDebugMessage:completeMessage]; 55 | } 56 | 57 | - (void)_sendRemoteDebugMessage:(NSString *)completeMessage { 58 | dispatch_async(self.messagesQueue, ^{ 59 | if (self.shouldLogLocallyAsWell) NSLog(@"%@", completeMessage); 60 | 61 | // make sure the stream exists 62 | if (!self.outputStream || !self.outputStream.hasSpaceAvailable) { 63 | [self _setupOutputStreamToServer:kDefaultServer onPort:kDefaultPort]; 64 | } 65 | 66 | // just write the message to our buffer 67 | [self _addMessageToBuffer:completeMessage]; 68 | }); 69 | } 70 | 71 | - (void)routeRemoteDebugMessagesToServer:(NSString *)server onPort:(UInt32)port { 72 | dispatch_async(self.messagesQueue, ^{ 73 | // sets up our stream 74 | [self _setupOutputStreamToServer:server onPort:port]; 75 | }); 76 | } 77 | 78 | #pragma mark - NSStreamDelegate 79 | 80 | - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode { 81 | switch (eventCode) { 82 | case NSStreamEventHasSpaceAvailable: { 83 | dispatch_async(self.messagesQueue, ^{ 84 | [self _flushBufferToStream]; 85 | }); 86 | } break; 87 | 88 | default: { 89 | // noop 90 | } break; 91 | } 92 | } 93 | 94 | #pragma mark - Private 95 | 96 | - (void)_addMessageToBuffer:(NSString *)message { 97 | // append it with a newline 98 | [self.buffer appendFormat:@"%@\n", message]; 99 | 100 | // if we have space on our stream 101 | // if ([self.outputStream hasSpaceAvailable]) { 102 | // we flush the buffer 103 | [self _flushBufferToStream]; 104 | // } 105 | } 106 | 107 | - (void)_flushBufferToStream { 108 | [self _writeStringToStream:self.buffer stream:self.outputStream]; 109 | [self.buffer setString:@""]; 110 | } 111 | 112 | - (void)_writeStringToStream:(NSString *)string stream:(NSOutputStream *)stream { 113 | // convert string to bytes 114 | NSData *messageData = [string dataUsingEncoding:NSUTF8StringEncoding]; 115 | 116 | // send string 117 | uint8_t *bytes = (uint8_t *)[messageData bytes]; 118 | [stream write:bytes maxLength:messageData.length]; 119 | } 120 | 121 | - (void)_setupOutputStreamToServer:(NSString *)server onPort:(UInt32)port { 122 | // clean up an old one if we have one 123 | if (self.outputStream) { 124 | [self.outputStream close]; 125 | self.outputStream = nil; 126 | } 127 | 128 | // open socket and stream 129 | NSOutputStream *outputStream; 130 | [NSStream getStreamsToHostWithName:server port:port inputStream:nil outputStream:&outputStream]; 131 | self.outputStream = outputStream; 132 | self.outputStream.delegate = self; 133 | [self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 134 | [self.outputStream open]; 135 | } 136 | 137 | @end 138 | 139 | void SendRemoteDebugMessage(NSString *message, ...) { 140 | va_list args; 141 | va_start(args, message); 142 | NSString *completeMessage = [[NSString alloc] initWithFormat:message arguments:args]; 143 | va_end(args); 144 | 145 | [[GBRemoteDebugMessages sharedMessages] _sendRemoteDebugMessage:completeMessage]; 146 | } 147 | 148 | void RouteRemoteDebugMessagesToServerOnPort(NSString *server, UInt32 port) { 149 | [[GBRemoteDebugMessages sharedMessages] routeRemoteDebugMessagesToServer:server onPort:port]; 150 | } 151 | -------------------------------------------------------------------------------- /GBToolbox/GBRoundBadgeView.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBRoundBadgeView.m 3 | // Russia 4 | // 5 | // Created by Luka Mirosevic on 26/06/2013. 6 | // Copyright (c) 2013 Goonbee. All rights reserved. 7 | // 8 | 9 | #import "GBRoundBadgeView.h" 10 | 11 | #import 12 | #import "GBUtility_iOS.h" 13 | 14 | static UIViewContentMode const kDefaultForegroundImageContentMode = UIViewContentModeScaleToFill; 15 | static UIEdgeInsets const kDefaultClippingMargin = (UIEdgeInsets){0, 0, 0, 0}; 16 | static UIEdgeInsets const kDefaultBackgroundImageMargin = (UIEdgeInsets){0, 0, 0, 0}; 17 | static UIEdgeInsets const kDefaultForegroundImageMargin = (UIEdgeInsets){0, 0, 0, 0}; 18 | 19 | @interface GBRoundBadgeView () 20 | 21 | @property (strong, nonatomic) UIImageView *backgroundImageView; 22 | @property (strong, nonatomic) UIImageView *foregroundImageView; 23 | 24 | @end 25 | 26 | @implementation GBRoundBadgeView 27 | 28 | #pragma mark - custom accessors 29 | 30 | -(void)setForegroundImage:(UIImage *)foregroundImage { 31 | self.foregroundImageView.image = foregroundImage; 32 | } 33 | 34 | -(UIImage *)foregroundImage { 35 | return self.foregroundImageView.image; 36 | } 37 | 38 | -(void)setBackgroundImage:(UIImage *)backgroundImage { 39 | self.backgroundImageView.image = backgroundImage; 40 | } 41 | 42 | -(UIImage *)backgroundImage { 43 | return self.backgroundImageView.image; 44 | } 45 | 46 | -(void)setClippingMargin:(UIEdgeInsets)clippingMargin { 47 | _clippingMargin = clippingMargin; 48 | 49 | [self _updateClippingMask]; 50 | } 51 | 52 | -(void)setForegroundImageMargin:(UIEdgeInsets)foregroundImageMargin { 53 | _foregroundImageMargin = foregroundImageMargin; 54 | 55 | [self _updateImageViewFrames]; 56 | } 57 | 58 | -(void)setBackgroundImageMargin:(UIEdgeInsets)backgroundImageMargin { 59 | _backgroundImageMargin = backgroundImageMargin; 60 | 61 | [self _updateImageViewFrames]; 62 | } 63 | 64 | -(void)setForegroundImageContentMode:(UIViewContentMode)foregroundImageContentMode { 65 | self.foregroundImageView.contentMode = foregroundImageContentMode; 66 | } 67 | 68 | -(UIViewContentMode)foregroundImageContentMode { 69 | return self.foregroundImageContentMode; 70 | } 71 | 72 | #pragma mark - life 73 | 74 | -(id)initWithFrame:(CGRect)frame { 75 | self = [super initWithFrame:frame]; 76 | if (self) { 77 | [self _init]; 78 | } 79 | return self; 80 | } 81 | 82 | - (id)initWithCoder:(NSCoder *)coder { 83 | self = [super initWithCoder:coder]; 84 | if (self) { 85 | [self _init]; 86 | } 87 | return self; 88 | } 89 | 90 | -(void)_init { 91 | self.backgroundColor = [UIColor clearColor]; 92 | 93 | self.foregroundImageView = [UIImageView new]; 94 | self.foregroundImageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 95 | 96 | self.backgroundImageView = [UIImageView new]; 97 | self.backgroundImageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 98 | 99 | [self addSubview:self.backgroundImageView]; 100 | [self addSubview:self.foregroundImageView]; 101 | 102 | //defaults 103 | self.foregroundImageContentMode = kDefaultForegroundImageContentMode; 104 | self.clippingMargin = kDefaultClippingMargin; 105 | self.backgroundImageMargin = kDefaultBackgroundImageMargin; 106 | self.foregroundImageMargin = kDefaultForegroundImageMargin; 107 | 108 | [self _updateImageViewFrames]; 109 | } 110 | 111 | #pragma mark - overrides 112 | 113 | -(void)setFrame:(CGRect)frame { 114 | [super setFrame:frame]; 115 | 116 | [self _updateClippingMask]; 117 | } 118 | 119 | #pragma mark - util 120 | 121 | -(void)_updateImageViewFrames { 122 | self.backgroundImageView.frame = CGRectMake(self.bounds.origin.x + self.backgroundImageMargin.left, 123 | self.bounds.origin.y + self.backgroundImageMargin.top, 124 | self.bounds.size.width - (self.backgroundImageMargin.left + self.backgroundImageMargin.right), 125 | self.bounds.size.height - (self.backgroundImageMargin.top + self.backgroundImageMargin.bottom)); 126 | 127 | self.foregroundImageView.frame = CGRectMake(self.bounds.origin.x + self.foregroundImageMargin.left, 128 | self.bounds.origin.y + self.foregroundImageMargin.top, 129 | self.bounds.size.width - (self.foregroundImageMargin.left + self.foregroundImageMargin.right), 130 | self.bounds.size.height - (self.foregroundImageMargin.top + self.foregroundImageMargin.bottom)); 131 | 132 | self.foregroundImageView.layer.mask = RoundClippingMaskInRectWithMargin(self.foregroundImageView.bounds, UIEdgeInsetsZero); 133 | self.backgroundImageView.layer.mask = RoundClippingMaskInRectWithMargin(self.backgroundImageView.bounds, UIEdgeInsetsZero); 134 | } 135 | 136 | -(void)_updateClippingMask { 137 | self.layer.mask = RoundClippingMaskInRectWithMargin(self.bounds, self.clippingMargin); 138 | } 139 | 140 | @end 141 | -------------------------------------------------------------------------------- /GBToolbox/GBColoredBarView.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBColoredBarView.m 3 | // Russia 4 | // 5 | // Created by Luka Mirosevic on 27/06/2013. 6 | // Copyright (c) 2013 Goonbee. All rights reserved. 7 | // 8 | 9 | #import "GBColoredBarView.h" 10 | 11 | #define kDefaultBorderColor [UIColor blackColor] 12 | #define kDefaultBackgroundColorWhenEmpty [UIColor clearColor] 13 | #define kDefaultBackgroundColorWhenFull [UIColor clearColor] 14 | static BOOL const kDefaultShowBorderWhenFull = NO; 15 | static BOOL const kDefaultShowBorderWhenEmpty = YES; 16 | static CGFloat const kDefaultBorderThickness = 1; 17 | 18 | @implementation GBColoredBarView 19 | 20 | #pragma mark - custom accessors 21 | 22 | -(void)setColors:(NSArray *)colors { 23 | if (colors.count >= self.fractions.count) { 24 | _colors = colors; 25 | 26 | [self setNeedsDisplay]; 27 | } 28 | else { 29 | NSLog(@"ColoredBarView: You must set more colors than fractions!"); 30 | } 31 | } 32 | 33 | -(void)setFractions:(NSArray *)fractions { 34 | if (fractions.count <= self.colors.count) { 35 | _fractions = fractions; 36 | 37 | [self setNeedsDisplay]; 38 | } 39 | else { 40 | NSLog(@"ColoredBarView: You must set less colors than fractions, set colors first!"); 41 | } 42 | } 43 | 44 | -(void)setBackgroundColorWhenEmpty:(UIColor *)backgroundColorWhenEmpty { 45 | _backgroundColorWhenEmpty = backgroundColorWhenEmpty; 46 | 47 | [self setNeedsDisplay]; 48 | } 49 | 50 | -(void)setBackgroundColorWhenFull:(UIColor *)backgroundColorWhenFull { 51 | _backgroundColorWhenFull = backgroundColorWhenFull; 52 | 53 | [self setNeedsDisplay]; 54 | } 55 | 56 | -(void)setBorderColor:(UIColor *)borderColor { 57 | _borderColor = borderColor; 58 | 59 | [self setNeedsDisplay]; 60 | } 61 | 62 | -(void)setBorderThickness:(CGFloat)borderThickness { 63 | _borderThickness = borderThickness; 64 | 65 | [self setNeedsDisplay]; 66 | } 67 | 68 | -(void)setShowBorderWhenFull:(BOOL)showBorderWhenFull { 69 | _showBorderWhenFull = showBorderWhenFull; 70 | 71 | [self setNeedsDisplay]; 72 | } 73 | 74 | -(void)setShowBorderWhenEmpty:(BOOL)showBorderWhenEmpty { 75 | _showBorderWhenEmpty = showBorderWhenEmpty; 76 | 77 | [self setNeedsDisplay]; 78 | } 79 | 80 | #pragma mark - life 81 | 82 | -(id)initWithFrame:(CGRect)frame { 83 | self = [super initWithFrame:frame]; 84 | if (self) { 85 | [self _init]; 86 | } 87 | return self; 88 | } 89 | 90 | -(id)initWithCoder:(NSCoder *)coder { 91 | self = [super initWithCoder:coder]; 92 | if (self) { 93 | [self _init]; 94 | } 95 | return self; 96 | } 97 | 98 | -(void)_init { 99 | self.backgroundColorWhenEmpty = kDefaultBackgroundColorWhenEmpty; 100 | self.backgroundColorWhenFull = kDefaultBackgroundColorWhenFull; 101 | self.borderColor = kDefaultBorderColor; 102 | self.showBorderWhenFull = kDefaultShowBorderWhenFull; 103 | self.showBorderWhenEmpty = kDefaultShowBorderWhenEmpty; 104 | self.borderThickness = kDefaultBorderThickness; 105 | 106 | self.backgroundColor = [UIColor clearColor]; 107 | } 108 | 109 | #pragma mark - util 110 | 111 | -(BOOL)_isEmpty { 112 | //bail if no fractions set 113 | if (!self.fractions) return YES; 114 | 115 | for (NSNumber *fraction in self.fractions) { 116 | //find a non zero fraction 117 | if (![fraction isEqualToNumber:@(0)]) { 118 | return NO; 119 | } 120 | } 121 | 122 | //if we didn't return yet, it means we're empty 123 | return YES; 124 | } 125 | 126 | -(UIColor *)_currentBackgroundColor { 127 | if ([self _isEmpty]) { 128 | return self.backgroundColorWhenEmpty; 129 | } 130 | else { 131 | return self.backgroundColorWhenFull; 132 | } 133 | } 134 | 135 | #pragma mark - drawing 136 | 137 | -(void)drawRect:(CGRect)rect { 138 | CGContextRef c = UIGraphicsGetCurrentContext(); 139 | 140 | //background 141 | CGContextSetFillColorWithColor(c, [self _currentBackgroundColor].CGColor); 142 | CGContextFillRect(c, rect); 143 | 144 | //fill areas 145 | if (![self _isEmpty]) { 146 | //calculate sum 147 | CGFloat fractionsSum = 0; 148 | for (NSNumber *fraction in self.fractions) { 149 | fractionsSum += [fraction floatValue]; 150 | } 151 | 152 | //do the drawing of individual areas 153 | CGFloat lastX1 = rect.origin.x; 154 | for (NSUInteger i=0; i