├── .gitignore ├── Example ├── CSAppDelegate.h ├── CSAppDelegate.m ├── CSViewController.h ├── CSViewController.m ├── Resources │ ├── Images.xcassets │ │ ├── Contents.json │ │ └── toast.imageset │ │ │ ├── Contents.json │ │ │ └── toast.png │ └── LaunchScreen.storyboard ├── Toast-Info.plist ├── Toast-Prefix.pch └── main.m ├── LICENSE ├── README.md ├── Toast-Framework ├── Info.plist └── Toast.h ├── Toast.podspec ├── Toast.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── Toast.xccheckout └── xcshareddata │ └── xcschemes │ ├── Toast-Example.xcscheme │ └── Toast.xcscheme ├── Toast ├── Resources │ └── PrivacyInfo.xcprivacy ├── UIView+Toast.h └── UIView+Toast.m └── toast_screenshots.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | 6 | ## Build generated 7 | build/ 8 | DerivedData 9 | 10 | ## Various settings 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata 20 | 21 | ## Other 22 | *.xccheckout 23 | *.moved-aside 24 | *.xcuserstate 25 | *.xcscmblueprint 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | .build/ 37 | 38 | # Carthage 39 | Carthage/Build 40 | -------------------------------------------------------------------------------- /Example/CSAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // CSAppDelegate.h 3 | // Toast 4 | // 5 | // Copyright (c) 2011-2024 Charles Scalesse. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a 8 | // copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included 16 | // in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | #import 27 | 28 | @class CSViewController; 29 | 30 | @interface CSAppDelegate : NSObject 31 | 32 | @property (nonatomic, retain) UIWindow *window; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Example/CSAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // CSAppDelegate.m 3 | // Toast 4 | // 5 | // Copyright (c) 2011-2024 Charles Scalesse. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a 8 | // copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included 16 | // in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | #import "CSAppDelegate.h" 27 | #import "CSViewController.h" 28 | 29 | @implementation CSAppDelegate 30 | 31 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 32 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 33 | 34 | CSViewController *viewController = [[CSViewController alloc] initWithStyle:UITableViewStylePlain]; 35 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController]; 36 | self.window.rootViewController = navigationController; 37 | [self.window makeKeyAndVisible]; 38 | 39 | [self setupAppearance]; 40 | 41 | return YES; 42 | } 43 | 44 | - (void)setupAppearance { 45 | if (@available(iOS 13.0, *)) { 46 | UINavigationBarAppearance *barAppearance = [[UINavigationBarAppearance alloc] init]; 47 | barAppearance.backgroundColor = [UIColor orangeColor]; 48 | barAppearance.titleTextAttributes = @{NSForegroundColorAttributeName : [UIColor whiteColor]}; 49 | 50 | [[UINavigationBar appearance] setStandardAppearance:barAppearance]; 51 | [[UINavigationBar appearance] setScrollEdgeAppearance:barAppearance]; 52 | } else { 53 | [[UINavigationBar appearance] setBarTintColor:[UIColor orangeColor]]; 54 | [[UINavigationBar appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor whiteColor]}]; 55 | } 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /Example/CSViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // CSAppViewController.h 3 | // Toast 4 | // 5 | // Copyright (c) 2011-2024 Charles Scalesse. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a 8 | // copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included 16 | // in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | #import 27 | 28 | @interface CSViewController : UITableViewController 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Example/CSViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // CSAppViewController.m 3 | // Toast 4 | // 5 | // Copyright (c) 2011-2024 Charles Scalesse. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a 8 | // copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included 16 | // in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | #import "CSViewController.h" 27 | #import "UIView+Toast.h" 28 | 29 | static NSString * ZOToastSwitchCellId = @"ZOToastSwitchCellId"; 30 | static NSString * ZOToastDemoCellId = @"ZOToastDemoCellId"; 31 | 32 | @interface CSViewController () 33 | 34 | @property (assign, nonatomic, getter=isShowingActivity) BOOL showingActivity; 35 | @property (strong, nonatomic) UISwitch *tapToDismissSwitch; 36 | @property (strong, nonatomic) UISwitch *queueSwitch; 37 | 38 | @end 39 | 40 | @implementation CSViewController 41 | 42 | #pragma mark - Constructors 43 | 44 | - (instancetype)initWithStyle:(UITableViewStyle)style { 45 | self = [super initWithStyle:style]; 46 | if (self) { 47 | self.title = @"Toast"; 48 | self.showingActivity = NO; 49 | } 50 | return self; 51 | } 52 | 53 | #pragma mark - View Lifecycle 54 | 55 | - (void)viewDidLoad { 56 | [super viewDidLoad]; 57 | [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:ZOToastDemoCellId]; 58 | } 59 | 60 | #pragma mark - Rotation 61 | 62 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { 63 | return YES; 64 | } 65 | 66 | #pragma mark - Events 67 | 68 | - (void)handleTapToDismissToggled { 69 | [CSToastManager setTapToDismissEnabled:![CSToastManager isTapToDismissEnabled]]; 70 | } 71 | 72 | - (void)handleQueueToggled { 73 | [CSToastManager setQueueEnabled:![CSToastManager isQueueEnabled]]; 74 | } 75 | 76 | #pragma mark - UITableViewDelegate & Datasource Methods 77 | 78 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 79 | return 2; 80 | } 81 | 82 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 83 | if (section == 0) { 84 | return 2; 85 | } else { 86 | return 11; 87 | } 88 | } 89 | 90 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 91 | return 60.0; 92 | } 93 | 94 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { 95 | return 40.0; 96 | } 97 | 98 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 99 | if (section == 0) { 100 | return @"SETTINGS"; 101 | } else { 102 | return @"DEMOS"; 103 | } 104 | } 105 | 106 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 107 | if (indexPath.section == 0 && indexPath.row == 0) { 108 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ZOToastSwitchCellId]; 109 | if (cell == nil) { 110 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ZOToastSwitchCellId]; 111 | self.tapToDismissSwitch = [[UISwitch alloc] init]; 112 | _tapToDismissSwitch.onTintColor = [UIColor colorWithRed:239.0 / 255.0 green:108.0 / 255.0 blue:0.0 / 255.0 alpha:1.0]; 113 | [_tapToDismissSwitch addTarget:self action:@selector(handleTapToDismissToggled) forControlEvents:UIControlEventValueChanged]; 114 | cell.accessoryView = _tapToDismissSwitch; 115 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 116 | cell.textLabel.font = [UIFont systemFontOfSize:16.0]; 117 | } 118 | cell.textLabel.text = @"Tap to Dismiss"; 119 | [_tapToDismissSwitch setOn:[CSToastManager isTapToDismissEnabled]]; 120 | return cell; 121 | } else if (indexPath.section == 0 && indexPath.row == 1) { 122 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ZOToastSwitchCellId]; 123 | if (cell == nil) { 124 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ZOToastSwitchCellId]; 125 | self.queueSwitch = [[UISwitch alloc] init]; 126 | _queueSwitch.onTintColor = [UIColor colorWithRed:239.0 / 255.0 green:108.0 / 255.0 blue:0.0 / 255.0 alpha:1.0]; 127 | [_queueSwitch addTarget:self action:@selector(handleQueueToggled) forControlEvents:UIControlEventValueChanged]; 128 | cell.accessoryView = _queueSwitch; 129 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 130 | cell.textLabel.font = [UIFont systemFontOfSize:16.0]; 131 | } 132 | cell.textLabel.text = @"Queue Toast"; 133 | [_queueSwitch setOn:[CSToastManager isQueueEnabled]]; 134 | return cell; 135 | } else { 136 | UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:ZOToastDemoCellId forIndexPath:indexPath]; 137 | cell.textLabel.numberOfLines = 2; 138 | cell.textLabel.font = [UIFont systemFontOfSize:16.0]; 139 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 140 | 141 | if (indexPath.row == 0) { 142 | cell.textLabel.text = @"Make toast"; 143 | } else if (indexPath.row == 1) { 144 | cell.textLabel.text = @"Make toast on top for 3 seconds"; 145 | } else if (indexPath.row == 2) { 146 | cell.textLabel.text = @"Make toast with a title"; 147 | } else if (indexPath.row == 3) { 148 | cell.textLabel.text = @"Make toast with an image"; 149 | } else if (indexPath.row == 4) { 150 | cell.textLabel.text = @"Make toast with a title, image, and completion block"; 151 | } else if (indexPath.row == 5) { 152 | cell.textLabel.text = @"Make toast with a custom style"; 153 | } else if (indexPath.row == 6) { 154 | cell.textLabel.text = @"Show a custom view as toast"; 155 | } else if (indexPath.row == 7) { 156 | cell.textLabel.text = @"Show an image as toast at point\n(110, 110)"; 157 | } else if (indexPath.row == 8) { 158 | cell.textLabel.text = (self.isShowingActivity) ? @"Hide toast activity" : @"Show toast activity"; 159 | } else if (indexPath.row == 9) { 160 | cell.textLabel.text = @"Hide toast"; 161 | } else if (indexPath.row == 10) { 162 | cell.textLabel.text = @"Hide all toasts"; 163 | } 164 | 165 | return cell; 166 | } 167 | } 168 | 169 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 170 | if (indexPath.section == 0) return; 171 | 172 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 173 | 174 | if (indexPath.row == 0) { 175 | 176 | // Make toast 177 | [self.navigationController.view makeToast:@"This is a piece of toast"]; 178 | 179 | } else if (indexPath.row == 1) { 180 | 181 | // Make toast with a duration and position 182 | [self.navigationController.view makeToast:@"This is a piece of toast on top for 3 seconds" 183 | duration:3.0 184 | position:CSToastPositionTop]; 185 | 186 | } else if (indexPath.row == 2) { 187 | 188 | // Make toast with a title 189 | [self.navigationController.view makeToast:@"This is a piece of toast with a title" 190 | duration:2.0 191 | position:CSToastPositionTop 192 | title:@"Toast Title" 193 | image:nil 194 | style:nil 195 | completion:nil]; 196 | 197 | } else if (indexPath.row == 3) { 198 | 199 | // Make toast with an image 200 | [self.navigationController.view makeToast:@"This is a piece of toast with an image" 201 | duration:2.0 202 | position:CSToastPositionCenter 203 | title:nil 204 | image:[UIImage imageNamed:@"toast.png"] 205 | style:nil 206 | completion:nil]; 207 | 208 | } else if (indexPath.row == 4) { 209 | 210 | // Make toast with an image, title, and completion block 211 | [self.navigationController.view makeToast:@"This is a piece of toast with a title, image, and completion block" 212 | duration:2.0 213 | position:CSToastPositionBottom 214 | title:@"Toast Title" 215 | image:[UIImage imageNamed:@"toast.png"] 216 | style:nil 217 | completion:^(BOOL didTap) { 218 | if (didTap) { 219 | NSLog(@"completion from tap"); 220 | } else { 221 | NSLog(@"completion without tap"); 222 | } 223 | }]; 224 | 225 | } else if (indexPath.row == 5) { 226 | 227 | // Make toast with a custom style 228 | CSToastStyle *style = [[CSToastStyle alloc] initWithDefaultStyle]; 229 | style.messageFont = [UIFont fontWithName:@"Zapfino" size:14.0]; 230 | style.messageColor = [UIColor redColor]; 231 | style.messageAlignment = NSTextAlignmentCenter; 232 | style.backgroundColor = [UIColor yellowColor]; 233 | 234 | [self.navigationController.view makeToast:@"This is a piece of toast with a custom style" 235 | duration:3.0 236 | position:CSToastPositionBottom 237 | style:style]; 238 | 239 | // @NOTE: Uncommenting the line below will set the shared style for all toast methods: 240 | // [CSToastManager setSharedStyle:style]; 241 | 242 | } else if (indexPath.row == 6) { 243 | 244 | // Show a custom view as toast 245 | UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 80, 400)]; 246 | [customView setAutoresizingMask:(UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin)]; // autoresizing masks are respected on custom views 247 | [customView setBackgroundColor:[UIColor orangeColor]]; 248 | 249 | [self.navigationController.view showToast:customView 250 | duration:2.0 251 | position:CSToastPositionCenter 252 | completion:nil]; 253 | 254 | } else if (indexPath.row == 7) { 255 | 256 | // Show an imageView as toast, on center at point (110,110) 257 | UIImageView *toastView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"toast.png"]]; 258 | 259 | [self.navigationController.view showToast:toastView 260 | duration:2.0 261 | position:[NSValue valueWithCGPoint:CGPointMake(110, 110)] // wrap CGPoint in an NSValue object 262 | completion:nil]; 263 | 264 | } else if (indexPath.row == 8) { 265 | 266 | // Make toast activity 267 | if (!self.isShowingActivity) { 268 | [self.navigationController.view makeToastActivity:CSToastPositionCenter]; 269 | } else { 270 | [self.navigationController.view hideToastActivity]; 271 | } 272 | _showingActivity = !self.isShowingActivity; 273 | 274 | [tableView reloadData]; 275 | 276 | } else if (indexPath.row == 9) { 277 | 278 | // Hide toast 279 | [self.navigationController.view hideToast]; 280 | 281 | } else if (indexPath.row == 10) { 282 | 283 | // Hide all toasts 284 | [self.navigationController.view hideAllToasts]; 285 | 286 | } 287 | } 288 | 289 | @end 290 | -------------------------------------------------------------------------------- /Example/Resources/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Resources/Images.xcassets/toast.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "toast.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Images.xcassets/toast.imageset/toast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalessec/Toast/46a5d1c11987bed95543323154b46cccdff0deca/Example/Resources/Images.xcassets/toast.imageset/toast.png -------------------------------------------------------------------------------- /Example/Resources/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Example/Toast-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIconFiles 14 | 15 | 16 | Icon.png 17 | Icon@2x.png 18 | 19 | CFBundleIcons 20 | 21 | CFBundleIcons~ipad 22 | 23 | CFBundleIdentifier 24 | $(PRODUCT_BUNDLE_IDENTIFIER) 25 | CFBundleInfoDictionaryVersion 26 | 6.0 27 | CFBundleName 28 | ${PRODUCT_NAME} 29 | CFBundlePackageType 30 | APPL 31 | CFBundleShortVersionString 32 | 4.1.0 33 | CFBundleSignature 34 | ???? 35 | CFBundleVersion 36 | 4.1.0 37 | LSRequiresIPhoneOS 38 | 39 | UILaunchStoryboardName 40 | LaunchScreen 41 | UIPrerenderedIcon 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Example/Toast-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'ToastTest' target in the 'ToastTest' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_3_0 8 | #warning "This project uses features only available in iPhone SDK 3.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /Example/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ToastTest 4 | // 5 | // Copyright 2011-2024 Charles Scalesse. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "CSAppDelegate.h" 10 | 11 | int main(int argc, char *argv[]) { 12 | @autoreleasepool { 13 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([CSAppDelegate class])); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2024 Charles Scalesse. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Toast for iOS 2 | ============= 3 | 4 | [![CocoaPods Version](https://img.shields.io/cocoapods/v/Toast.svg)](http://cocoadocs.org/docsets/Toast) 5 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 6 | 7 | Toast is an Objective-C category that adds toast notifications to the `UIView` object class. It is intended to be simple, lightweight, and easy to use. Most 8 | toast notifications can be triggered with a single line of code. 9 | 10 | **Using Swift? A native swift port of this library is now available: [Toast-Swift](https://github.com/scalessec/Toast-Swift "Toast-Swift")** 11 | 12 | Screenshots 13 | --------- 14 | ![Toast Screenshots](toast_screenshots.jpg) 15 | 16 | 17 | Basic Examples 18 | --------- 19 | ```objc 20 | // basic usage 21 | [self.view makeToast:@"This is a piece of toast."]; 22 | 23 | // toast with a specific duration and position 24 | [self.view makeToast:@"This is a piece of toast with a specific duration and position." 25 | duration:3.0 26 | position:CSToastPositionTop]; 27 | 28 | // toast with all possible options 29 | [self.view makeToast:@"This is a piece of toast with a title & image" 30 | duration:3.0 31 | position:[NSValue valueWithCGPoint:CGPointMake(110, 110)] 32 | title:@"Toast Title" 33 | image:[UIImage imageNamed:@"toast.png"] 34 | style:nil 35 | completion:^(BOOL didTap) { 36 | if (didTap) { 37 | NSLog(@"completion from tap"); 38 | } else { 39 | NSLog(@"completion without tap"); 40 | } 41 | }]; 42 | 43 | // display toast with an activity spinner 44 | [self.view makeToastActivity:CSToastPositionCenter]; 45 | 46 | // display any view as toast 47 | [self.view showToast:myView]; 48 | ``` 49 | 50 | But wait, there's more! 51 | --------- 52 | ```objc 53 | // create a new style 54 | CSToastStyle *style = [[CSToastStyle alloc] initWithDefaultStyle]; 55 | 56 | // this is just one of many style options 57 | style.messageColor = [UIColor orangeColor]; 58 | 59 | // present the toast with the new style 60 | [self.view makeToast:@"This is a piece of toast." 61 | duration:3.0 62 | position:CSToastPositionBottom 63 | style:style]; 64 | 65 | // or perhaps you want to use this style for all toasts going forward? 66 | // just set the shared style and there's no need to provide the style again 67 | [CSToastManager setSharedStyle:style]; 68 | 69 | // toggle "tap to dismiss" functionality 70 | [CSToastManager setTapToDismissEnabled:YES]; 71 | 72 | // toggle queueing behavior 73 | [CSToastManager setQueueEnabled:YES]; 74 | 75 | // immediately hides all toast views in self.view 76 | [self.view hideAllToasts]; 77 | ``` 78 | 79 | See the demo project for more examples. 80 | 81 | Setup Instructions 82 | ------------------ 83 | 84 | [CocoaPods](http://cocoapods.org) 85 | ------------------ 86 | 87 | Install with CocoaPods by adding the following to your `Podfile`: 88 | ```ruby 89 | pod 'Toast', '~> 4.1.1' 90 | ``` 91 | 92 | [Carthage](https://github.com/Carthage/Carthage) 93 | ------------------ 94 | 95 | Install with Carthage by adding the following to your `Cartfile`: 96 | ```ogdl 97 | github "scalessec/Toast" ~> 4.1.1 98 | ``` 99 | Run `carthage update --use-xcframeworks` to build the framework and link against `Toast.xcframework`. Then, `#import `. 100 | 101 | Manually 102 | -------- 103 | 104 | 1. Add `UIView+Toast.h` & `UIView+Toast.m` to your project. 105 | 2. `#import "UIView+Toast.h"` 106 | 3. Grab yourself a cold 🍺. 107 | 108 | Privacy 109 | ----------- 110 | Toast does not collect any data. A [privacy manifest](Toast/Resources/PrivacyInfo.xcprivacy) is provided with the library. See [Apple's documentation](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files) for related details. 111 | 112 | MIT License 113 | ----------- 114 | Copyright (c) 2011-2024 Charles Scalesse. 115 | 116 | Permission is hereby granted, free of charge, to any person obtaining a 117 | copy of this software and associated documentation files (the 118 | "Software"), to deal in the Software without restriction, including 119 | without limitation the rights to use, copy, modify, merge, publish, 120 | distribute, sublicense, and/or sell copies of the Software, and to 121 | permit persons to whom the Software is furnished to do so, subject to 122 | the following conditions: 123 | 124 | The above copyright notice and this permission notice shall be included 125 | in all copies or substantial portions of the Software. 126 | 127 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 128 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 129 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 130 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 131 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 132 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 133 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 134 | -------------------------------------------------------------------------------- /Toast-Framework/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 4.1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Toast-Framework/Toast.h: -------------------------------------------------------------------------------- 1 | // 2 | // Toast.h 3 | // Toast 4 | // 5 | // Copyright 2011-2024 Charles Scalesse. All rights reserved. 6 | // 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Toast. 12 | FOUNDATION_EXPORT double ToastVersionNumber; 13 | 14 | //! Project version string for Toast. 15 | FOUNDATION_EXPORT const unsigned char ToastVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | #import 19 | -------------------------------------------------------------------------------- /Toast.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Toast" 3 | s.version = "4.1.1" 4 | s.summary = "A UIView category that adds Android-style toast notifications to iOS." 5 | s.homepage = "https://github.com/scalessec/Toast" 6 | s.license = 'MIT' 7 | s.author = { "Charles Scalesse" => "scalessec@gmail.com" } 8 | s.source = { :git => "https://github.com/scalessec/Toast.git", :tag => s.version.to_s } 9 | s.platform = :ios 10 | s.source_files = 'Toast', 'Toast-Framework/Toast.h' 11 | s.resource_bundles = {'Toast' => ['Toast/Resources/PrivacyInfo.xcprivacy']} 12 | s.framework = 'QuartzCore' 13 | s.requires_arc = true 14 | s.ios.deployment_target = '12.0' 15 | end 16 | -------------------------------------------------------------------------------- /Toast.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 151FC0D713D34B7A007DC282 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 151FC0D613D34B7A007DC282 /* UIKit.framework */; }; 11 | 151FC0D913D34B7A007DC282 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 151FC0D813D34B7A007DC282 /* Foundation.framework */; }; 12 | 151FC0DB13D34B7A007DC282 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 151FC0DA13D34B7A007DC282 /* CoreGraphics.framework */; }; 13 | 151FC12B13D3558E007DC282 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 151FC12A13D3558E007DC282 /* main.m */; }; 14 | 151FC13413D36FCB007DC282 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 151FC13313D36FCB007DC282 /* QuartzCore.framework */; }; 15 | 152C43F41DCC1E0E00430487 /* Toast.h in Headers */ = {isa = PBXBuildFile; fileRef = 152C43F21DCC1E0E00430487 /* Toast.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | 152C44201DCC226200430487 /* UIView+Toast.m in Sources */ = {isa = PBXBuildFile; fileRef = 1536A60516B0E0CF0021C622 /* UIView+Toast.m */; }; 17 | 152C44441DCC246700430487 /* UIView+Toast.h in Headers */ = {isa = PBXBuildFile; fileRef = 1536A60416B0E0CF0021C622 /* UIView+Toast.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | 1536A60616B0E0CF0021C622 /* UIView+Toast.m in Sources */ = {isa = PBXBuildFile; fileRef = 1536A60516B0E0CF0021C622 /* UIView+Toast.m */; }; 19 | 1536A60D16B0E0D60021C622 /* CSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1536A60916B0E0D60021C622 /* CSAppDelegate.m */; }; 20 | 1536A60E16B0E0D60021C622 /* CSViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1536A60B16B0E0D60021C622 /* CSViewController.m */; }; 21 | 1566460D1FC1ED8D00ECDFCE /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1566460C1FC1ED8D00ECDFCE /* Images.xcassets */; }; 22 | 15F305681FC1EFAF0058458A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 15F305671FC1EFAF0058458A /* LaunchScreen.storyboard */; }; 23 | 56E531712B4A3AF50034D6F5 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 56E531702B4A3AF50034D6F5 /* PrivacyInfo.xcprivacy */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 151FC0D213D34B7A007DC282 /* Toast.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Toast.app; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 151FC0D613D34B7A007DC282 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 29 | 151FC0D813D34B7A007DC282 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 30 | 151FC0DA13D34B7A007DC282 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 31 | 151FC12713D35583007DC282 /* Toast-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "Toast-Info.plist"; path = "Example/Toast-Info.plist"; sourceTree = SOURCE_ROOT; }; 32 | 151FC12813D35583007DC282 /* Toast-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "Toast-Prefix.pch"; path = "Example/Toast-Prefix.pch"; sourceTree = SOURCE_ROOT; }; 33 | 151FC12A13D3558E007DC282 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Example/main.m; sourceTree = SOURCE_ROOT; }; 34 | 151FC13313D36FCB007DC282 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 35 | 152C43F01DCC1E0E00430487 /* Toast.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Toast.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 152C43F21DCC1E0E00430487 /* Toast.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Toast.h; path = "../Toast-Framework/Toast.h"; sourceTree = ""; }; 37 | 152C43F31DCC1E0E00430487 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../Toast-Framework/Info.plist"; sourceTree = ""; }; 38 | 1536A60416B0E0CF0021C622 /* UIView+Toast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Toast.h"; sourceTree = ""; }; 39 | 1536A60516B0E0CF0021C622 /* UIView+Toast.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Toast.m"; sourceTree = ""; }; 40 | 1536A60816B0E0D60021C622 /* CSAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSAppDelegate.h; sourceTree = ""; }; 41 | 1536A60916B0E0D60021C622 /* CSAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSAppDelegate.m; sourceTree = ""; }; 42 | 1536A60A16B0E0D60021C622 /* CSViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSViewController.h; sourceTree = ""; }; 43 | 1536A60B16B0E0D60021C622 /* CSViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSViewController.m; sourceTree = ""; }; 44 | 1566460C1FC1ED8D00ECDFCE /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Example/Resources/Images.xcassets; sourceTree = SOURCE_ROOT; }; 45 | 15F305671FC1EFAF0058458A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; 46 | 56E531702B4A3AF50034D6F5 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | 151FC0CF13D34B7A007DC282 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | 151FC13413D36FCB007DC282 /* QuartzCore.framework in Frameworks */, 55 | 151FC0D713D34B7A007DC282 /* UIKit.framework in Frameworks */, 56 | 151FC0D913D34B7A007DC282 /* Foundation.framework in Frameworks */, 57 | 151FC0DB13D34B7A007DC282 /* CoreGraphics.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | 152C43EC1DCC1E0E00430487 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 151FC0C713D34B79007DC282 = { 72 | isa = PBXGroup; 73 | children = ( 74 | 1536A60316B0E0CF0021C622 /* Toast */, 75 | 1536A60716B0E0D60021C622 /* Example */, 76 | 152C43F11DCC1E0E00430487 /* Carthage */, 77 | 151FC0D513D34B7A007DC282 /* Frameworks */, 78 | 151FC0D313D34B7A007DC282 /* Products */, 79 | ); 80 | sourceTree = ""; 81 | }; 82 | 151FC0D313D34B7A007DC282 /* Products */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 151FC0D213D34B7A007DC282 /* Toast.app */, 86 | 152C43F01DCC1E0E00430487 /* Toast.framework */, 87 | ); 88 | name = Products; 89 | sourceTree = ""; 90 | }; 91 | 151FC0D513D34B7A007DC282 /* Frameworks */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 151FC0D613D34B7A007DC282 /* UIKit.framework */, 95 | 151FC0D813D34B7A007DC282 /* Foundation.framework */, 96 | 151FC0DA13D34B7A007DC282 /* CoreGraphics.framework */, 97 | 151FC13313D36FCB007DC282 /* QuartzCore.framework */, 98 | ); 99 | name = Frameworks; 100 | sourceTree = ""; 101 | }; 102 | 151FC0DD13D34B7A007DC282 /* Supporting Files */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 151FC12713D35583007DC282 /* Toast-Info.plist */, 106 | 151FC12813D35583007DC282 /* Toast-Prefix.pch */, 107 | 151FC12A13D3558E007DC282 /* main.m */, 108 | ); 109 | name = "Supporting Files"; 110 | path = ../..; 111 | sourceTree = ""; 112 | }; 113 | 152C43F11DCC1E0E00430487 /* Carthage */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 152C43F21DCC1E0E00430487 /* Toast.h */, 117 | 152C43F31DCC1E0E00430487 /* Info.plist */, 118 | ); 119 | name = Carthage; 120 | path = Toast; 121 | sourceTree = ""; 122 | }; 123 | 1536A60316B0E0CF0021C622 /* Toast */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 1536A60416B0E0CF0021C622 /* UIView+Toast.h */, 127 | 1536A60516B0E0CF0021C622 /* UIView+Toast.m */, 128 | 56E5316F2B4A3ACA0034D6F5 /* Resources */, 129 | ); 130 | path = Toast; 131 | sourceTree = SOURCE_ROOT; 132 | }; 133 | 1536A60716B0E0D60021C622 /* Example */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 1536A60816B0E0D60021C622 /* CSAppDelegate.h */, 137 | 1536A60916B0E0D60021C622 /* CSAppDelegate.m */, 138 | 1536A60A16B0E0D60021C622 /* CSViewController.h */, 139 | 1536A60B16B0E0D60021C622 /* CSViewController.m */, 140 | 151FC0DD13D34B7A007DC282 /* Supporting Files */, 141 | 1536A61016B0E0E20021C622 /* Resources */, 142 | ); 143 | path = Example; 144 | sourceTree = SOURCE_ROOT; 145 | }; 146 | 1536A61016B0E0E20021C622 /* Resources */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 1566460C1FC1ED8D00ECDFCE /* Images.xcassets */, 150 | 15F305671FC1EFAF0058458A /* LaunchScreen.storyboard */, 151 | ); 152 | path = Resources; 153 | sourceTree = ""; 154 | }; 155 | 56E5316F2B4A3ACA0034D6F5 /* Resources */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | 56E531702B4A3AF50034D6F5 /* PrivacyInfo.xcprivacy */, 159 | ); 160 | path = Resources; 161 | sourceTree = ""; 162 | }; 163 | /* End PBXGroup section */ 164 | 165 | /* Begin PBXHeadersBuildPhase section */ 166 | 152C43ED1DCC1E0E00430487 /* Headers */ = { 167 | isa = PBXHeadersBuildPhase; 168 | buildActionMask = 2147483647; 169 | files = ( 170 | 152C43F41DCC1E0E00430487 /* Toast.h in Headers */, 171 | 152C44441DCC246700430487 /* UIView+Toast.h in Headers */, 172 | ); 173 | runOnlyForDeploymentPostprocessing = 0; 174 | }; 175 | /* End PBXHeadersBuildPhase section */ 176 | 177 | /* Begin PBXNativeTarget section */ 178 | 151FC0D113D34B7A007DC282 /* Toast-Example */ = { 179 | isa = PBXNativeTarget; 180 | buildConfigurationList = 151FC0F313D34B7A007DC282 /* Build configuration list for PBXNativeTarget "Toast-Example" */; 181 | buildPhases = ( 182 | 151FC0CE13D34B7A007DC282 /* Sources */, 183 | 151FC0CF13D34B7A007DC282 /* Frameworks */, 184 | 151FC0D013D34B7A007DC282 /* Resources */, 185 | ); 186 | buildRules = ( 187 | ); 188 | dependencies = ( 189 | ); 190 | name = "Toast-Example"; 191 | productName = ToastTest; 192 | productReference = 151FC0D213D34B7A007DC282 /* Toast.app */; 193 | productType = "com.apple.product-type.application"; 194 | }; 195 | 152C43EF1DCC1E0E00430487 /* Toast */ = { 196 | isa = PBXNativeTarget; 197 | buildConfigurationList = 152C43F51DCC1E0E00430487 /* Build configuration list for PBXNativeTarget "Toast" */; 198 | buildPhases = ( 199 | 152C43EB1DCC1E0E00430487 /* Sources */, 200 | 152C43EC1DCC1E0E00430487 /* Frameworks */, 201 | 152C43ED1DCC1E0E00430487 /* Headers */, 202 | 152C43EE1DCC1E0E00430487 /* Resources */, 203 | ); 204 | buildRules = ( 205 | ); 206 | dependencies = ( 207 | ); 208 | name = Toast; 209 | productName = Toast; 210 | productReference = 152C43F01DCC1E0E00430487 /* Toast.framework */; 211 | productType = "com.apple.product-type.framework"; 212 | }; 213 | /* End PBXNativeTarget section */ 214 | 215 | /* Begin PBXProject section */ 216 | 151FC0C913D34B79007DC282 /* Project object */ = { 217 | isa = PBXProject; 218 | attributes = { 219 | BuildIndependentTargetsInParallel = YES; 220 | LastUpgradeCheck = 1500; 221 | TargetAttributes = { 222 | 151FC0D113D34B7A007DC282 = { 223 | DevelopmentTeam = 3NC359BQ4D; 224 | }; 225 | 152C43EF1DCC1E0E00430487 = { 226 | CreatedOnToolsVersion = 8.0; 227 | ProvisioningStyle = Automatic; 228 | }; 229 | }; 230 | }; 231 | buildConfigurationList = 151FC0CC13D34B79007DC282 /* Build configuration list for PBXProject "Toast" */; 232 | compatibilityVersion = "Xcode 3.2"; 233 | developmentRegion = English; 234 | hasScannedForEncodings = 0; 235 | knownRegions = ( 236 | English, 237 | en, 238 | ); 239 | mainGroup = 151FC0C713D34B79007DC282; 240 | productRefGroup = 151FC0D313D34B7A007DC282 /* Products */; 241 | projectDirPath = ""; 242 | projectRoot = ""; 243 | targets = ( 244 | 151FC0D113D34B7A007DC282 /* Toast-Example */, 245 | 152C43EF1DCC1E0E00430487 /* Toast */, 246 | ); 247 | }; 248 | /* End PBXProject section */ 249 | 250 | /* Begin PBXResourcesBuildPhase section */ 251 | 151FC0D013D34B7A007DC282 /* Resources */ = { 252 | isa = PBXResourcesBuildPhase; 253 | buildActionMask = 2147483647; 254 | files = ( 255 | 1566460D1FC1ED8D00ECDFCE /* Images.xcassets in Resources */, 256 | 15F305681FC1EFAF0058458A /* LaunchScreen.storyboard in Resources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | 152C43EE1DCC1E0E00430487 /* Resources */ = { 261 | isa = PBXResourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | 56E531712B4A3AF50034D6F5 /* PrivacyInfo.xcprivacy in Resources */, 265 | ); 266 | runOnlyForDeploymentPostprocessing = 0; 267 | }; 268 | /* End PBXResourcesBuildPhase section */ 269 | 270 | /* Begin PBXSourcesBuildPhase section */ 271 | 151FC0CE13D34B7A007DC282 /* Sources */ = { 272 | isa = PBXSourcesBuildPhase; 273 | buildActionMask = 2147483647; 274 | files = ( 275 | 1536A60616B0E0CF0021C622 /* UIView+Toast.m in Sources */, 276 | 1536A60D16B0E0D60021C622 /* CSAppDelegate.m in Sources */, 277 | 1536A60E16B0E0D60021C622 /* CSViewController.m in Sources */, 278 | 151FC12B13D3558E007DC282 /* main.m in Sources */, 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | 152C43EB1DCC1E0E00430487 /* Sources */ = { 283 | isa = PBXSourcesBuildPhase; 284 | buildActionMask = 2147483647; 285 | files = ( 286 | 152C44201DCC226200430487 /* UIView+Toast.m in Sources */, 287 | ); 288 | runOnlyForDeploymentPostprocessing = 0; 289 | }; 290 | /* End PBXSourcesBuildPhase section */ 291 | 292 | /* Begin XCBuildConfiguration section */ 293 | 151FC0F113D34B7A007DC282 /* Debug */ = { 294 | isa = XCBuildConfiguration; 295 | buildSettings = { 296 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 297 | CLANG_ENABLE_OBJC_ARC = YES; 298 | CODE_SIGN_IDENTITY = "iPhone Developer"; 299 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 300 | ENABLE_TESTABILITY = YES; 301 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 302 | GCC_C_LANGUAGE_STANDARD = gnu99; 303 | GCC_OPTIMIZATION_LEVEL = 0; 304 | GCC_PREPROCESSOR_DEFINITIONS = DEBUG; 305 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 306 | GCC_VERSION = com.apple.compilers.llvmgcc42; 307 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 308 | GCC_WARN_UNUSED_VARIABLE = YES; 309 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 310 | MARKETING_VERSION = 4.1.0; 311 | ONLY_ACTIVE_ARCH = YES; 312 | PROVISIONING_PROFILE = ""; 313 | "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; 314 | SDKROOT = iphoneos; 315 | }; 316 | name = Debug; 317 | }; 318 | 151FC0F213D34B7A007DC282 /* Release */ = { 319 | isa = XCBuildConfiguration; 320 | buildSettings = { 321 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 322 | CLANG_ENABLE_OBJC_ARC = YES; 323 | CODE_SIGN_IDENTITY = "iPhone Developer"; 324 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 325 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 326 | GCC_C_LANGUAGE_STANDARD = gnu99; 327 | GCC_VERSION = com.apple.compilers.llvmgcc42; 328 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 329 | GCC_WARN_UNUSED_VARIABLE = YES; 330 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 331 | MARKETING_VERSION = 4.1.0; 332 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 333 | PROVISIONING_PROFILE = ""; 334 | "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; 335 | SDKROOT = iphoneos; 336 | }; 337 | name = Release; 338 | }; 339 | 151FC0F413D34B7A007DC282 /* Debug */ = { 340 | isa = XCBuildConfiguration; 341 | buildSettings = { 342 | ALWAYS_SEARCH_USER_PATHS = NO; 343 | CLANG_ENABLE_OBJC_ARC = YES; 344 | CODE_SIGN_IDENTITY = "iPhone Developer"; 345 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 346 | COPY_PHASE_STRIP = NO; 347 | GCC_DYNAMIC_NO_PIC = NO; 348 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 349 | GCC_PREFIX_HEADER = "Example/Toast-Prefix.pch"; 350 | GCC_VERSION = ""; 351 | INFOPLIST_FILE = "Example/Toast-Info.plist"; 352 | PRODUCT_BUNDLE_IDENTIFIER = com.scalessec.toastexample; 353 | PRODUCT_NAME = Toast; 354 | PROVISIONING_PROFILE = ""; 355 | "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | WRAPPER_EXTENSION = app; 358 | }; 359 | name = Debug; 360 | }; 361 | 151FC0F513D34B7A007DC282 /* Release */ = { 362 | isa = XCBuildConfiguration; 363 | buildSettings = { 364 | ALWAYS_SEARCH_USER_PATHS = NO; 365 | CLANG_ENABLE_OBJC_ARC = YES; 366 | CODE_SIGN_IDENTITY = "iPhone Developer"; 367 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 368 | COPY_PHASE_STRIP = YES; 369 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 370 | GCC_PREFIX_HEADER = "Example/Toast-Prefix.pch"; 371 | GCC_VERSION = ""; 372 | INFOPLIST_FILE = "Example/Toast-Info.plist"; 373 | PRODUCT_BUNDLE_IDENTIFIER = com.scalessec.toastexample; 374 | PRODUCT_NAME = Toast; 375 | PROVISIONING_PROFILE = ""; 376 | "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; 377 | TARGETED_DEVICE_FAMILY = "1,2"; 378 | VALIDATE_PRODUCT = YES; 379 | WRAPPER_EXTENSION = app; 380 | }; 381 | name = Release; 382 | }; 383 | 152C43F61DCC1E0E00430487 /* Debug */ = { 384 | isa = XCBuildConfiguration; 385 | buildSettings = { 386 | ALWAYS_SEARCH_USER_PATHS = NO; 387 | CLANG_ANALYZER_NONNULL = YES; 388 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 389 | CLANG_CXX_LIBRARY = "libc++"; 390 | CLANG_ENABLE_MODULES = YES; 391 | CLANG_WARN_BOOL_CONVERSION = YES; 392 | CLANG_WARN_CONSTANT_CONVERSION = YES; 393 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 394 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 395 | CLANG_WARN_EMPTY_BODY = YES; 396 | CLANG_WARN_ENUM_CONVERSION = YES; 397 | CLANG_WARN_INFINITE_RECURSION = YES; 398 | CLANG_WARN_INT_CONVERSION = YES; 399 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 400 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 401 | CLANG_WARN_UNREACHABLE_CODE = YES; 402 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 403 | CODE_SIGN_IDENTITY = ""; 404 | COPY_PHASE_STRIP = NO; 405 | CURRENT_PROJECT_VERSION = 1; 406 | DEBUG_INFORMATION_FORMAT = dwarf; 407 | DEFINES_MODULE = YES; 408 | DYLIB_COMPATIBILITY_VERSION = 1; 409 | DYLIB_CURRENT_VERSION = 1; 410 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 411 | ENABLE_MODULE_VERIFIER = YES; 412 | ENABLE_STRICT_OBJC_MSGSEND = YES; 413 | GCC_DYNAMIC_NO_PIC = NO; 414 | GCC_NO_COMMON_BLOCKS = YES; 415 | GCC_PREPROCESSOR_DEFINITIONS = ( 416 | "DEBUG=1", 417 | "$(inherited)", 418 | ); 419 | GCC_VERSION = ""; 420 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 421 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 422 | GCC_WARN_UNDECLARED_SELECTOR = YES; 423 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 424 | GCC_WARN_UNUSED_FUNCTION = YES; 425 | INFOPLIST_FILE = "Toast-Framework/Info.plist"; 426 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 427 | LD_RUNPATH_SEARCH_PATHS = ( 428 | "$(inherited)", 429 | "@executable_path/Frameworks", 430 | "@loader_path/Frameworks", 431 | ); 432 | MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; 433 | MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; 434 | MTL_ENABLE_DEBUG_INFO = YES; 435 | PRODUCT_BUNDLE_IDENTIFIER = com.scalessec.Toast; 436 | PRODUCT_NAME = "$(TARGET_NAME)"; 437 | SKIP_INSTALL = YES; 438 | TARGETED_DEVICE_FAMILY = "1,2"; 439 | VERSIONING_SYSTEM = "apple-generic"; 440 | VERSION_INFO_PREFIX = ""; 441 | }; 442 | name = Debug; 443 | }; 444 | 152C43F71DCC1E0E00430487 /* Release */ = { 445 | isa = XCBuildConfiguration; 446 | buildSettings = { 447 | ALWAYS_SEARCH_USER_PATHS = NO; 448 | CLANG_ANALYZER_NONNULL = YES; 449 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 450 | CLANG_CXX_LIBRARY = "libc++"; 451 | CLANG_ENABLE_MODULES = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_CONSTANT_CONVERSION = YES; 454 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 455 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 456 | CLANG_WARN_EMPTY_BODY = YES; 457 | CLANG_WARN_ENUM_CONVERSION = YES; 458 | CLANG_WARN_INFINITE_RECURSION = YES; 459 | CLANG_WARN_INT_CONVERSION = YES; 460 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 461 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 462 | CLANG_WARN_UNREACHABLE_CODE = YES; 463 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 464 | CODE_SIGN_IDENTITY = ""; 465 | COPY_PHASE_STRIP = NO; 466 | CURRENT_PROJECT_VERSION = 1; 467 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 468 | DEFINES_MODULE = YES; 469 | DYLIB_COMPATIBILITY_VERSION = 1; 470 | DYLIB_CURRENT_VERSION = 1; 471 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 472 | ENABLE_MODULE_VERIFIER = YES; 473 | ENABLE_NS_ASSERTIONS = NO; 474 | ENABLE_STRICT_OBJC_MSGSEND = YES; 475 | GCC_NO_COMMON_BLOCKS = YES; 476 | GCC_VERSION = ""; 477 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 478 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 479 | GCC_WARN_UNDECLARED_SELECTOR = YES; 480 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 481 | GCC_WARN_UNUSED_FUNCTION = YES; 482 | INFOPLIST_FILE = "Toast-Framework/Info.plist"; 483 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 484 | LD_RUNPATH_SEARCH_PATHS = ( 485 | "$(inherited)", 486 | "@executable_path/Frameworks", 487 | "@loader_path/Frameworks", 488 | ); 489 | MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; 490 | MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; 491 | MTL_ENABLE_DEBUG_INFO = NO; 492 | PRODUCT_BUNDLE_IDENTIFIER = com.scalessec.Toast; 493 | PRODUCT_NAME = "$(TARGET_NAME)"; 494 | SKIP_INSTALL = YES; 495 | TARGETED_DEVICE_FAMILY = "1,2"; 496 | VALIDATE_PRODUCT = YES; 497 | VERSIONING_SYSTEM = "apple-generic"; 498 | VERSION_INFO_PREFIX = ""; 499 | }; 500 | name = Release; 501 | }; 502 | /* End XCBuildConfiguration section */ 503 | 504 | /* Begin XCConfigurationList section */ 505 | 151FC0CC13D34B79007DC282 /* Build configuration list for PBXProject "Toast" */ = { 506 | isa = XCConfigurationList; 507 | buildConfigurations = ( 508 | 151FC0F113D34B7A007DC282 /* Debug */, 509 | 151FC0F213D34B7A007DC282 /* Release */, 510 | ); 511 | defaultConfigurationIsVisible = 0; 512 | defaultConfigurationName = Release; 513 | }; 514 | 151FC0F313D34B7A007DC282 /* Build configuration list for PBXNativeTarget "Toast-Example" */ = { 515 | isa = XCConfigurationList; 516 | buildConfigurations = ( 517 | 151FC0F413D34B7A007DC282 /* Debug */, 518 | 151FC0F513D34B7A007DC282 /* Release */, 519 | ); 520 | defaultConfigurationIsVisible = 0; 521 | defaultConfigurationName = Release; 522 | }; 523 | 152C43F51DCC1E0E00430487 /* Build configuration list for PBXNativeTarget "Toast" */ = { 524 | isa = XCConfigurationList; 525 | buildConfigurations = ( 526 | 152C43F61DCC1E0E00430487 /* Debug */, 527 | 152C43F71DCC1E0E00430487 /* Release */, 528 | ); 529 | defaultConfigurationIsVisible = 0; 530 | defaultConfigurationName = Release; 531 | }; 532 | /* End XCConfigurationList section */ 533 | }; 534 | rootObject = 151FC0C913D34B79007DC282 /* Project object */; 535 | } 536 | -------------------------------------------------------------------------------- /Toast.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Toast.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Toast.xcodeproj/project.xcworkspace/xcshareddata/Toast.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 99D81D0A-2E13-4408-B32F-F65CC95B20BE 9 | IDESourceControlProjectName 10 | Toast 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 775E195363DA268E1E1924859088052B9B8BE981 14 | https://github.com/scalessec/Toast.git 15 | 16 | IDESourceControlProjectPath 17 | Toast.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 775E195363DA268E1E1924859088052B9B8BE981 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/scalessec/Toast.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 775E195363DA268E1E1924859088052B9B8BE981 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 775E195363DA268E1E1924859088052B9B8BE981 36 | IDESourceControlWCCName 37 | Toast 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Toast.xcodeproj/xcshareddata/xcschemes/Toast-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 54 | 56 | 62 | 63 | 64 | 65 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Toast.xcodeproj/xcshareddata/xcschemes/Toast.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Toast/Resources/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyTracking 6 | 7 | NSPrivacyCollectedDataTypes 8 | 9 | NSPrivacyTrackingDomains 10 | 11 | NSPrivacyAccessedAPITypes 12 | 13 | 14 | -------------------------------------------------------------------------------- /Toast/UIView+Toast.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Toast.h 3 | // Toast 4 | // 5 | // Copyright (c) 2011-2024 Charles Scalesse. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a 8 | // copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included 16 | // in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | #import 27 | 28 | extern const NSString * CSToastPositionTop; 29 | extern const NSString * CSToastPositionCenter; 30 | extern const NSString * CSToastPositionBottom; 31 | 32 | @class CSToastStyle; 33 | 34 | /** 35 | Toast is an Objective-C category that adds toast notifications to the UIView 36 | object class. It is intended to be simple, lightweight, and easy to use. Most 37 | toast notifications can be triggered with a single line of code. 38 | 39 | The `makeToast:` methods create a new view and then display it as toast. 40 | 41 | The `showToast:` methods display any view as toast. 42 | 43 | */ 44 | @interface UIView (Toast) 45 | 46 | /** 47 | Creates and presents a new toast view with a message and displays it with the 48 | default duration and position. Styled using the shared style. 49 | 50 | @param message The message to be displayed 51 | */ 52 | - (void)makeToast:(NSString *)message; 53 | 54 | /** 55 | Creates and presents a new toast view with a message. Duration and position 56 | can be set explicitly. Styled using the shared style. 57 | 58 | @param message The message to be displayed 59 | @param duration The toast duration 60 | @param position The toast's center point. Can be one of the predefined CSToastPosition 61 | constants or a `CGPoint` wrapped in an `NSValue` object. 62 | */ 63 | - (void)makeToast:(NSString *)message 64 | duration:(NSTimeInterval)duration 65 | position:(id)position; 66 | 67 | /** 68 | Creates and presents a new toast view with a message. Duration, position, and 69 | style can be set explicitly. 70 | 71 | @param message The message to be displayed 72 | @param duration The toast duration 73 | @param position The toast's center point. Can be one of the predefined CSToastPosition 74 | constants or a `CGPoint` wrapped in an `NSValue` object. 75 | @param style The style. The shared style will be used when nil 76 | */ 77 | - (void)makeToast:(NSString *)message 78 | duration:(NSTimeInterval)duration 79 | position:(id)position 80 | style:(CSToastStyle *)style; 81 | 82 | /** 83 | Creates and presents a new toast view with a message, title, and image. Duration, 84 | position, and style can be set explicitly. The completion block executes when the 85 | toast view completes. `didTap` will be `YES` if the toast view was dismissed from 86 | a tap. 87 | 88 | @param message The message to be displayed 89 | @param duration The toast duration 90 | @param position The toast's center point. Can be one of the predefined CSToastPosition 91 | constants or a `CGPoint` wrapped in an `NSValue` object. 92 | @param title The title 93 | @param image The image 94 | @param style The style. The shared style will be used when nil 95 | @param completion The completion block, executed after the toast view disappears. 96 | didTap will be `YES` if the toast view was dismissed from a tap. 97 | */ 98 | - (void)makeToast:(NSString *)message 99 | duration:(NSTimeInterval)duration 100 | position:(id)position 101 | title:(NSString *)title 102 | image:(UIImage *)image 103 | style:(CSToastStyle *)style 104 | completion:(void(^)(BOOL didTap))completion; 105 | 106 | /** 107 | Creates a new toast view with any combination of message, title, and image. 108 | The look and feel is configured via the style. Unlike the `makeToast:` methods, 109 | this method does not present the toast view automatically. One of the showToast: 110 | methods must be used to present the resulting view. 111 | 112 | @warning if message, title, and image are all nil, this method will return nil. 113 | 114 | @param message The message to be displayed 115 | @param title The title 116 | @param image The image 117 | @param style The style. The shared style will be used when nil 118 | @return The newly created toast view 119 | */ 120 | - (UIView *)toastViewForMessage:(NSString *)message 121 | title:(NSString *)title 122 | image:(UIImage *)image 123 | style:(CSToastStyle *)style; 124 | 125 | /** 126 | Hides the active toast. If there are multiple toasts active in a view, this method 127 | hides the oldest toast (the first of the toasts to have been presented). 128 | 129 | @see `hideAllToasts` to remove all active toasts from a view. 130 | 131 | @warning This method has no effect on activity toasts. Use `hideToastActivity` to 132 | hide activity toasts. 133 | */ 134 | - (void)hideToast; 135 | 136 | /** 137 | Hides an active toast. 138 | 139 | @param toast The active toast view to dismiss. Any toast that is currently being displayed 140 | on the screen is considered active. 141 | 142 | @warning this does not clear a toast view that is currently waiting in the queue. 143 | */ 144 | - (void)hideToast:(UIView *)toast; 145 | 146 | /** 147 | Hides all active toast views and clears the queue. 148 | */ 149 | - (void)hideAllToasts; 150 | 151 | /** 152 | Hides all active toast views, with options to hide activity and clear the queue. 153 | 154 | @param includeActivity If `true`, toast activity will also be hidden. Default is `false`. 155 | @param clearQueue If `true`, removes all toast views from the queue. Default is `true`. 156 | */ 157 | - (void)hideAllToasts:(BOOL)includeActivity clearQueue:(BOOL)clearQueue; 158 | 159 | /** 160 | Removes all toast views from the queue. This has no effect on toast views that are 161 | active. Use `hideAllToasts` to hide the active toasts views and clear the queue. 162 | */ 163 | - (void)clearToastQueue; 164 | 165 | /** 166 | Creates and displays a new toast activity indicator view at a specified position. 167 | 168 | @warning Only one toast activity indicator view can be presented per superview. Subsequent 169 | calls to `makeToastActivity:` will be ignored until hideToastActivity is called. 170 | 171 | @warning `makeToastActivity:` works independently of the showToast: methods. Toast activity 172 | views can be presented and dismissed while toast views are being displayed. `makeToastActivity:` 173 | has no effect on the queueing behavior of the showToast: methods. 174 | 175 | @param position The toast's center point. Can be one of the predefined CSToastPosition 176 | constants or a `CGPoint` wrapped in an `NSValue` object. 177 | */ 178 | - (void)makeToastActivity:(id)position; 179 | 180 | /** 181 | Dismisses the active toast activity indicator view. 182 | */ 183 | - (void)hideToastActivity; 184 | 185 | /** 186 | Displays any view as toast using the default duration and position. 187 | 188 | @param toast The view to be displayed as toast 189 | */ 190 | - (void)showToast:(UIView *)toast; 191 | 192 | /** 193 | Displays any view as toast at a provided position and duration. The completion block 194 | executes when the toast view completes. `didTap` will be `YES` if the toast view was 195 | dismissed from a tap. 196 | 197 | @param toast The view to be displayed as toast 198 | @param duration The notification duration 199 | @param position The toast's center point. Can be one of the predefined CSToastPosition 200 | constants or a `CGPoint` wrapped in an `NSValue` object. 201 | @param completion The completion block, executed after the toast view disappears. 202 | didTap will be `YES` if the toast view was dismissed from a tap. 203 | */ 204 | - (void)showToast:(UIView *)toast 205 | duration:(NSTimeInterval)duration 206 | position:(id)position 207 | completion:(void(^)(BOOL didTap))completion; 208 | 209 | @end 210 | 211 | /** 212 | `CSToastStyle` instances define the look and feel for toast views created via the 213 | `makeToast:` methods as well for toast views created directly with 214 | `toastViewForMessage:title:image:style:`. 215 | 216 | @warning `CSToastStyle` offers relatively simple styling options for the default 217 | toast view. If you require a toast view with more complex UI, it probably makes more 218 | sense to create your own custom UIView subclass and present it with the `showToast:` 219 | methods. 220 | */ 221 | @interface CSToastStyle : NSObject 222 | 223 | /** 224 | The background color. Default is `[UIColor blackColor]` at 80% opacity. 225 | */ 226 | @property (strong, nonatomic) UIColor *backgroundColor; 227 | 228 | /** 229 | The title color. Default is `[UIColor whiteColor]`. 230 | */ 231 | @property (strong, nonatomic) UIColor *titleColor; 232 | 233 | /** 234 | The message color. Default is `[UIColor whiteColor]`. 235 | */ 236 | @property (strong, nonatomic) UIColor *messageColor; 237 | 238 | /** 239 | A percentage value from 0.0 to 1.0, representing the maximum width of the toast 240 | view relative to it's superview. Default is 0.8 (80% of the superview's width). 241 | */ 242 | @property (assign, nonatomic) CGFloat maxWidthPercentage; 243 | 244 | /** 245 | A percentage value from 0.0 to 1.0, representing the maximum height of the toast 246 | view relative to it's superview. Default is 0.8 (80% of the superview's height). 247 | */ 248 | @property (assign, nonatomic) CGFloat maxHeightPercentage; 249 | 250 | /** 251 | The spacing from the horizontal edge of the toast view to the content. When an image 252 | is present, this is also used as the padding between the image and the text. 253 | Default is 10.0. 254 | */ 255 | @property (assign, nonatomic) CGFloat horizontalPadding; 256 | 257 | /** 258 | The spacing from the vertical edge of the toast view to the content. When a title 259 | is present, this is also used as the padding between the title and the message. 260 | Default is 10.0. 261 | */ 262 | @property (assign, nonatomic) CGFloat verticalPadding; 263 | 264 | /** 265 | The corner radius. Default is 10.0. 266 | */ 267 | @property (assign, nonatomic) CGFloat cornerRadius; 268 | 269 | /** 270 | The title font. Default is `[UIFont boldSystemFontOfSize:16.0]`. 271 | */ 272 | @property (strong, nonatomic) UIFont *titleFont; 273 | 274 | /** 275 | The message font. Default is `[UIFont systemFontOfSize:16.0]`. 276 | */ 277 | @property (strong, nonatomic) UIFont *messageFont; 278 | 279 | /** 280 | The title text alignment. Default is `NSTextAlignmentLeft`. 281 | */ 282 | @property (assign, nonatomic) NSTextAlignment titleAlignment; 283 | 284 | /** 285 | The message text alignment. Default is `NSTextAlignmentLeft`. 286 | */ 287 | @property (assign, nonatomic) NSTextAlignment messageAlignment; 288 | 289 | /** 290 | The maximum number of lines for the title. The default is 0 (no limit). 291 | */ 292 | @property (assign, nonatomic) NSInteger titleNumberOfLines; 293 | 294 | /** 295 | The maximum number of lines for the message. The default is 0 (no limit). 296 | */ 297 | @property (assign, nonatomic) NSInteger messageNumberOfLines; 298 | 299 | /** 300 | Enable or disable a shadow on the toast view. Default is `NO`. 301 | */ 302 | @property (assign, nonatomic) BOOL displayShadow; 303 | 304 | /** 305 | The shadow color. Default is `[UIColor blackColor]`. 306 | */ 307 | @property (strong, nonatomic) UIColor *shadowColor; 308 | 309 | /** 310 | A value from 0.0 to 1.0, representing the opacity of the shadow. 311 | Default is 0.8 (80% opacity). 312 | */ 313 | @property (assign, nonatomic) CGFloat shadowOpacity; 314 | 315 | /** 316 | The shadow radius. Default is 6.0. 317 | */ 318 | @property (assign, nonatomic) CGFloat shadowRadius; 319 | 320 | /** 321 | The shadow offset. The default is `CGSizeMake(4.0, 4.0)`. 322 | */ 323 | @property (assign, nonatomic) CGSize shadowOffset; 324 | 325 | /** 326 | The image size. The default is `CGSizeMake(80.0, 80.0)`. 327 | */ 328 | @property (assign, nonatomic) CGSize imageSize; 329 | 330 | /** 331 | The size of the toast activity view when `makeToastActivity:` is called. 332 | Default is `CGSizeMake(100.0, 100.0)`. 333 | */ 334 | @property (assign, nonatomic) CGSize activitySize; 335 | 336 | /** 337 | The fade in/out animation duration. Default is 0.2. 338 | */ 339 | @property (assign, nonatomic) NSTimeInterval fadeDuration; 340 | 341 | /** 342 | Creates a new instance of `CSToastStyle` with all the default values set. 343 | */ 344 | - (instancetype)initWithDefaultStyle NS_DESIGNATED_INITIALIZER; 345 | 346 | /** 347 | @warning Only the designated initializer should be used to create 348 | an instance of `CSToastStyle`. 349 | */ 350 | - (instancetype)init NS_UNAVAILABLE; 351 | 352 | @end 353 | 354 | /** 355 | `CSToastManager` provides general configuration options for all toast 356 | notifications. Backed by a singleton instance. 357 | */ 358 | @interface CSToastManager : NSObject 359 | 360 | /** 361 | Sets the shared style on the singleton. The shared style is used whenever 362 | a `makeToast:` method (or `toastViewForMessage:title:image:style:`) is called 363 | with with a nil style. By default, this is set to `CSToastStyle`'s default 364 | style. 365 | 366 | @param sharedStyle the shared style 367 | */ 368 | + (void)setSharedStyle:(CSToastStyle *)sharedStyle; 369 | 370 | /** 371 | Gets the shared style from the singlton. By default, this is 372 | `CSToastStyle`'s default style. 373 | 374 | @return the shared style 375 | */ 376 | + (CSToastStyle *)sharedStyle; 377 | 378 | /** 379 | Enables or disables tap to dismiss on toast views. Default is `YES`. 380 | 381 | @param tapToDismissEnabled YES or NO 382 | */ 383 | + (void)setTapToDismissEnabled:(BOOL)tapToDismissEnabled; 384 | 385 | /** 386 | Returns `YES` if tap to dismiss is enabled, otherwise `NO`. 387 | Default is `YES`. 388 | 389 | @return BOOL YES or NO 390 | */ 391 | + (BOOL)isTapToDismissEnabled; 392 | 393 | /** 394 | Enables or disables queueing behavior for toast views. When `YES`, 395 | toast views will appear one after the other. When `NO`, multiple Toast 396 | views will appear at the same time (potentially overlapping depending 397 | on their positions). This has no effect on the toast activity view, 398 | which operates independently of normal toast views. Default is `NO`. 399 | 400 | @param queueEnabled YES or NO 401 | */ 402 | + (void)setQueueEnabled:(BOOL)queueEnabled; 403 | 404 | /** 405 | Returns `YES` if the queue is enabled, otherwise `NO`. 406 | Default is `NO`. 407 | 408 | @return BOOL 409 | */ 410 | + (BOOL)isQueueEnabled; 411 | 412 | /** 413 | Sets the default duration. Used for the `makeToast:` and 414 | `showToast:` methods that don't require an explicit duration. 415 | Default is 3.0. 416 | 417 | @param duration The toast duration 418 | */ 419 | + (void)setDefaultDuration:(NSTimeInterval)duration; 420 | 421 | /** 422 | Returns the default duration. Default is 3.0. 423 | 424 | @return duration The toast duration 425 | */ 426 | + (NSTimeInterval)defaultDuration; 427 | 428 | /** 429 | Sets the default position. Used for the `makeToast:` and 430 | `showToast:` methods that don't require an explicit position. 431 | Default is `CSToastPositionBottom`. 432 | 433 | @param position The default center point. Can be one of the predefined 434 | CSToastPosition constants or a `CGPoint` wrapped in an `NSValue` object. 435 | */ 436 | + (void)setDefaultPosition:(id)position; 437 | 438 | /** 439 | Returns the default toast position. Default is `CSToastPositionBottom`. 440 | 441 | @return position The default center point. Will be one of the predefined 442 | CSToastPosition constants or a `CGPoint` wrapped in an `NSValue` object. 443 | */ 444 | + (id)defaultPosition; 445 | 446 | @end 447 | -------------------------------------------------------------------------------- /Toast/UIView+Toast.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Toast.m 3 | // Toast 4 | // 5 | // Copyright (c) 2011-2024 Charles Scalesse. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a 8 | // copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included 16 | // in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | #import "UIView+Toast.h" 27 | #import 28 | #import 29 | 30 | // Positions 31 | NSString * CSToastPositionTop = @"CSToastPositionTop"; 32 | NSString * CSToastPositionCenter = @"CSToastPositionCenter"; 33 | NSString * CSToastPositionBottom = @"CSToastPositionBottom"; 34 | 35 | // Keys for values associated with toast views 36 | static const NSString * CSToastTimerKey = @"CSToastTimerKey"; 37 | static const NSString * CSToastDurationKey = @"CSToastDurationKey"; 38 | static const NSString * CSToastPositionKey = @"CSToastPositionKey"; 39 | static const NSString * CSToastCompletionKey = @"CSToastCompletionKey"; 40 | 41 | // Keys for values associated with self 42 | static const NSString * CSToastActiveKey = @"CSToastActiveKey"; 43 | static const NSString * CSToastActivityViewKey = @"CSToastActivityViewKey"; 44 | static const NSString * CSToastQueueKey = @"CSToastQueueKey"; 45 | 46 | @interface UIView (ToastPrivate) 47 | 48 | /** 49 | These private methods are being prefixed with "cs_" to reduce the likelihood of non-obvious 50 | naming conflicts with other UIView methods. 51 | 52 | @discussion Should the public API also use the cs_ prefix? Technically it should, but it 53 | results in code that is less legible. The current public method names seem unlikely to cause 54 | conflicts so I think we should favor the cleaner API for now. 55 | */ 56 | - (void)cs_showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)position; 57 | - (void)cs_hideToast:(UIView *)toast; 58 | - (void)cs_hideToast:(UIView *)toast fromTap:(BOOL)fromTap; 59 | - (void)cs_toastTimerDidFinish:(NSTimer *)timer; 60 | - (void)cs_handleToastTapped:(UITapGestureRecognizer *)recognizer; 61 | - (CGPoint)cs_centerPointForPosition:(id)position withToast:(UIView *)toast; 62 | - (NSMutableArray *)cs_toastQueue; 63 | 64 | @end 65 | 66 | @implementation UIView (Toast) 67 | 68 | #pragma mark - Make Toast Methods 69 | 70 | - (void)makeToast:(NSString *)message { 71 | [self makeToast:message duration:[CSToastManager defaultDuration] position:[CSToastManager defaultPosition] style:nil]; 72 | } 73 | 74 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position { 75 | [self makeToast:message duration:duration position:position style:nil]; 76 | } 77 | 78 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position style:(CSToastStyle *)style { 79 | UIView *toast = [self toastViewForMessage:message title:nil image:nil style:style]; 80 | [self showToast:toast duration:duration position:position completion:nil]; 81 | } 82 | 83 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position title:(NSString *)title image:(UIImage *)image style:(CSToastStyle *)style completion:(void(^)(BOOL didTap))completion { 84 | UIView *toast = [self toastViewForMessage:message title:title image:image style:style]; 85 | [self showToast:toast duration:duration position:position completion:completion]; 86 | } 87 | 88 | #pragma mark - Show Toast Methods 89 | 90 | - (void)showToast:(UIView *)toast { 91 | [self showToast:toast duration:[CSToastManager defaultDuration] position:[CSToastManager defaultPosition] completion:nil]; 92 | } 93 | 94 | - (void)showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)position completion:(void(^)(BOOL didTap))completion { 95 | // sanity 96 | if (toast == nil) return; 97 | 98 | // store the completion block on the toast view 99 | objc_setAssociatedObject(toast, &CSToastCompletionKey, completion, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 100 | 101 | if ([CSToastManager isQueueEnabled] && [self.cs_activeToasts count] > 0) { 102 | // we're about to queue this toast view so we need to store the duration and position as well 103 | objc_setAssociatedObject(toast, &CSToastDurationKey, @(duration), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 104 | objc_setAssociatedObject(toast, &CSToastPositionKey, position, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 105 | 106 | // enqueue 107 | [self.cs_toastQueue addObject:toast]; 108 | } else { 109 | // present 110 | [self cs_showToast:toast duration:duration position:position]; 111 | } 112 | } 113 | 114 | #pragma mark - Hide Toast Methods 115 | 116 | - (void)hideToast { 117 | [self hideToast:[[self cs_activeToasts] firstObject]]; 118 | } 119 | 120 | - (void)hideToast:(UIView *)toast { 121 | // sanity 122 | if (!toast || ![[self cs_activeToasts] containsObject:toast]) return; 123 | 124 | [self cs_hideToast:toast]; 125 | } 126 | 127 | - (void)hideAllToasts { 128 | [self hideAllToasts:NO clearQueue:YES]; 129 | } 130 | 131 | - (void)hideAllToasts:(BOOL)includeActivity clearQueue:(BOOL)clearQueue { 132 | if (clearQueue) { 133 | [self clearToastQueue]; 134 | } 135 | 136 | for (UIView *toast in [self cs_activeToasts]) { 137 | [self hideToast:toast]; 138 | } 139 | 140 | if (includeActivity) { 141 | [self hideToastActivity]; 142 | } 143 | } 144 | 145 | - (void)clearToastQueue { 146 | [[self cs_toastQueue] removeAllObjects]; 147 | } 148 | 149 | #pragma mark - Private Show/Hide Methods 150 | 151 | - (void)cs_showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)position { 152 | toast.center = [self cs_centerPointForPosition:position withToast:toast]; 153 | toast.alpha = 0.0; 154 | 155 | if ([CSToastManager isTapToDismissEnabled]) { 156 | UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(cs_handleToastTapped:)]; 157 | [toast addGestureRecognizer:recognizer]; 158 | toast.userInteractionEnabled = YES; 159 | toast.exclusiveTouch = YES; 160 | } 161 | 162 | [[self cs_activeToasts] addObject:toast]; 163 | 164 | [self addSubview:toast]; 165 | 166 | [UIView animateWithDuration:[[CSToastManager sharedStyle] fadeDuration] 167 | delay:0.0 168 | options:(UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction) 169 | animations:^{ 170 | toast.alpha = 1.0; 171 | } completion:^(BOOL finished) { 172 | NSTimer *timer = [NSTimer timerWithTimeInterval:duration target:self selector:@selector(cs_toastTimerDidFinish:) userInfo:toast repeats:NO]; 173 | [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 174 | objc_setAssociatedObject(toast, &CSToastTimerKey, timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 175 | }]; 176 | } 177 | 178 | - (void)cs_hideToast:(UIView *)toast { 179 | [self cs_hideToast:toast fromTap:NO]; 180 | } 181 | 182 | - (void)cs_hideToast:(UIView *)toast fromTap:(BOOL)fromTap { 183 | NSTimer *timer = (NSTimer *)objc_getAssociatedObject(toast, &CSToastTimerKey); 184 | [timer invalidate]; 185 | 186 | [UIView animateWithDuration:[[CSToastManager sharedStyle] fadeDuration] 187 | delay:0.0 188 | options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState) 189 | animations:^{ 190 | toast.alpha = 0.0; 191 | } completion:^(BOOL finished) { 192 | [toast removeFromSuperview]; 193 | 194 | // remove 195 | [[self cs_activeToasts] removeObject:toast]; 196 | 197 | // execute the completion block, if necessary 198 | void (^completion)(BOOL didTap) = objc_getAssociatedObject(toast, &CSToastCompletionKey); 199 | if (completion) { 200 | completion(fromTap); 201 | } 202 | 203 | if ([self.cs_toastQueue count] > 0) { 204 | // dequeue 205 | UIView *nextToast = [[self cs_toastQueue] firstObject]; 206 | [[self cs_toastQueue] removeObjectAtIndex:0]; 207 | 208 | // present the next toast 209 | NSTimeInterval duration = [objc_getAssociatedObject(nextToast, &CSToastDurationKey) doubleValue]; 210 | id position = objc_getAssociatedObject(nextToast, &CSToastPositionKey); 211 | [self cs_showToast:nextToast duration:duration position:position]; 212 | } 213 | }]; 214 | } 215 | 216 | #pragma mark - View Construction 217 | 218 | - (UIView *)toastViewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image style:(CSToastStyle *)style { 219 | // sanity 220 | if (message == nil && title == nil && image == nil) return nil; 221 | 222 | // default to the shared style 223 | if (style == nil) { 224 | style = [CSToastManager sharedStyle]; 225 | } 226 | 227 | // dynamically build a toast view with any combination of message, title, & image 228 | UILabel *messageLabel = nil; 229 | UILabel *titleLabel = nil; 230 | UIImageView *imageView = nil; 231 | 232 | UIView *wrapperView = [[UIView alloc] init]; 233 | wrapperView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin); 234 | wrapperView.layer.cornerRadius = style.cornerRadius; 235 | 236 | if (style.displayShadow) { 237 | wrapperView.layer.shadowColor = style.shadowColor.CGColor; 238 | wrapperView.layer.shadowOpacity = style.shadowOpacity; 239 | wrapperView.layer.shadowRadius = style.shadowRadius; 240 | wrapperView.layer.shadowOffset = style.shadowOffset; 241 | } 242 | 243 | wrapperView.backgroundColor = style.backgroundColor; 244 | 245 | if(image != nil) { 246 | imageView = [[UIImageView alloc] initWithImage:image]; 247 | imageView.contentMode = UIViewContentModeScaleAspectFit; 248 | imageView.frame = CGRectMake(style.horizontalPadding, style.verticalPadding, style.imageSize.width, style.imageSize.height); 249 | } 250 | 251 | CGRect imageRect = CGRectZero; 252 | 253 | if(imageView != nil) { 254 | imageRect.origin.x = style.horizontalPadding; 255 | imageRect.origin.y = style.verticalPadding; 256 | imageRect.size.width = imageView.bounds.size.width; 257 | imageRect.size.height = imageView.bounds.size.height; 258 | } 259 | 260 | if (title != nil) { 261 | titleLabel = [[UILabel alloc] init]; 262 | titleLabel.numberOfLines = style.titleNumberOfLines; 263 | titleLabel.font = style.titleFont; 264 | titleLabel.textAlignment = style.titleAlignment; 265 | titleLabel.lineBreakMode = NSLineBreakByTruncatingTail; 266 | titleLabel.textColor = style.titleColor; 267 | titleLabel.backgroundColor = [UIColor clearColor]; 268 | titleLabel.alpha = 1.0; 269 | titleLabel.text = title; 270 | 271 | // size the title label according to the length of the text 272 | CGSize maxSizeTitle = CGSizeMake((self.bounds.size.width * style.maxWidthPercentage) - imageRect.size.width, self.bounds.size.height * style.maxHeightPercentage); 273 | CGSize expectedSizeTitle = [titleLabel sizeThatFits:maxSizeTitle]; 274 | // UILabel can return a size larger than the max size when the number of lines is 1 275 | expectedSizeTitle = CGSizeMake(MIN(maxSizeTitle.width, expectedSizeTitle.width), MIN(maxSizeTitle.height, expectedSizeTitle.height)); 276 | titleLabel.frame = CGRectMake(0.0, 0.0, expectedSizeTitle.width, expectedSizeTitle.height); 277 | } 278 | 279 | if (message != nil) { 280 | messageLabel = [[UILabel alloc] init]; 281 | messageLabel.numberOfLines = style.messageNumberOfLines; 282 | messageLabel.font = style.messageFont; 283 | messageLabel.textAlignment = style.messageAlignment; 284 | messageLabel.lineBreakMode = NSLineBreakByTruncatingTail; 285 | messageLabel.textColor = style.messageColor; 286 | messageLabel.backgroundColor = [UIColor clearColor]; 287 | messageLabel.alpha = 1.0; 288 | messageLabel.text = message; 289 | 290 | CGSize maxSizeMessage = CGSizeMake((self.bounds.size.width * style.maxWidthPercentage) - imageRect.size.width, self.bounds.size.height * style.maxHeightPercentage); 291 | CGSize expectedSizeMessage = [messageLabel sizeThatFits:maxSizeMessage]; 292 | // UILabel can return a size larger than the max size when the number of lines is 1 293 | expectedSizeMessage = CGSizeMake(MIN(maxSizeMessage.width, expectedSizeMessage.width), MIN(maxSizeMessage.height, expectedSizeMessage.height)); 294 | messageLabel.frame = CGRectMake(0.0, 0.0, expectedSizeMessage.width, expectedSizeMessage.height); 295 | } 296 | 297 | CGRect titleRect = CGRectZero; 298 | 299 | if(titleLabel != nil) { 300 | titleRect.origin.x = imageRect.origin.x + imageRect.size.width + style.horizontalPadding; 301 | titleRect.origin.y = style.verticalPadding; 302 | titleRect.size.width = titleLabel.bounds.size.width; 303 | titleRect.size.height = titleLabel.bounds.size.height; 304 | } 305 | 306 | CGRect messageRect = CGRectZero; 307 | 308 | if(messageLabel != nil) { 309 | messageRect.origin.x = imageRect.origin.x + imageRect.size.width + style.horizontalPadding; 310 | messageRect.origin.y = titleRect.origin.y + titleRect.size.height + style.verticalPadding; 311 | messageRect.size.width = messageLabel.bounds.size.width; 312 | messageRect.size.height = messageLabel.bounds.size.height; 313 | } 314 | 315 | CGFloat longerWidth = MAX(titleRect.size.width, messageRect.size.width); 316 | CGFloat longerX = MAX(titleRect.origin.x, messageRect.origin.x); 317 | 318 | // Wrapper width uses the longerWidth or the image width, whatever is larger. Same logic applies to the wrapper height. 319 | CGFloat wrapperWidth = MAX((imageRect.size.width + (style.horizontalPadding * 2.0)), (longerX + longerWidth + style.horizontalPadding)); 320 | CGFloat wrapperHeight = MAX((messageRect.origin.y + messageRect.size.height + style.verticalPadding), (imageRect.size.height + (style.verticalPadding * 2.0))); 321 | 322 | wrapperView.frame = CGRectMake(0.0, 0.0, wrapperWidth, wrapperHeight); 323 | 324 | if(titleLabel != nil) { 325 | titleLabel.frame = titleRect; 326 | [wrapperView addSubview:titleLabel]; 327 | } 328 | 329 | if(messageLabel != nil) { 330 | messageLabel.frame = messageRect; 331 | [wrapperView addSubview:messageLabel]; 332 | } 333 | 334 | if(imageView != nil) { 335 | [wrapperView addSubview:imageView]; 336 | } 337 | 338 | return wrapperView; 339 | } 340 | 341 | #pragma mark - Storage 342 | 343 | - (NSMutableArray *)cs_activeToasts { 344 | NSMutableArray *cs_activeToasts = objc_getAssociatedObject(self, &CSToastActiveKey); 345 | if (cs_activeToasts == nil) { 346 | cs_activeToasts = [[NSMutableArray alloc] init]; 347 | objc_setAssociatedObject(self, &CSToastActiveKey, cs_activeToasts, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 348 | } 349 | return cs_activeToasts; 350 | } 351 | 352 | - (NSMutableArray *)cs_toastQueue { 353 | NSMutableArray *cs_toastQueue = objc_getAssociatedObject(self, &CSToastQueueKey); 354 | if (cs_toastQueue == nil) { 355 | cs_toastQueue = [[NSMutableArray alloc] init]; 356 | objc_setAssociatedObject(self, &CSToastQueueKey, cs_toastQueue, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 357 | } 358 | return cs_toastQueue; 359 | } 360 | 361 | #pragma mark - Events 362 | 363 | - (void)cs_toastTimerDidFinish:(NSTimer *)timer { 364 | [self cs_hideToast:(UIView *)timer.userInfo]; 365 | } 366 | 367 | - (void)cs_handleToastTapped:(UITapGestureRecognizer *)recognizer { 368 | UIView *toast = recognizer.view; 369 | NSTimer *timer = (NSTimer *)objc_getAssociatedObject(toast, &CSToastTimerKey); 370 | [timer invalidate]; 371 | 372 | [self cs_hideToast:toast fromTap:YES]; 373 | } 374 | 375 | #pragma mark - Activity Methods 376 | 377 | - (void)makeToastActivity:(id)position { 378 | // sanity 379 | UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey); 380 | if (existingActivityView != nil) return; 381 | 382 | CSToastStyle *style = [CSToastManager sharedStyle]; 383 | 384 | UIView *activityView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, style.activitySize.width, style.activitySize.height)]; 385 | activityView.center = [self cs_centerPointForPosition:position withToast:activityView]; 386 | activityView.backgroundColor = style.backgroundColor; 387 | activityView.alpha = 0.0; 388 | activityView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin); 389 | activityView.layer.cornerRadius = style.cornerRadius; 390 | 391 | if (style.displayShadow) { 392 | activityView.layer.shadowColor = style.shadowColor.CGColor; 393 | activityView.layer.shadowOpacity = style.shadowOpacity; 394 | activityView.layer.shadowRadius = style.shadowRadius; 395 | activityView.layer.shadowOffset = style.shadowOffset; 396 | } 397 | 398 | UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; 399 | activityIndicatorView.center = CGPointMake(activityView.bounds.size.width / 2, activityView.bounds.size.height / 2); 400 | [activityView addSubview:activityIndicatorView]; 401 | [activityIndicatorView startAnimating]; 402 | 403 | // associate the activity view with self 404 | objc_setAssociatedObject (self, &CSToastActivityViewKey, activityView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 405 | 406 | [self addSubview:activityView]; 407 | 408 | [UIView animateWithDuration:style.fadeDuration 409 | delay:0.0 410 | options:UIViewAnimationOptionCurveEaseOut 411 | animations:^{ 412 | activityView.alpha = 1.0; 413 | } completion:nil]; 414 | } 415 | 416 | - (void)hideToastActivity { 417 | UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey); 418 | if (existingActivityView != nil) { 419 | [UIView animateWithDuration:[[CSToastManager sharedStyle] fadeDuration] 420 | delay:0.0 421 | options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState) 422 | animations:^{ 423 | existingActivityView.alpha = 0.0; 424 | } completion:^(BOOL finished) { 425 | [existingActivityView removeFromSuperview]; 426 | objc_setAssociatedObject (self, &CSToastActivityViewKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 427 | }]; 428 | } 429 | } 430 | 431 | #pragma mark - Helpers 432 | 433 | - (CGPoint)cs_centerPointForPosition:(id)point withToast:(UIView *)toast { 434 | CSToastStyle *style = [CSToastManager sharedStyle]; 435 | 436 | UIEdgeInsets safeInsets = UIEdgeInsetsZero; 437 | if (@available(iOS 11.0, *)) { 438 | safeInsets = self.safeAreaInsets; 439 | } 440 | 441 | CGFloat topPadding = style.verticalPadding + safeInsets.top; 442 | CGFloat bottomPadding = style.verticalPadding + safeInsets.bottom; 443 | 444 | if([point isKindOfClass:[NSString class]]) { 445 | if([point caseInsensitiveCompare:CSToastPositionTop] == NSOrderedSame) { 446 | return CGPointMake(self.bounds.size.width / 2.0, (toast.frame.size.height / 2.0) + topPadding); 447 | } else if([point caseInsensitiveCompare:CSToastPositionCenter] == NSOrderedSame) { 448 | return CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0); 449 | } 450 | } else if ([point isKindOfClass:[NSValue class]]) { 451 | return [point CGPointValue]; 452 | } 453 | 454 | // default to bottom 455 | return CGPointMake(self.bounds.size.width / 2.0, (self.bounds.size.height - (toast.frame.size.height / 2.0)) - bottomPadding); 456 | } 457 | 458 | @end 459 | 460 | @implementation CSToastStyle 461 | 462 | #pragma mark - Constructors 463 | 464 | - (instancetype)initWithDefaultStyle { 465 | self = [super init]; 466 | if (self) { 467 | self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.8]; 468 | self.titleColor = [UIColor whiteColor]; 469 | self.messageColor = [UIColor whiteColor]; 470 | self.maxWidthPercentage = 0.8; 471 | self.maxHeightPercentage = 0.8; 472 | self.horizontalPadding = 10.0; 473 | self.verticalPadding = 10.0; 474 | self.cornerRadius = 10.0; 475 | self.titleFont = [UIFont boldSystemFontOfSize:16.0]; 476 | self.messageFont = [UIFont systemFontOfSize:16.0]; 477 | self.titleAlignment = NSTextAlignmentLeft; 478 | self.messageAlignment = NSTextAlignmentLeft; 479 | self.titleNumberOfLines = 0; 480 | self.messageNumberOfLines = 0; 481 | self.displayShadow = NO; 482 | self.shadowOpacity = 0.8; 483 | self.shadowRadius = 6.0; 484 | self.shadowOffset = CGSizeMake(4.0, 4.0); 485 | self.imageSize = CGSizeMake(80.0, 80.0); 486 | self.activitySize = CGSizeMake(100.0, 100.0); 487 | self.fadeDuration = 0.2; 488 | } 489 | return self; 490 | } 491 | 492 | - (void)setMaxWidthPercentage:(CGFloat)maxWidthPercentage { 493 | _maxWidthPercentage = MAX(MIN(maxWidthPercentage, 1.0), 0.0); 494 | } 495 | 496 | - (void)setMaxHeightPercentage:(CGFloat)maxHeightPercentage { 497 | _maxHeightPercentage = MAX(MIN(maxHeightPercentage, 1.0), 0.0); 498 | } 499 | 500 | - (instancetype)init NS_UNAVAILABLE { 501 | return nil; 502 | } 503 | 504 | @end 505 | 506 | @interface CSToastManager () 507 | 508 | @property (strong, nonatomic) CSToastStyle *sharedStyle; 509 | @property (assign, nonatomic, getter=isTapToDismissEnabled) BOOL tapToDismissEnabled; 510 | @property (assign, nonatomic, getter=isQueueEnabled) BOOL queueEnabled; 511 | @property (assign, nonatomic) NSTimeInterval defaultDuration; 512 | @property (strong, nonatomic) id defaultPosition; 513 | 514 | @end 515 | 516 | @implementation CSToastManager 517 | 518 | #pragma mark - Constructors 519 | 520 | + (instancetype)sharedManager { 521 | static CSToastManager *_sharedManager = nil; 522 | static dispatch_once_t oncePredicate; 523 | dispatch_once(&oncePredicate, ^{ 524 | _sharedManager = [[self alloc] init]; 525 | }); 526 | 527 | return _sharedManager; 528 | } 529 | 530 | - (instancetype)init { 531 | self = [super init]; 532 | if (self) { 533 | self.sharedStyle = [[CSToastStyle alloc] initWithDefaultStyle]; 534 | self.tapToDismissEnabled = YES; 535 | self.queueEnabled = NO; 536 | self.defaultDuration = 3.0; 537 | self.defaultPosition = CSToastPositionBottom; 538 | } 539 | return self; 540 | } 541 | 542 | #pragma mark - Singleton Methods 543 | 544 | + (void)setSharedStyle:(CSToastStyle *)sharedStyle { 545 | [[self sharedManager] setSharedStyle:sharedStyle]; 546 | } 547 | 548 | + (CSToastStyle *)sharedStyle { 549 | return [[self sharedManager] sharedStyle]; 550 | } 551 | 552 | + (void)setTapToDismissEnabled:(BOOL)tapToDismissEnabled { 553 | [[self sharedManager] setTapToDismissEnabled:tapToDismissEnabled]; 554 | } 555 | 556 | + (BOOL)isTapToDismissEnabled { 557 | return [[self sharedManager] isTapToDismissEnabled]; 558 | } 559 | 560 | + (void)setQueueEnabled:(BOOL)queueEnabled { 561 | [[self sharedManager] setQueueEnabled:queueEnabled]; 562 | } 563 | 564 | + (BOOL)isQueueEnabled { 565 | return [[self sharedManager] isQueueEnabled]; 566 | } 567 | 568 | + (void)setDefaultDuration:(NSTimeInterval)duration { 569 | [[self sharedManager] setDefaultDuration:duration]; 570 | } 571 | 572 | + (NSTimeInterval)defaultDuration { 573 | return [[self sharedManager] defaultDuration]; 574 | } 575 | 576 | + (void)setDefaultPosition:(id)position { 577 | if ([position isKindOfClass:[NSString class]] || [position isKindOfClass:[NSValue class]]) { 578 | [[self sharedManager] setDefaultPosition:position]; 579 | } 580 | } 581 | 582 | + (id)defaultPosition { 583 | return [[self sharedManager] defaultPosition]; 584 | } 585 | 586 | @end 587 | -------------------------------------------------------------------------------- /toast_screenshots.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalessec/Toast/46a5d1c11987bed95543323154b46cccdff0deca/toast_screenshots.jpg --------------------------------------------------------------------------------