├── Gifs ├── ipad.gif ├── portraitPhone.gif └── landscapePhone.gif ├── Demo └── BIPActionsheet │ ├── BIPActionsheet │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── asCallIcon.imageset │ │ │ ├── asCallIcon.png │ │ │ ├── asCallIcon@2x.png │ │ │ ├── asCallIcon@3x.png │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── ViewController.h │ ├── AppDelegate.h │ ├── main.m │ ├── Info.plist │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── ViewController.m │ └── AppDelegate.m │ ├── BIPActionsheet.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── project.pbxproj │ └── BIPActionsheetTests │ ├── Info.plist │ ├── BIPActionsheetTests.m │ ├── BIPActionSheetStackTests.m │ ├── BIPActionSheetItemTests.m │ └── BIPActionSheetItemViewTests.m ├── Source ├── UIApplication+BIPActionSheet.h ├── UIView+BIPActionSheet.h ├── BIPActionSheetKit.h ├── UIImage+BIPActionSheet.h ├── UIWindow+BIPActionSheet.h ├── UIApplication+BIPActionSheet.m ├── UIView+BIPActionSheet.m ├── BIPActionSheetConstants.h ├── BIPActionSheetConstants.m ├── BIPActionSheetStack.h ├── UIImage+BIPActionSheet.m ├── UIWindow+BIPActionSheet.m ├── BIPActionSheetItem.h ├── BIPActionSheet.h ├── BIPActionSheetItemView.h ├── BIPActionSheetStack.m ├── BIPActionSheetItem.m ├── BIPActionSheetItemView.m └── BIPActionSheet.m ├── BIPActionSheet.podspec ├── LICENSE ├── .gitignore └── README.md /Gifs/ipad.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turkcell/BIPActionSheet/HEAD/Gifs/ipad.gif -------------------------------------------------------------------------------- /Gifs/portraitPhone.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turkcell/BIPActionSheet/HEAD/Gifs/portraitPhone.gif -------------------------------------------------------------------------------- /Gifs/landscapePhone.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turkcell/BIPActionSheet/HEAD/Gifs/landscapePhone.gif -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet/Assets.xcassets/asCallIcon.imageset/asCallIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turkcell/BIPActionSheet/HEAD/Demo/BIPActionsheet/BIPActionsheet/Assets.xcassets/asCallIcon.imageset/asCallIcon.png -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet/Assets.xcassets/asCallIcon.imageset/asCallIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turkcell/BIPActionSheet/HEAD/Demo/BIPActionsheet/BIPActionsheet/Assets.xcassets/asCallIcon.imageset/asCallIcon@2x.png -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet/Assets.xcassets/asCallIcon.imageset/asCallIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turkcell/BIPActionSheet/HEAD/Demo/BIPActionsheet/BIPActionsheet/Assets.xcassets/asCallIcon.imageset/asCallIcon@3x.png -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 08/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Source/UIApplication+BIPActionSheet.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIApplication+BIPActionSheet.h 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 08/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIApplication (BIPActionSheet) 12 | 13 | - (BOOL)bip_isLandscape; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Source/UIView+BIPActionSheet.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+BIPActionSheet.h 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 08/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIView (BIPActionSheet) 12 | 13 | - (CGFloat)bip_yOrigin; 14 | - (void)setBip_yOrigin:(CGFloat)yOrigin; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 08/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Source/BIPActionSheetKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionSheetKit.h 3 | // BIP 4 | // 5 | // Created by AHMET KAZIM GUNAY on 02/06/2017. 6 | // Copyright © 2017 Turkcell. All rights reserved. 7 | // 8 | 9 | #ifndef BIPActionSheetKit_h 10 | #define BIPActionSheetKit_h 11 | 12 | #import "BIPActionSheet.h" 13 | #import "BIPActionSheetItem.h" 14 | #import "BIPActionSheetItemView.h" 15 | #import "BIPActionSheetStack.h" 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /Source/UIImage+BIPActionSheet.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+BIPActionSheet.h 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 08/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIImage (BIPActionSheet) 12 | 13 | + (UIImage *)bip_imageWithColor:(UIColor *)color; 14 | + (UIImage *)bip_imageWithColor:(UIColor *)color withFrame:(CGRect)frame; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 08/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/UIWindow+BIPActionSheet.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIWindow+BIPActionSheet.h 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 08/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIWindow (BIPActionSheet) 12 | 13 | + (UIWindow *)bip_windowWithLevel:(UIWindowLevel)windowLevel; 14 | + (CGFloat)bip_maxWindowLevelCurrently; 15 | + (UIEdgeInsets)safeAreaInset; 16 | 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Source/UIApplication+BIPActionSheet.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIApplication+BIPActionSheet.m 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 08/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import "UIApplication+BIPActionSheet.h" 10 | 11 | @implementation UIApplication (BIPActionSheet) 12 | 13 | - (BOOL)bip_isLandscape 14 | { 15 | return UIInterfaceOrientationIsLandscape([self statusBarOrientation]); 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet/Assets.xcassets/asCallIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "asCallIcon.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "asCallIcon@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "asCallIcon@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Source/UIView+BIPActionSheet.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+BIPActionSheet.m 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 08/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import "UIView+BIPActionSheet.h" 10 | 11 | @implementation UIView (BIPActionSheet) 12 | 13 | - (CGFloat)bip_yOrigin 14 | { 15 | return CGRectGetMinY(self.frame); 16 | } 17 | 18 | - (void)setBip_yOrigin:(CGFloat)yOrigin 19 | { 20 | CGRect frame = self.frame; 21 | frame.origin.y = yOrigin; 22 | self.frame = frame; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Source/BIPActionSheetConstants.h: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionSheetConstants.h 3 | // BIP 4 | // 5 | // Created by AHMET KAZIM GUNAY on 02/06/2017. 6 | // Copyright © 2017 Turkcell. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BIPActionSheetConstants : NSObject 12 | 13 | extern CGFloat const kBIPActionSheetPaddingOffset; 14 | extern CGFloat const kBIPActionSheetPaddingOffsetiPAD; 15 | extern CGFloat const kBIPActionSheetBackgroundAlpha; 16 | extern CGFloat const kBIPActionSheetShowAnimationDuration; 17 | extern CGFloat const kBIPActionSheetHideAnimationDuration; 18 | extern CGFloat const kBIPActionSheetRowHeight; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Source/BIPActionSheetConstants.m: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionSheetConstants.m 3 | // BIP 4 | // 5 | // Created by AHMET KAZIM GUNAY on 02/06/2017. 6 | // Copyright © 2017 Turkcell. All rights reserved. 7 | // 8 | 9 | #import "BIPActionSheetConstants.h" 10 | 11 | @implementation BIPActionSheetConstants 12 | 13 | CGFloat const kBIPActionSheetPaddingOffset = 10.f; 14 | CGFloat const kBIPActionSheetPaddingOffsetiPAD = 150.f; 15 | CGFloat const kBIPActionSheetBackgroundAlpha = 0.5; 16 | CGFloat const kBIPActionSheetShowAnimationDuration = 0.4; 17 | CGFloat const kBIPActionSheetHideAnimationDuration = 0.3; 18 | CGFloat const kBIPActionSheetRowHeight = 56.f; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Source/BIPActionSheetStack.h: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionSheetStack.h 3 | // BIP 4 | // 5 | // Created by AHMET KAZIM GUNAY on 02/06/2017. 6 | // Copyright © 2017 Turkcell. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class BIPActionSheet; 12 | 13 | @interface BIPActionSheetStack : NSObject 14 | 15 | + (instancetype)sharedInstance; 16 | 17 | - (void)push:(BIPActionSheet *)actionSheet; 18 | - (void)pop:(BIPActionSheet *)actionSheet; 19 | - (void)pop:(BIPActionSheet *)actionSheet showNext:(BOOL)showNext; 20 | 21 | - (BOOL)containsActionSheetInStack:(BIPActionSheet *)actionsheet; 22 | 23 | @property (nonatomic, strong) NSMutableArray *actionSheets; 24 | 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheetTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /BIPActionSheet.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "BIPActionSheet" 3 | s.version = '1.0.1' 4 | s.summary = "Custom ActionSheet replacement with image support" 5 | s.description = <<-DESC 6 | Custom ActionSheet with image and text support, which is easy to use and modify. We developed to use in our BIP Messenger Application 7 | DESC 8 | s.homepage = "https://github.com/Turkcell/BIPActionSheet" 9 | s.license = 'MIT' 10 | s.author = { "Ahmet Kazım Günay" => "ahmetkgunay@gmail.com" } 11 | s.source = { :git => "https://github.com/Turkcell/BIPActionSheet.git", :tag => s.version.to_s } 12 | s.social_media_url = 'https://twitter.com/ahmtgny' 13 | s.platform = :ios, '7.0' 14 | s.requires_arc = true 15 | 16 | s.source_files = 'Source/*.{h,m}' 17 | end 18 | -------------------------------------------------------------------------------- /Source/UIImage+BIPActionSheet.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+BIPActionSheet.m 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 08/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import "UIImage+BIPActionSheet.h" 10 | 11 | @implementation UIImage (BIPActionSheet) 12 | 13 | + (UIImage *)bip_imageWithColor:(UIColor *)color 14 | { 15 | CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f); 16 | 17 | return [[self class] bip_imageWithColor:color withFrame:rect]; 18 | } 19 | 20 | + (UIImage *)bip_imageWithColor:(UIColor *)color withFrame:(CGRect)frame 21 | { 22 | UIGraphicsBeginImageContextWithOptions(frame.size, NO, 0.0); 23 | CGContextRef context = UIGraphicsGetCurrentContext(); 24 | 25 | CGContextSetFillColorWithColor(context, [color CGColor]); 26 | CGContextFillRect(context, frame); 27 | 28 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 29 | UIGraphicsEndImageContext(); 30 | 31 | return image; 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Turkcell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheetTests/BIPActionsheetTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionsheetTests.m 3 | // BIPActionsheetTests 4 | // 5 | // Created by AHMET KAZIM GUNAY on 12/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "BIPActionSheet.h" 11 | 12 | @interface BIPActionsheetTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation BIPActionsheetTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | 21 | } 22 | 23 | - (void)testInitWithEmptyItems { 24 | 25 | BIPActionSheet *actionSheet = [[BIPActionSheet alloc] initWithItems:@[] cancelButtonTitle:@"Cancel" cancelHandler:nil]; 26 | XCTAssertNotNil(actionSheet); 27 | } 28 | 29 | - (void)tearDown { 30 | // Put teardown code here. This method is called after the invocation of each test method in the class. 31 | [super tearDown]; 32 | } 33 | 34 | - (void)testExample { 35 | // This is an example of a functional test case. 36 | // Use XCTAssert and related functions to verify your tests produce the correct results. 37 | } 38 | 39 | - (void)testPerformanceExample { 40 | // This is an example of a performance test case. 41 | [self measureBlock:^{ 42 | // Put the code you want to measure the time of here. 43 | }]; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Source/UIWindow+BIPActionSheet.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIWindow+BIPActionSheet.m 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 08/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import "UIWindow+BIPActionSheet.h" 10 | 11 | @implementation UIWindow (BIPActionSheet) 12 | 13 | + (UIWindow *)bip_windowWithLevel:(UIWindowLevel)windowLevel 14 | { 15 | NSArray *windows = [[UIApplication sharedApplication] windows]; 16 | for (UIWindow *window in windows) 17 | { 18 | if (window.windowLevel == windowLevel) 19 | { 20 | return window; 21 | } 22 | } 23 | 24 | return nil; 25 | } 26 | 27 | + (CGFloat)bip_maxWindowLevelCurrently 28 | { 29 | CGFloat maxWindowLevel = 0; 30 | NSArray * windows = [[UIApplication sharedApplication] windows]; 31 | 32 | for (UIWindow * window in windows) 33 | { 34 | CGFloat currentWindowLevel = window.windowLevel; 35 | 36 | if (currentWindowLevel > maxWindowLevel) 37 | { 38 | maxWindowLevel = currentWindowLevel; 39 | } 40 | } 41 | 42 | return maxWindowLevel; 43 | } 44 | 45 | + (UIEdgeInsets)safeAreaInset 46 | { 47 | if(@available(iOS 11.0, *)) 48 | { 49 | return [UIApplication sharedApplication].keyWindow.safeAreaInsets; 50 | } 51 | else 52 | { 53 | return UIEdgeInsetsZero; 54 | } 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheetTests/BIPActionSheetStackTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionSheetStackTests.m 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 12/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "BIPActionSheetKit.h" 11 | 12 | @interface BIPActionSheetStackTests : XCTestCase 13 | 14 | @property (nonatomic, strong) BIPActionSheetStack *stack; 15 | 16 | @end 17 | 18 | @implementation BIPActionSheetStackTests 19 | 20 | #pragma mark - Setup 21 | 22 | - (void)setUp { 23 | [super setUp]; 24 | // Put setup code here. This method is called before the invocation of each test method in the class. 25 | self.stack = [BIPActionSheetStack sharedInstance]; 26 | } 27 | 28 | #pragma mark - Methods 29 | 30 | - (void)test_ContainsActionSheetInStack { 31 | 32 | BIPActionSheet *sheet = [BIPActionSheet showActionSheetWithItems:@[] cancelButtonTitle:@"Cancel" cancelHandler:nil]; 33 | XCTAssertTrue([self.stack containsActionSheetInStack:sheet]); 34 | } 35 | 36 | #pragma mark - Tear Down 37 | 38 | - (void)tearDown { 39 | // Put teardown code here. This method is called after the invocation of each test method in the class. 40 | [super tearDown]; 41 | } 42 | 43 | #pragma mark - Performance 44 | 45 | - (void)testPerformanceExample { 46 | // This is an example of a performance test case. 47 | [self measureBlock:^{ 48 | // Put the code you want to measure the time of here. 49 | }]; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /Source/BIPActionSheetItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionSheetItem.h 3 | // BIP 4 | // 5 | // Created by AHMET KAZIM GUNAY on 02/06/2017. 6 | // Copyright © 2017 Turkcell. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class BIPActionSheet; 12 | 13 | typedef void(^BIPActionSheetActionHandler)(BIPActionSheet *actionSheet); 14 | 15 | @interface BIPActionSheetItem : NSObject 16 | 17 | + (instancetype)itemWithTitle:(NSString *)title 18 | image:(UIImage *)image 19 | actionHandler:(BIPActionSheetActionHandler)actionHandler; 20 | 21 | + (instancetype)itemWithTitle:(NSString *)title 22 | image:(UIImage *)image 23 | textColor:(UIColor *)textColor 24 | actionHandler:(BIPActionSheetActionHandler)actionHandler; 25 | 26 | - (instancetype)initWithTitle:(NSString *)title 27 | image:(UIImage *)image 28 | actionHandler:(BIPActionSheetActionHandler)actionHandler; 29 | 30 | - (instancetype)initWithTitle:(NSString *)title 31 | image:(UIImage *)image 32 | textColor:(UIColor *)textColor 33 | actionHandler:(BIPActionSheetActionHandler)actionHandler; 34 | 35 | 36 | @property (nonatomic, copy) NSString *title; 37 | @property (nonatomic, strong) UIImage *image; 38 | @property (nonatomic, strong) UIColor *textColor; 39 | 40 | @property (nonatomic, copy) void (^actionHandler)(BIPActionSheet *actionSheet); 41 | 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | -------------------------------------------------------------------------------- /Source/BIPActionSheet.h: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionSheet.h 3 | // BIP 4 | // 5 | // Created by AHMET KAZIM GUNAY on 02/06/2017. 6 | // Copyright © 2017 Turkcell. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class BIPActionSheetItem; 12 | 13 | @interface BIPActionSheet : UIViewController 14 | 15 | - (instancetype)initWithItems:(NSArray *)items 16 | cancelButtonTitle:(NSString *)cancelButtonTitle 17 | cancelHandler:(void(^)(void))cancelHandler; 18 | 19 | - (instancetype)initWithTitle:(NSString *)title 20 | items:(NSArray *)items 21 | cancelButtonTitle:(NSString *)cancelButtonTitle 22 | cancelHandler:(void(^)(void))cancelHandler; 23 | 24 | + (instancetype)showActionSheetWithItems:(NSArray *)items 25 | cancelButtonTitle:(NSString *)cancelButtonTitle 26 | cancelHandler:(void(^)(void))cancelHandler; 27 | 28 | + (instancetype)showActionSheetWithTitle:(NSString *)title 29 | items:(NSArray *)items 30 | cancelButtonTitle:(NSString *)cancelButtonTitle 31 | cancelHandler:(void(^)(void))cancelHandler; 32 | 33 | - (void)show; 34 | - (void)hide; 35 | 36 | - (void)showInternal; 37 | - (void)dismiss; 38 | 39 | @property (nonatomic, assign, readonly, getter=isVisible) BOOL visible; 40 | @property (nonatomic, strong) UIButton *cancelButton; 41 | 42 | + (BOOL)isAnyActionsheetVisible; 43 | + (void)dismissVisibleActionsheet; 44 | + (void)dismissAllActionsheets; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Source/BIPActionSheetItemView.h: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionSheetItemView.h 3 | // BIP 4 | // 5 | // Created by AHMET KAZIM GUNAY on 02/06/2017. 6 | // Copyright © 2017 Turkcell. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSUInteger, BIPActionSheetImageAllignment) { 12 | Left, 13 | Right 14 | }; 15 | 16 | @class BIPActionSheetItem, BIPActionSheet; 17 | 18 | @interface BIPActionSheetItemView : UIView 19 | 20 | /** Initialize with Item to show up */ 21 | 22 | - (instancetype)initWithItem:(BIPActionSheetItem *)item 23 | isHeadlineItem:(BOOL)isHeadlineItem 24 | ownerSheet:(BIPActionSheet *)ownerSheet; 25 | 26 | /** Customize fonti color attributes using Apperance */ 27 | 28 | @property (nonatomic, strong) UIFont *titleFont UI_APPEARANCE_SELECTOR; // defaults [UIFont systemFontOfSize:13.f] 29 | 30 | @property (nonatomic, strong) UIColor *titleTextColor UI_APPEARANCE_SELECTOR; // defaults [UIColor darkGrayColor] 31 | 32 | @property (nonatomic, strong) UIFont *itemFont UI_APPEARANCE_SELECTOR; // defaults [UIFont systemFontOfSize:18] 33 | 34 | @property (nonatomic, strong) UIColor *itemTextColor UI_APPEARANCE_SELECTOR; // defaults [UIColor darkGrayColor] 35 | 36 | @property (nonatomic, strong) UIFont *cancelButtonFont UI_APPEARANCE_SELECTOR; // defaults [UIFont boldSystemFontOfSize:18] 37 | 38 | @property (nonatomic, strong) UIColor *cancelButtonColor UI_APPEARANCE_SELECTOR; // defaults [UIColor redColor] 39 | 40 | @property (nonatomic, assign) BIPActionSheetImageAllignment imageAlignment UI_APPEARANCE_SELECTOR; // defaults Left 41 | 42 | @property (nonatomic, assign) CGFloat imageWidth UI_APPEARANCE_SELECTOR; // defaults 23.5 43 | @property (nonatomic, assign) CGFloat imageHeight UI_APPEARANCE_SELECTOR; // defaults 23.5 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Source/BIPActionSheetStack.m: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionSheetStack.m 3 | // BIP 4 | // 5 | // Created by AHMET KAZIM GUNAY on 02/06/2017. 6 | // Copyright © 2017 Turkcell. All rights reserved. 7 | // 8 | 9 | #import "BIPActionSheetStack.h" 10 | #import "BIPActionSheet.h" 11 | #import "BIPActionSheetConstants.h" 12 | 13 | @implementation BIPActionSheetStack 14 | 15 | #pragma mark - Initialize 16 | 17 | + (instancetype)sharedInstance 18 | { 19 | static BIPActionSheetStack *_sharedInstance = nil; 20 | static dispatch_once_t onceToken; 21 | dispatch_once(&onceToken, ^{ 22 | _sharedInstance = [[BIPActionSheetStack alloc] init]; 23 | _sharedInstance.actionSheets = [NSMutableArray array]; 24 | }); 25 | 26 | return _sharedInstance; 27 | } 28 | 29 | #pragma mark - Push / Pop Stacks 30 | 31 | - (void)push:(BIPActionSheet *)actionSheet 32 | { 33 | @synchronized (self) { 34 | [self.actionSheets addObject:actionSheet]; 35 | 36 | if (self.actionSheets.count == 1) 37 | { 38 | [actionSheet showInternal]; 39 | } 40 | } 41 | } 42 | 43 | - (void)pop:(BIPActionSheet *)actionSheet showNext:(BOOL)showNext 44 | { 45 | @synchronized (self) { 46 | 47 | [actionSheet hide]; 48 | [self.actionSheets removeObject:actionSheet]; 49 | 50 | if (showNext) 51 | { 52 | BIPActionSheet *last = [self.actionSheets lastObject]; 53 | 54 | if (last) 55 | { 56 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kBIPActionSheetHideAnimationDuration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 57 | [last showInternal]; 58 | }); 59 | } 60 | } 61 | } 62 | } 63 | 64 | - (void)pop:(BIPActionSheet *)actionSheet 65 | { 66 | [self pop:actionSheet showNext:YES]; 67 | } 68 | 69 | #pragma mark - Check Contains 70 | 71 | - (BOOL)containsActionSheetInStack:(BIPActionSheet *)actionsheet 72 | { 73 | return [self.actionSheets containsObject:actionsheet]; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 08/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "BIPActionSheetKit.h" 11 | 12 | @interface ViewController () 13 | 14 | @end 15 | 16 | @implementation ViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | } 21 | 22 | - (IBAction)btnTapped:(id)sender { 23 | 24 | BIPActionSheetItem *item = [BIPActionSheetItem itemWithTitle:@"Item1" image:[UIImage imageNamed:@"asCallIcon"] actionHandler:^(BIPActionSheet *actionSheet) { 25 | 26 | NSLog(@"Item1 Tapped"); 27 | }]; 28 | 29 | BIPActionSheetItem *item2 = [BIPActionSheetItem itemWithTitle:@"Item2" image:[UIImage imageNamed:@"asCallIcon"] textColor:[UIColor blackColor] actionHandler:^(BIPActionSheet *actionSheet) { 30 | 31 | NSLog(@"Item2 Tapped"); 32 | }]; 33 | 34 | BIPActionSheetItem *item3 = [BIPActionSheetItem itemWithTitle:@"Item3" image:[UIImage imageNamed:@"asCallIcon"] actionHandler:^(BIPActionSheet *actionSheet) { 35 | 36 | NSLog(@"Item3 Tapped"); 37 | }]; 38 | 39 | BIPActionSheetItem *item4 = [BIPActionSheetItem itemWithTitle:@"Item4" image:[UIImage imageNamed:@"asCallIcon"] actionHandler:^(BIPActionSheet *actionSheet) { 40 | 41 | NSLog(@"Item4 Tapped"); 42 | }]; 43 | 44 | BIPActionSheetItem *item5 = [BIPActionSheetItem itemWithTitle:@"Item5" image:[UIImage imageNamed:@"asCallIcon"] actionHandler:^(BIPActionSheet *actionSheet) { 45 | 46 | NSLog(@"Item5 Tapped"); 47 | }]; 48 | 49 | BIPActionSheetItem *item6 = [BIPActionSheetItem itemWithTitle:@"Item6" image:[UIImage imageNamed:@"asCallIcon"] actionHandler:^(BIPActionSheet *actionSheet) { 50 | 51 | NSLog(@"Item6 Tapped"); 52 | }]; 53 | 54 | [BIPActionSheet showActionSheetWithTitle:@"This is title" items:@[item, item2, item3, item4, item5, item6] cancelButtonTitle:@"Cancel" cancelHandler:^{ 55 | 56 | NSLog(@"Cancel Tapped"); 57 | }]; 58 | } 59 | 60 | - (void)didReceiveMemoryWarning { 61 | [super didReceiveMemoryWarning]; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Source/BIPActionSheetItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionSheetItem.m 3 | // BIP 4 | // 5 | // Created by AHMET KAZIM GUNAY on 02/06/2017. 6 | // Copyright © 2017 Turkcell. All rights reserved. 7 | // 8 | 9 | #import "BIPActionSheetItem.h" 10 | #import "BIPActionSheetConstants.h" 11 | 12 | @implementation BIPActionSheetItem 13 | 14 | #pragma mark - Initialize 15 | 16 | - (instancetype)initWithTitle:(NSString *)title 17 | image:(UIImage *)image 18 | actionHandler:(BIPActionSheetActionHandler)actionHandler 19 | { 20 | if (self = [super init]) 21 | { 22 | self.title = [title copy]; 23 | self.image = image; 24 | self.actionHandler = actionHandler; 25 | } 26 | return self; 27 | } 28 | 29 | + (instancetype)itemWithTitle:(NSString *)title 30 | image:(UIImage *)image 31 | actionHandler:(BIPActionSheetActionHandler)actionHandler 32 | { 33 | return [[self alloc] initWithTitle:title 34 | image:image 35 | textColor:nil 36 | actionHandler:actionHandler]; 37 | } 38 | 39 | - (instancetype)initWithTitle:(NSString *)title 40 | image:(UIImage *)image 41 | textColor:(UIColor *)textColor 42 | actionHandler:(BIPActionSheetActionHandler)actionHandler 43 | { 44 | if (self = [super init]) 45 | { 46 | self.title = [title copy]; 47 | self.image = image; 48 | self.textColor = textColor; 49 | self.actionHandler = actionHandler; 50 | } 51 | return self; 52 | } 53 | 54 | + (instancetype)itemWithTitle:(NSString *)title 55 | image:(UIImage *)image 56 | textColor:(UIColor *)textColor 57 | actionHandler:(BIPActionSheetActionHandler)actionHandler 58 | { 59 | return [[self alloc] initWithTitle:title 60 | image:image 61 | textColor:textColor 62 | actionHandler:actionHandler]; 63 | } 64 | 65 | #pragma mark - NSCopying 66 | 67 | - (id)copyWithZone:(NSZone *)zone 68 | { 69 | BIPActionSheetItem *copyItem = [[self class] allocWithZone:zone]; 70 | 71 | if (copyItem) 72 | { 73 | copyItem.title = self.title; 74 | copyItem.image = self.image; 75 | copyItem.textColor = self.textColor; 76 | copyItem.actionHandler = self.actionHandler; 77 | } 78 | 79 | return copyItem; 80 | } 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 08/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "BIPActionSheetKit.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | 22 | [[BIPActionSheetItemView appearance] setCancelButtonColor:[UIColor blueColor]]; 23 | [[BIPActionSheetItemView appearance] setCancelButtonFont:[UIFont fontWithName:@"Helvetica-Bold" size:17]]; 24 | 25 | [[BIPActionSheetItemView appearance] setItemFont:[UIFont fontWithName:@"Helvetica" size:17]]; 26 | [[BIPActionSheetItemView appearance] setItemTextColor:[UIColor darkGrayColor]]; 27 | 28 | [[BIPActionSheetItemView appearance] setTitleFont:[UIFont fontWithName:@"Helvetica" size:14]]; 29 | [[BIPActionSheetItemView appearance] setTitleTextColor:[UIColor redColor]]; 30 | 31 | [[BIPActionSheetItemView appearance] setImageAlignment:Left]; 32 | [[BIPActionSheetItemView appearance] setImageHeight:30.f]; 33 | [[BIPActionSheetItemView appearance] setImageWidth:30.f]; 34 | 35 | return YES; 36 | } 37 | 38 | 39 | - (void)applicationWillResignActive:(UIApplication *)application { 40 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 41 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 42 | } 43 | 44 | 45 | - (void)applicationDidEnterBackground:(UIApplication *)application { 46 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 47 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 48 | } 49 | 50 | 51 | - (void)applicationWillEnterForeground:(UIApplication *)application { 52 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 53 | } 54 | 55 | 56 | - (void)applicationDidBecomeActive:(UIApplication *)application { 57 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 58 | } 59 | 60 | 61 | - (void)applicationWillTerminate:(UIApplication *)application { 62 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 63 | } 64 | 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheet/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheetTests/BIPActionSheetItemTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionSheetItemTests.m 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 12/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "BIPActionSheetItem.h" 11 | 12 | @interface BIPActionSheetItemTests : XCTestCase 13 | 14 | @property (nonatomic, copy) NSString *firstItemTitle; 15 | @property (nonatomic, strong) UIImage *firstItemImage; 16 | @property (nonatomic, copy) NSString *secondItemTitle; 17 | @property (nonatomic, strong) UIImage *secondItemImage; 18 | @property (nonatomic, strong) UIColor *firstItemTitleColor; 19 | 20 | @property (nonatomic, strong) BIPActionSheetItem *firstItem; 21 | @property (nonatomic, strong) BIPActionSheetItem *secondItem; 22 | 23 | @end 24 | 25 | @implementation BIPActionSheetItemTests 26 | 27 | - (void)setUp { 28 | [super setUp]; 29 | // Put setup code here. This method is called before the invocation of each test method in the class. 30 | 31 | [self initializeItems]; 32 | } 33 | 34 | - (void)initializeItems { 35 | 36 | _firstItemTitle = @"First"; 37 | _secondItemTitle = @"Second"; 38 | 39 | _firstItemImage = [UIImage imageNamed:@""]; 40 | _secondItemImage = [UIImage imageNamed:@""]; 41 | 42 | _firstItemTitleColor = [UIColor blueColor]; 43 | 44 | _firstItem = [[BIPActionSheetItem alloc] initWithTitle:_firstItemTitle 45 | image:_firstItemImage 46 | textColor:_firstItemTitleColor 47 | actionHandler:nil]; 48 | 49 | _secondItem = [BIPActionSheetItem itemWithTitle:_secondItemTitle 50 | image:_secondItemImage 51 | actionHandler:nil]; 52 | } 53 | 54 | - (void)test_InitWithTitleWithColor { 55 | 56 | XCTAssertNotNil(_firstItem); 57 | } 58 | 59 | - (void)test_InitWithTitle { 60 | 61 | BIPActionSheetItem *item = [[BIPActionSheetItem alloc] initWithTitle:_firstItemTitle 62 | image:_firstItemImage 63 | actionHandler:nil]; 64 | XCTAssertNotNil(item); 65 | } 66 | 67 | - (void)test_ItemWithClassMethod { 68 | 69 | XCTAssertNotNil(_secondItem); 70 | } 71 | 72 | - (void)test_ItemWithClassMethodWithColor { 73 | 74 | BIPActionSheetItem *item = [BIPActionSheetItem itemWithTitle:_secondItemTitle 75 | image:_secondItemImage 76 | textColor:[UIColor redColor] 77 | actionHandler:nil]; 78 | XCTAssertNotNil(item); 79 | } 80 | 81 | - (void)test_ItemTitle { 82 | 83 | XCTAssertEqual(_firstItemTitle, _firstItem.title); 84 | } 85 | 86 | - (void)test_ItemImage { 87 | 88 | XCTAssertEqual(_firstItemImage, _firstItem.image); 89 | } 90 | 91 | - (void)test_ItemTextColor { 92 | 93 | XCTAssertEqual(_firstItemTitleColor, _firstItem.textColor); 94 | } 95 | 96 | - (void)test_CopyWithZone { 97 | 98 | BIPActionSheetItem *copyItem = [_firstItem copy]; 99 | XCTAssertNotNil(copyItem); 100 | } 101 | 102 | - (void)test_TitleEqualsWithCopy { 103 | 104 | BIPActionSheetItem *copyItem = [_firstItem copy]; 105 | XCTAssertEqual(_firstItem.title, copyItem.title); 106 | } 107 | 108 | - (void)test_TextColorEqualsWithCopy { 109 | 110 | BIPActionSheetItem *copyItem = [_firstItem copy]; 111 | XCTAssertEqual(_firstItem.textColor, copyItem.textColor); 112 | } 113 | 114 | - (void)test_ImageEqualsWithCopy { 115 | 116 | BIPActionSheetItem *copyItem = [_firstItem copy]; 117 | XCTAssertEqual(_firstItem.image, copyItem.image); 118 | } 119 | 120 | - (void)tearDown { 121 | // Put teardown code here. This method is called after the invocation of each test method in the class. 122 | _firstItem = nil; 123 | _secondItem = nil; 124 | _firstItemTitleColor = nil; 125 | _firstItemImage = nil; 126 | _firstItemTitle = nil; 127 | _secondItemImage = nil; 128 | _secondItemTitle = nil; 129 | 130 | [super tearDown]; 131 | } 132 | 133 | - (void)testPerformanceExample { 134 | // This is an example of a performance test case. 135 | [self measureBlock:^{ 136 | // Put the code you want to measure the time of here. 137 | }]; 138 | } 139 | 140 | @end 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BIPActionSheet 2 | Custom ActionSheet with image and text support, which is easy to use and modify. We developed to use in our BIP Messenger Application 3 | 4 | #### Demo for iPhone 5 | ![Anim](https://github.com/Turkcell/BIPActionSheet/blob/master/Gifs/landscapePhone.gif) 6 | ![Anim](https://github.com/Turkcell/BIPActionSheet/blob/master/Gifs/portraitPhone.gif) 7 | 8 | #### Demo for iPad 9 | ![Anim](https://github.com/Turkcell/BIPActionSheet/blob/master/Gifs/ipad.gif) 10 | 11 | 12 | ### Usage 13 | 14 | Usage is simple, just import "BIPActionSheetKit.h" to your class where you want to use this library. 15 | 16 | For example if you want to show actionsheet when button tapped: 17 | 18 | ```objective-c 19 | 20 | #import "BIPActionSheetKit.h" 21 | 22 | @implementation ViewController 23 | 24 | - (IBAction)btnTapped:(id)sender { 25 | 26 | BIPActionSheetItem *item = [BIPActionSheetItem itemWithTitle:@"Item1" image:[UIImage imageNamed:@"asCallIcon"] actionHandler:^(BIPActionSheet *actionSheet) { 27 | 28 | NSLog(@"Item1 Tapped"); 29 | }]; 30 | 31 | BIPActionSheetItem *item2 = [BIPActionSheetItem itemWithTitle:@"Item2" image:[UIImage imageNamed:@"asCallIcon"] textColor:[UIColor blackColor] actionHandler:^(BIPActionSheet *actionSheet) { 32 | 33 | NSLog(@"Item2 Tapped"); 34 | }]; 35 | 36 | BIPActionSheetItem *item3 = [BIPActionSheetItem itemWithTitle:@"Item3" image:[UIImage imageNamed:@"asCallIcon"] actionHandler:^(BIPActionSheet *actionSheet) { 37 | 38 | NSLog(@"Item3 Tapped"); 39 | }]; 40 | 41 | BIPActionSheetItem *item4 = [BIPActionSheetItem itemWithTitle:@"Item4" image:[UIImage imageNamed:@"asCallIcon"] actionHandler:^(BIPActionSheet *actionSheet) { 42 | 43 | NSLog(@"Item4 Tapped"); 44 | }]; 45 | 46 | BIPActionSheetItem *item5 = [BIPActionSheetItem itemWithTitle:@"Item5" image:[UIImage imageNamed:@"asCallIcon"] actionHandler:^(BIPActionSheet *actionSheet) { 47 | 48 | NSLog(@"Item5 Tapped"); 49 | }]; 50 | 51 | BIPActionSheetItem *item6 = [BIPActionSheetItem itemWithTitle:@"Item6" image:[UIImage imageNamed:@"asCallIcon"] actionHandler:^(BIPActionSheet *actionSheet) { 52 | 53 | NSLog(@"Item6 Tapped"); 54 | }]; 55 | 56 | [BIPActionSheet showActionSheetWithTitle:@"This is title" items:@[item, item2, item3, item4, item5, item6] cancelButtonTitle:@"Cancel" cancelHandler:^{ 57 | 58 | NSLog(@"Cancel Tapped"); 59 | }]; 60 | } 61 | 62 | @end 63 | ``` 64 | - You can check if actionsheet is visible with Actionsheet's public property 65 | 66 | ```objective-c 67 | @property (nonatomic, assign, readonly, getter=isVisible) BOOL visible; 68 | ``` 69 | 70 | 71 | - You can easily control application-wide actionsheet controls with class methods of BIPActionSheet class 72 | 73 | ```objective-c 74 | + (BOOL)isAnyActionsheetVisible; 75 | + (void)dismissLastActiveActionsheet; 76 | + (void)dismissAllActionsheets; 77 | ``` 78 | 79 | ### Appearence Support 80 | 81 | Application-wide appearence of Actionsheet can be set. 82 | 83 | NOTE: If you initialize BIPActionSheetItem with spesific text color, so this library firsly listens how you initialize the item to show up about appearence. 84 | 85 | For Example: You are initializing actionSheetItem with blueColor as shown below, Then this library firstly choose this item textColor as Blue regardless you initialized appearence with black color for spesific item. 86 | 87 | ```objective-c 88 | BIPActionSheetItem *item2 = [BIPActionSheetItem itemWithTitle:@"Item2" image:[UIImage imageNamed:@"asCallIcon"] textColor:[UIColor blueColor] actionHandler:^(BIPActionSheet *actionSheet) { 89 | 90 | NSLog(@"Item2 Tapped"); 91 | }]; 92 | ``` 93 | 94 | ```objective-c 95 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 96 | // Override point for customization after application launch. 97 | 98 | [[BIPActionSheetItemView appearance] setCancelButtonColor:[UIColor blueColor]]; 99 | [[BIPActionSheetItemView appearance] setCancelButtonFont:[UIFont fontWithName:@"Helvetica-Bold" size:17]]; 100 | 101 | [[BIPActionSheetItemView appearance] setItemFont:[UIFont fontWithName:@"Helvetica" size:17]]; 102 | [[BIPActionSheetItemView appearance] setItemTextColor:[UIColor darkGrayColor]]; 103 | 104 | [[BIPActionSheetItemView appearance] setTitleFont:[UIFont fontWithName:@"Helvetica" size:14]]; 105 | [[BIPActionSheetItemView appearance] setTitleTextColor:[UIColor redColor]]; 106 | 107 | [[BIPActionSheetItemView appearance] setImageAlignment:Left]; 108 | [[BIPActionSheetItemView appearance] setImageHeight:30.f]; 109 | [[BIPActionSheetItemView appearance] setImageWidth:30.f]; 110 | 111 | return YES; 112 | } 113 | ``` 114 | 115 | ### Stack Design 116 | 117 | You can easily manage your app-wide actionsheets with stack design. 118 | 119 | For example: it is easy to check if spesific actionsheet is already presented with below method of BIPActionSheetStack global instance: 120 | 121 | ```objective-c 122 | - (BOOL)containsActionSheetInStack:(BIPActionSheet *)actionsheet; 123 | ``` 124 | 125 | Anytime easy to access all actionsheets on stack with below property of BIPActionSheetStack global instance: 126 | 127 | ```objective-c 128 | @property (nonatomic, strong) NSMutableArray *actionSheets; 129 | ``` 130 | 131 | ### Customize With Constants 132 | 133 | These constants are for UI Customization of BIPActionSheet Library as shown below: 134 | You can change them with needs of your project. 135 | 136 | ```objective-c 137 | @interface BIPActionSheetConstants : NSObject 138 | 139 | extern CGFloat const kBIPActionSheetPaddingOffset; 140 | extern CGFloat const kBIPActionSheetPaddingOffsetiPAD; 141 | extern CGFloat const kBIPActionSheetBackgroundAlpha; 142 | extern CGFloat const kBIPActionSheetShowAnimationDuration; 143 | extern CGFloat const kBIPActionSheetHideAnimationDuration; 144 | extern CGFloat const kBIPActionSheetRowHeight; 145 | 146 | @end 147 | ``` 148 | 149 | For more information please check the Demo App. 150 | 151 | ## Installation 152 | 153 | There are two ways to use BIPActionSheet in your project: 154 | - using CocoaPods 155 | - by cloning the project into your repository 156 | 157 | ### Installation with CocoaPods 158 | 159 | [CocoaPods](http://cocoapods.org/) is a dependency manager for Swift and Objective-C, which automates and simplifies the process of using 3rd-party libraries in your projects. See the [Get Started](http://cocoapods.org/#get_started) section for more details. 160 | 161 | #### Podfile 162 | ```ruby 163 | platform :ios, '8.0' 164 | use_frameworks! 165 | pod 'BIPActionSheet', '~> 1.0.1' 166 | ``` 167 | 168 | ## Requirements 169 | 170 | This library requires a deployment target of iOS 7.0 or greater. 171 | 172 | 173 | ## Author 174 | 175 | For any kind of question, you can easily access: 176 | - Ahmet Kazım Günay, ahmetkgunay@gmail.com 177 | 178 | ## License 179 | 180 | BIPActionSheet is available under the MIT license. See the LICENSE file for more info. 181 | -------------------------------------------------------------------------------- /Source/BIPActionSheetItemView.m: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionSheetItemView.m 3 | // BIP 4 | // 5 | // Created by AHMET KAZIM GUNAY on 02/06/2017. 6 | // Copyright © 2017 Turkcell. All rights reserved. 7 | // 8 | 9 | #import "BIPActionSheetItemView.h" 10 | #import "BIPActionSheetItem.h" 11 | #import "BIPActionSheetConstants.h" 12 | #import "BIPActionSheet.h" 13 | #import "UIImage+BIPActionSheet.h" 14 | 15 | @interface BIPActionSheetItemView() 16 | 17 | @property (nonatomic, strong) UIImageView *imageView; 18 | 19 | @property (nonatomic, strong) UILabel *itemLabel; 20 | @property (nonatomic, strong) UILabel *titleLabel; 21 | 22 | @property (nonatomic, strong) UIButton *button; 23 | @property (nonatomic, strong) UIView *lineView; 24 | @property (nonatomic, strong) BIPActionSheetItem *item; 25 | @property (nonatomic, strong) BIPActionSheet *ownerActionSheet; 26 | 27 | @end 28 | 29 | @implementation BIPActionSheetItemView 30 | 31 | #pragma mark - Initialize 32 | 33 | - (instancetype)initWithItem:(BIPActionSheetItem *)item isHeadlineItem:(BOOL)isHeadlineItem ownerSheet:(BIPActionSheet *)ownerSheet 34 | { 35 | if (self = [super init]) 36 | { 37 | self.item = item; 38 | self.ownerActionSheet = ownerSheet; 39 | 40 | self.imageView.image = item.image; 41 | 42 | if (item.textColor) 43 | { 44 | self.itemLabel.textColor = item.textColor; 45 | } 46 | 47 | [self addSubview:self.imageView]; 48 | 49 | if (!isHeadlineItem) // no need to add button because title of actionsheet will not have any action handler 50 | { 51 | [self addSubview:self.button]; 52 | [self addSubview:self.itemLabel]; 53 | self.itemLabel.text = item.title; 54 | } 55 | else 56 | { 57 | [self addSubview:self.titleLabel]; 58 | self.titleLabel.text = item.title; 59 | } 60 | 61 | [self addSubview:self.lineView]; 62 | 63 | self.backgroundColor = [UIColor whiteColor]; 64 | } 65 | return self; 66 | } 67 | 68 | #pragma mark - Lazy Initializers 69 | 70 | - (UIButton *)button 71 | { 72 | if (!_button) 73 | { 74 | _button = [UIButton buttonWithType:UIButtonTypeCustom]; 75 | [_button setBackgroundImage:[UIImage bip_imageWithColor:[UIColor colorWithWhite:0 alpha:0.1]] forState:UIControlStateHighlighted]; 76 | [_button setBackgroundImage:[UIImage bip_imageWithColor:[UIColor colorWithWhite:0 alpha:0.1]] forState:UIControlStateSelected]; 77 | [_button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside]; 78 | } 79 | 80 | return _button; 81 | } 82 | 83 | - (UILabel *)itemLabel 84 | { 85 | if (!_itemLabel) 86 | { 87 | _itemLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 88 | _itemLabel.textColor = [UIColor darkGrayColor]; 89 | _itemLabel.backgroundColor = [UIColor clearColor]; 90 | _itemLabel.font = [UIFont systemFontOfSize:18]; 91 | } 92 | 93 | return _itemLabel; 94 | } 95 | 96 | - (UILabel *)titleLabel 97 | { 98 | if (!_titleLabel) 99 | { 100 | _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 101 | _titleLabel.backgroundColor = [UIColor clearColor]; 102 | _titleLabel.font = [UIFont systemFontOfSize:13.f]; 103 | _titleLabel.textColor = [UIColor darkGrayColor]; 104 | _titleLabel.numberOfLines = 0.f; 105 | _titleLabel.adjustsFontSizeToFitWidth = YES; 106 | _titleLabel.minimumScaleFactor = 0.5; 107 | _titleLabel.textAlignment = NSTextAlignmentCenter; 108 | } 109 | 110 | return _titleLabel; 111 | } 112 | 113 | - (UIView *)lineView 114 | { 115 | if (!_lineView) 116 | { 117 | _lineView = [[UIView alloc] initWithFrame:CGRectZero]; 118 | _lineView.backgroundColor = [UIColor lightGrayColor]; 119 | _lineView.alpha = 0.2f; 120 | } 121 | 122 | return _lineView; 123 | } 124 | 125 | - (UIImageView *)imageView 126 | { 127 | if (!_imageView) 128 | { 129 | _imageView = [[UIImageView alloc] initWithFrame:CGRectZero]; 130 | _imageView.contentMode = UIViewContentModeScaleAspectFit; 131 | _imageView.clipsToBounds = YES; 132 | } 133 | 134 | return _imageView; 135 | } 136 | 137 | #pragma mark - Button Action 138 | 139 | - (void)buttonTapped:(UIButton *)sender 140 | { 141 | sender.selected = YES; 142 | 143 | [_ownerActionSheet dismiss]; 144 | 145 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kBIPActionSheetHideAnimationDuration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 146 | if (_item.actionHandler) 147 | { 148 | _item.actionHandler(_ownerActionSheet); 149 | } 150 | }); 151 | } 152 | 153 | #pragma mark - Appearence Setters 154 | 155 | - (void)setTitleFont:(UIFont *)titleFont 156 | { 157 | _titleFont = titleFont; 158 | _titleLabel.font = titleFont; 159 | } 160 | 161 | - (void)setItemFont:(UIFont *)itemFont 162 | { 163 | _itemFont = itemFont; 164 | _itemLabel.font = itemFont; 165 | } 166 | 167 | - (void)setItemTextColor:(UIColor *)itemTextColor 168 | { 169 | _itemTextColor = itemTextColor; 170 | _itemLabel.textColor = self.item.textColor ?: itemTextColor; 171 | } 172 | 173 | - (void)setTitleTextColor:(UIColor *)titleTextColor 174 | { 175 | _titleTextColor = titleTextColor; 176 | _titleLabel.textColor = titleTextColor; 177 | } 178 | 179 | - (void)setCancelButtonFont:(UIFont *)cancelButtonFont 180 | { 181 | _cancelButtonFont = cancelButtonFont; 182 | [_ownerActionSheet.cancelButton.titleLabel setFont:cancelButtonFont]; 183 | } 184 | 185 | - (void)setCancelButtonColor:(UIColor *)cancelButtonColor 186 | { 187 | _cancelButtonColor = cancelButtonColor; 188 | [_ownerActionSheet.cancelButton setTitleColor:cancelButtonColor forState:UIControlStateNormal]; 189 | } 190 | 191 | - (void)setImageAlignment:(BIPActionSheetImageAllignment)imageAlignment 192 | { 193 | _imageAlignment = imageAlignment; 194 | 195 | if (imageAlignment == Right) // defaults left 196 | { 197 | [self setNeedsLayout]; 198 | } 199 | } 200 | 201 | - (void)setImageWidth:(CGFloat)imageWidth 202 | { 203 | _imageWidth = imageWidth; 204 | [self setNeedsLayout]; 205 | } 206 | 207 | - (void)setImageHeight:(CGFloat)imageHeight 208 | { 209 | _imageHeight = imageHeight; 210 | [self setNeedsLayout]; 211 | } 212 | 213 | #pragma mark - Layout 214 | 215 | - (void)layoutSubviews 216 | { 217 | [super layoutSubviews]; 218 | 219 | CGFloat padding = 24.f; 220 | CGFloat lineViewHeight = 1.f; 221 | CGFloat imageHeight = self.imageHeight ?: 23.5; 222 | CGFloat imageWidth = self.imageWidth ?: 23.5; 223 | 224 | // imageView Frame 225 | CGRect imageViewRect = CGRectZero; 226 | imageViewRect.origin.x = self.imageAlignment == Left ? padding : self.frame.size.width - padding - imageWidth; 227 | imageViewRect.size.width = imageWidth; 228 | imageViewRect.size.height = imageHeight; 229 | imageViewRect.origin.y = (self.frame.size.height - imageViewRect.size.height) / 2; 230 | _imageView.frame = imageViewRect; 231 | 232 | // textlabel Frame 233 | CGRect textLabelRect = CGRectZero; 234 | textLabelRect.origin.x = self.imageAlignment == Left ? imageViewRect.origin.x + imageViewRect.size.width + padding : padding; 235 | 236 | if (_item.image == nil) 237 | { 238 | _itemLabel.textAlignment = NSTextAlignmentCenter; 239 | textLabelRect.origin.x = padding; 240 | } 241 | 242 | textLabelRect.size.width = self.frame.size.width - (textLabelRect.origin.x + padding); 243 | textLabelRect.origin.y = lineViewHeight; 244 | textLabelRect.size.height = self.frame.size.height - 2 * lineViewHeight; 245 | _itemLabel.frame = textLabelRect; 246 | 247 | _titleLabel.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); 248 | 249 | 250 | // imageView Frame 251 | CGRect lineViewRect = CGRectZero; 252 | lineViewRect.origin.x = 0; 253 | lineViewRect.origin.y = self.frame.size.height - lineViewHeight; 254 | lineViewRect.size.width = self.frame.size.width; 255 | lineViewRect.size.height = lineViewHeight; 256 | _lineView.frame = lineViewRect; 257 | 258 | // button Frame 259 | _button.frame = self.bounds; 260 | } 261 | 262 | #pragma mark - NSCopying 263 | 264 | - (id)copyWithZone:(NSZone *)zone 265 | { 266 | BIPActionSheetItemView *copyItemView = [[self class] allocWithZone:zone]; 267 | 268 | if (copyItemView) 269 | { 270 | copyItemView.imageView = self.imageView; 271 | copyItemView.itemLabel = self.itemLabel; 272 | copyItemView.button = self.button; 273 | copyItemView.lineView = self.lineView; 274 | copyItemView.item = [self.item copyWithZone:zone]; 275 | copyItemView.ownerActionSheet = [self.ownerActionSheet copyWithZone:zone]; 276 | copyItemView.imageAlignment = self.imageAlignment; 277 | copyItemView.titleLabel = self.titleLabel; 278 | copyItemView.imageWidth = self.imageWidth; 279 | copyItemView.imageHeight = self.imageHeight; 280 | } 281 | 282 | return copyItemView; 283 | } 284 | 285 | @end 286 | -------------------------------------------------------------------------------- /Demo/BIPActionsheet/BIPActionsheetTests/BIPActionSheetItemViewTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionSheetItemViewTests.m 3 | // BIPActionsheet 4 | // 5 | // Created by AHMET KAZIM GUNAY on 12/07/2017. 6 | // Copyright © 2017 TURKCELL. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "BIPActionSheetItemView.h" 11 | #import "BIPActionSheetItem.h" 12 | #import "BIPActionSheetConstants.h" 13 | 14 | @interface BIPActionSheetItemViewTests : XCTestCase 15 | 16 | @property (nonatomic, copy) NSString *firstItemTitle; 17 | @property (nonatomic, copy) NSString *secondItemTitle; 18 | @property (nonatomic, strong) UIColor *firstItemTitleColor; 19 | 20 | @property (nonatomic, strong) BIPActionSheetItem *itemWithTextColor; 21 | @property (nonatomic, strong) BIPActionSheetItem *itemWithoutTextColor; 22 | 23 | 24 | 25 | @end 26 | 27 | @interface BIPActionSheetItemView (Testing) 28 | 29 | @property (nonatomic, strong) UIImageView *imageView; 30 | 31 | @property (nonatomic, strong) UILabel *itemLabel; 32 | @property (nonatomic, strong) UILabel *titleLabel; 33 | 34 | @property (nonatomic, strong) UIButton *button; 35 | @property (nonatomic, strong) UIView *lineView; 36 | @property (nonatomic, strong) BIPActionSheetItem *item; 37 | @property (nonatomic, strong) BIPActionSheet *ownerActionSheet; 38 | 39 | - (void)buttonTapped:(UIButton *)sender; 40 | 41 | @end 42 | 43 | @implementation BIPActionSheetItemViewTests 44 | 45 | - (void)setUp { 46 | [super setUp]; 47 | // Put setup code here. This method is called before the invocation of each test method in the class. 48 | [self initializeItems]; 49 | } 50 | 51 | - (void)initializeItems { 52 | 53 | _firstItemTitle = @"First"; 54 | _secondItemTitle = @"Second"; 55 | 56 | _firstItemTitleColor = [UIColor blueColor]; 57 | 58 | _itemWithTextColor = [[BIPActionSheetItem alloc] initWithTitle:_firstItemTitle 59 | image:nil 60 | textColor:_firstItemTitleColor 61 | actionHandler:^(BIPActionSheet *actionSheet) { 62 | NSLog(@"Called Item With TextColor Handler"); 63 | }]; 64 | 65 | _itemWithoutTextColor = [BIPActionSheetItem itemWithTitle:_secondItemTitle 66 | image:nil 67 | actionHandler:^(BIPActionSheet *actionSheet) { 68 | NSLog(@"Called Item Without TextColor Handler"); 69 | }]; 70 | } 71 | 72 | - (void)test_InitializeWithItem { 73 | 74 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithoutTextColor isHeadlineItem:NO ownerSheet:nil]; 75 | XCTAssertNotNil(itemView); 76 | } 77 | 78 | - (void)test_InitializeWithHeadLineItem { 79 | 80 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithoutTextColor isHeadlineItem:YES ownerSheet:nil]; 81 | XCTAssertNotNil(itemView); 82 | } 83 | 84 | - (void)test_ItemViewContaintsTitleLabelIfHeadline { 85 | 86 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithoutTextColor isHeadlineItem:YES ownerSheet:nil]; 87 | XCTAssertTrue([itemView.subviews containsObject:itemView.titleLabel]); 88 | } 89 | 90 | - (void)test_ItemViewNotContaintsTitleLabelIfHeadline { 91 | 92 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithoutTextColor isHeadlineItem:NO ownerSheet:nil]; 93 | XCTAssertTrue(![itemView.subviews containsObject:itemView.titleLabel]); 94 | } 95 | 96 | #pragma mark - Test Appearences 97 | 98 | - (void)test_TitleTextColorAppearence { 99 | 100 | UIColor *titleColor = [UIColor yellowColor]; 101 | 102 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithoutTextColor isHeadlineItem:YES ownerSheet:nil]; 103 | 104 | [itemView setTitleTextColor:titleColor]; 105 | 106 | XCTAssertTrue(CGColorEqualToColor(itemView.titleLabel.textColor.CGColor, titleColor.CGColor)); 107 | } 108 | 109 | - (void)test_ItemTextColorAppearence { 110 | 111 | UIColor *itemColor = [UIColor blueColor]; 112 | 113 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithoutTextColor isHeadlineItem:NO ownerSheet:nil]; 114 | 115 | [itemView setItemTextColor:itemColor]; 116 | 117 | XCTAssertTrue(CGColorEqualToColor(itemView.itemLabel.textColor.CGColor, itemColor.CGColor)); 118 | } 119 | 120 | - (void)test_ItemTextColorAppearenceIfHeadline { 121 | 122 | // Should Be false because headline item doesnt have itemLabel 123 | 124 | UIColor *itemColor = [UIColor blueColor]; 125 | 126 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithoutTextColor isHeadlineItem:YES ownerSheet:nil]; 127 | 128 | [itemView setItemTextColor:itemColor]; 129 | 130 | XCTAssertFalse(CGColorEqualToColor(itemView.itemLabel.textColor.CGColor, itemColor.CGColor)); 131 | } 132 | 133 | - (void)test_ShouldHave_ItemTextColor_Not_AppearenceColor { 134 | 135 | // Should have color of which set while creating item as text color, 136 | // Appearence shouldn't work here because, developer has changed spesific item text color while initializing the item 137 | 138 | UIColor *itemColor = [UIColor yellowColor]; 139 | 140 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithTextColor isHeadlineItem:NO ownerSheet:nil]; 141 | 142 | [itemView setItemTextColor:itemColor]; 143 | 144 | XCTAssertFalse(CGColorEqualToColor(itemView.itemLabel.textColor.CGColor, itemColor.CGColor)); 145 | } 146 | 147 | - (void)test_ItemImageAlignmentAppearence { 148 | 149 | BIPActionSheetImageAllignment alignment = Right; 150 | 151 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithoutTextColor isHeadlineItem:YES ownerSheet:nil]; 152 | 153 | [itemView setImageAlignment:alignment]; 154 | 155 | XCTAssertEqual(itemView.imageAlignment, alignment); 156 | } 157 | 158 | - (void)test_CancelButtonFont { 159 | 160 | UIColor *cancelColor = [UIColor yellowColor]; 161 | 162 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithoutTextColor isHeadlineItem:NO ownerSheet:nil]; 163 | 164 | [itemView setCancelButtonColor:cancelColor]; 165 | 166 | XCTAssertEqual(itemView.cancelButtonColor, cancelColor); 167 | } 168 | 169 | - (void)test_CancelButtonColor { 170 | 171 | UIFont *font = [UIFont fontWithName:@"Helvetica-Bold" size:14]; 172 | 173 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithoutTextColor isHeadlineItem:NO ownerSheet:nil]; 174 | 175 | [itemView setCancelButtonFont:font]; 176 | 177 | XCTAssertEqual(itemView.cancelButtonFont, font); 178 | } 179 | 180 | - (void)test_ItemFont { 181 | 182 | UIFont *font = [UIFont fontWithName:@"Helvetica-Bold" size:14]; 183 | 184 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithoutTextColor isHeadlineItem:NO ownerSheet:nil]; 185 | 186 | [itemView setItemFont:font]; 187 | 188 | XCTAssertEqual(itemView.itemFont, font); 189 | } 190 | 191 | - (void)test_TitleFont { 192 | 193 | UIFont *font = [UIFont fontWithName:@"Helvetica-Bold" size:14]; 194 | 195 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithoutTextColor isHeadlineItem:NO ownerSheet:nil]; 196 | 197 | [itemView setTitleFont:font]; 198 | 199 | XCTAssertEqual(itemView.titleFont, font); 200 | } 201 | 202 | #pragma mark - Button Actions 203 | 204 | - (void)test_ButtonActionShouldCallHandler { 205 | 206 | XCTestExpectation *expectation = [self expectationWithDescription:@"Should Call Handler On Dismiss"]; 207 | 208 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithoutTextColor isHeadlineItem:NO ownerSheet:nil]; 209 | 210 | [itemView.item setActionHandler:^(BIPActionSheet *actionSheet){ 211 | 212 | [expectation fulfill]; 213 | }]; 214 | 215 | [itemView buttonTapped:nil]; 216 | 217 | [self waitForExpectationsWithTimeout:kBIPActionSheetHideAnimationDuration handler:^(NSError *error) { 218 | if (error) { 219 | NSLog(@"Timeout Error: %@", error); 220 | } 221 | }]; 222 | } 223 | 224 | #pragma mark - Test Copy 225 | 226 | - (void)test_CopyWithZone { 227 | 228 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:_itemWithTextColor isHeadlineItem:YES ownerSheet:nil]; 229 | BIPActionSheetItem *copyItemView = [itemView copy]; 230 | XCTAssertNotNil(copyItemView); 231 | } 232 | 233 | #pragma mark - Tear Down 234 | 235 | - (void)tearDown { 236 | // Put teardown code here. This method is called after the invocation of each test method in the class. 237 | 238 | _itemWithTextColor = nil; 239 | _itemWithoutTextColor = nil; 240 | _firstItemTitleColor = nil; 241 | _firstItemTitle = nil; 242 | _secondItemTitle = nil; 243 | 244 | [super tearDown]; 245 | } 246 | 247 | #pragma mark - Performance 248 | 249 | - (void)testPerformanceExample { 250 | // This is an example of a performance test case. 251 | [self measureBlock:^{ 252 | // Put the code you want to measure the time of here. 253 | }]; 254 | } 255 | 256 | @end 257 | -------------------------------------------------------------------------------- /Source/BIPActionSheet.m: -------------------------------------------------------------------------------- 1 | // 2 | // BIPActionSheet.m 3 | // BIP 4 | // 5 | // Created by AHMET KAZIM GUNAY on 02/06/2017. 6 | // Copyright © 2017 Turkcell. All rights reserved. 7 | // 8 | 9 | #import "BIPActionSheet.h" 10 | #import "BIPActionSheetItem.h" 11 | #import "BIPActionSheetStack.h" 12 | #import "BIPActionSheetItemView.h" 13 | #import "BIPActionSheetConstants.h" 14 | #import "UIWindow+BIPActionSheet.h" 15 | #import "UIImage+BIPActionSheet.h" 16 | #import "UIView+BIPActionSheet.h" 17 | #import "UIApplication+BIPActionSheet.h" 18 | 19 | @interface BIPActionSheet() 20 | 21 | @property (nonatomic, strong) NSMutableArray *items; 22 | @property (nonatomic, strong) NSMutableArray *itemViews; 23 | 24 | @property (nonatomic, strong) UIWindow *actionSheetWindow; 25 | @property (nonatomic, strong) UIWindow *applicationWindow; 26 | @property (nonatomic, strong) UIScrollView *scrollView; 27 | @property (nonatomic, strong) UIView *containerView; 28 | @property (nonatomic, strong) UIView *backgroundView; 29 | 30 | @property (nonatomic, copy) NSString *cancelButtonTitle; 31 | 32 | @property (nonatomic, assign, readwrite, getter=isVisible) BOOL visible; 33 | 34 | @property (nonatomic, assign) CGFloat containerYOrigin; 35 | @property (nonatomic, assign) CGFloat cancelYOrigin; 36 | @property (nonatomic, assign) CGFloat contentViewHeight; 37 | @property (nonatomic, assign) CGFloat contentViewWidth; 38 | @property (nonatomic, assign) CGFloat paddingOffset; 39 | 40 | @property (nonatomic, copy) void (^inlineCancelHandler)(void); 41 | 42 | @property (nonatomic, strong) BIPActionSheetItemView *titleItemView; 43 | 44 | @property (nonatomic, assign) BOOL isStatusBarHiddenForRootViewController; 45 | 46 | @end 47 | 48 | @implementation BIPActionSheet 49 | 50 | static inline bool bip_isPad() { 51 | 52 | return ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad); 53 | } 54 | 55 | #pragma mark - Initialize 56 | 57 | - (instancetype)initWithTitle:(NSString *)title 58 | items:(NSArray *)items 59 | cancelButtonTitle:(NSString *)cancelButtonTitle 60 | cancelHandler:(void (^)(void))cancelHandler 61 | { 62 | if (self = [[BIPActionSheet alloc] initWithItems:items cancelButtonTitle:cancelButtonTitle cancelHandler:cancelHandler]) 63 | { 64 | if (title.length) 65 | { 66 | BIPActionSheetItem *titleItem = [BIPActionSheetItem itemWithTitle:title image:nil actionHandler:nil]; 67 | self.titleItemView = [[BIPActionSheetItemView alloc] initWithItem:titleItem isHeadlineItem:YES ownerSheet:self]; 68 | [self.containerView addSubview:self.titleItemView]; 69 | } 70 | } 71 | return self; 72 | } 73 | 74 | - (instancetype)initWithItems:(NSArray *)items 75 | cancelButtonTitle:(NSString *)cancelButtonTitle 76 | cancelHandler:(void (^)(void))cancelHandler 77 | { 78 | if (self = [super init]) 79 | { 80 | self.inlineCancelHandler = cancelHandler; 81 | self.items = [NSMutableArray arrayWithArray:items]; 82 | self.itemViews = [NSMutableArray arrayWithCapacity:items.count]; 83 | self.cancelButtonTitle = cancelButtonTitle; 84 | self.applicationWindow = [UIWindow bip_windowWithLevel:UIWindowLevelNormal]; 85 | 86 | for (BIPActionSheetItem *item in items) 87 | { 88 | BIPActionSheetItemView *itemView = [[BIPActionSheetItemView alloc] initWithItem:item isHeadlineItem:NO ownerSheet:self]; 89 | [self.scrollView addSubview:itemView]; 90 | [self.itemViews addObject:itemView]; 91 | } 92 | 93 | [self.containerView addSubview:self.scrollView]; 94 | [self.view addSubview:self.backgroundView]; 95 | [self.view addSubview:self.containerView]; 96 | 97 | if (!bip_isPad()) 98 | { 99 | [self.view addSubview:self.cancelButton]; 100 | } 101 | 102 | UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)]; 103 | [self.backgroundView addGestureRecognizer:tgr]; 104 | } 105 | return self; 106 | } 107 | 108 | #pragma mark - Tap Gesture 109 | 110 | - (void)tapped:(UITapGestureRecognizer *)recognizer 111 | { 112 | [self actionSheetCancelButtonTapped:self.cancelButton]; 113 | } 114 | 115 | #pragma mark - Show ActionSheet 116 | 117 | + (instancetype)showActionSheetWithItems:(NSArray *)items 118 | cancelButtonTitle:(NSString *)cancelButtonTitle 119 | cancelHandler:(void(^)(void))cancelHandler; 120 | { 121 | BIPActionSheet *actionSheet = [[BIPActionSheet alloc] initWithItems:items 122 | cancelButtonTitle:cancelButtonTitle 123 | cancelHandler:cancelHandler]; 124 | [actionSheet show]; 125 | 126 | return actionSheet; 127 | } 128 | 129 | + (instancetype)showActionSheetWithTitle:(NSString *)title 130 | items:(NSArray *)items 131 | cancelButtonTitle:(NSString *)cancelButtonTitle 132 | cancelHandler:(void (^)(void))cancelHandler 133 | { 134 | BIPActionSheet *actionSheet = [[BIPActionSheet alloc] initWithTitle:title 135 | items:items 136 | cancelButtonTitle:cancelButtonTitle 137 | cancelHandler:cancelHandler]; 138 | [actionSheet show]; 139 | 140 | return actionSheet; 141 | } 142 | 143 | - (void)show 144 | { 145 | [[BIPActionSheetStack sharedInstance] push:self]; 146 | } 147 | 148 | - (void)showInternal 149 | { 150 | dispatch_async(dispatch_get_main_queue(), ^{ 151 | 152 | self.isStatusBarHiddenForRootViewController = self.applicationWindow.rootViewController.prefersStatusBarHidden; 153 | 154 | [self.actionSheetWindow setRootViewController:self]; 155 | [self.actionSheetWindow makeKeyAndVisible]; 156 | self.visible = YES; 157 | 158 | [UIView animateWithDuration:kBIPActionSheetShowAnimationDuration 159 | animations:^{ 160 | self.backgroundView.alpha = kBIPActionSheetBackgroundAlpha; 161 | } 162 | ]; 163 | 164 | if (!bip_isPad()) 165 | { 166 | [UIView animateWithDuration:kBIPActionSheetShowAnimationDuration 167 | delay:kBIPActionSheetShowAnimationDuration / 2 168 | usingSpringWithDamping:0.6 169 | initialSpringVelocity:0.6 170 | options:UIViewAnimationOptionCurveEaseOut 171 | animations:^{ 172 | self.cancelButton.bip_yOrigin = self.cancelYOrigin; 173 | } 174 | completion:^(BOOL finished) { 175 | //Completion Block 176 | } 177 | ]; 178 | 179 | [UIView animateWithDuration:kBIPActionSheetShowAnimationDuration 180 | delay:0 181 | usingSpringWithDamping:0.6 182 | initialSpringVelocity:0.6 183 | options:UIViewAnimationOptionCurveEaseOut 184 | animations:^{ 185 | self.containerView.bip_yOrigin = self.containerYOrigin; 186 | } 187 | completion:^(BOOL finished) { 188 | //Completion Block 189 | } 190 | ]; 191 | } 192 | else 193 | { 194 | [self showAlertAnimation]; 195 | } 196 | 197 | }); 198 | } 199 | 200 | #pragma mark - Class Methods 201 | 202 | + (BOOL)isAnyActionsheetVisible 203 | { 204 | return [BIPActionSheetStack sharedInstance].actionSheets.count > 0; 205 | } 206 | 207 | + (void)dismissVisibleActionsheet 208 | { 209 | if ([self isAnyActionsheetVisible]) 210 | { 211 | BIPActionSheet *as = [BIPActionSheetStack sharedInstance].actionSheets.lastObject; 212 | [as dismiss]; 213 | } 214 | } 215 | 216 | + (void)dismissAllActionsheets 217 | { 218 | while ([BIPActionSheetStack sharedInstance].actionSheets.count > 0) 219 | { 220 | BIPActionSheet *as = [BIPActionSheetStack sharedInstance].actionSheets.lastObject; 221 | [[BIPActionSheetStack sharedInstance] pop:as showNext:NO]; 222 | } 223 | } 224 | 225 | #pragma mark - Hide ActionSheet 226 | 227 | - (void)dismiss 228 | { 229 | [[BIPActionSheetStack sharedInstance] pop:self]; 230 | } 231 | 232 | - (void)hide 233 | { 234 | dispatch_async(dispatch_get_main_queue(), ^{ 235 | 236 | [UIView animateWithDuration:kBIPActionSheetHideAnimationDuration 237 | delay:0 238 | options:UIViewAnimationOptionCurveEaseInOut 239 | animations:^{ 240 | self.backgroundView.alpha = 0; 241 | self.containerView.bip_yOrigin = self.view.frame.size.height; 242 | self.cancelButton.bip_yOrigin = self.view.frame.size.height; 243 | } 244 | completion:^(BOOL finished) { 245 | self.visible = NO; 246 | [self.actionSheetWindow removeFromSuperview]; 247 | self.actionSheetWindow.rootViewController = nil; 248 | self.actionSheetWindow = nil; 249 | [self.applicationWindow makeKeyAndVisible]; 250 | } 251 | ]; 252 | }); 253 | } 254 | 255 | #pragma mark - Layout 256 | 257 | - (void)viewWillLayoutSubviews 258 | { 259 | [super viewWillLayoutSubviews]; 260 | 261 | self.backgroundView.frame = self.actionSheetWindow.bounds; 262 | 263 | if (!bip_isPad()) 264 | { 265 | [self validateLayoutForiPhone]; 266 | } 267 | else 268 | { 269 | [self validateLayoutForiPad]; 270 | } 271 | } 272 | 273 | - (void)validateLayoutForiPhone 274 | { 275 | // cancelButton Frame 276 | CGRect cancelButtonRect = CGRectZero; 277 | cancelButtonRect.origin.x = self.paddingOffset; 278 | cancelButtonRect.size.width = self.contentViewWidth; 279 | cancelButtonRect.size.height = kBIPActionSheetRowHeight; 280 | 281 | // containerView Frame 282 | CGRect containerViewRect = CGRectZero; 283 | containerViewRect.origin.x = self.paddingOffset; 284 | containerViewRect.size.width = self.contentViewWidth; 285 | containerViewRect.size.height = self.contentViewHeight; 286 | 287 | if (self.isVisible) 288 | { 289 | cancelButtonRect.origin.y = self.cancelYOrigin; 290 | containerViewRect.origin.y = self.containerYOrigin; 291 | } 292 | 293 | self.cancelButton.frame = cancelButtonRect; 294 | self.containerView.frame = containerViewRect; 295 | 296 | self.scrollView.frame = [self isTitleViewExist] ? CGRectMake(0, kBIPActionSheetRowHeight, self.contentViewWidth, self.contentViewHeight - kBIPActionSheetRowHeight) : self.containerView.bounds; 297 | 298 | CGFloat contentHeight = self.items.count * kBIPActionSheetRowHeight; 299 | self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width, contentHeight); 300 | 301 | for (int i = 0; i