├── screen3.png ├── actionsheet.gif ├── screenshot.png ├── screenshot2.gif ├── STModalDemo ├── STModalDemo │ ├── lufei@2x.jpg │ ├── onepiece@2x.jpeg │ ├── share_friend@2x.png │ ├── share_wechat@2x.png │ ├── AlertViewController.h │ ├── ActionSheetViewController.h │ ├── ViewController.h │ ├── AppDelegate.h │ ├── main.m │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── AppDelegate.m │ ├── AlertViewController.m │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── ActionSheetViewController.m │ └── ViewController.m ├── STModalDemo.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── project.pbxproj ├── STModal │ ├── STFrontModal │ │ ├── STFrontModal.h │ │ └── STFrontModal.m │ ├── STModalUtil.h │ ├── STAlertView │ │ ├── STAlertView.h │ │ └── STAlertView.m │ ├── STModal.h │ ├── STActionSheet │ │ ├── STActionSheet.h │ │ └── STActionSheet.m │ └── STModal.m └── STModalDemoTests │ ├── Info.plist │ └── STModalDemoTests.m ├── .gitignore ├── README.md └── LICENSE /screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhenlintie/STModalDemo/HEAD/screen3.png -------------------------------------------------------------------------------- /actionsheet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhenlintie/STModalDemo/HEAD/actionsheet.gif -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhenlintie/STModalDemo/HEAD/screenshot.png -------------------------------------------------------------------------------- /screenshot2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhenlintie/STModalDemo/HEAD/screenshot2.gif -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/lufei@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhenlintie/STModalDemo/HEAD/STModalDemo/STModalDemo/lufei@2x.jpg -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/onepiece@2x.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhenlintie/STModalDemo/HEAD/STModalDemo/STModalDemo/onepiece@2x.jpeg -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/share_friend@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhenlintie/STModalDemo/HEAD/STModalDemo/STModalDemo/share_friend@2x.png -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/share_wechat@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhenlintie/STModalDemo/HEAD/STModalDemo/STModalDemo/share_wechat@2x.png -------------------------------------------------------------------------------- /STModalDemo/STModalDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/AlertViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AlertViewController.h 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/5. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AlertViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/ActionSheetViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ActionSheetViewController.h 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/7. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ActionSheetViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/5. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "STModal.h" 11 | 12 | @interface ViewController : UIViewController 13 | 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/5. 6 | // Copyright (c) 2015年 sTeven. 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 | -------------------------------------------------------------------------------- /STModalDemo/STModal/STFrontModal/STFrontModal.h: -------------------------------------------------------------------------------- 1 | // 2 | // STFrontModal.h 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/8/14. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import "STModal.h" 10 | 11 | @interface STFrontModal : NSObject 12 | 13 | + (void)showView:(UIView *)view animated:(BOOL)animated; 14 | + (void)hide:(BOOL)animated; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/5. 6 | // Copyright (c) 2015年 sTeven. 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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | #Pods/ 27 | -------------------------------------------------------------------------------- /STModalDemo/STModal/STModalUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // Header.h 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/7. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | 10 | #define STModalRGBA(r,g,b,a) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a] 11 | 12 | static inline UIImage *st_imageWithColor(UIColor *color){ 13 | UIGraphicsBeginImageContext(CGSizeMake(1, 1)); 14 | CGContextRef context = UIGraphicsGetCurrentContext(); 15 | CGContextSetFillColorWithColor(context, color.CGColor); 16 | CGContextFillRect(context, CGRectMake(0, 0, 1, 1)); 17 | UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); 18 | UIGraphicsEndImageContext(); 19 | return img; 20 | } -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /STModalDemo/STModalDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.steven.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 自定义弹出视图 2 | 3 | ## STModal介绍 4 | ### 特点 5 | 6 | 1. 只需关心需要弹出的视图 7 | 2. 可自定义动画 8 | 3. 当弹出多个视图时,以栈的方式显示 9 | 10 | ### 更新 11 | 2015-08-14 优化了显示方式,增加了一个显示方式STFrontModal 12 | 13 | ### 用法 14 | 15 | ``` 16 | UIView *contentView = [UIView new]; 17 | ... 18 | 19 | STModal *modal = [STModal modalWithContentView:contentView]; 20 | [modal show:YES]; 21 | 22 | //或者 23 | STModal *modal = [STModal modal]; 24 | [modal showContentView:contentView animated:YES]; 25 | 26 | ``` 27 | ### 截图 28 | 29 | ![](https://github.com/zhenlintie/STModalDemo/raw/master/screenshot.png) 30 | 31 | ## STAlertView 32 | 33 | * 基于`STModal`做的一个提示视图。 34 | * 效果图 35 | 36 | ![](https://github.com/zhenlintie/STModalDemo/raw/master/screenshot2.gif) 37 | 38 | ## STActionSheet 39 | * 基于`STModal`做的一个选择视图。 40 | * 效果图 41 | 42 | ![](https://github.com/zhenlintie/STModalDemo/raw/master/actionsheet.gif) 43 | 44 | ## STFrontModal 45 | ![](https://github.com/zhenlintie/STModalDemo/raw/master/screen3.png) -------------------------------------------------------------------------------- /STModalDemo/STModalDemoTests/STModalDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // STModalDemoTests.m 3 | // STModalDemoTests 4 | // 5 | // Created by zhenlintie on 15/6/5. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface STModalDemoTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation STModalDemoTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 sTeven 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 | 23 | -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.steven.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /STModalDemo/STModal/STAlertView/STAlertView.h: -------------------------------------------------------------------------------- 1 | // 2 | // STAlertView.h 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/5. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface STAlertView : UIView 12 | 13 | /** 14 | * 初始化一个视图 15 | */ 16 | - (instancetype)initWithTitle:(NSString *)title 17 | image:(UIImage *)image 18 | message:(NSString *)message 19 | buttonTitles:(NSArray *)buttonTitles; 20 | 21 | /** 22 | * 显示/隐藏 23 | */ 24 | - (void)show:(BOOL)animated; 25 | - (void)hide:(BOOL)animated; 26 | 27 | /** 28 | * 事件回调 29 | */ 30 | @property (strong, nonatomic) void (^actionHandler)(NSInteger index); 31 | /** 32 | * 显示隐藏后回调 33 | */ 34 | @property (strong, nonatomic) void (^didShowHandler)(); 35 | @property (strong, nonatomic) void (^didHideHandler)(); 36 | 37 | @property (copy, nonatomic, readonly) NSString *title; 38 | @property (copy, nonatomic, readonly) NSString *message; 39 | @property (copy, nonatomic, readonly) UIImage *image; 40 | @property (copy, nonatomic, readonly) NSArray *buttonTitles; 41 | 42 | @property (assign, nonatomic, readonly) BOOL onShow; 43 | 44 | // 点击外部,是否隐藏,默认NO 45 | @property (assign, nonatomic) BOOL hideWhenTapOutside; 46 | 47 | @end 48 | 49 | @interface STAlertView (Show) 50 | 51 | + (instancetype)showTitle:(NSString *)title 52 | image:(UIImage *)image 53 | message:(NSString *)message 54 | buttonTitles:(NSArray *)buttonTitles 55 | handler:(void(^)(NSInteger index))handler; 56 | 57 | + (instancetype)showTitle:(NSString *)title message:(NSString *)message hideDelay:(CGFloat)delay; 58 | + (instancetype)showTitle:(NSString *)title message:(NSString *)message; 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/5. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // 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. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // 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. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // 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. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/AlertViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AlertViewController.m 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/5. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import "AlertViewController.h" 10 | #import "STAlertView.h" 11 | 12 | @interface AlertViewController () 13 | 14 | @end 15 | 16 | @implementation AlertViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view. 21 | } 22 | 23 | - (IBAction)tip:(id)sender { 24 | NSArray *buttons = nil; 25 | // buttons = @[@"查看更多",@"稍后提醒",@"不看了"]; 26 | STAlertView *alert = [[STAlertView alloc] initWithTitle:@"海贼王" 27 | image:[UIImage imageNamed:@"onepiece.jpeg"] 28 | message:@"传奇海贼哥尔·D·罗杰在临死前曾留下关于其毕生的财富“One Piece”的消息,由此引得群雄并起,众海盗们为了这笔传说中的巨额财富展开争夺,各种势力、政权不断交替,整个世界进入了动荡混乱的“大海贼时代”。\n生长在东海某小村庄的路飞受到海贼香克斯的精神指引,决定成为一名出色的海盗。为了达成这个目标,并找到万众瞩目的One Piece,路飞踏上艰苦的旅程。一路上他遇到了无数磨难,也结识了索隆、娜美、乌索普、香吉、罗宾等一众性格各异的好友。他们携手一同展开充满传奇色彩的大冒险。" 29 | buttonTitles:buttons]; 30 | 31 | alert.hideWhenTapOutside = YES; 32 | [alert setDidShowHandler:^{ 33 | NSLog(@"显示了"); 34 | }]; 35 | [alert setDidHideHandler:^{ 36 | NSLog(@"消失了"); 37 | }]; 38 | [alert setActionHandler:^(NSInteger index) { 39 | switch (index) { 40 | case 0: 41 | { 42 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://baike.baidu.com/link?url=puPuzaUO9pAbfCUuJygbsxhrO6ZCXaUeatthEUEcJHRkGskVaciOLfQ1JK_PTtIiZntrID_VtO5TQxtsd1TnrEUJF1UoPGTjXjGk4-swLnWE1GN2J6n0TS81-7bKdQiFpRX1FihvRNcDOCifv7WnldMKoX9MbtmbCvLUw4_ui-BCURimyIOuMXToghtTwA2P"]]; 43 | break; 44 | } 45 | default: 46 | break; 47 | } 48 | }]; 49 | [alert show:YES]; 50 | } 51 | 52 | - (IBAction)notif:(id)sender { 53 | NSString *title = @"通知"; 54 | NSString *message = @"您的手机已经欠费了,速交!"; 55 | //// [STAlertView showTitle:nil message:message]; 56 | //// [STAlertView showTitle:title message:message hideDelay:2]; 57 | //// [STAlertView showTitle:title message:nil]; 58 | [STAlertView showTitle:title 59 | image:nil 60 | message:message 61 | buttonTitles:@[@"这就去",@"就不交"] 62 | handler:^(NSInteger index) { 63 | 64 | }]; 65 | 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /STModalDemo/STModal/STModal.h: -------------------------------------------------------------------------------- 1 | // 2 | // STModal.h 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/5. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * @discussion 主视图显示位置 13 | */ 14 | typedef NS_ENUM(NSUInteger, STModelPositionMode) { 15 | STModelPositionCenter, 16 | STModelPositionCenterTop, 17 | STModelPositionCenterBottom, 18 | STModelPositionCustom 19 | }; 20 | 21 | /** 22 | * @discussion 执行的动画块 23 | * @return 动画所需时间 24 | */ 25 | typedef CGFloat (^st_modal_animation)(); 26 | 27 | /** 28 | * @discussion 回调 29 | */ 30 | typedef void (^st_modal_block)(); 31 | 32 | /** 33 | * @discussion 弹出视图控制器 34 | */ 35 | @interface STModal : NSObject 36 | 37 | + (instancetype)modal; 38 | + (instancetype)modalWithContentView:(UIView *)contentView; 39 | 40 | /** 41 | * @discussion 所显示的主视图 42 | */ 43 | @property (strong, nonatomic, readonly) UIView *contentView; 44 | 45 | /** 46 | * @discussion contentView位置,默认STModelPositionCenter 47 | */ 48 | @property (assign, nonatomic) STModelPositionMode positionMode; 49 | 50 | /** 51 | * @discussion 自定义位置,只有当position为STModelPositionCustom时生效 52 | */ 53 | @property (assign, nonatomic) CGPoint position; 54 | 55 | /** 56 | * @discussion 点击contentView外的区域是否执行hide,默认为NO 57 | */ 58 | @property (assign, nonatomic) BOOL hideWhenTouchOutside; 59 | 60 | /** 61 | * @discussion 当touchOutDismiss为YES时,是否发生动画,默认是YES 62 | */ 63 | @property (assign, nonatomic) BOOL animatedHideWhenTouchOutside; 64 | 65 | /** 66 | * @discussion 背景是否加蒙版,默认为YES 67 | */ 68 | @property (assign, nonatomic) BOOL dimBackgroundWhenShow; 69 | 70 | /** 71 | * @discussion 自定义背景蒙版颜色,默认为nil 72 | */ 73 | @property (strong, nonatomic) UIColor *dimBackgroundColor; 74 | 75 | 76 | //// 当不设置执行动画时,且animated为YES,会默认执行一个过渡动画 77 | /** 78 | * @discussion 显示时的动画 79 | */ 80 | @property (strong, nonatomic) st_modal_animation showAnimation; 81 | 82 | /** 83 | * @discussion 隐藏时的动画 84 | */ 85 | @property (strong, nonatomic) st_modal_animation hideAnimation; 86 | 87 | /** 88 | * @discussion 显示后/隐藏后回调 89 | */ 90 | @property (strong, nonatomic) st_modal_block didShowHandler; 91 | @property (strong, nonatomic) st_modal_block didHideHandler; 92 | 93 | /** 94 | * @discussion 显示 95 | */ 96 | - (void)show:(BOOL)animated; 97 | 98 | /** 99 | * @discussion 显示指定的contentView,若之前已设置,则替换掉。 100 | */ 101 | - (void)showContentView:(UIView *)contentView animated:(BOOL)animated; 102 | 103 | /** 104 | * @discussion 是否已经显示 105 | */ 106 | @property (assign, readonly, nonatomic) BOOL onShow; 107 | 108 | /** 109 | * @discussion 隐藏 110 | */ 111 | - (void)hide:(BOOL)animated; 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /STModalDemo/STModal/STFrontModal/STFrontModal.m: -------------------------------------------------------------------------------- 1 | // 2 | // STFrontModal.m 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/8/14. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import "STFrontModal.h" 10 | 11 | @interface STModal (STFrontModal) 12 | 13 | @property (strong, nonatomic) UIView *containerView; 14 | 15 | @end 16 | 17 | @interface STFrontModal () 18 | 19 | @property (strong, nonatomic) STModal *modal; 20 | 21 | @end 22 | 23 | @implementation STFrontModal 24 | 25 | + (instancetype)sharedFrontModal{ 26 | static STFrontModal *_shared = nil; 27 | static dispatch_once_t onceToken; 28 | dispatch_once(&onceToken, ^{ 29 | _shared = [[STFrontModal alloc] _init]; 30 | }); 31 | return _shared; 32 | } 33 | 34 | - (instancetype)_init{ 35 | if (self = [super init]){ 36 | _modal = [STModal new]; 37 | _modal.hideWhenTouchOutside = YES; 38 | _modal.positionMode = STModelPositionCenterBottom; 39 | _modal.showAnimation = [self showAnimation]; 40 | _modal.hideAnimation = [self hideAnimation]; 41 | } 42 | return self; 43 | } 44 | 45 | - (instancetype)init{ 46 | return [[self class] sharedFrontModal]; 47 | } 48 | 49 | + (void)showView:(UIView *)view animated:(BOOL)animated{ 50 | [[self sharedFrontModal] showView:view animated:animated]; 51 | } 52 | 53 | + (void)hide:(BOOL)animated{ 54 | [[self sharedFrontModal] hide:animated]; 55 | } 56 | 57 | - (void)showView:(UIView *)view animated:(BOOL)animated{ 58 | [_modal showContentView:view animated:animated]; 59 | } 60 | 61 | - (void)hide:(BOOL)animated{ 62 | [_modal hide:animated]; 63 | } 64 | 65 | - (st_modal_animation)showAnimation{ 66 | return ^CGFloat(){ 67 | _modal.contentView.transform = CGAffineTransformMakeTranslation(0.0f, CGRectGetHeight(_modal.contentView.frame)); 68 | 69 | CGFloat d = 0.35; 70 | [UIView animateWithDuration:d 71 | delay:0 72 | usingSpringWithDamping:1 73 | initialSpringVelocity:0 74 | options:UIViewAnimationOptionCurveEaseOut 75 | animations:^{ 76 | _modal.contentView.transform = CGAffineTransformIdentity; 77 | [self keyWindow].transform = CGAffineTransformMakeScale(0.85, 0.85); 78 | } 79 | completion:nil]; 80 | return d; 81 | }; 82 | } 83 | 84 | - (st_modal_animation)hideAnimation{ 85 | return ^CGFloat(){ 86 | CGFloat d = 0.3; 87 | [UIView animateWithDuration:d 88 | delay:0 89 | usingSpringWithDamping:1 90 | initialSpringVelocity:0 91 | options:UIViewAnimationOptionCurveEaseInOut 92 | animations:^{ 93 | _modal.contentView.transform = CGAffineTransformMakeTranslation(0.0f, CGRectGetHeight(_modal.contentView.frame)+5); 94 | [self keyWindow].transform = CGAffineTransformIdentity; 95 | } 96 | completion:nil]; 97 | return d; 98 | }; 99 | } 100 | 101 | - (UIWindow *)keyWindow{ 102 | return [UIApplication sharedApplication].keyWindow; 103 | } 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/ActionSheetViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ActionSheetViewController.m 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/7. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import "ActionSheetViewController.h" 10 | #import "STActionSheet.h" 11 | 12 | @interface ActionSheetViewController () 13 | 14 | @end 15 | 16 | @implementation ActionSheetViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | 21 | } 22 | 23 | - (IBAction)wxShare:(id)sender { 24 | NSArray *items = @[[STActionItem itemWithTitle:@"好友" icon:[UIImage imageNamed:@"share_friend"]], 25 | [STActionItem itemWithTitle:@"朋友圈" icon:[UIImage imageNamed:@"share_wechat"]]]; 26 | STActionSheet *actionSheet = [[STActionSheet alloc] initWithTitle:@"分享到微信" 27 | cancelItem:[STActionItem itemWithTitle:@"取消"] 28 | items:items]; 29 | [actionSheet show:YES]; 30 | } 31 | - (IBAction)deleteClicked:(id)sender { 32 | 33 | [STActionSheet showTitle:@"确定删除?\n删除后将无法恢复数据" 34 | items:@[[STActionItem desdructiveItem:@"删除"]] 35 | cancel:[STActionItem itemWithTitle:@"取消"] 36 | actionHandler:^(STActionItem *item, NSInteger index) { 37 | NSLog(@"删除了..."); 38 | } 39 | cancelHandler:^(STActionItem *cancelItem) { 40 | NSLog(@"取消了"); 41 | }]; 42 | } 43 | - (IBAction)persons:(id)sender { 44 | NSDictionary *att = @{NSFontAttributeName:[UIFont boldSystemFontOfSize:16], 45 | NSForegroundColorAttributeName:[UIColor colorWithRed:0.8 green:0.8 blue:0.95 alpha:0.9]}; 46 | STActionSheet *actionSheet = [[STActionSheet alloc] initWithTitle:@"传奇海贼哥尔·D·罗杰在临死前曾留下关于其毕生的财富“One Piece”的消息,由此引得群雄并起,众海盗们为了这笔传说中的巨额财富展开争夺,各种势力、政权不断交替,整个世界进入了动荡混乱的“大海贼时代”。\n生长在东海某小村庄的路飞受到海贼香克斯的精神指引,决定成为一名出色的海盗。为了达成这个目标,并找到万众瞩目的One Piece,路飞踏上艰苦的旅程。一路上他遇到了无数磨难,也结识了索隆、娜美、乌索普、香吉、罗宾等一众性格各异的好友。他们携手一同展开充满传奇色彩的大冒险。\n\n主要人物:" 47 | cancelTitle:nil 48 | otherButtonTitles:@[@"蒙奇•D•路飞", 49 | @"罗罗诺亚•索隆", 50 | @"娜美", 51 | @"乌索普", 52 | @"山治", 53 | @"托尼托尼•乔巴", 54 | @"妮可·罗宾", 55 | @"弗兰奇", 56 | @"布鲁克", 57 | @"哥尔·D·罗杰", 58 | @"西尔巴兹·雷利", 59 | @"爱德华·纽盖特(白胡子)", 60 | @"香克斯(红发)", 61 | @"马歇尔·D·蒂奇(黑胡子)", 62 | @"乔拉可尔·米霍克(鹰眼)"] 63 | attribute:att]; 64 | actionSheet.hideWhenTouchOutside = YES; 65 | [actionSheet show:YES]; 66 | } 67 | 68 | - (IBAction)createBuilding:(id)sender { 69 | [STActionSheet showTitle:nil 70 | otherButtonTitles:@[@"楼主",@"1楼",@"2楼",@"3楼",@"4楼",@"5楼",@"6楼",@"7楼",] 71 | cancel:nil 72 | attribute:nil]; 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /STModalDemo/STModal/STActionSheet/STActionSheet.h: -------------------------------------------------------------------------------- 1 | // 2 | // STActionSheet.h 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/6. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class STActionItem; 12 | typedef void(^st_sheet_action_handler)(STActionItem *item, NSInteger index); 13 | 14 | @interface STActionSheet : UIView 15 | 16 | /** 17 | * `cancelTitle`与`otherButtonTitles`的属性,当`attribute`存在,则用`attribute` 18 | * 否则用默认的 19 | */ 20 | - (instancetype)initWithTitle:(NSString *)title 21 | cancelTitle:(NSString *)cancelTitle 22 | otherButtonTitles:(NSArray *)otherButtonTitles 23 | attribute:(NSDictionary *)attribute; 24 | 25 | // 此初始化忽略`itemAttribute` 26 | - (instancetype)initWithTitle:(NSString *)title cancelItem:(STActionItem *)cancelItem items:(NSArray *)items; 27 | 28 | /** 29 | * 显示/隐藏 30 | */ 31 | - (void)show:(BOOL)animated; 32 | - (void)hide:(BOOL)animated; 33 | 34 | /** 35 | * 点击外部是否隐藏,默认为YES 36 | */ 37 | @property (nonatomic, assign) BOOL hideWhenTouchOutside; 38 | 39 | @property (nonatomic, readonly) NSString *title; 40 | @property (nonatomic, readonly) STActionItem *cancelItem; 41 | @property (nonatomic, readonly) NSArray *items; 42 | 43 | /** 44 | * 事件回调 45 | */ 46 | @property (strong, nonatomic) st_sheet_action_handler actionHandler; 47 | @property (strong, nonatomic) void(^didCancelHandler)(STActionItem *cancelItem); 48 | @property (strong, nonatomic) void (^didShowHandler)(); 49 | @property (strong, nonatomic) void (^didHideHandler)(); 50 | 51 | @end 52 | 53 | 54 | @interface STActionSheet (Show) 55 | 56 | + (instancetype)showTitle:(NSString *)title 57 | items:(NSArray *)items 58 | cancel:(STActionItem *)cancelItem; 59 | + (instancetype)showItems:(NSArray *)items 60 | actionHandler:(st_sheet_action_handler)actionHandler; 61 | + (instancetype)showTitle:(NSString *)title 62 | items:(NSArray *)items 63 | actionHandler:(st_sheet_action_handler)actionHandler; 64 | + (instancetype)showTitle:(NSString *)title 65 | items:(NSArray *)items 66 | cancel:(STActionItem *)cancelItem 67 | actionHandler:(st_sheet_action_handler)actionHandler 68 | cancelHandler:(void(^)(STActionItem *cancelItem))canceledHandler; 69 | 70 | + (instancetype)showTitle:(NSString *)title 71 | otherButtonTitles:(NSArray *)otherButtonTitles 72 | cancel:(NSString *)cancelTitle 73 | attribute:(NSDictionary *)attribute; 74 | + (instancetype)showTitle:(NSString *)title 75 | otherButtonTitles:(NSArray *)otherButtonTitles 76 | cancel:(NSString *)cancelTitle 77 | attribute:(NSDictionary *)attribute 78 | actionHandler:(st_sheet_action_handler)actionHandler 79 | cancelHandler:(void(^)(STActionItem *cancelItem))canceledHandler; 80 | 81 | @end 82 | 83 | 84 | /** 85 | * STActionSheet的元素 86 | */ 87 | @interface STActionItem : NSObject 88 | 89 | /** 90 | * 快速生成 91 | */ 92 | + (instancetype)itemWithTitle:(NSString *)title; 93 | + (instancetype)itemWithAttributedTitle:(NSAttributedString *)attributedTitle; 94 | + (instancetype)itemWithTitle:(NSString *)title icon:(UIImage *)icon; 95 | + (instancetype)itemWithAttributedTitle:(NSAttributedString *)attributedTitle icon:(UIImage *)icon; 96 | + (instancetype)itemWithTitle:(NSString *)title attribute:(NSDictionary *)attribute; 97 | 98 | /** 99 | * 初始化 100 | */ 101 | - (instancetype)initWithTitle:(NSString *)title icon:(UIImage *)icon; 102 | - (instancetype)initWithAttributedTitle:(NSAttributedString *)attributedTitle icon:(UIImage *)icon; 103 | 104 | @property (copy, nonatomic, readonly) NSString *title; 105 | @property (copy, nonatomic, readonly) UIImage *icon; 106 | 107 | @property (copy, nonatomic, readonly) NSAttributedString *attributedTitle; 108 | 109 | @end 110 | 111 | @interface STActionItem (Common) 112 | 113 | /** 114 | * 提醒格式的item 115 | */ 116 | + (instancetype)desdructiveItem:(NSString *)title; 117 | + (instancetype)desdructiveItem:(NSString *)title icon:(UIImage *)icon; 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/5. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "STModalUtil.h" 11 | #import "STFrontModal.h" 12 | 13 | @interface ViewController () 14 | 16 | 17 | @property (strong, nonatomic) UILabel *aboutLabel; 18 | 19 | @end 20 | 21 | @implementation ViewController{ 22 | STModal *_aboutModal; 23 | STModal *_storyModal; 24 | STModal *_customModal; 25 | UITableView *_settingTableView; 26 | } 27 | 28 | - (void)viewDidLoad { 29 | [super viewDidLoad]; 30 | [self test1]; 31 | } 32 | 33 | - (void)test1{ 34 | [self addButton:120 tag:0 title:@"关于"]; 35 | [self addButton:180 tag:1 title:@"故事"]; 36 | [self addButton:260 tag:2 title:@"随机位置"]; 37 | [self addButton:320 tag:3 title:@"设置"]; 38 | } 39 | 40 | - (void)addButton:(CGFloat)top tag:(NSInteger)tag title:(NSString *)title{ 41 | UIButton *tap = [UIButton buttonWithType:UIButtonTypeSystem]; 42 | tap.frame = CGRectMake(120, top, 80, 45); 43 | tap.titleLabel.font = [UIFont boldSystemFontOfSize:20]; 44 | [tap setTitle:title forState:UIControlStateNormal]; 45 | tap.tag = tag; 46 | tap.backgroundColor = [UIColor lightGrayColor]; 47 | [tap addTarget:self action:@selector(tapped:) forControlEvents:UIControlEventTouchUpInside]; 48 | [self.view addSubview:tap]; 49 | } 50 | 51 | - (void)tapped:(UIButton *)button{ 52 | switch (button.tag) { 53 | case 0:{ 54 | [self showAboutModal]; 55 | break; 56 | } 57 | case 1:{ 58 | [self showStoryModal]; 59 | break; 60 | } 61 | case 2:{ 62 | [self showCustomModal]; 63 | break; 64 | } 65 | case 3:{ 66 | [self showSetting]; 67 | break; 68 | } 69 | default: 70 | break; 71 | } 72 | } 73 | 74 | - (void)showAboutModal{ 75 | if (!_aboutModal){ 76 | _aboutModal = [self createModal:nil]; 77 | } 78 | if (!_aboutLabel){ 79 | _aboutLabel = [self createLabel]; 80 | _aboutLabel.userInteractionEnabled = YES; 81 | // _aboutLabel.backgroundColor = [UIColor blackColor]; 82 | _aboutLabel.text = @"这是一个自定义的小标签\n\n\n"; 83 | UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 20, 80, 40)]; 84 | btn.backgroundColor = [UIColor colorWithRed:0.5 green:0.3 blue:0.3 alpha:0.3]; 85 | [btn setTitle:@"看故事" forState:UIControlStateNormal]; 86 | [btn addTarget:self action:@selector(showStoryModal) forControlEvents:UIControlEventTouchUpInside]; 87 | [_aboutLabel addSubview:btn]; 88 | [_aboutLabel sizeToFit]; 89 | } 90 | [_aboutModal showContentView:_aboutLabel animated:YES]; 91 | } 92 | 93 | - (void)showStoryModal{ 94 | if (!_storyModal){ 95 | UILabel *label = [self createLabel]; 96 | label.backgroundColor = [UIColor colorWithRed:0.3 green:0.1 blue:0.5 alpha:.1]; 97 | label.textColor = [UIColor colorWithWhite:0 alpha:1]; 98 | label.text = @"老师把小明叫到了教室外。\n老师:“你的作业做的越来越差了,这是怎么回事?”\n小明:“老师,等我找找原因,下午告诉你好吗?”\n老师:“好吧!”\n下午,老师又把小明叫到了教室外。\n老师:“找到原因了吗?”\n小明:“找到了,我爷爷说作业越来越难,他也没办法。”\n老师:“滚出去!”\n\n\n"; 99 | CGSize s = [label sizeThatFits:CGSizeMake(300, 1000)]; 100 | label.frame = CGRectMake(0, 0, s.width, s.height); 101 | label.userInteractionEnabled = YES; 102 | 103 | UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, s.height-40, 190, 40)]; 104 | [btn setBackgroundImage:st_imageWithColor([UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:1]) forState:UIControlStateNormal]; 105 | [btn setTitle:@"显示自定义" forState:UIControlStateNormal]; 106 | [btn addTarget:self action:@selector(showCustomModal) forControlEvents:UIControlEventTouchUpInside]; 107 | [label addSubview:btn]; 108 | 109 | _storyModal = [self createModal:label]; 110 | _storyModal.dimBackgroundColor = [[UIColor yellowColor] colorWithAlphaComponent:0.2]; 111 | } 112 | [_storyModal show:YES]; 113 | } 114 | 115 | - (void)showCustomModal{ 116 | if (!_customModal){ 117 | UILabel *customLabel = [self createLabel]; 118 | customLabel.text = @"*****************\n自定义的位置\n*****************"; 119 | customLabel.textColor = [UIColor blackColor]; 120 | [customLabel sizeToFit]; 121 | _customModal = [STModal modalWithContentView:customLabel]; 122 | _customModal.positionMode = STModelPositionCustom; 123 | _customModal.dimBackgroundColor = [[UIColor purpleColor] colorWithAlphaComponent:.1]; 124 | _customModal.hideWhenTouchOutside = YES; 125 | } 126 | _customModal.position = CGPointMake(110+random()%100, 300+random()%200); 127 | [_customModal show:YES]; 128 | } 129 | 130 | - (void)showSetting{ 131 | if (!_settingTableView){ 132 | _settingTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 300) style:UITableViewStylePlain]; 133 | _settingTableView.delegate = self; 134 | _settingTableView.dataSource = self; 135 | [_settingTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; 136 | _settingTableView.backgroundColor = [UIColor whiteColor]; 137 | _settingTableView.rowHeight = 44; 138 | 139 | UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 300-40, 190, 40)]; 140 | [btn setBackgroundImage:st_imageWithColor([UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:1]) forState:UIControlStateNormal]; 141 | [btn setTitle:@"显示自定义" forState:UIControlStateNormal]; 142 | [btn addTarget:self action:@selector(showCustomModal) forControlEvents:UIControlEventTouchUpInside]; 143 | [_settingTableView addSubview:btn]; 144 | } 145 | [STFrontModal showView:_settingTableView animated:YES]; 146 | } 147 | 148 | - (UILabel *)createLabel{ 149 | UILabel *label = [UILabel new]; 150 | label.numberOfLines = 0; 151 | label.textAlignment = NSTextAlignmentLeft; 152 | label.textColor = [UIColor colorWithWhite:1 alpha:0.85]; 153 | return label; 154 | } 155 | 156 | - (STModal *)createModal:(UIView *)contentView{ 157 | STModal *modal = [STModal modalWithContentView:contentView]; 158 | modal.hideWhenTouchOutside = YES; 159 | return modal; 160 | } 161 | 162 | #pragma mark - tableview delegate / datasource 163 | 164 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 165 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; 166 | cell.textLabel.text = @"点击设置"; 167 | cell.accessoryType = random()%4+1; 168 | return cell; 169 | } 170 | 171 | - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index{ 172 | return 1; 173 | } 174 | 175 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 176 | return 10; 177 | } 178 | 179 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ 180 | [STFrontModal hide:YES]; 181 | } 182 | 183 | @end 184 | -------------------------------------------------------------------------------- /STModalDemo/STModalDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 30 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 71 | 81 | 91 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /STModalDemo/STModal/STModal.m: -------------------------------------------------------------------------------- 1 | // 2 | // STModal.m 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/5. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import "STModal.h" 10 | 11 | /** 12 | * @discussion 公共窗口类 13 | */ 14 | @interface STModalWindow : NSObject 15 | 16 | + (instancetype)sharedModalWindow; 17 | 18 | - (void)showModal:(STModal *)modal animated:(BOOL)animated duration:(CGFloat)duration completion:(void(^)())comletion; 19 | 20 | - (void)hideModal:(STModal *)modal animated:(BOOL)animated duration:(CGFloat)duration completion:(void(^)())comletion; 21 | 22 | @end 23 | 24 | 25 | @interface STModal () 26 | 27 | @property (strong, nonatomic) UIView *containerView; 28 | 29 | @property (strong, nonatomic) UIView *backgroundTapView; 30 | 31 | @property (strong, nonatomic) UITapGestureRecognizer *tap; 32 | 33 | @property (assign, nonatomic) BOOL onShowing; 34 | 35 | @property (assign, nonatomic) BOOL onTop; 36 | 37 | @end 38 | 39 | @implementation STModal 40 | 41 | + (instancetype)modal{ 42 | return [self new]; 43 | } 44 | 45 | + (instancetype)modalWithContentView:(UIView *)contentView{ 46 | STModal *modal = [self modal]; 47 | [modal addModalContentView:contentView]; 48 | return modal;; 49 | } 50 | 51 | - (instancetype)init{ 52 | if (self = [super init]){ 53 | _positionMode = STModelPositionCenter; 54 | _position = CGPointMake(CGRectGetWidth([UIScreen mainScreen].bounds)/2.0, CGRectGetHeight([UIScreen mainScreen].bounds)/2.0); 55 | _onShowing = NO; 56 | _onTop = NO; 57 | _hideWhenTouchOutside = NO; 58 | _animatedHideWhenTouchOutside = YES; 59 | _dimBackgroundWhenShow = YES; 60 | } 61 | return self; 62 | } 63 | 64 | - (void)addModalContentView:(UIView *)contentView{ 65 | _contentView = contentView; 66 | } 67 | 68 | #pragma mark - setter / getter 69 | 70 | - (void)setPositionMode:(STModelPositionMode)positionMode{ 71 | if (_positionMode != positionMode){ 72 | _positionMode = positionMode; 73 | [self updateContentViewPosition]; 74 | } 75 | } 76 | 77 | - (UIView *)containerView{ 78 | if (!_containerView){ 79 | CGSize size = [UIScreen mainScreen].bounds.size; 80 | _containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)]; 81 | _containerView.backgroundColor = [UIColor clearColor]; 82 | 83 | _backgroundTapView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)]; 84 | _backgroundTapView.backgroundColor = [UIColor clearColor]; 85 | [_containerView addSubview:_backgroundTapView]; 86 | 87 | _tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped)]; 88 | _tap.enabled = _hideWhenTouchOutside; 89 | [_backgroundTapView addGestureRecognizer:_tap]; 90 | } 91 | return _containerView; 92 | } 93 | 94 | - (st_modal_animation)showAnimation{ 95 | if (!_showAnimation){ 96 | return ^CGFloat(){ 97 | CGFloat d = 0.3; 98 | _containerView.alpha = 0; 99 | [UIView animateWithDuration:d 100 | animations:^{ 101 | _containerView.alpha = 1; 102 | }]; 103 | return d; 104 | }; 105 | } 106 | return _showAnimation; 107 | } 108 | 109 | - (st_modal_animation)hideAnimation{ 110 | if (!_hideAnimation){ 111 | return ^CGFloat(){ 112 | CGFloat d = 0.3; 113 | [UIView animateWithDuration:d 114 | animations:^{ 115 | _containerView.alpha = 0; 116 | }]; 117 | return d; 118 | }; 119 | } 120 | return _hideAnimation; 121 | } 122 | 123 | - (BOOL)onShow{ 124 | return _onShowing; 125 | } 126 | 127 | - (STModalWindow *)window{ 128 | return [STModalWindow sharedModalWindow]; 129 | } 130 | 131 | #pragma mark - action 132 | 133 | - (void)setHideWhenTouchOutside:(BOOL)hideWhenTouchOutside{ 134 | _hideWhenTouchOutside = hideWhenTouchOutside; 135 | _tap.enabled = _hideWhenTouchOutside; 136 | } 137 | 138 | - (void)tapped{ 139 | if (_hideWhenTouchOutside && !CGRectContainsPoint(_contentView.frame, [_tap locationInView:_containerView])){ 140 | [self hide:_animatedHideWhenTouchOutside]; 141 | } 142 | } 143 | 144 | #pragma mark - for show / hide 145 | 146 | - (void)updateContentViewPosition{ 147 | switch (_positionMode) { 148 | case STModelPositionCenter:{ 149 | _contentView.center = CGPointMake(CGRectGetMidX(_containerView.bounds), CGRectGetMidY(_containerView.bounds)); 150 | break; 151 | } 152 | case STModelPositionCenterTop:{ 153 | _contentView.center = CGPointMake(CGRectGetMidX(_containerView.bounds), CGRectGetHeight(_contentView.bounds)/2.0); 154 | break; 155 | } 156 | case STModelPositionCenterBottom:{ 157 | _contentView.center = CGPointMake(CGRectGetMidX(_containerView.bounds), CGRectGetHeight(_containerView.bounds)-CGRectGetHeight(_contentView.bounds)/2.0); 158 | break; 159 | } 160 | case STModelPositionCustom:{ 161 | _contentView.center = self.position; 162 | } 163 | default: 164 | break; 165 | } 166 | } 167 | 168 | - (void)prepareUIForShow{ 169 | [self.containerView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 170 | [_containerView addSubview:_backgroundTapView]; 171 | [self.containerView addSubview:_contentView]; 172 | [self updateContentViewPosition]; 173 | } 174 | 175 | - (void)showContentView:(UIView *)contentView animated:(BOOL)animated{ 176 | NSAssert(contentView!=nil, @"没有可显示的视图"); 177 | [self addModalContentView:contentView]; 178 | [self show:animated]; 179 | } 180 | 181 | - (void)show:(BOOL)animated{ 182 | NSAssert(_contentView!=nil, @"没有可显示的视图"); 183 | [self prepareUIForShow]; 184 | [self.window showModal:self animated:animated duration:animated?self.showAnimation():0 completion:self.didShowHandler]; 185 | } 186 | 187 | - (void)hide:(BOOL)animated{ 188 | [self.window hideModal:self animated:animated duration:animated?self.hideAnimation():0 completion:self.didHideHandler]; 189 | } 190 | 191 | @end 192 | 193 | 194 | #define STModalWindowDefaultBackgroundColor [UIColor colorWithWhite:0 alpha:0.55] 195 | 196 | @interface STModalWindow () 197 | 198 | @property (strong, nonatomic) NSMutableArray *modalsStack; 199 | 200 | @property (strong, nonatomic) UIWindow *window; 201 | 202 | @property (strong, nonatomic) UIView *dimBackgroundView; 203 | 204 | @property (assign, nonatomic) BOOL shouldDimBackground; 205 | 206 | @end 207 | 208 | @implementation STModalWindow{ 209 | BOOL _onShowing; 210 | } 211 | 212 | + (instancetype)sharedModalWindow{ 213 | static dispatch_once_t onceToken; 214 | static STModalWindow *_sharedModalWindow = nil; 215 | dispatch_once(&onceToken, ^{ 216 | _sharedModalWindow = [[self alloc] init]; 217 | }); 218 | return _sharedModalWindow; 219 | } 220 | 221 | - (instancetype)init{ 222 | if (self = [super init]){ 223 | _modalsStack = [[NSMutableArray alloc] init]; 224 | _window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 225 | _window.windowLevel = UIWindowLevelAlert; 226 | _window.backgroundColor = [UIColor clearColor]; 227 | _window.opaque = NO; 228 | _dimBackgroundView = [[UIView alloc] initWithFrame:_window.bounds]; 229 | [_window addSubview:_dimBackgroundView]; 230 | } 231 | return self; 232 | } 233 | 234 | #pragma mark - public modal operation 235 | 236 | - (void)showModal:(STModal *)modal animated:(BOOL)animated duration:(CGFloat)duration completion:(void(^)())comletion{ 237 | STModal *topModal = [self topModal]; 238 | [self pushModal:modal]; 239 | [self reloadData]; 240 | _window.hidden = NO; 241 | [self transitionFromModal:topModal 242 | toModal:modal 243 | animated:animated 244 | duration:duration 245 | completion:comletion]; 246 | } 247 | 248 | - (void)hideModal:(STModal *)modal animated:(BOOL)animated duration:(CGFloat)duration completion:(void(^)())comletion{ 249 | if ([self hasModal:modal]){ 250 | if ([[self topModal] isEqual:modal]){ 251 | STModal *toModal = (_modalsStack.count>1)?_modalsStack[_modalsStack.count-2]:nil; 252 | [self transitionFromModal:modal 253 | toModal:toModal 254 | animated:animated 255 | duration:duration 256 | completion:^{ 257 | [self popModal:modal]; 258 | [modal.containerView removeFromSuperview]; 259 | 260 | BOOL isNoModal = nil == [self topModal]; 261 | if (isNoModal){ 262 | _window.hidden = YES; 263 | } 264 | else{ 265 | [self reloadData]; 266 | } 267 | if (comletion){ 268 | comletion(); 269 | } 270 | }]; 271 | } 272 | else{ 273 | [self popModal:modal]; 274 | [modal.containerView removeFromSuperview]; 275 | } 276 | } 277 | } 278 | 279 | #pragma mark - show or hide 280 | 281 | - (void)transitionFromModal:(STModal *)fromModal 282 | toModal:(STModal *)toModal 283 | animated:(BOOL)animated 284 | duration:(CGFloat)duration 285 | completion:(void(^)())completion{ 286 | 287 | CGFloat fromA = 0, toA = 0; 288 | UIColor *fromColor = nil, *toColor = nil; 289 | 290 | BOOL(^colosIsEqual)(UIColor *, UIColor *) = ^BOOL(UIColor *color1, UIColor *color2){ 291 | if (color1 && color2){ 292 | CGFloat r1, r2, g1, g2, b1, b2, a1, a2; 293 | [color1 getRed:&r1 green:&g1 blue:&b1 alpha:&a1]; 294 | [color2 getRed:&r2 green:&g2 blue:&b2 alpha:&a2]; 295 | return (r1==r2)&&(g1==g2)&&(b1==b2)&&(a1==a2); 296 | } 297 | else if (!color1 && !color2){ 298 | return YES; 299 | } 300 | return NO; 301 | }; 302 | 303 | if (nil != fromModal){ 304 | fromA = fromModal.dimBackgroundWhenShow?1:0; 305 | fromColor = fromModal.dimBackgroundColor?:STModalWindowDefaultBackgroundColor; 306 | } 307 | 308 | if (nil != toModal){ 309 | toA = toModal.dimBackgroundWhenShow?1:0; 310 | toColor = toModal.dimBackgroundColor?:STModalWindowDefaultBackgroundColor; 311 | } 312 | 313 | _dimBackgroundView.alpha = fromA; 314 | _dimBackgroundView.backgroundColor = fromColor; 315 | if (fromA == toA && colosIsEqual(fromColor, toColor)){ 316 | // 如果没有任何变化UIView动画会立即结束,采用以下方法进行回调 317 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 318 | if (completion){ 319 | completion(); 320 | } 321 | }); 322 | } 323 | else{ 324 | [UIView animateWithDuration:animated?duration:0 325 | animations:^{ 326 | _dimBackgroundView.alpha = toA; 327 | _dimBackgroundView.backgroundColor = toColor; 328 | } 329 | completion:^(BOOL finished) { 330 | if (completion){ 331 | completion(); 332 | } 333 | }]; 334 | } 335 | } 336 | 337 | - (void)reloadData{ 338 | STModal *topModal = [self topModal]; 339 | if (topModal){ 340 | self.shouldDimBackground = topModal.dimBackgroundWhenShow; 341 | _dimBackgroundView.backgroundColor = topModal.dimBackgroundColor?:STModalWindowDefaultBackgroundColor; 342 | 343 | [topModal.containerView removeFromSuperview]; 344 | [_window addSubview:topModal.containerView]; 345 | 346 | [[_modalsStack valueForKey:@"containerView"] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 347 | [(UIView *)obj setHidden:(obj!=topModal.containerView)]; 348 | }]; 349 | } 350 | } 351 | 352 | - (void)setShouldDimBackground:(BOOL)shouldDimBackground{ 353 | _shouldDimBackground = shouldDimBackground; 354 | _dimBackgroundView.alpha = _shouldDimBackground?1:0; 355 | } 356 | 357 | #pragma mark - stack operation 358 | 359 | - (void)pushModal:(STModal *)modal{ 360 | if ([self hasModal:modal]){ 361 | [self popModal:modal]; 362 | } 363 | [_modalsStack addObject:modal]; 364 | } 365 | 366 | - (STModal *)popModal:(STModal *)modal{ 367 | if ([self hasModal:modal]){ 368 | [_modalsStack removeObject:modal]; 369 | } 370 | return [self topModal]; 371 | } 372 | 373 | - (STModal *)topModal{ 374 | if (_modalsStack.count > 0){ 375 | return [_modalsStack lastObject]; 376 | } 377 | return nil; 378 | } 379 | 380 | - (BOOL)hasModal:(STModal *)modal{ 381 | return (nil!=modal)&&([_modalsStack indexOfObject:modal] != NSNotFound); 382 | } 383 | 384 | @end -------------------------------------------------------------------------------- /STModalDemo/STModal/STAlertView/STAlertView.m: -------------------------------------------------------------------------------- 1 | // 2 | // STAlertView.m 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/5. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import "STAlertView.h" 10 | #import "STModal.h" 11 | #import "STModalUtil.h" 12 | 13 | #define kSTAlertWidth 300 14 | #define kSTAlertPaddingV 11 15 | #define kSTAlertPaddingH 18 16 | #define kSTAlertRadius 13 17 | #define kSTAlertButtonHeight 40 18 | 19 | @interface STAlertView () 20 | 21 | @property (strong, nonatomic) UIView *backgroundView; 22 | @property (strong, nonatomic) UIView *containerView; 23 | @property (strong, nonatomic) UIScrollView *scrollView; 24 | @property (strong, nonatomic) UILabel *titleLabel; 25 | @property (strong, nonatomic) UIImageView *imageView; 26 | @property (strong, nonatomic) UILabel *messageLabel; 27 | @property (strong, nonatomic) NSMutableArray *buttons; 28 | @property (strong, nonatomic) NSMutableArray *lines; 29 | 30 | @end 31 | 32 | @implementation STAlertView{ 33 | STModal *_modal; 34 | BOOL _didLayouted; 35 | 36 | CGFloat _scrollBottom; 37 | CGFloat _buttonsHeight; 38 | CGFloat _maxContentWidth; 39 | CGFloat _maxAlertViewHeight; 40 | } 41 | 42 | - (instancetype)initWithTitle:(NSString *)title image:(UIImage *)image message:(NSString *)message buttonTitles:(NSArray *)buttonTitles{ 43 | if (self = [self initWithFrame:CGRectZero]){ 44 | _title = [title copy]; 45 | _image = image; 46 | _message = [message copy]; 47 | _buttonTitles = [NSArray arrayWithArray:buttonTitles]; 48 | } 49 | return self; 50 | } 51 | 52 | - (instancetype)initWithFrame:(CGRect)frame{ 53 | if (self = [super initWithFrame:CGRectMake(0, 0, kSTAlertWidth, 0)]){ 54 | [self loadData]; 55 | [self loadUI]; 56 | } 57 | return self; 58 | } 59 | 60 | - (void)loadData{ 61 | _didLayouted = NO; 62 | _hideWhenTapOutside = NO; 63 | _buttons = [NSMutableArray new]; 64 | _lines = [NSMutableArray new]; 65 | 66 | _modal = [STModal modalWithContentView:self]; 67 | _modal.hideWhenTouchOutside = NO; 68 | _modal.dimBackgroundWhenShow = NO; 69 | _modal.showAnimation = [self showAnimation]; 70 | _modal.hideAnimation = [self hideAnimation]; 71 | } 72 | 73 | - (void)loadUI{ 74 | _backgroundView = [UIView new]; 75 | _backgroundView.backgroundColor = STModalRGBA(120, 125, 130, 1); 76 | 77 | _backgroundView.layer.cornerRadius = kSTAlertRadius; 78 | _backgroundView.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.5].CGColor; 79 | _backgroundView.layer.shadowOffset = CGSizeZero; 80 | _backgroundView.layer.shadowOpacity = 1; 81 | _backgroundView.layer.shadowRadius = kSTAlertRadius; 82 | _backgroundView.layer.borderWidth = 0.5; 83 | _backgroundView.layer.borderColor = STModalRGBA(110, 115, 120, 1).CGColor; 84 | 85 | _containerView = [UIView new]; 86 | _containerView.layer.cornerRadius = kSTAlertRadius; 87 | _containerView.layer.masksToBounds = YES; 88 | 89 | _scrollView = [[UIScrollView alloc] initWithFrame:self.bounds]; 90 | [_containerView addSubview:_scrollView]; 91 | 92 | [self addSubview:_backgroundView]; 93 | [self addSubview:_containerView]; 94 | } 95 | 96 | - (st_modal_animation)showAnimation{ 97 | return ^CGFloat(){ 98 | self.alpha = 0; 99 | CGFloat d1 = 0.2, d2 = 0.15; 100 | self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.4, 0.4); 101 | [UIView animateWithDuration:d1 animations:^{ 102 | self.alpha = 1; 103 | self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.1, 1.1); 104 | } completion:^(BOOL finished) { 105 | [UIView animateWithDuration:d2 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ 106 | self.alpha = 1; 107 | self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1); 108 | } completion:^(BOOL finished2) { 109 | }]; 110 | }]; 111 | return (d1+d2); 112 | }; 113 | } 114 | 115 | - (st_modal_animation)hideAnimation{ 116 | return ^CGFloat(){ 117 | CGFloat d1 = 0.2, d2 = 0.1; 118 | [UIView animateWithDuration:d2 animations:^{ 119 | self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.1, 1.1); 120 | } completion:^(BOOL finished){ 121 | [UIView animateWithDuration:d1 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ 122 | self.alpha = 0; 123 | self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.4, 0.4); 124 | } completion:^(BOOL finished2){ 125 | }]; 126 | }]; 127 | return (d1+d2); 128 | }; 129 | } 130 | 131 | #pragma mark - prepare for show 132 | 133 | - (void)prepareForShow{ 134 | if (_didLayouted){ 135 | return; 136 | } 137 | [self resetViews]; 138 | _scrollBottom = 0; 139 | CGFloat insetY = kSTAlertPaddingV; 140 | _maxContentWidth = kSTAlertWidth-2*kSTAlertPaddingH; 141 | _maxAlertViewHeight = [UIScreen mainScreen].bounds.size.height-50; 142 | [self loadTitle]; 143 | [self loadImage]; 144 | [self loadMessage]; 145 | _buttonsHeight = kSTAlertButtonHeight*((_buttonTitles.count>2||_buttonTitles.count==0)?_buttonTitles.count:1); 146 | self.frame = CGRectMake(0, 0, kSTAlertWidth, MIN(MAX(_scrollBottom+2*insetY+_buttonsHeight, 2*kSTAlertRadius+kSTAlertPaddingV), _maxAlertViewHeight)); 147 | _backgroundView.frame = self.bounds; 148 | _backgroundView.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath; 149 | _containerView.frame = self.bounds; 150 | _scrollView.frame = CGRectMake(0, insetY, CGRectGetWidth(_containerView.frame),MIN(_scrollBottom, CGRectGetHeight(_containerView.frame)-2*insetY-_buttonsHeight)); 151 | _scrollView.contentSize = CGSizeMake(_maxContentWidth, _scrollBottom); 152 | _didLayouted = YES; 153 | 154 | [self loadButtons]; 155 | } 156 | 157 | - (void)resetViews{ 158 | if (_titleLabel){ 159 | [_titleLabel removeFromSuperview]; 160 | _titleLabel.text = @""; 161 | } 162 | if (_imageView){ 163 | [_imageView removeFromSuperview]; 164 | _imageView.image = nil; 165 | } 166 | if (_messageLabel){ 167 | [_messageLabel removeFromSuperview]; 168 | _messageLabel.text = @""; 169 | } 170 | if (_buttons.count > 0){ 171 | [_buttons makeObjectsPerformSelector:@selector(removeFromSuperview)]; 172 | [_buttons removeAllObjects]; 173 | } 174 | if (_lines.count > 0){ 175 | [_lines makeObjectsPerformSelector:@selector(removeFromSuperview)]; 176 | [_lines removeAllObjects]; 177 | } 178 | } 179 | 180 | - (void)addLabel:(UILabel *)label maxHeight:(CGFloat)maxHeight{ 181 | CGSize size = [label sizeThatFits:CGSizeMake(_maxContentWidth, maxHeight)]; 182 | label.frame = CGRectMake(kSTAlertPaddingH, _scrollBottom, _maxContentWidth, size.height); 183 | [_scrollView addSubview:label]; 184 | 185 | _scrollBottom = CGRectGetMaxY(label.frame)+kSTAlertPaddingV; 186 | } 187 | 188 | - (void)addLine:(CGRect)frame toView:(UIView *)view{ 189 | UIView *line = [[UIView alloc] initWithFrame:frame]; 190 | line.backgroundColor = STModalRGBA(160, 170, 160, 0.5); 191 | [view addSubview:line]; 192 | [_lines addObject:line]; 193 | } 194 | 195 | - (void)loadTitle{ 196 | if (!_title){ 197 | return; 198 | } 199 | if (!_titleLabel){ 200 | _titleLabel = [UILabel new]; 201 | _titleLabel.textColor = [UIColor colorWithWhite:1 alpha:0.85]; 202 | _titleLabel.font = [UIFont systemFontOfSize:15]; 203 | _titleLabel.textAlignment = NSTextAlignmentCenter; 204 | _titleLabel.numberOfLines = 0; 205 | } 206 | _titleLabel.text = _title; 207 | [self addLabel:_titleLabel maxHeight:100]; 208 | [self addLine:CGRectMake(kSTAlertPaddingH, _scrollBottom, _maxContentWidth, 0.5) toView:_scrollView]; 209 | _scrollBottom += kSTAlertPaddingV; 210 | } 211 | 212 | - (void)loadImage{ 213 | if (!_image){ 214 | return; 215 | } 216 | if (!_imageView){ 217 | _imageView = [UIImageView new]; 218 | } 219 | _imageView.image = _image; 220 | CGSize size = _image.size; 221 | if (size.width > _maxContentWidth){ 222 | size = CGSizeMake(_maxContentWidth, size.height/size.width*_maxContentWidth); 223 | } 224 | _imageView.frame = CGRectMake(kSTAlertPaddingH+_maxContentWidth/2-size.width/2, _scrollBottom, size.width, size.height); 225 | [_scrollView addSubview:_imageView]; 226 | 227 | _scrollBottom = CGRectGetMaxY(_imageView.frame)+kSTAlertPaddingV; 228 | } 229 | 230 | - (void)loadMessage{ 231 | if (!_message){ 232 | return; 233 | } 234 | if (!_messageLabel){ 235 | _messageLabel = [UILabel new]; 236 | _messageLabel.textColor = STModalRGBA(240, 245, 255, 1); 237 | _messageLabel.font = [UIFont systemFontOfSize:17]; 238 | _messageLabel.textAlignment = NSTextAlignmentCenter; 239 | _messageLabel.numberOfLines = 0; 240 | } 241 | _messageLabel.text = _message; 242 | [self addLabel:_messageLabel maxHeight:100000]; 243 | } 244 | 245 | - (void)loadButtons{ 246 | if (!_buttonTitles || _buttonTitles.count==0){ 247 | return; 248 | } 249 | CGFloat buttonHeight = kSTAlertButtonHeight; 250 | CGFloat buttonWidth = kSTAlertWidth; 251 | CGFloat top = CGRectGetHeight(_containerView.frame)-_buttonsHeight; 252 | [self addLine:CGRectMake(0, top-0.5, buttonWidth, 0.5) toView:_containerView]; 253 | if (1 == _buttonTitles.count){ 254 | [self addButton:CGRectMake(0, top, buttonWidth, buttonHeight) title:[_buttonTitles firstObject] tag:0]; 255 | } 256 | else if (2 == _buttonTitles.count){ 257 | [self addButton:CGRectMake(0, top, buttonWidth/2, buttonHeight) title:[_buttonTitles firstObject] tag:0]; 258 | [self addButton:CGRectMake(0+buttonWidth/2, top, buttonWidth/2, buttonHeight) title:[_buttonTitles lastObject] tag:1]; 259 | [self addLine:CGRectMake(0+buttonWidth/2-.5, top, 0.5, buttonHeight) toView:_containerView]; 260 | } 261 | else{ 262 | 263 | for (NSInteger i=0; i<_buttonTitles.count; i++){ 264 | [self addButton:CGRectMake(0, top, buttonWidth, buttonHeight) title:_buttonTitles[i] tag:i]; 265 | top += buttonHeight; 266 | if (_buttonTitles.count-1!=i){ 267 | [self addLine:CGRectMake(0, top, buttonWidth, 0.5) toView:_containerView]; 268 | } 269 | } 270 | [_lines enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 271 | [_containerView bringSubviewToFront:obj]; 272 | }]; 273 | 274 | } 275 | } 276 | 277 | - (UIButton *)addButton:(CGRect)frame title:(NSString *)title tag:(NSInteger)tag{ 278 | UIButton *button = [[UIButton alloc] initWithFrame:frame]; 279 | [button setTitle:title forState:UIControlStateNormal]; 280 | button.titleLabel.font = [UIFont boldSystemFontOfSize:17]; 281 | button.tag = tag; 282 | [button setTitleColor:STModalRGBA(220, 210, 200, 1) forState:UIControlStateNormal]; 283 | [button setBackgroundImage:st_imageWithColor(STModalRGBA(135, 140, 145, 0.65)) forState:UIControlStateNormal]; 284 | [button setBackgroundImage:st_imageWithColor(STModalRGBA(135, 140, 145, 0.45)) forState:UIControlStateHighlighted]; 285 | [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside]; 286 | [_containerView addSubview:button]; 287 | [_buttons addObject:button]; 288 | return button; 289 | } 290 | 291 | - (void)buttonClicked:(UIButton *)button{ 292 | [self hide:YES]; 293 | if (self.actionHandler){ 294 | self.actionHandler(button.tag); 295 | } 296 | } 297 | 298 | #pragma mark - show / hide 299 | 300 | - (void)show:(BOOL)animated{ 301 | [self prepareForShow]; 302 | _modal.hideWhenTouchOutside = self.hideWhenTapOutside; 303 | _modal.didShowHandler = self.didShowHandler; 304 | _modal.didHideHandler = self.didHideHandler; 305 | [_modal show:animated]; 306 | } 307 | 308 | - (void)hide:(BOOL)animated{ 309 | [_modal hide:animated]; 310 | } 311 | 312 | - (BOOL)onShow{ 313 | return _modal.onShow; 314 | } 315 | 316 | @end 317 | 318 | @implementation STAlertView (Show) 319 | 320 | + (instancetype)showTitle:(NSString *)title 321 | image:(UIImage *)image 322 | message:(NSString *)message 323 | buttonTitles:(NSArray *)buttonTitles 324 | handler:(void (^)(NSInteger))handler{ 325 | STAlertView *alert = [[STAlertView alloc] initWithTitle:title 326 | image:image 327 | message:message 328 | buttonTitles:buttonTitles]; 329 | [alert setActionHandler:handler]; 330 | [alert show:YES]; 331 | return alert; 332 | } 333 | 334 | + (instancetype)showTitle:(NSString *)title 335 | message:(NSString *)message{ 336 | STAlertView *alert = [[STAlertView alloc] initWithTitle:title 337 | image:nil 338 | message:message 339 | buttonTitles:nil]; 340 | alert.hideWhenTapOutside = YES; 341 | [alert show:YES]; 342 | return alert; 343 | } 344 | 345 | + (instancetype)showTitle:(NSString *)title 346 | message:(NSString *)message 347 | hideDelay:(CGFloat)delay{ 348 | if (delay>0){ 349 | STAlertView *alert = [self showTitle:title message:message]; 350 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 351 | [alert hide:YES]; 352 | }); 353 | return alert; 354 | } 355 | return nil; 356 | } 357 | 358 | @end 359 | -------------------------------------------------------------------------------- /STModalDemo/STModal/STActionSheet/STActionSheet.m: -------------------------------------------------------------------------------- 1 | // 2 | // STActionSheet.m 3 | // STModalDemo 4 | // 5 | // Created by zhenlintie on 15/6/6. 6 | // Copyright (c) 2015年 sTeven. All rights reserved. 7 | // 8 | 9 | #import "STActionSheet.h" 10 | #import "STModal.h" 11 | #import "STModalUtil.h" 12 | 13 | #define kSTSheetRadius 13 14 | #define kSTSheetSidePaddingV 8 15 | #define kSTSheetSidePaddingH 8 16 | #define kSTSheetContentPadding 8 17 | #define kSTSheetButtonHeight 44 18 | #define kSTSheetLineColor STModalRGBA(78, 78, 80, 1) 19 | #define kSTSheetBackColor STModalRGBA(150, 164, 168, 1) 20 | #define kSTSheetTitleColor STModalRGBA(242, 249, 255, 1) 21 | 22 | /** 23 | * 按钮容器 24 | */ 25 | @interface STASContainerView : UIView 26 | @property (strong, nonatomic) UIView *containerView; 27 | @end 28 | 29 | @implementation STASContainerView 30 | 31 | - (instancetype)initWithFrame:(CGRect)frame{ 32 | if (self = [super initWithFrame:frame]){ 33 | self.layer.cornerRadius = kSTSheetRadius; 34 | self.layer.borderWidth = 0.5; 35 | self.layer.borderColor = kSTSheetLineColor.CGColor; 36 | // self.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.3].CGColor; 37 | // self.layer.shadowOffset = CGSizeZero; 38 | // self.layer.shadowOpacity = 0.3; 39 | // self.layer.shadowRadius = kSTSheetRadius*2; 40 | 41 | _containerView = [[UIView alloc] initWithFrame:CGRectZero]; 42 | _containerView.layer.cornerRadius = kSTSheetRadius; 43 | _containerView.layer.masksToBounds = YES; 44 | _containerView.backgroundColor = kSTSheetBackColor; 45 | [self addSubview:_containerView]; 46 | } 47 | return self; 48 | } 49 | 50 | - (void)addSubview:(UIView *)view{ 51 | if (![view isEqual:_containerView] && ![view isKindOfClass:[UIToolbar class]]){ 52 | [_containerView addSubview:view]; 53 | return; 54 | } 55 | [super addSubview:view]; 56 | } 57 | 58 | - (void)layoutSubviews{ 59 | _containerView.frame = self.bounds; 60 | // self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath; 61 | } 62 | 63 | @end 64 | 65 | 66 | 67 | @interface STASButton : UIButton 68 | @property (strong, nonatomic) STActionItem *item; 69 | @property (assign, nonatomic) CGFloat iconWidth; 70 | @property (assign, nonatomic) CGFloat iconPadding; 71 | @end 72 | 73 | @implementation STASButton 74 | 75 | - (instancetype)initWithFrame:(CGRect)frame{ 76 | if (self = [super initWithFrame:frame]){ 77 | [self setBackgroundImage:st_imageWithColor(STModalRGBA(120, 134, 138, 0.7)) forState:UIControlStateHighlighted]; 78 | self.adjustsImageWhenHighlighted = NO; 79 | self.adjustsImageWhenDisabled = NO; 80 | self.titleLabel.textAlignment = NSTextAlignmentCenter; 81 | } 82 | return self; 83 | } 84 | 85 | - (void)setItem:(STActionItem *)item{ 86 | _item = item; 87 | [self reloadData]; 88 | } 89 | 90 | - (void)reloadData{ 91 | if (_item.icon){ 92 | [self setImage:_item.icon forState:UIControlStateNormal]; 93 | self.imageView.contentMode = UIViewContentModeScaleAspectFit; 94 | } 95 | [self setAttributedTitle:_item.attributedTitle forState:UIControlStateNormal]; 96 | 97 | } 98 | 99 | - (CGRect)imageRectForContentRect:(CGRect)contentRect{ 100 | if (_item.icon){ 101 | return CGRectMake(_iconPadding*(1.3), 0, _iconWidth, kSTSheetButtonHeight); 102 | } 103 | return CGRectZero; 104 | } 105 | 106 | - (CGRect)titleRectForContentRect:(CGRect)contentRect{ 107 | if (_item.icon){ 108 | return CGRectMake(_iconWidth+(1.3+0.4)*_iconPadding, 0, CGRectGetWidth(contentRect)-_iconWidth-(3)*_iconPadding, kSTSheetButtonHeight); 109 | } 110 | return self.bounds; 111 | } 112 | 113 | @end 114 | 115 | 116 | /** 117 | * 类似UIActionSheet 118 | */ 119 | @interface STActionSheet () 120 | 121 | @property (strong, nonatomic) STModal *modal; 122 | 123 | @property (strong, nonatomic) STASContainerView *topContainerView; 124 | @property (strong, nonatomic) UIScrollView *scrollView; 125 | @property (strong, nonatomic) UILabel *titleLabel; 126 | @property (strong, nonatomic) STASContainerView *cancelContainerView; 127 | @property (strong, nonatomic) STASButton *cancelButton; 128 | @property (strong, nonatomic) NSMutableArray *otherButtons; 129 | @property (strong, nonatomic) NSMutableArray *lines; 130 | 131 | @end 132 | 133 | @implementation STActionSheet{ 134 | BOOL _didLayouted; 135 | 136 | CGSize _screenSize; 137 | CGFloat _otherButtonsHeight; 138 | CGFloat _maxTopContainerHeight; 139 | CGFloat _contentWidth; 140 | CGSize _buttonSize; 141 | } 142 | 143 | - (instancetype)initWithTitle:(NSString *)title 144 | cancelItem:(STActionItem *)cancelItem 145 | items:(NSArray *)items{ 146 | if (self = [self initWithFrame:CGRectZero]){ 147 | _title = title?[title copy]:nil; 148 | _cancelItem = cancelItem?[STActionItem itemWithAttributedTitle:cancelItem.attributedTitle icon:cancelItem.icon]:nil; 149 | _items = items?[NSArray arrayWithArray:items]:nil; 150 | } 151 | return self; 152 | } 153 | 154 | - (instancetype)initWithTitle:(NSString *)title 155 | cancelTitle:(NSString *)cancelTitle 156 | otherButtonTitles:(NSArray *)otherButtonTitles 157 | attribute:(NSDictionary *)attribute 158 | { 159 | if (self = [self initWithFrame:CGRectZero]){ 160 | _title = title?[title copy]:nil; 161 | _cancelItem = cancelTitle?[STActionItem itemWithTitle:cancelTitle attribute:attribute]:nil; 162 | if (otherButtonTitles.count > 0){ 163 | NSMutableArray *items = [NSMutableArray arrayWithCapacity:otherButtonTitles.count]; 164 | for (NSString *otherTitle in otherButtonTitles){ 165 | [items addObject:[STActionItem itemWithTitle:otherTitle attribute:attribute]]; 166 | } 167 | _items = [NSArray arrayWithArray:items]; 168 | } 169 | } 170 | return self; 171 | } 172 | 173 | - (instancetype)initWithFrame:(CGRect)frame{ 174 | if (self = [super initWithFrame:CGRectZero]){ 175 | [self loadData]; 176 | [self loadUI]; 177 | } 178 | return self; 179 | } 180 | 181 | - (void)loadData{ 182 | _hideWhenTouchOutside = YES; 183 | _didLayouted = NO; 184 | _lines = [NSMutableArray new]; 185 | 186 | self.modal = [STModal modalWithContentView:self]; 187 | _modal.positionMode = STModelPositionCenterBottom; 188 | _modal.dimBackgroundWhenShow = YES; 189 | _modal.dimBackgroundColor = [UIColor colorWithWhite:0 alpha:0.1]; 190 | [_modal setShowAnimation:[self showAnimation]]; 191 | [_modal setHideAnimation:[self hideAnimation]]; 192 | 193 | _screenSize = [UIScreen mainScreen].bounds.size; 194 | _contentWidth = _screenSize.width-2*kSTSheetSidePaddingH; 195 | _buttonSize = CGSizeMake(_contentWidth, kSTSheetButtonHeight); 196 | } 197 | 198 | - (void)loadUI{ 199 | _topContainerView = [STASContainerView new]; 200 | _scrollView = [[UIScrollView alloc] initWithFrame:CGRectZero]; 201 | [_topContainerView addSubview:_scrollView]; 202 | 203 | _cancelContainerView = [[STASContainerView alloc] initWithFrame:CGRectMake(0, 0, _buttonSize.width, _buttonSize.height)]; 204 | _cancelButton = [[STASButton alloc] initWithFrame:_cancelContainerView.bounds]; 205 | [_cancelContainerView addSubview:_cancelButton]; 206 | [_cancelButton addTarget:self action:@selector(cancelClicked:) forControlEvents:UIControlEventTouchUpInside]; 207 | 208 | [self addSubview:_topContainerView]; 209 | } 210 | 211 | #pragma mark - getter 212 | 213 | - (st_modal_animation)showAnimation{ 214 | return ^CGFloat(){ 215 | self.transform = CGAffineTransformMakeTranslation(0.0f, CGRectGetHeight(self.frame)); 216 | CGFloat d = 0.35; 217 | [UIView animateWithDuration:d 218 | delay:0 219 | usingSpringWithDamping:1 220 | initialSpringVelocity:0 221 | options:UIViewAnimationOptionCurveEaseOut 222 | animations:^{ 223 | self.transform = CGAffineTransformIdentity; 224 | } 225 | completion:^(BOOL finished) { 226 | }]; 227 | return d; 228 | }; 229 | } 230 | 231 | - (st_modal_animation)hideAnimation{ 232 | return ^CGFloat(){ 233 | CGFloat d = 0.3; 234 | [UIView animateWithDuration:d 235 | delay:0 236 | usingSpringWithDamping:1 237 | initialSpringVelocity:0 238 | options:UIViewAnimationOptionCurveEaseInOut 239 | animations:^{ 240 | self.transform = CGAffineTransformMakeTranslation(0.0f, CGRectGetHeight(self.frame)+5); 241 | } 242 | completion:^(BOOL finished) { 243 | }]; 244 | return d; 245 | }; 246 | } 247 | 248 | #pragma mark - prepare for show 249 | 250 | - (void)reset{ 251 | [_topContainerView removeFromSuperview]; 252 | [_cancelContainerView removeFromSuperview]; 253 | [_otherButtons makeObjectsPerformSelector:@selector(removeFromSuperview)]; 254 | [_lines makeObjectsPerformSelector:@selector(removeFromSuperview)]; 255 | [_lines removeAllObjects]; 256 | [_otherButtons removeAllObjects]; 257 | [_titleLabel removeFromSuperview]; 258 | } 259 | 260 | - (void)prepareForShow{ 261 | if (_didLayouted){ 262 | return; 263 | } 264 | [self reset]; 265 | 266 | CGFloat maxActionSheetHeight = _screenSize.height-kSTSheetSidePaddingV; 267 | CGFloat bottomPadding = kSTSheetSidePaddingV; 268 | CGFloat maxContentHeight = maxActionSheetHeight-bottomPadding;; 269 | 270 | CGFloat cancelHeight = _cancelItem?(kSTSheetButtonHeight):0; 271 | _maxTopContainerHeight = maxContentHeight-cancelHeight-(_cancelItem?kSTSheetSidePaddingV:0); 272 | _otherButtonsHeight = kSTSheetButtonHeight*_items.count; 273 | [self addCancelButton]; 274 | [self addOtherButtons]; 275 | [self addTitle]; 276 | 277 | CGFloat titleBottom = _title?CGRectGetHeight(_titleLabel.frame)+2*kSTSheetContentPadding:0; 278 | CGFloat scrollHeight = MIN(_otherButtonsHeight, _maxTopContainerHeight-titleBottom); 279 | 280 | CGFloat contentPadding = (((scrollHeight+titleBottom)&&_cancelItem>0)?kSTSheetContentPadding:0); 281 | CGFloat totalHeight = MIN(maxActionSheetHeight, scrollHeight+titleBottom+cancelHeight+contentPadding+bottomPadding); 282 | 283 | self.frame = CGRectMake(kSTSheetSidePaddingH, _screenSize.height-totalHeight-kSTSheetSidePaddingH, _contentWidth,totalHeight); 284 | _topContainerView.frame = CGRectMake(0, 0, _contentWidth, scrollHeight+titleBottom); 285 | _scrollView.frame = CGRectMake(0, titleBottom, _contentWidth, scrollHeight); 286 | _scrollView.contentSize = CGSizeMake(_contentWidth, _otherButtonsHeight); 287 | _cancelContainerView.frame = CGRectMake(0, CGRectGetMaxY(_topContainerView.frame)+contentPadding, _buttonSize.width, _buttonSize.height); 288 | 289 | _didLayouted = YES; 290 | } 291 | 292 | - (void)addCancelButton{ 293 | if (!_cancelItem){ 294 | return; 295 | } 296 | [self addSubview:_cancelContainerView]; 297 | _cancelButton.item = _cancelItem; 298 | } 299 | 300 | - (void)addOtherButtons{ 301 | if (_items.count == 0){ 302 | return; 303 | } 304 | if (!_otherButtons){ 305 | _otherButtons = [NSMutableArray new]; 306 | } 307 | [self addSubview:_topContainerView]; 308 | 309 | CGFloat iconWidth = kSTSheetButtonHeight-2*kSTSheetContentPadding; 310 | CGFloat maxWidth = MIN([self maxOtherButtonsTitleWidth],_contentWidth-iconWidth-3*kSTSheetContentPadding); 311 | CGFloat top = 0; 312 | for (int i = 0; i < _items.count; i++){ 313 | STASButton *button = [[STASButton alloc] initWithFrame:CGRectMake(0, top, _buttonSize.width, _buttonSize.height)]; 314 | button.tag = i; 315 | button.item = _items[i]; 316 | button.iconWidth = iconWidth; 317 | button.exclusiveTouch = YES; 318 | button.iconPadding = (_contentWidth-maxWidth-button.iconWidth)/3.0; 319 | [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside]; 320 | [_scrollView addSubview:button]; 321 | [_otherButtons addObject:button]; 322 | [self addLine:CGRectMake(0, top, _buttonSize.width, 0.5) toView:self.scrollView]; 323 | 324 | top += _buttonSize.height; 325 | } 326 | } 327 | 328 | - (void)addLine:(CGRect)frame toView:(UIView *)view{ 329 | UIView *line = [[UIView alloc] initWithFrame:frame]; 330 | line.backgroundColor = [kSTSheetLineColor colorWithAlphaComponent:0.25]; 331 | [view addSubview:line]; 332 | [_lines addObject:line]; 333 | } 334 | 335 | - (void)addTitle{ 336 | if (!_title){ 337 | return; 338 | } 339 | if (!_titleLabel){ 340 | _titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, kSTSheetContentPadding, 0, 0)]; 341 | _titleLabel.textColor = [kSTSheetTitleColor colorWithAlphaComponent:0.75]; 342 | _titleLabel.font = [UIFont systemFontOfSize:14]; 343 | _titleLabel.textAlignment = NSTextAlignmentCenter; 344 | _titleLabel.numberOfLines = 20; 345 | } 346 | [self addSubview:_topContainerView]; 347 | [_topContainerView addSubview:_titleLabel]; 348 | _titleLabel.text = _title; 349 | CGFloat titleWidth = _contentWidth-2*kSTSheetContentPadding; 350 | CGSize size = [_titleLabel sizeThatFits:CGSizeMake(titleWidth, 10000)]; 351 | _titleLabel.frame = CGRectMake(kSTSheetContentPadding, kSTSheetContentPadding, titleWidth, size.height); 352 | } 353 | 354 | - (CGFloat)maxOtherButtonsTitleWidth{ 355 | __block CGFloat width = 0; 356 | [_items enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 357 | NSAttributedString *title = [(STActionItem *)obj attributedTitle]; 358 | CGSize size = [title boundingRectWithSize:CGSizeMake(_contentWidth-kSTSheetContentPadding*2, kSTSheetButtonHeight) options:NSStringDrawingTruncatesLastVisibleLine context:nil].size; 359 | if (size.width > width){ 360 | width = size.width; 361 | } 362 | }]; 363 | return width; 364 | } 365 | 366 | #pragma mark - show / hide 367 | 368 | - (void)show:(BOOL)animated{ 369 | [self prepareForShow]; 370 | _modal.hideWhenTouchOutside = self.hideWhenTouchOutside; 371 | _modal.didShowHandler = self.didShowHandler; 372 | _modal.didHideHandler = self.didHideHandler; 373 | [_modal show:YES]; 374 | } 375 | 376 | - (void)hide:(BOOL)animated{ 377 | [_modal hide:YES]; 378 | } 379 | 380 | #pragma mark - action 381 | 382 | - (void)buttonClicked:(UIButton *)button{ 383 | if (_actionHandler){ 384 | _actionHandler(_items[button.tag], button.tag); 385 | } 386 | [self hide:YES]; 387 | } 388 | 389 | - (void)cancelClicked:(UIButton *)button{ 390 | if (_didCancelHandler){ 391 | _didCancelHandler(_cancelItem); 392 | } 393 | [self hide:YES]; 394 | } 395 | 396 | @end 397 | 398 | 399 | 400 | @implementation STActionSheet (Show) 401 | 402 | + (instancetype)showTitle:(NSString *)title 403 | items:(NSArray *)items 404 | cancel:(STActionItem *)cancelItem{ 405 | return [self showTitle:title items:items cancel:cancelItem actionHandler:nil cancelHandler:nil]; 406 | } 407 | 408 | + (instancetype)showItems:(NSArray *)items actionHandler:(st_sheet_action_handler)actionHandler{ 409 | return [self showTitle:nil items:items actionHandler:actionHandler]; 410 | } 411 | 412 | + (instancetype)showTitle:(NSString *)title items:(NSArray *)items actionHandler:(st_sheet_action_handler)actionHandler{ 413 | return [self showTitle:title items:items cancel:nil actionHandler:actionHandler cancelHandler:nil]; 414 | } 415 | 416 | + (instancetype)showTitle:(NSString *)title 417 | items:(NSArray *)items 418 | cancel:(STActionItem *)cancelItem 419 | actionHandler:(st_sheet_action_handler)actionHandler 420 | cancelHandler:(void(^)(STActionItem *cancelItem))canceledHandler{ 421 | STActionSheet *actionSheet = [[STActionSheet alloc] initWithTitle:title 422 | cancelItem:cancelItem 423 | items:items]; 424 | [actionSheet setActionHandler:actionHandler]; 425 | [actionSheet setDidCancelHandler:canceledHandler]; 426 | [actionSheet show:YES]; 427 | return actionSheet; 428 | } 429 | 430 | + (instancetype)showTitle:(NSString *)title 431 | otherButtonTitles:(NSArray *)otherButtonTitles 432 | cancel:(NSString *)cancelTitle 433 | attribute:(NSDictionary *)attribute{ 434 | return [self showTitle:title otherButtonTitles:otherButtonTitles cancel:cancelTitle attribute:attribute actionHandler:nil cancelHandler:nil]; 435 | } 436 | 437 | + (instancetype)showTitle:(NSString *)title 438 | otherButtonTitles:(NSArray *)otherButtonTitles 439 | cancel:(NSString *)cancelTitle 440 | attribute:(NSDictionary *)attribute 441 | actionHandler:(st_sheet_action_handler)actionHandler 442 | cancelHandler:(void(^)(STActionItem *cancelItem))canceledHandler{ 443 | STActionSheet *actionSheet = [[STActionSheet alloc] initWithTitle:title 444 | cancelTitle:cancelTitle 445 | otherButtonTitles:otherButtonTitles 446 | attribute:attribute]; 447 | [actionSheet setActionHandler:actionHandler]; 448 | [actionSheet setDidCancelHandler:canceledHandler]; 449 | [actionSheet show:YES]; 450 | return actionSheet; 451 | } 452 | 453 | @end 454 | 455 | 456 | #pragma mark ########################## 457 | 458 | @implementation STActionItem{ 459 | NSAttributedString *_customAttributedTitle; 460 | } 461 | 462 | /** 463 | * 快速生成 464 | */ 465 | + (instancetype)itemWithTitle:(NSString *)title{ 466 | return [self itemWithTitle:title icon:nil]; 467 | } 468 | 469 | + (instancetype)itemWithTitle:(NSString *)title icon:(UIImage *)icon{ 470 | return [[self alloc] initWithTitle:title icon:icon]; 471 | } 472 | 473 | + (instancetype)itemWithAttributedTitle:(NSAttributedString *)attributedTitle{ 474 | return [self itemWithAttributedTitle:attributedTitle icon:nil]; 475 | } 476 | 477 | + (instancetype)itemWithAttributedTitle:(NSAttributedString *)attributedTitle icon:(UIImage *)icon{ 478 | return [[self alloc] initWithAttributedTitle:attributedTitle icon:icon]; 479 | } 480 | 481 | + (instancetype)itemWithTitle:(NSString *)title attribute:(NSDictionary *)attribute;{ 482 | if (attribute){ 483 | return [self itemWithAttributedTitle:[[NSAttributedString alloc] initWithString:title 484 | attributes:attribute]]; 485 | } 486 | return [self itemWithTitle:title]; 487 | } 488 | 489 | - (instancetype)initWithTitle:(NSString *)title icon:(UIImage *)icon{ 490 | if (self = [super init]){ 491 | _title = title; 492 | _icon = icon; 493 | } 494 | return self; 495 | } 496 | 497 | - (instancetype)initWithAttributedTitle:(NSAttributedString *)attributedTitle icon:(UIImage *)icon{ 498 | if (self = [super init]){ 499 | _customAttributedTitle = attributedTitle; 500 | _icon = icon; 501 | } 502 | return self; 503 | } 504 | 505 | - (NSAttributedString *)attributedTitle{ 506 | if (_customAttributedTitle){ 507 | return _customAttributedTitle; 508 | } 509 | else if (_title){ 510 | return [[NSAttributedString alloc] initWithString:_title 511 | attributes:[self defaultAttribute]]; 512 | } 513 | return nil; 514 | } 515 | 516 | - (NSDictionary *)defaultAttribute{ 517 | return @{NSFontAttributeName:[UIFont systemFontOfSize:17], 518 | NSForegroundColorAttributeName:kSTSheetTitleColor}; 519 | } 520 | 521 | @end 522 | 523 | @implementation STActionItem (Common) 524 | 525 | + (instancetype)desdructiveItem:(NSString *)title{ 526 | return [self desdructiveItem:title icon:nil]; 527 | } 528 | 529 | + (instancetype)desdructiveItem:(NSString *)title icon:(UIImage *)icon{ 530 | NSAttributedString *attTitle = [[NSAttributedString alloc] initWithString:title 531 | attributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:17], 532 | NSForegroundColorAttributeName:STModalRGBA(230, 10, 10, 1)}]; 533 | return [self itemWithAttributedTitle:attTitle icon:icon]; 534 | } 535 | 536 | @end 537 | -------------------------------------------------------------------------------- /STModalDemo/STModalDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9C90E3121B7DDC120064AB89 /* STFrontModal.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C90E3111B7DDC120064AB89 /* STFrontModal.m */; }; 11 | 9CCAF58D1B21608600A7647F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CCAF58C1B21608600A7647F /* main.m */; }; 12 | 9CCAF5901B21608600A7647F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CCAF58F1B21608600A7647F /* AppDelegate.m */; }; 13 | 9CCAF5931B21608600A7647F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CCAF5921B21608600A7647F /* ViewController.m */; }; 14 | 9CCAF5981B21608600A7647F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9CCAF5971B21608600A7647F /* Images.xcassets */; }; 15 | 9CCAF59B1B21608600A7647F /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9CCAF5991B21608600A7647F /* LaunchScreen.xib */; }; 16 | 9CCAF5A71B21608600A7647F /* STModalDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CCAF5A61B21608600A7647F /* STModalDemoTests.m */; }; 17 | 9CCAF5B31B2160BB00A7647F /* STModal.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CCAF5B21B2160BB00A7647F /* STModal.m */; }; 18 | 9CCAF5B81B2178DF00A7647F /* STAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CCAF5B71B2178DF00A7647F /* STAlertView.m */; }; 19 | 9CCAF5BB1B21A04300A7647F /* AlertViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CCAF5BA1B21A04300A7647F /* AlertViewController.m */; }; 20 | 9CCAF5BF1B21AAE100A7647F /* lufei@2x.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 9CCAF5BD1B21AAE100A7647F /* lufei@2x.jpg */; }; 21 | 9CCAF5C01B21AAE100A7647F /* onepiece@2x.jpeg in Resources */ = {isa = PBXBuildFile; fileRef = 9CCAF5BE1B21AAE100A7647F /* onepiece@2x.jpeg */; }; 22 | 9CCC338F1B25445C00B890F2 /* share_friend@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9CCC338D1B25445C00B890F2 /* share_friend@2x.png */; }; 23 | 9CCC33901B25445C00B890F2 /* share_wechat@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9CCC338E1B25445C00B890F2 /* share_wechat@2x.png */; }; 24 | 9CCC33931B2564BA00B890F2 /* STActionSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CCC33921B2564BA00B890F2 /* STActionSheet.m */; }; 25 | 9CCC33961B25675C00B890F2 /* ActionSheetViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CCC33951B25675C00B890F2 /* ActionSheetViewController.m */; }; 26 | 9CCC33991B25676400B890F2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9CCC33971B25676400B890F2 /* Main.storyboard */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | 9CCAF5A11B21608600A7647F /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 9CCAF57F1B21608500A7647F /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 9CCAF5861B21608600A7647F; 35 | remoteInfo = STModalDemo; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 9C90E3101B7DDC120064AB89 /* STFrontModal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STFrontModal.h; sourceTree = ""; }; 41 | 9C90E3111B7DDC120064AB89 /* STFrontModal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STFrontModal.m; sourceTree = ""; }; 42 | 9CCAF5871B21608600A7647F /* STModalDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = STModalDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 9CCAF58B1B21608600A7647F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | 9CCAF58C1B21608600A7647F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 45 | 9CCAF58E1B21608600A7647F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 46 | 9CCAF58F1B21608600A7647F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 47 | 9CCAF5911B21608600A7647F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 48 | 9CCAF5921B21608600A7647F /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 49 | 9CCAF5971B21608600A7647F /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 50 | 9CCAF59A1B21608600A7647F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 51 | 9CCAF5A01B21608600A7647F /* STModalDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = STModalDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 9CCAF5A51B21608600A7647F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | 9CCAF5A61B21608600A7647F /* STModalDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STModalDemoTests.m; sourceTree = ""; }; 54 | 9CCAF5B11B2160BB00A7647F /* STModal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STModal.h; sourceTree = ""; }; 55 | 9CCAF5B21B2160BB00A7647F /* STModal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STModal.m; sourceTree = ""; }; 56 | 9CCAF5B61B2178DF00A7647F /* STAlertView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STAlertView.h; sourceTree = ""; }; 57 | 9CCAF5B71B2178DF00A7647F /* STAlertView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STAlertView.m; sourceTree = ""; }; 58 | 9CCAF5B91B21A04300A7647F /* AlertViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AlertViewController.h; sourceTree = ""; }; 59 | 9CCAF5BA1B21A04300A7647F /* AlertViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AlertViewController.m; sourceTree = ""; }; 60 | 9CCAF5BD1B21AAE100A7647F /* lufei@2x.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "lufei@2x.jpg"; sourceTree = ""; }; 61 | 9CCAF5BE1B21AAE100A7647F /* onepiece@2x.jpeg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "onepiece@2x.jpeg"; sourceTree = ""; }; 62 | 9CCB63841B248BA300F951AD /* STModalUtil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STModalUtil.h; sourceTree = ""; }; 63 | 9CCC338D1B25445C00B890F2 /* share_friend@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "share_friend@2x.png"; sourceTree = ""; }; 64 | 9CCC338E1B25445C00B890F2 /* share_wechat@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "share_wechat@2x.png"; sourceTree = ""; }; 65 | 9CCC33911B2564BA00B890F2 /* STActionSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STActionSheet.h; sourceTree = ""; }; 66 | 9CCC33921B2564BA00B890F2 /* STActionSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STActionSheet.m; sourceTree = ""; }; 67 | 9CCC33941B25675C00B890F2 /* ActionSheetViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ActionSheetViewController.h; sourceTree = ""; }; 68 | 9CCC33951B25675C00B890F2 /* ActionSheetViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ActionSheetViewController.m; sourceTree = ""; }; 69 | 9CCC33981B25676400B890F2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 70 | /* End PBXFileReference section */ 71 | 72 | /* Begin PBXFrameworksBuildPhase section */ 73 | 9CCAF5841B21608600A7647F /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | 9CCAF59D1B21608600A7647F /* Frameworks */ = { 81 | isa = PBXFrameworksBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | /* End PBXFrameworksBuildPhase section */ 88 | 89 | /* Begin PBXGroup section */ 90 | 9C90E30F1B7DDC120064AB89 /* STFrontModal */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 9C90E3101B7DDC120064AB89 /* STFrontModal.h */, 94 | 9C90E3111B7DDC120064AB89 /* STFrontModal.m */, 95 | ); 96 | path = STFrontModal; 97 | sourceTree = ""; 98 | }; 99 | 9CCAF57E1B21608500A7647F = { 100 | isa = PBXGroup; 101 | children = ( 102 | 9CCAF5B01B2160A100A7647F /* STModal */, 103 | 9CCAF5891B21608600A7647F /* STModalDemo */, 104 | 9CCAF5A31B21608600A7647F /* STModalDemoTests */, 105 | 9CCAF5881B21608600A7647F /* Products */, 106 | ); 107 | sourceTree = ""; 108 | }; 109 | 9CCAF5881B21608600A7647F /* Products */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 9CCAF5871B21608600A7647F /* STModalDemo.app */, 113 | 9CCAF5A01B21608600A7647F /* STModalDemoTests.xctest */, 114 | ); 115 | name = Products; 116 | sourceTree = ""; 117 | }; 118 | 9CCAF5891B21608600A7647F /* STModalDemo */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 9CCC33971B25676400B890F2 /* Main.storyboard */, 122 | 9CCAF5911B21608600A7647F /* ViewController.h */, 123 | 9CCAF5921B21608600A7647F /* ViewController.m */, 124 | 9CCAF5B91B21A04300A7647F /* AlertViewController.h */, 125 | 9CCAF5BA1B21A04300A7647F /* AlertViewController.m */, 126 | 9CCC33941B25675C00B890F2 /* ActionSheetViewController.h */, 127 | 9CCC33951B25675C00B890F2 /* ActionSheetViewController.m */, 128 | 9CCAF5BC1B21A05000A7647F /* res */, 129 | ); 130 | path = STModalDemo; 131 | sourceTree = ""; 132 | }; 133 | 9CCAF58A1B21608600A7647F /* Supporting Files */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 9CCAF58B1B21608600A7647F /* Info.plist */, 137 | 9CCAF58C1B21608600A7647F /* main.m */, 138 | ); 139 | name = "Supporting Files"; 140 | sourceTree = ""; 141 | }; 142 | 9CCAF5A31B21608600A7647F /* STModalDemoTests */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 9CCAF5A61B21608600A7647F /* STModalDemoTests.m */, 146 | 9CCAF5A41B21608600A7647F /* Supporting Files */, 147 | ); 148 | path = STModalDemoTests; 149 | sourceTree = ""; 150 | }; 151 | 9CCAF5A41B21608600A7647F /* Supporting Files */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 9CCAF5A51B21608600A7647F /* Info.plist */, 155 | ); 156 | name = "Supporting Files"; 157 | sourceTree = ""; 158 | }; 159 | 9CCAF5B01B2160A100A7647F /* STModal */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 9C90E30F1B7DDC120064AB89 /* STFrontModal */, 163 | 9CCB63831B248B8E00F951AD /* util */, 164 | 9CCAF5B41B21789100A7647F /* STActionSheet */, 165 | 9CCAF5B51B21789100A7647F /* STAlertView */, 166 | 9CCAF5B11B2160BB00A7647F /* STModal.h */, 167 | 9CCAF5B21B2160BB00A7647F /* STModal.m */, 168 | ); 169 | path = STModal; 170 | sourceTree = ""; 171 | }; 172 | 9CCAF5B41B21789100A7647F /* STActionSheet */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | 9CCC33911B2564BA00B890F2 /* STActionSheet.h */, 176 | 9CCC33921B2564BA00B890F2 /* STActionSheet.m */, 177 | ); 178 | path = STActionSheet; 179 | sourceTree = ""; 180 | }; 181 | 9CCAF5B51B21789100A7647F /* STAlertView */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | 9CCAF5B61B2178DF00A7647F /* STAlertView.h */, 185 | 9CCAF5B71B2178DF00A7647F /* STAlertView.m */, 186 | ); 187 | path = STAlertView; 188 | sourceTree = ""; 189 | }; 190 | 9CCAF5BC1B21A05000A7647F /* res */ = { 191 | isa = PBXGroup; 192 | children = ( 193 | 9CCC338D1B25445C00B890F2 /* share_friend@2x.png */, 194 | 9CCC338E1B25445C00B890F2 /* share_wechat@2x.png */, 195 | 9CCAF5BD1B21AAE100A7647F /* lufei@2x.jpg */, 196 | 9CCAF5BE1B21AAE100A7647F /* onepiece@2x.jpeg */, 197 | 9CCAF58A1B21608600A7647F /* Supporting Files */, 198 | 9CCAF58E1B21608600A7647F /* AppDelegate.h */, 199 | 9CCAF58F1B21608600A7647F /* AppDelegate.m */, 200 | 9CCAF5971B21608600A7647F /* Images.xcassets */, 201 | 9CCAF5991B21608600A7647F /* LaunchScreen.xib */, 202 | ); 203 | name = res; 204 | sourceTree = ""; 205 | }; 206 | 9CCB63831B248B8E00F951AD /* util */ = { 207 | isa = PBXGroup; 208 | children = ( 209 | 9CCB63841B248BA300F951AD /* STModalUtil.h */, 210 | ); 211 | name = util; 212 | sourceTree = ""; 213 | }; 214 | /* End PBXGroup section */ 215 | 216 | /* Begin PBXNativeTarget section */ 217 | 9CCAF5861B21608600A7647F /* STModalDemo */ = { 218 | isa = PBXNativeTarget; 219 | buildConfigurationList = 9CCAF5AA1B21608600A7647F /* Build configuration list for PBXNativeTarget "STModalDemo" */; 220 | buildPhases = ( 221 | 9CCAF5831B21608600A7647F /* Sources */, 222 | 9CCAF5841B21608600A7647F /* Frameworks */, 223 | 9CCAF5851B21608600A7647F /* Resources */, 224 | ); 225 | buildRules = ( 226 | ); 227 | dependencies = ( 228 | ); 229 | name = STModalDemo; 230 | productName = STModalDemo; 231 | productReference = 9CCAF5871B21608600A7647F /* STModalDemo.app */; 232 | productType = "com.apple.product-type.application"; 233 | }; 234 | 9CCAF59F1B21608600A7647F /* STModalDemoTests */ = { 235 | isa = PBXNativeTarget; 236 | buildConfigurationList = 9CCAF5AD1B21608600A7647F /* Build configuration list for PBXNativeTarget "STModalDemoTests" */; 237 | buildPhases = ( 238 | 9CCAF59C1B21608600A7647F /* Sources */, 239 | 9CCAF59D1B21608600A7647F /* Frameworks */, 240 | 9CCAF59E1B21608600A7647F /* Resources */, 241 | ); 242 | buildRules = ( 243 | ); 244 | dependencies = ( 245 | 9CCAF5A21B21608600A7647F /* PBXTargetDependency */, 246 | ); 247 | name = STModalDemoTests; 248 | productName = STModalDemoTests; 249 | productReference = 9CCAF5A01B21608600A7647F /* STModalDemoTests.xctest */; 250 | productType = "com.apple.product-type.bundle.unit-test"; 251 | }; 252 | /* End PBXNativeTarget section */ 253 | 254 | /* Begin PBXProject section */ 255 | 9CCAF57F1B21608500A7647F /* Project object */ = { 256 | isa = PBXProject; 257 | attributes = { 258 | LastUpgradeCheck = 0630; 259 | ORGANIZATIONNAME = sTeven; 260 | TargetAttributes = { 261 | 9CCAF5861B21608600A7647F = { 262 | CreatedOnToolsVersion = 6.3.2; 263 | }; 264 | 9CCAF59F1B21608600A7647F = { 265 | CreatedOnToolsVersion = 6.3.2; 266 | TestTargetID = 9CCAF5861B21608600A7647F; 267 | }; 268 | }; 269 | }; 270 | buildConfigurationList = 9CCAF5821B21608500A7647F /* Build configuration list for PBXProject "STModalDemo" */; 271 | compatibilityVersion = "Xcode 3.2"; 272 | developmentRegion = English; 273 | hasScannedForEncodings = 0; 274 | knownRegions = ( 275 | en, 276 | Base, 277 | ); 278 | mainGroup = 9CCAF57E1B21608500A7647F; 279 | productRefGroup = 9CCAF5881B21608600A7647F /* Products */; 280 | projectDirPath = ""; 281 | projectRoot = ""; 282 | targets = ( 283 | 9CCAF5861B21608600A7647F /* STModalDemo */, 284 | 9CCAF59F1B21608600A7647F /* STModalDemoTests */, 285 | ); 286 | }; 287 | /* End PBXProject section */ 288 | 289 | /* Begin PBXResourcesBuildPhase section */ 290 | 9CCAF5851B21608600A7647F /* Resources */ = { 291 | isa = PBXResourcesBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | 9CCC33991B25676400B890F2 /* Main.storyboard in Resources */, 295 | 9CCC338F1B25445C00B890F2 /* share_friend@2x.png in Resources */, 296 | 9CCC33901B25445C00B890F2 /* share_wechat@2x.png in Resources */, 297 | 9CCAF59B1B21608600A7647F /* LaunchScreen.xib in Resources */, 298 | 9CCAF5C01B21AAE100A7647F /* onepiece@2x.jpeg in Resources */, 299 | 9CCAF5BF1B21AAE100A7647F /* lufei@2x.jpg in Resources */, 300 | 9CCAF5981B21608600A7647F /* Images.xcassets in Resources */, 301 | ); 302 | runOnlyForDeploymentPostprocessing = 0; 303 | }; 304 | 9CCAF59E1B21608600A7647F /* Resources */ = { 305 | isa = PBXResourcesBuildPhase; 306 | buildActionMask = 2147483647; 307 | files = ( 308 | ); 309 | runOnlyForDeploymentPostprocessing = 0; 310 | }; 311 | /* End PBXResourcesBuildPhase section */ 312 | 313 | /* Begin PBXSourcesBuildPhase section */ 314 | 9CCAF5831B21608600A7647F /* Sources */ = { 315 | isa = PBXSourcesBuildPhase; 316 | buildActionMask = 2147483647; 317 | files = ( 318 | 9CCC33931B2564BA00B890F2 /* STActionSheet.m in Sources */, 319 | 9CCAF5B31B2160BB00A7647F /* STModal.m in Sources */, 320 | 9C90E3121B7DDC120064AB89 /* STFrontModal.m in Sources */, 321 | 9CCAF5931B21608600A7647F /* ViewController.m in Sources */, 322 | 9CCAF5901B21608600A7647F /* AppDelegate.m in Sources */, 323 | 9CCC33961B25675C00B890F2 /* ActionSheetViewController.m in Sources */, 324 | 9CCAF5BB1B21A04300A7647F /* AlertViewController.m in Sources */, 325 | 9CCAF58D1B21608600A7647F /* main.m in Sources */, 326 | 9CCAF5B81B2178DF00A7647F /* STAlertView.m in Sources */, 327 | ); 328 | runOnlyForDeploymentPostprocessing = 0; 329 | }; 330 | 9CCAF59C1B21608600A7647F /* Sources */ = { 331 | isa = PBXSourcesBuildPhase; 332 | buildActionMask = 2147483647; 333 | files = ( 334 | 9CCAF5A71B21608600A7647F /* STModalDemoTests.m in Sources */, 335 | ); 336 | runOnlyForDeploymentPostprocessing = 0; 337 | }; 338 | /* End PBXSourcesBuildPhase section */ 339 | 340 | /* Begin PBXTargetDependency section */ 341 | 9CCAF5A21B21608600A7647F /* PBXTargetDependency */ = { 342 | isa = PBXTargetDependency; 343 | target = 9CCAF5861B21608600A7647F /* STModalDemo */; 344 | targetProxy = 9CCAF5A11B21608600A7647F /* PBXContainerItemProxy */; 345 | }; 346 | /* End PBXTargetDependency section */ 347 | 348 | /* Begin PBXVariantGroup section */ 349 | 9CCAF5991B21608600A7647F /* LaunchScreen.xib */ = { 350 | isa = PBXVariantGroup; 351 | children = ( 352 | 9CCAF59A1B21608600A7647F /* Base */, 353 | ); 354 | name = LaunchScreen.xib; 355 | sourceTree = ""; 356 | }; 357 | 9CCC33971B25676400B890F2 /* Main.storyboard */ = { 358 | isa = PBXVariantGroup; 359 | children = ( 360 | 9CCC33981B25676400B890F2 /* Base */, 361 | ); 362 | name = Main.storyboard; 363 | sourceTree = ""; 364 | }; 365 | /* End PBXVariantGroup section */ 366 | 367 | /* Begin XCBuildConfiguration section */ 368 | 9CCAF5A81B21608600A7647F /* Debug */ = { 369 | isa = XCBuildConfiguration; 370 | buildSettings = { 371 | ALWAYS_SEARCH_USER_PATHS = NO; 372 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 373 | CLANG_CXX_LIBRARY = "libc++"; 374 | CLANG_ENABLE_MODULES = YES; 375 | CLANG_ENABLE_OBJC_ARC = YES; 376 | CLANG_WARN_BOOL_CONVERSION = YES; 377 | CLANG_WARN_CONSTANT_CONVERSION = YES; 378 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 379 | CLANG_WARN_EMPTY_BODY = YES; 380 | CLANG_WARN_ENUM_CONVERSION = YES; 381 | CLANG_WARN_INT_CONVERSION = YES; 382 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 383 | CLANG_WARN_UNREACHABLE_CODE = YES; 384 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 385 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 386 | COPY_PHASE_STRIP = NO; 387 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 388 | ENABLE_STRICT_OBJC_MSGSEND = YES; 389 | GCC_C_LANGUAGE_STANDARD = gnu99; 390 | GCC_DYNAMIC_NO_PIC = NO; 391 | GCC_NO_COMMON_BLOCKS = YES; 392 | GCC_OPTIMIZATION_LEVEL = 0; 393 | GCC_PREPROCESSOR_DEFINITIONS = ( 394 | "DEBUG=1", 395 | "$(inherited)", 396 | ); 397 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 398 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 399 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 400 | GCC_WARN_UNDECLARED_SELECTOR = YES; 401 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 402 | GCC_WARN_UNUSED_FUNCTION = YES; 403 | GCC_WARN_UNUSED_VARIABLE = YES; 404 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 405 | MTL_ENABLE_DEBUG_INFO = YES; 406 | ONLY_ACTIVE_ARCH = YES; 407 | SDKROOT = iphoneos; 408 | }; 409 | name = Debug; 410 | }; 411 | 9CCAF5A91B21608600A7647F /* Release */ = { 412 | isa = XCBuildConfiguration; 413 | buildSettings = { 414 | ALWAYS_SEARCH_USER_PATHS = NO; 415 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 416 | CLANG_CXX_LIBRARY = "libc++"; 417 | CLANG_ENABLE_MODULES = YES; 418 | CLANG_ENABLE_OBJC_ARC = YES; 419 | CLANG_WARN_BOOL_CONVERSION = YES; 420 | CLANG_WARN_CONSTANT_CONVERSION = YES; 421 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 422 | CLANG_WARN_EMPTY_BODY = YES; 423 | CLANG_WARN_ENUM_CONVERSION = YES; 424 | CLANG_WARN_INT_CONVERSION = YES; 425 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 426 | CLANG_WARN_UNREACHABLE_CODE = YES; 427 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 428 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 429 | COPY_PHASE_STRIP = NO; 430 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 431 | ENABLE_NS_ASSERTIONS = NO; 432 | ENABLE_STRICT_OBJC_MSGSEND = YES; 433 | GCC_C_LANGUAGE_STANDARD = gnu99; 434 | GCC_NO_COMMON_BLOCKS = YES; 435 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 436 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 437 | GCC_WARN_UNDECLARED_SELECTOR = YES; 438 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 439 | GCC_WARN_UNUSED_FUNCTION = YES; 440 | GCC_WARN_UNUSED_VARIABLE = YES; 441 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 442 | MTL_ENABLE_DEBUG_INFO = NO; 443 | SDKROOT = iphoneos; 444 | VALIDATE_PRODUCT = YES; 445 | }; 446 | name = Release; 447 | }; 448 | 9CCAF5AB1B21608600A7647F /* Debug */ = { 449 | isa = XCBuildConfiguration; 450 | buildSettings = { 451 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 452 | INFOPLIST_FILE = STModalDemo/Info.plist; 453 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 454 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 455 | PRODUCT_NAME = "$(TARGET_NAME)"; 456 | }; 457 | name = Debug; 458 | }; 459 | 9CCAF5AC1B21608600A7647F /* Release */ = { 460 | isa = XCBuildConfiguration; 461 | buildSettings = { 462 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 463 | INFOPLIST_FILE = STModalDemo/Info.plist; 464 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 465 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 466 | PRODUCT_NAME = "$(TARGET_NAME)"; 467 | }; 468 | name = Release; 469 | }; 470 | 9CCAF5AE1B21608600A7647F /* Debug */ = { 471 | isa = XCBuildConfiguration; 472 | buildSettings = { 473 | BUNDLE_LOADER = "$(TEST_HOST)"; 474 | FRAMEWORK_SEARCH_PATHS = ( 475 | "$(SDKROOT)/Developer/Library/Frameworks", 476 | "$(inherited)", 477 | ); 478 | GCC_PREPROCESSOR_DEFINITIONS = ( 479 | "DEBUG=1", 480 | "$(inherited)", 481 | ); 482 | INFOPLIST_FILE = STModalDemoTests/Info.plist; 483 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 484 | PRODUCT_NAME = "$(TARGET_NAME)"; 485 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/STModalDemo.app/STModalDemo"; 486 | }; 487 | name = Debug; 488 | }; 489 | 9CCAF5AF1B21608600A7647F /* Release */ = { 490 | isa = XCBuildConfiguration; 491 | buildSettings = { 492 | BUNDLE_LOADER = "$(TEST_HOST)"; 493 | FRAMEWORK_SEARCH_PATHS = ( 494 | "$(SDKROOT)/Developer/Library/Frameworks", 495 | "$(inherited)", 496 | ); 497 | INFOPLIST_FILE = STModalDemoTests/Info.plist; 498 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 499 | PRODUCT_NAME = "$(TARGET_NAME)"; 500 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/STModalDemo.app/STModalDemo"; 501 | }; 502 | name = Release; 503 | }; 504 | /* End XCBuildConfiguration section */ 505 | 506 | /* Begin XCConfigurationList section */ 507 | 9CCAF5821B21608500A7647F /* Build configuration list for PBXProject "STModalDemo" */ = { 508 | isa = XCConfigurationList; 509 | buildConfigurations = ( 510 | 9CCAF5A81B21608600A7647F /* Debug */, 511 | 9CCAF5A91B21608600A7647F /* Release */, 512 | ); 513 | defaultConfigurationIsVisible = 0; 514 | defaultConfigurationName = Release; 515 | }; 516 | 9CCAF5AA1B21608600A7647F /* Build configuration list for PBXNativeTarget "STModalDemo" */ = { 517 | isa = XCConfigurationList; 518 | buildConfigurations = ( 519 | 9CCAF5AB1B21608600A7647F /* Debug */, 520 | 9CCAF5AC1B21608600A7647F /* Release */, 521 | ); 522 | defaultConfigurationIsVisible = 0; 523 | defaultConfigurationName = Release; 524 | }; 525 | 9CCAF5AD1B21608600A7647F /* Build configuration list for PBXNativeTarget "STModalDemoTests" */ = { 526 | isa = XCConfigurationList; 527 | buildConfigurations = ( 528 | 9CCAF5AE1B21608600A7647F /* Debug */, 529 | 9CCAF5AF1B21608600A7647F /* Release */, 530 | ); 531 | defaultConfigurationIsVisible = 0; 532 | defaultConfigurationName = Release; 533 | }; 534 | /* End XCConfigurationList section */ 535 | }; 536 | rootObject = 9CCAF57F1B21608500A7647F /* Project object */; 537 | } 538 | --------------------------------------------------------------------------------