├── .gitignore ├── E84PopOutMenu.podspec ├── E84PopOutMenu ├── E84PopOutMenu.h └── E84PopOutMenu.m ├── Example ├── Example.gif ├── Example.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── Example │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── E84AdjustmentMenuItem.imageset │ │ │ ├── 304-adjustments.png │ │ │ ├── 304-adjustments@2x.png │ │ │ ├── 304-adjustments@3x.png │ │ │ └── Contents.json │ │ ├── E84BackgroundImage.imageset │ │ │ ├── 3205394-1064832790-006Ch.png │ │ │ └── Contents.json │ │ ├── E84PlayMenuItem.imageset │ │ │ ├── 461-play1.png │ │ │ ├── 461-play1@2x.png │ │ │ ├── 461-play1@3x.png │ │ │ └── Contents.json │ │ ├── E84SingleScreenMenuItem.imageset │ │ │ ├── 693-ipad.png │ │ │ ├── 693-ipad@2x.png │ │ │ ├── 693-ipad@3x.png │ │ │ └── Contents.json │ │ └── E84SplitScreenMenuItem.imageset │ │ │ ├── 362-2up.png │ │ │ ├── 362-2up@2x.png │ │ │ ├── 362-2up@3x.png │ │ │ └── Contents.json │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m └── ExampleTests │ ├── ExampleTests.m │ └── Info.plist ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | -------------------------------------------------------------------------------- /E84PopOutMenu.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'E84PopOutMenu' 3 | s.version = '0.0.5' 4 | s.license = 'MIT' 5 | s.platform = :ios, '7.0' 6 | s.summary = 'Small UIControl for presenting a menu with any number of menu items with a nice open and close animation.' 7 | s.homepage = 'https://github.com/Element84/E84PopOutMenu' 8 | s.author = { 'Paul Pilone' => 'paul@element84.com' } 9 | s.source = { :git => 'https://github.com/Element84/E84PopOutMenu.git', :tag => s.version.to_s } 10 | s.source_files = 'E84PopOutMenu/*.{h,m}' 11 | s.requires_arc = true 12 | end 13 | -------------------------------------------------------------------------------- /E84PopOutMenu/E84PopOutMenu.h: -------------------------------------------------------------------------------- 1 | // 2 | // E84PopOutMenu.h 3 | // Example 4 | // 5 | // Created by Paul Pilone on 3/5/15. 6 | // Copyright (c) 2015 Element 84. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, E84PopOutMenuMaskType) { 12 | E84PopOutMenuMaskTypeNone = 0, // no background when open; allows user interaction while displayed 13 | E84PopOutMenuMaskTypeClear, // clear background behind menu; closes if tap occurs outside of menu 14 | E84PopOutMenuMaskTypeBlack // dims UI behind menu; closes if tap occurs outside of menu 15 | }; 16 | 17 | typedef NS_ENUM(NSInteger, E84PopOutMenuDirection) { 18 | E84PopOutMenuDirectionLeft = 0, 19 | E84PopOutMenuDirectionUp, 20 | E84PopOutMenuDirectionRight, 21 | E84PopOutMenuDirectionDown 22 | }; 23 | 24 | typedef NS_ENUM(NSInteger, E84PopOutMenuMode) { 25 | E84PopOutMenuModeRecentlyUsed = 0, 26 | E84PopOutMenuModeOrdered 27 | }; 28 | 29 | @interface E84PopOutMenu : UIControl 30 | 31 | /** 32 | The identifier for the currently selected item. Manually setting this will 33 | show the menu item but will not open or close the menu. 34 | */ 35 | @property (nonatomic, copy) NSString *selectedIdentifier; 36 | 37 | /** 38 | Indicates whether or not the menu is currently open. 39 | */ 40 | @property (nonatomic, getter=isOpen) BOOL open; 41 | 42 | /** 43 | Mask type to use when the menu is open. See E84PopOutMenuMaskType for descriptions. 44 | */ 45 | @property (nonatomic) enum E84PopOutMenuMaskType maskType; 46 | 47 | /** 48 | Opening direction of the menu. Default is E84PopOutMenuDirectionRight. 49 | */ 50 | @property (nonatomic) enum E84PopOutMenuDirection menuDirection; 51 | 52 | /** 53 | Controls the way items are ordered when the menu is open. Default is E84PopOutMenuModeRecentlyUsed. 54 | */ 55 | @property (nonatomic) enum E84PopOutMenuMode mode; 56 | 57 | /** 58 | The duration of the animation used to open or close the menu. Defaults to 0.4. 59 | */ 60 | @property (nonatomic) CGFloat animationDuration; 61 | 62 | /** 63 | The delay used between opening animations for each item. Defaults to 0.06. 64 | */ 65 | @property (nonatomic) CGFloat itemAnimationDelay; 66 | 67 | /** 68 | The number of points used to separate each item of the menu when open. Defaults to 75. 69 | */ 70 | @property (nonatomic) CGFloat interitemSpacing; 71 | 72 | /** 73 | The damping ratio used to control the elasticity of the open/close animation. Values range 74 | between 0 and 1, where 1 is no oscillation at the end of the animation. Defaults to 0.85. 75 | */ 76 | @property (nonatomic) CGFloat dampingRatio; 77 | 78 | /** 79 | The initial velocity of the open/close animation. Defaults to 0.4. 80 | */ 81 | @property (nonatomic) CGFloat velocity; 82 | 83 | /** 84 | Adds the given view as an item to the menu. Menu items are resized to 85 | fit the frame of the menu itself. Items are inserted as a subview below any existing 86 | menu items. 87 | 88 | If the menu item responds to setSelected: E84PopoutMenu will call forward 89 | selection status to the item. 90 | 91 | @param menuItem The item to add to the menu 92 | @param identifier The unique identifier associated with this menu item 93 | */ 94 | - (void)addPopOutMenuItem:(UIView *)menuItem forIdentifier:(NSString *)identifier; 95 | 96 | /** 97 | Removes a menu item from the menu. Does not recalculate item position 98 | if open. Recommend ensuring the menu is closed before calling this 99 | method. 100 | 101 | @param identifier The unique identifier of the menu item to remove 102 | */ 103 | - (void)removeMenuItemWithIdentifier:(NSString *)identifier; 104 | 105 | /** 106 | Manually open or close the menu with an option to animate. 107 | 108 | @param open Whether or not the menu should be opened 109 | @param animated Whether or not opening/closing is animated 110 | */ 111 | - (void)setOpen:(BOOL)open animated:(BOOL)animated; 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /E84PopOutMenu/E84PopOutMenu.m: -------------------------------------------------------------------------------- 1 | // 2 | // E84PopOutMenu.m 3 | // Example 4 | // 5 | // Created by Paul Pilone on 3/5/15. 6 | // Copyright (c) 2015 Element 84. All rights reserved. 7 | // 8 | 9 | #import "E84PopOutMenu.h" 10 | 11 | @interface E84PopOutMenu () < UIGestureRecognizerDelegate > 12 | 13 | @property (nonatomic, strong) NSMutableArray *menuItems; 14 | 15 | @property (nonatomic, strong) NSMutableDictionary *menuItemInfo; 16 | 17 | @property (nonatomic, strong) UIView *maskView; 18 | 19 | @end 20 | 21 | @implementation E84PopOutMenu 22 | 23 | /* */ 24 | - (instancetype)init { 25 | return [self initWithFrame:CGRectZero]; 26 | } 27 | 28 | /* */ 29 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 30 | self = [super initWithCoder:aDecoder]; 31 | if (self) { 32 | [self baseInit]; 33 | } 34 | 35 | return self; 36 | } 37 | 38 | /* */ 39 | - (instancetype)initWithFrame:(CGRect)frame { 40 | self = [super initWithFrame:frame]; 41 | if (self) { 42 | [self baseInit]; 43 | } 44 | 45 | return self; 46 | } 47 | 48 | /* */ 49 | - (void)addPopOutMenuItem:(UIView *)menuItem forIdentifier:(NSString *)identifier { 50 | // If we have no other items this is the selected item. 51 | if ([self.subviews count] == 0) { 52 | _selectedIdentifier = identifier; 53 | [self forwardSelected:YES toMenuItem:menuItem]; 54 | } else { 55 | menuItem.alpha = self.open ? 1.f : 0.f; 56 | menuItem.hidden = !self.open; 57 | } 58 | 59 | [self.menuItemInfo setObject:menuItem forKey:identifier]; 60 | [self.menuItems addObject:menuItem]; 61 | 62 | UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(menuItemSelected:)]; 63 | tapGesture.delegate = self; 64 | [menuItem addGestureRecognizer:tapGesture]; 65 | 66 | [self insertSubview:menuItem atIndex:0]; 67 | NSDictionary *views = NSDictionaryOfVariableBindings(menuItem); 68 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[menuItem]|" options:0 metrics:nil views:views]]; 69 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[menuItem]|" options:0 metrics:nil views:views]]; 70 | } 71 | 72 | /* */ 73 | - (void)removeMenuItemWithIdentifier:(NSString *)identifier { 74 | UIView *menuItem = self.menuItemInfo[identifier]; 75 | 76 | if (menuItem) { 77 | [self.menuItemInfo removeObjectForKey:identifier]; 78 | [self.menuItems removeObject:menuItem]; 79 | [menuItem removeFromSuperview]; 80 | } 81 | } 82 | 83 | /* */ 84 | - (void)setOpen:(BOOL)open { 85 | [self setOpen:open animated:YES]; 86 | } 87 | 88 | /* */ 89 | - (void)setOpen:(BOOL)open animated:(BOOL)animated { 90 | if (_open == open) { 91 | return; 92 | } 93 | 94 | // If opening: 95 | if (open) { 96 | CGFloat duration = animated ? 0.25 : 0.f; 97 | [UIView animateWithDuration:duration animations:^{ 98 | for (NSInteger i = 0; i < [self.subviews count]; i++) { 99 | UIView *menuItem = self.subviews[i]; 100 | menuItem.hidden = NO; 101 | menuItem.alpha = 1.f; 102 | } 103 | }]; 104 | 105 | [self.superview bringSubviewToFront:self]; 106 | if (self.maskType != E84PopOutMenuMaskTypeNone) { 107 | self.maskView.alpha = 0.f; 108 | [self.superview insertSubview:self.maskView belowSubview:self]; 109 | } 110 | } 111 | 112 | CGFloat duration = animated ? self.animationDuration : 0.f; 113 | for (NSInteger i = 0; i < [self.subviews count]; i++) { 114 | UIView *menuItem = !open ? self.subviews[[self.subviews count] - (i + 1)] : self.subviews[i]; 115 | 116 | CGFloat delay = animated ? self.itemAnimationDelay * i : 0.f; 117 | [UIView animateWithDuration:duration delay:delay usingSpringWithDamping:self.dampingRatio initialSpringVelocity:self.velocity 118 | options:UIViewAnimationOptionAllowUserInteraction 119 | animations:^{ 120 | CGAffineTransform transform; 121 | CGFloat maskAlpha = 0.f; 122 | if (!open) { 123 | transform = CGAffineTransformIdentity; 124 | } else { 125 | transform = [self openTransformForMenuItemAtIndex:i direction:self.menuDirection]; 126 | maskAlpha = 1.f; 127 | } 128 | 129 | menuItem.transform = transform; 130 | if (self.maskType != E84PopOutMenuMaskTypeNone) { 131 | self.maskView.alpha = maskAlpha; 132 | } 133 | } completion:^(BOOL finished) { 134 | // Finally, after all animations have finished, 135 | // toggle the open flag. 136 | if (finished && i == [self.subviews count] - 1) { 137 | if (!open) { 138 | CGFloat duration = animated ? 0.25 : 0.f; 139 | [UIView animateWithDuration:duration animations:^{ 140 | for (NSInteger j = 0; j < [self.subviews count] - 1; j++) { 141 | ((UIView *)self.subviews[j]).alpha = 0.f; 142 | } 143 | } completion:^(BOOL finished) { 144 | if (finished) { 145 | for (NSInteger j = 0; j < [self.subviews count] - 1; j++) { 146 | ((UIView *)self.subviews[j]).hidden = YES; 147 | } 148 | } 149 | 150 | }]; 151 | 152 | if (self.maskType != E84PopOutMenuMaskTypeNone) { 153 | [self.maskView removeFromSuperview]; 154 | } 155 | } 156 | 157 | _open = open; 158 | } 159 | }]; 160 | } 161 | } 162 | 163 | /* */ 164 | - (void)setMaskType:(enum E84PopOutMenuMaskType)maskType { 165 | if (_maskType == maskType) { 166 | return; 167 | } 168 | 169 | _maskType = maskType; 170 | 171 | if (self.maskView) { 172 | self.maskView = nil; 173 | } 174 | } 175 | 176 | /* */ 177 | - (void)setSelectedIdentifier:(NSString *)selectedIdentifier { 178 | if ([_selectedIdentifier isEqualToString:selectedIdentifier]) { 179 | return; 180 | } 181 | 182 | // Make sure we're getting passed a valid identifier. 183 | if (self.menuItemInfo[selectedIdentifier]) { 184 | // Get the previously selected menu item. 185 | UIView *oldMenuItem = self.menuItemInfo[_selectedIdentifier]; 186 | if (oldMenuItem) { 187 | [self forwardSelected:NO toMenuItem:oldMenuItem]; 188 | 189 | // Return the old menu item to appropriate subview position 190 | // based on the mode of the menu: 191 | // 192 | // E84PopOutMenuModeRecentlyUsed: Do nothing. Bringing the selected item to the front will shift items correctly. 193 | // E84PopOutMenuModeOrdered: Insert the old item into the list of subviews at the order it was originally added. 194 | if (self.mode == E84PopOutMenuModeOrdered) { 195 | NSInteger toIndexOfMenuItem = [self.menuItems count] - [self.menuItems indexOfObject:oldMenuItem] - 1; 196 | [self insertSubview:oldMenuItem atIndex:toIndexOfMenuItem]; 197 | } 198 | } 199 | 200 | // Bring it to the front so that it lays over the other items when closed. 201 | UIView *menuItem = self.menuItemInfo[selectedIdentifier]; 202 | [self bringSubviewToFront:menuItem]; 203 | [self forwardSelected:YES toMenuItem:menuItem]; 204 | 205 | _selectedIdentifier = selectedIdentifier; 206 | } 207 | } 208 | 209 | #pragma mark - 210 | #pragma mark Private 211 | 212 | /* */ 213 | - (void)baseInit { 214 | self.backgroundColor = [UIColor clearColor]; 215 | 216 | _maskType = E84PopOutMenuMaskTypeNone; 217 | _menuDirection = E84PopOutMenuDirectionRight; 218 | _menuItems = [NSMutableArray array]; 219 | _menuItemInfo = [NSMutableDictionary dictionary]; 220 | _mode = E84PopOutMenuModeRecentlyUsed; 221 | _open = NO; 222 | 223 | _animationDuration = 0.4; 224 | _dampingRatio = 0.85; 225 | _itemAnimationDelay = 0.06; 226 | _interitemSpacing = 75.f; 227 | _velocity = 0.4; 228 | } 229 | 230 | /* */ 231 | - (void)close:(UITapGestureRecognizer *)gesture { 232 | [self setOpen:NO animated:YES]; 233 | } 234 | 235 | /* */ 236 | - (UIView *)maskView { 237 | if (!_maskView) { 238 | _maskView = [[UIView alloc] initWithFrame:self.window.bounds]; 239 | _maskView.backgroundColor = self.maskType == E84PopOutMenuMaskTypeBlack ? [UIColor colorWithWhite:0.f alpha:0.75] : [UIColor clearColor]; 240 | 241 | UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(close:)]; 242 | [_maskView addGestureRecognizer:tapGesture]; 243 | } 244 | 245 | return _maskView; 246 | } 247 | 248 | /* */ 249 | - (void)menuItemSelected:(UITapGestureRecognizer *)tapGesture { 250 | UIView *menuItem = tapGesture.view; 251 | NSString *identifier = [[self.menuItemInfo allKeysForObject:menuItem] firstObject]; 252 | 253 | if (![identifier isEqualToString:self.selectedIdentifier]) { 254 | // Update our selected identifier. 255 | self.selectedIdentifier = identifier; 256 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 257 | } 258 | 259 | // Toggle the menu. 260 | self.open = !self.open; 261 | } 262 | 263 | /* Not implemented (nothing calls this yet). */ 264 | - (void)forwardHighlighted:(BOOL)highlighted toMenuItem:(UIView *)menuItem { 265 | if ([menuItem respondsToSelector:@selector(setSelected:)]) { 266 | NSMethodSignature *methodSignature = [[menuItem class] instanceMethodSignatureForSelector:@selector(setHighlighted:)]; 267 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; 268 | [invocation setTarget:menuItem]; 269 | [invocation setSelector:@selector(setHighlighted:)]; 270 | [invocation setArgument:&highlighted atIndex:2]; 271 | [invocation invoke]; 272 | } 273 | } 274 | 275 | /* */ 276 | - (void)forwardSelected:(BOOL)selected toMenuItem:(UIView *)menuItem { 277 | if ([menuItem respondsToSelector:@selector(setSelected:)]) { 278 | NSMethodSignature *methodSignature = [[menuItem class] instanceMethodSignatureForSelector:@selector(setSelected:)]; 279 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; 280 | [invocation setTarget:menuItem]; 281 | [invocation setSelector:@selector(setSelected:)]; 282 | [invocation setArgument:&selected atIndex:2]; 283 | [invocation invoke]; 284 | } 285 | } 286 | 287 | /* */ 288 | - (CGAffineTransform)openTransformForMenuItemAtIndex:(NSInteger)index direction:(enum E84PopOutMenuDirection)direction { 289 | CGAffineTransform transform; 290 | CGFloat delta = self.interitemSpacing * ([self.subviews count] - (index + 1)); 291 | 292 | switch (direction) { 293 | case E84PopOutMenuDirectionLeft: 294 | transform = CGAffineTransformMakeTranslation(-delta, 0.f); 295 | break; 296 | case E84PopOutMenuDirectionUp: 297 | transform = CGAffineTransformMakeTranslation(0.f, -delta); 298 | break; 299 | case E84PopOutMenuDirectionRight: 300 | transform = CGAffineTransformMakeTranslation(delta, 0.f); 301 | break; 302 | case E84PopOutMenuDirectionDown: 303 | transform = CGAffineTransformMakeTranslation(0.f, delta); 304 | break; 305 | default: 306 | break; 307 | } 308 | 309 | return transform; 310 | } 311 | 312 | #pragma mark - 313 | #pragma mark UIView 314 | 315 | /* */ 316 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { 317 | for (UIView *menuItem in self.subviews) { 318 | if (CGRectContainsPoint(menuItem.frame, point)) { 319 | return YES; 320 | } 321 | } 322 | 323 | return [super pointInside:point withEvent:event]; 324 | } 325 | 326 | @end 327 | -------------------------------------------------------------------------------- /Example/Example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/E84PopOutMenu/8c5377cf8290df7a12d36a61dc157f4f65ce9a32/Example/Example.gif -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DA3D20271AA8BD2F00B221EB /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3D20261AA8BD2F00B221EB /* main.m */; }; 11 | DA3D202A1AA8BD2F00B221EB /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3D20291AA8BD2F00B221EB /* AppDelegate.m */; }; 12 | DA3D202D1AA8BD2F00B221EB /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3D202C1AA8BD2F00B221EB /* ViewController.m */; }; 13 | DA3D20301AA8BD2F00B221EB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DA3D202E1AA8BD2F00B221EB /* Main.storyboard */; }; 14 | DA3D20321AA8BD2F00B221EB /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA3D20311AA8BD2F00B221EB /* Images.xcassets */; }; 15 | DA3D20351AA8BD2F00B221EB /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA3D20331AA8BD2F00B221EB /* LaunchScreen.xib */; }; 16 | DA3D20411AA8BD2F00B221EB /* ExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3D20401AA8BD2F00B221EB /* ExampleTests.m */; }; 17 | DA3D204D1AA8CF6600B221EB /* E84PopOutMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3D204C1AA8CF6600B221EB /* E84PopOutMenu.m */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | DA3D203B1AA8BD2F00B221EB /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = DA3D20191AA8BD2F00B221EB /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = DA3D20201AA8BD2F00B221EB; 26 | remoteInfo = Example; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | DA3D20211AA8BD2F00B221EB /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | DA3D20251AA8BD2F00B221EB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | DA3D20261AA8BD2F00B221EB /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 34 | DA3D20281AA8BD2F00B221EB /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 35 | DA3D20291AA8BD2F00B221EB /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 36 | DA3D202B1AA8BD2F00B221EB /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 37 | DA3D202C1AA8BD2F00B221EB /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 38 | DA3D202F1AA8BD2F00B221EB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 39 | DA3D20311AA8BD2F00B221EB /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 40 | DA3D20341AA8BD2F00B221EB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 41 | DA3D203A1AA8BD2F00B221EB /* ExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | DA3D203F1AA8BD2F00B221EB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 43 | DA3D20401AA8BD2F00B221EB /* ExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExampleTests.m; sourceTree = ""; }; 44 | DA3D204B1AA8CF6600B221EB /* E84PopOutMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = E84PopOutMenu.h; path = ../E84PopOutMenu/E84PopOutMenu.h; sourceTree = ""; }; 45 | DA3D204C1AA8CF6600B221EB /* E84PopOutMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = E84PopOutMenu.m; path = ../E84PopOutMenu/E84PopOutMenu.m; sourceTree = ""; }; 46 | /* End PBXFileReference section */ 47 | 48 | /* Begin PBXFrameworksBuildPhase section */ 49 | DA3D201E1AA8BD2F00B221EB /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | ); 54 | runOnlyForDeploymentPostprocessing = 0; 55 | }; 56 | DA3D20371AA8BD2F00B221EB /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | DA3D20181AA8BD2F00B221EB = { 67 | isa = PBXGroup; 68 | children = ( 69 | DA3D20231AA8BD2F00B221EB /* Example */, 70 | DA3D203D1AA8BD2F00B221EB /* ExampleTests */, 71 | DA3D20221AA8BD2F00B221EB /* Products */, 72 | ); 73 | sourceTree = ""; 74 | }; 75 | DA3D20221AA8BD2F00B221EB /* Products */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | DA3D20211AA8BD2F00B221EB /* Example.app */, 79 | DA3D203A1AA8BD2F00B221EB /* ExampleTests.xctest */, 80 | ); 81 | name = Products; 82 | sourceTree = ""; 83 | }; 84 | DA3D20231AA8BD2F00B221EB /* Example */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | DA3D204A1AA8BD3500B221EB /* E84PopOutMenu */, 88 | DA3D20281AA8BD2F00B221EB /* AppDelegate.h */, 89 | DA3D20291AA8BD2F00B221EB /* AppDelegate.m */, 90 | DA3D202B1AA8BD2F00B221EB /* ViewController.h */, 91 | DA3D202C1AA8BD2F00B221EB /* ViewController.m */, 92 | DA3D202E1AA8BD2F00B221EB /* Main.storyboard */, 93 | DA3D20311AA8BD2F00B221EB /* Images.xcassets */, 94 | DA3D20331AA8BD2F00B221EB /* LaunchScreen.xib */, 95 | DA3D20241AA8BD2F00B221EB /* Supporting Files */, 96 | ); 97 | path = Example; 98 | sourceTree = ""; 99 | }; 100 | DA3D20241AA8BD2F00B221EB /* Supporting Files */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | DA3D20251AA8BD2F00B221EB /* Info.plist */, 104 | DA3D20261AA8BD2F00B221EB /* main.m */, 105 | ); 106 | name = "Supporting Files"; 107 | sourceTree = ""; 108 | }; 109 | DA3D203D1AA8BD2F00B221EB /* ExampleTests */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | DA3D20401AA8BD2F00B221EB /* ExampleTests.m */, 113 | DA3D203E1AA8BD2F00B221EB /* Supporting Files */, 114 | ); 115 | path = ExampleTests; 116 | sourceTree = ""; 117 | }; 118 | DA3D203E1AA8BD2F00B221EB /* Supporting Files */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | DA3D203F1AA8BD2F00B221EB /* Info.plist */, 122 | ); 123 | name = "Supporting Files"; 124 | sourceTree = ""; 125 | }; 126 | DA3D204A1AA8BD3500B221EB /* E84PopOutMenu */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | DA3D204B1AA8CF6600B221EB /* E84PopOutMenu.h */, 130 | DA3D204C1AA8CF6600B221EB /* E84PopOutMenu.m */, 131 | ); 132 | name = E84PopOutMenu; 133 | path = ..; 134 | sourceTree = ""; 135 | }; 136 | /* End PBXGroup section */ 137 | 138 | /* Begin PBXNativeTarget section */ 139 | DA3D20201AA8BD2F00B221EB /* Example */ = { 140 | isa = PBXNativeTarget; 141 | buildConfigurationList = DA3D20441AA8BD2F00B221EB /* Build configuration list for PBXNativeTarget "Example" */; 142 | buildPhases = ( 143 | DA3D201D1AA8BD2F00B221EB /* Sources */, 144 | DA3D201E1AA8BD2F00B221EB /* Frameworks */, 145 | DA3D201F1AA8BD2F00B221EB /* Resources */, 146 | ); 147 | buildRules = ( 148 | ); 149 | dependencies = ( 150 | ); 151 | name = Example; 152 | productName = Example; 153 | productReference = DA3D20211AA8BD2F00B221EB /* Example.app */; 154 | productType = "com.apple.product-type.application"; 155 | }; 156 | DA3D20391AA8BD2F00B221EB /* ExampleTests */ = { 157 | isa = PBXNativeTarget; 158 | buildConfigurationList = DA3D20471AA8BD2F00B221EB /* Build configuration list for PBXNativeTarget "ExampleTests" */; 159 | buildPhases = ( 160 | DA3D20361AA8BD2F00B221EB /* Sources */, 161 | DA3D20371AA8BD2F00B221EB /* Frameworks */, 162 | DA3D20381AA8BD2F00B221EB /* Resources */, 163 | ); 164 | buildRules = ( 165 | ); 166 | dependencies = ( 167 | DA3D203C1AA8BD2F00B221EB /* PBXTargetDependency */, 168 | ); 169 | name = ExampleTests; 170 | productName = ExampleTests; 171 | productReference = DA3D203A1AA8BD2F00B221EB /* ExampleTests.xctest */; 172 | productType = "com.apple.product-type.bundle.unit-test"; 173 | }; 174 | /* End PBXNativeTarget section */ 175 | 176 | /* Begin PBXProject section */ 177 | DA3D20191AA8BD2F00B221EB /* Project object */ = { 178 | isa = PBXProject; 179 | attributes = { 180 | LastUpgradeCheck = 0610; 181 | ORGANIZATIONNAME = "Element 84"; 182 | TargetAttributes = { 183 | DA3D20201AA8BD2F00B221EB = { 184 | CreatedOnToolsVersion = 6.1.1; 185 | }; 186 | DA3D20391AA8BD2F00B221EB = { 187 | CreatedOnToolsVersion = 6.1.1; 188 | TestTargetID = DA3D20201AA8BD2F00B221EB; 189 | }; 190 | }; 191 | }; 192 | buildConfigurationList = DA3D201C1AA8BD2F00B221EB /* Build configuration list for PBXProject "Example" */; 193 | compatibilityVersion = "Xcode 3.2"; 194 | developmentRegion = English; 195 | hasScannedForEncodings = 0; 196 | knownRegions = ( 197 | en, 198 | Base, 199 | ); 200 | mainGroup = DA3D20181AA8BD2F00B221EB; 201 | productRefGroup = DA3D20221AA8BD2F00B221EB /* Products */; 202 | projectDirPath = ""; 203 | projectRoot = ""; 204 | targets = ( 205 | DA3D20201AA8BD2F00B221EB /* Example */, 206 | DA3D20391AA8BD2F00B221EB /* ExampleTests */, 207 | ); 208 | }; 209 | /* End PBXProject section */ 210 | 211 | /* Begin PBXResourcesBuildPhase section */ 212 | DA3D201F1AA8BD2F00B221EB /* Resources */ = { 213 | isa = PBXResourcesBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | DA3D20301AA8BD2F00B221EB /* Main.storyboard in Resources */, 217 | DA3D20351AA8BD2F00B221EB /* LaunchScreen.xib in Resources */, 218 | DA3D20321AA8BD2F00B221EB /* Images.xcassets in Resources */, 219 | ); 220 | runOnlyForDeploymentPostprocessing = 0; 221 | }; 222 | DA3D20381AA8BD2F00B221EB /* Resources */ = { 223 | isa = PBXResourcesBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | }; 229 | /* End PBXResourcesBuildPhase section */ 230 | 231 | /* Begin PBXSourcesBuildPhase section */ 232 | DA3D201D1AA8BD2F00B221EB /* Sources */ = { 233 | isa = PBXSourcesBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | DA3D202D1AA8BD2F00B221EB /* ViewController.m in Sources */, 237 | DA3D202A1AA8BD2F00B221EB /* AppDelegate.m in Sources */, 238 | DA3D20271AA8BD2F00B221EB /* main.m in Sources */, 239 | DA3D204D1AA8CF6600B221EB /* E84PopOutMenu.m in Sources */, 240 | ); 241 | runOnlyForDeploymentPostprocessing = 0; 242 | }; 243 | DA3D20361AA8BD2F00B221EB /* Sources */ = { 244 | isa = PBXSourcesBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | DA3D20411AA8BD2F00B221EB /* ExampleTests.m in Sources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | /* End PBXSourcesBuildPhase section */ 252 | 253 | /* Begin PBXTargetDependency section */ 254 | DA3D203C1AA8BD2F00B221EB /* PBXTargetDependency */ = { 255 | isa = PBXTargetDependency; 256 | target = DA3D20201AA8BD2F00B221EB /* Example */; 257 | targetProxy = DA3D203B1AA8BD2F00B221EB /* PBXContainerItemProxy */; 258 | }; 259 | /* End PBXTargetDependency section */ 260 | 261 | /* Begin PBXVariantGroup section */ 262 | DA3D202E1AA8BD2F00B221EB /* Main.storyboard */ = { 263 | isa = PBXVariantGroup; 264 | children = ( 265 | DA3D202F1AA8BD2F00B221EB /* Base */, 266 | ); 267 | name = Main.storyboard; 268 | sourceTree = ""; 269 | }; 270 | DA3D20331AA8BD2F00B221EB /* LaunchScreen.xib */ = { 271 | isa = PBXVariantGroup; 272 | children = ( 273 | DA3D20341AA8BD2F00B221EB /* Base */, 274 | ); 275 | name = LaunchScreen.xib; 276 | sourceTree = ""; 277 | }; 278 | /* End PBXVariantGroup section */ 279 | 280 | /* Begin XCBuildConfiguration section */ 281 | DA3D20421AA8BD2F00B221EB /* Debug */ = { 282 | isa = XCBuildConfiguration; 283 | buildSettings = { 284 | ALWAYS_SEARCH_USER_PATHS = NO; 285 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 286 | CLANG_CXX_LIBRARY = "libc++"; 287 | CLANG_ENABLE_MODULES = YES; 288 | CLANG_ENABLE_OBJC_ARC = YES; 289 | CLANG_WARN_BOOL_CONVERSION = YES; 290 | CLANG_WARN_CONSTANT_CONVERSION = YES; 291 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 292 | CLANG_WARN_EMPTY_BODY = YES; 293 | CLANG_WARN_ENUM_CONVERSION = YES; 294 | CLANG_WARN_INT_CONVERSION = YES; 295 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 296 | CLANG_WARN_UNREACHABLE_CODE = YES; 297 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 298 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 299 | COPY_PHASE_STRIP = NO; 300 | ENABLE_STRICT_OBJC_MSGSEND = YES; 301 | GCC_C_LANGUAGE_STANDARD = gnu99; 302 | GCC_DYNAMIC_NO_PIC = NO; 303 | GCC_OPTIMIZATION_LEVEL = 0; 304 | GCC_PREPROCESSOR_DEFINITIONS = ( 305 | "DEBUG=1", 306 | "$(inherited)", 307 | ); 308 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 309 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 310 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 311 | GCC_WARN_UNDECLARED_SELECTOR = YES; 312 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 313 | GCC_WARN_UNUSED_FUNCTION = YES; 314 | GCC_WARN_UNUSED_VARIABLE = YES; 315 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 316 | MTL_ENABLE_DEBUG_INFO = YES; 317 | ONLY_ACTIVE_ARCH = YES; 318 | SDKROOT = iphoneos; 319 | TARGETED_DEVICE_FAMILY = 2; 320 | }; 321 | name = Debug; 322 | }; 323 | DA3D20431AA8BD2F00B221EB /* Release */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | ALWAYS_SEARCH_USER_PATHS = NO; 327 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 328 | CLANG_CXX_LIBRARY = "libc++"; 329 | CLANG_ENABLE_MODULES = YES; 330 | CLANG_ENABLE_OBJC_ARC = YES; 331 | CLANG_WARN_BOOL_CONVERSION = YES; 332 | CLANG_WARN_CONSTANT_CONVERSION = YES; 333 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 334 | CLANG_WARN_EMPTY_BODY = YES; 335 | CLANG_WARN_ENUM_CONVERSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 338 | CLANG_WARN_UNREACHABLE_CODE = YES; 339 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 340 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 341 | COPY_PHASE_STRIP = YES; 342 | ENABLE_NS_ASSERTIONS = NO; 343 | ENABLE_STRICT_OBJC_MSGSEND = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 346 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 347 | GCC_WARN_UNDECLARED_SELECTOR = YES; 348 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 349 | GCC_WARN_UNUSED_FUNCTION = YES; 350 | GCC_WARN_UNUSED_VARIABLE = YES; 351 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 352 | MTL_ENABLE_DEBUG_INFO = NO; 353 | SDKROOT = iphoneos; 354 | TARGETED_DEVICE_FAMILY = 2; 355 | VALIDATE_PRODUCT = YES; 356 | }; 357 | name = Release; 358 | }; 359 | DA3D20451AA8BD2F00B221EB /* Debug */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 363 | INFOPLIST_FILE = Example/Info.plist; 364 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 365 | PRODUCT_NAME = "$(TARGET_NAME)"; 366 | }; 367 | name = Debug; 368 | }; 369 | DA3D20461AA8BD2F00B221EB /* Release */ = { 370 | isa = XCBuildConfiguration; 371 | buildSettings = { 372 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 373 | INFOPLIST_FILE = Example/Info.plist; 374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | }; 377 | name = Release; 378 | }; 379 | DA3D20481AA8BD2F00B221EB /* Debug */ = { 380 | isa = XCBuildConfiguration; 381 | buildSettings = { 382 | BUNDLE_LOADER = "$(TEST_HOST)"; 383 | FRAMEWORK_SEARCH_PATHS = ( 384 | "$(SDKROOT)/Developer/Library/Frameworks", 385 | "$(inherited)", 386 | ); 387 | GCC_PREPROCESSOR_DEFINITIONS = ( 388 | "DEBUG=1", 389 | "$(inherited)", 390 | ); 391 | INFOPLIST_FILE = ExampleTests/Info.plist; 392 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 393 | PRODUCT_NAME = "$(TARGET_NAME)"; 394 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/Example"; 395 | }; 396 | name = Debug; 397 | }; 398 | DA3D20491AA8BD2F00B221EB /* Release */ = { 399 | isa = XCBuildConfiguration; 400 | buildSettings = { 401 | BUNDLE_LOADER = "$(TEST_HOST)"; 402 | FRAMEWORK_SEARCH_PATHS = ( 403 | "$(SDKROOT)/Developer/Library/Frameworks", 404 | "$(inherited)", 405 | ); 406 | INFOPLIST_FILE = ExampleTests/Info.plist; 407 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 408 | PRODUCT_NAME = "$(TARGET_NAME)"; 409 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/Example"; 410 | }; 411 | name = Release; 412 | }; 413 | /* End XCBuildConfiguration section */ 414 | 415 | /* Begin XCConfigurationList section */ 416 | DA3D201C1AA8BD2F00B221EB /* Build configuration list for PBXProject "Example" */ = { 417 | isa = XCConfigurationList; 418 | buildConfigurations = ( 419 | DA3D20421AA8BD2F00B221EB /* Debug */, 420 | DA3D20431AA8BD2F00B221EB /* Release */, 421 | ); 422 | defaultConfigurationIsVisible = 0; 423 | defaultConfigurationName = Release; 424 | }; 425 | DA3D20441AA8BD2F00B221EB /* Build configuration list for PBXNativeTarget "Example" */ = { 426 | isa = XCConfigurationList; 427 | buildConfigurations = ( 428 | DA3D20451AA8BD2F00B221EB /* Debug */, 429 | DA3D20461AA8BD2F00B221EB /* Release */, 430 | ); 431 | defaultConfigurationIsVisible = 0; 432 | }; 433 | DA3D20471AA8BD2F00B221EB /* Build configuration list for PBXNativeTarget "ExampleTests" */ = { 434 | isa = XCConfigurationList; 435 | buildConfigurations = ( 436 | DA3D20481AA8BD2F00B221EB /* Debug */, 437 | DA3D20491AA8BD2F00B221EB /* Release */, 438 | ); 439 | defaultConfigurationIsVisible = 0; 440 | }; 441 | /* End XCConfigurationList section */ 442 | }; 443 | rootObject = DA3D20191AA8BD2F00B221EB /* Project object */; 444 | } 445 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Example 4 | // 5 | // Created by Paul Pilone on 3/5/15. 6 | // Copyright (c) 2015 Element 84. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Example/Example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Example 4 | // 5 | // Created by Paul Pilone on 3/5/15. 6 | // Copyright (c) 2015 Element 84. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Example/Example/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/Example/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 85 | 91 | 100 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "ipad", 5 | "size" : "29x29", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "ipad", 10 | "size" : "29x29", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "ipad", 15 | "size" : "40x40", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "ipad", 20 | "size" : "40x40", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "76x76", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "76x76", 31 | "scale" : "2x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84AdjustmentMenuItem.imageset/304-adjustments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/E84PopOutMenu/8c5377cf8290df7a12d36a61dc157f4f65ce9a32/Example/Example/Images.xcassets/E84AdjustmentMenuItem.imageset/304-adjustments.png -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84AdjustmentMenuItem.imageset/304-adjustments@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/E84PopOutMenu/8c5377cf8290df7a12d36a61dc157f4f65ce9a32/Example/Example/Images.xcassets/E84AdjustmentMenuItem.imageset/304-adjustments@2x.png -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84AdjustmentMenuItem.imageset/304-adjustments@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/E84PopOutMenu/8c5377cf8290df7a12d36a61dc157f4f65ce9a32/Example/Example/Images.xcassets/E84AdjustmentMenuItem.imageset/304-adjustments@3x.png -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84AdjustmentMenuItem.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "304-adjustments.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "304-adjustments@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "304-adjustments@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode", 22 | "template-rendering-intent" : "template" 23 | } 24 | } -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84BackgroundImage.imageset/3205394-1064832790-006Ch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/E84PopOutMenu/8c5377cf8290df7a12d36a61dc157f4f65ce9a32/Example/Example/Images.xcassets/E84BackgroundImage.imageset/3205394-1064832790-006Ch.png -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84BackgroundImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "3205394-1064832790-006Ch.png" 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/Example/Images.xcassets/E84PlayMenuItem.imageset/461-play1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/E84PopOutMenu/8c5377cf8290df7a12d36a61dc157f4f65ce9a32/Example/Example/Images.xcassets/E84PlayMenuItem.imageset/461-play1.png -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84PlayMenuItem.imageset/461-play1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/E84PopOutMenu/8c5377cf8290df7a12d36a61dc157f4f65ce9a32/Example/Example/Images.xcassets/E84PlayMenuItem.imageset/461-play1@2x.png -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84PlayMenuItem.imageset/461-play1@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/E84PopOutMenu/8c5377cf8290df7a12d36a61dc157f4f65ce9a32/Example/Example/Images.xcassets/E84PlayMenuItem.imageset/461-play1@3x.png -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84PlayMenuItem.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "461-play1.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "461-play1@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "461-play1@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode", 22 | "template-rendering-intent" : "template" 23 | } 24 | } -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84SingleScreenMenuItem.imageset/693-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/E84PopOutMenu/8c5377cf8290df7a12d36a61dc157f4f65ce9a32/Example/Example/Images.xcassets/E84SingleScreenMenuItem.imageset/693-ipad.png -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84SingleScreenMenuItem.imageset/693-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/E84PopOutMenu/8c5377cf8290df7a12d36a61dc157f4f65ce9a32/Example/Example/Images.xcassets/E84SingleScreenMenuItem.imageset/693-ipad@2x.png -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84SingleScreenMenuItem.imageset/693-ipad@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/E84PopOutMenu/8c5377cf8290df7a12d36a61dc157f4f65ce9a32/Example/Example/Images.xcassets/E84SingleScreenMenuItem.imageset/693-ipad@3x.png -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84SingleScreenMenuItem.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "693-ipad.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "693-ipad@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "693-ipad@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode", 22 | "template-rendering-intent" : "template" 23 | } 24 | } -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84SplitScreenMenuItem.imageset/362-2up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/E84PopOutMenu/8c5377cf8290df7a12d36a61dc157f4f65ce9a32/Example/Example/Images.xcassets/E84SplitScreenMenuItem.imageset/362-2up.png -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84SplitScreenMenuItem.imageset/362-2up@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/E84PopOutMenu/8c5377cf8290df7a12d36a61dc157f4f65ce9a32/Example/Example/Images.xcassets/E84SplitScreenMenuItem.imageset/362-2up@2x.png -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84SplitScreenMenuItem.imageset/362-2up@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/E84PopOutMenu/8c5377cf8290df7a12d36a61dc157f4f65ce9a32/Example/Example/Images.xcassets/E84SplitScreenMenuItem.imageset/362-2up@3x.png -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/E84SplitScreenMenuItem.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "362-2up.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "362-2up@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "362-2up@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode", 22 | "template-rendering-intent" : "template" 23 | } 24 | } -------------------------------------------------------------------------------- /Example/Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.element84.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations~ipad 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationPortraitUpsideDown 37 | UIInterfaceOrientationLandscapeLeft 38 | UIInterfaceOrientationLandscapeRight 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/Example/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Example 4 | // 5 | // Created by Paul Pilone on 3/5/15. 6 | // Copyright (c) 2015 Element 84. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Example/Example/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Example 4 | // 5 | // Created by Paul Pilone on 3/5/15. 6 | // Copyright (c) 2015 Element 84. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | #import "E84PopOutMenu.h" 12 | 13 | @interface ViewController () 14 | @property (weak, nonatomic) IBOutlet E84PopOutMenu *popOutMenu; 15 | @property (weak, nonatomic) IBOutlet UILabel *selectedIdentifierLabel; 16 | 17 | @end 18 | 19 | @implementation ViewController 20 | 21 | - (IBAction)addPlayMenuItem:(id)sender { 22 | UIButton *menuItem = [UIButton buttonWithType:UIButtonTypeCustom]; 23 | menuItem.translatesAutoresizingMaskIntoConstraints = NO; 24 | [menuItem setBackgroundColor:[UIColor whiteColor]]; 25 | [menuItem setImage:[UIImage imageNamed:@"E84PlayMenuItem"] forState:UIControlStateNormal]; 26 | [self.popOutMenu addPopOutMenuItem:menuItem forIdentifier:@"Play"]; 27 | } 28 | 29 | - (IBAction)removePlayMenuItem:(id)sender { 30 | [self.popOutMenu removeMenuItemWithIdentifier:@"Play"]; 31 | } 32 | 33 | - (IBAction)directionValueChanged:(id)sender { 34 | self.popOutMenu.menuDirection = ((UISegmentedControl *)sender).selectedSegmentIndex; 35 | } 36 | 37 | - (IBAction)maskValueChanged:(id)sender { 38 | self.popOutMenu.maskType = ((UISegmentedControl *)sender).selectedSegmentIndex; 39 | } 40 | 41 | - (IBAction)modeValueChanged:(id)sender { 42 | self.popOutMenu.mode = ((UISegmentedControl *)sender).selectedSegmentIndex; 43 | } 44 | 45 | - (IBAction)toggleMenu:(id)sender { 46 | [self.popOutMenu setOpen:!self.popOutMenu.open animated:YES]; 47 | } 48 | 49 | - (void)popOutMenuValueChanged:(id)sender { 50 | self.selectedIdentifierLabel.text = [NSString stringWithFormat:@"Selected menu item: %@", self.popOutMenu.selectedIdentifier]; 51 | } 52 | 53 | - (void)viewDidLoad { 54 | [super viewDidLoad]; 55 | 56 | // Do any additional setup after loading the view, typically from a nib. 57 | self.popOutMenu.interitemSpacing = 100.f; 58 | [self.popOutMenu addTarget:self action:@selector(popOutMenuValueChanged:) forControlEvents:UIControlEventValueChanged]; 59 | 60 | UIColor *menuItemBackgroundColor = self.view.backgroundColor; 61 | 62 | UIButton *menuItem = [UIButton buttonWithType:UIButtonTypeCustom]; 63 | menuItem.translatesAutoresizingMaskIntoConstraints = NO; 64 | [menuItem setBackgroundColor:menuItemBackgroundColor]; 65 | [menuItem setImage:[UIImage imageNamed:@"E84SingleScreenMenuItem"] forState:UIControlStateNormal]; 66 | [self.popOutMenu addPopOutMenuItem:menuItem forIdentifier:@"SingleScreen"]; 67 | 68 | menuItem = [UIButton buttonWithType:UIButtonTypeCustom]; 69 | menuItem.translatesAutoresizingMaskIntoConstraints = NO; 70 | [menuItem setBackgroundColor:menuItemBackgroundColor]; 71 | [menuItem setImage:[UIImage imageNamed:@"E84SplitScreenMenuItem"] forState:UIControlStateNormal]; 72 | [self.popOutMenu addPopOutMenuItem:menuItem forIdentifier:@"SplitScreen"]; 73 | 74 | menuItem = [UIButton buttonWithType:UIButtonTypeCustom]; 75 | menuItem.translatesAutoresizingMaskIntoConstraints = NO; 76 | [menuItem setBackgroundColor:menuItemBackgroundColor]; 77 | [menuItem setImage:[UIImage imageNamed:@"E84PlayMenuItem"] forState:UIControlStateNormal]; 78 | [self.popOutMenu addPopOutMenuItem:menuItem forIdentifier:@"Play"]; 79 | 80 | menuItem = [UIButton buttonWithType:UIButtonTypeCustom]; 81 | menuItem.translatesAutoresizingMaskIntoConstraints = NO; 82 | [menuItem setBackgroundColor:menuItemBackgroundColor]; 83 | [menuItem setImage:[UIImage imageNamed:@"E84AdjustmentMenuItem"] forState:UIControlStateNormal]; 84 | [self.popOutMenu addPopOutMenuItem:menuItem forIdentifier:@"Adjustment"]; 85 | 86 | self.selectedIdentifierLabel.text = [NSString stringWithFormat:@"Selected menu item: %@", self.popOutMenu.selectedIdentifier]; 87 | } 88 | 89 | - (void)didReceiveMemoryWarning { 90 | [super didReceiveMemoryWarning]; 91 | // Dispose of any resources that can be recreated. 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /Example/Example/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Example 4 | // 5 | // Created by Paul Pilone on 3/5/15. 6 | // Copyright (c) 2015 Element 84. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Example/ExampleTests/ExampleTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleTests.m 3 | // ExampleTests 4 | // 5 | // Created by Paul Pilone on 3/5/15. 6 | // Copyright (c) 2015 Element 84. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface ExampleTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation ExampleTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Example/ExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.element84.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Element 84, INC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #E84PopOutMenu 2 | 3 | Small UIControl for presenting a menu with any number of menu items with a nice open and close animation. Supports multiple directions, mask types, and configurable animation options. 4 | 5 | ![Example](Example/Example.gif) 6 | 7 | ## Installation 8 | 9 | E84PopOutMenu is available through [CocoaPods](http://cocoapods.org). To install it, simply add the following to your Podfile: 10 | 11 | pod 'E84PopOutMenu' 12 | 13 | You can also clone the repository and copy E84PopOutMenu/E84PopOutMenu.{h,m} into your project. 14 | 15 | ## Usage 16 | 17 | The quickest way to see E84PopOutMenu in action is to clone the repository and run the Example project. This project allows you to play around with some of the options to test different ways of presenting menu items. 18 | 19 | E84PopOutMenu is configured with reasonable defaults to make it work nicely right out of the box. The only thing you really need to do is add menu items, most likely in the `viewDidLoad` method of your view controller. Menu items are added by calling: 20 | 21 | - (void)addPopOutMenuItem:(UIView *)menuItem forIdentifier:(NSString *)identifier; 22 | 23 | on your E84PopOutMenu. Menu items are shown in the order they are added. 24 | 25 | To listen for changes in the selected menu item, add an IBAction for the `UIControlEventValueChanged` control event of your menu. 26 | 27 | ## Author 28 | 29 | Paul Pilone, 30 | 31 | Element 84, 32 | 33 | ## License 34 | 35 | E84PopOutMenu is available under the MIT license. See the LICENSE file for more info. 36 | --------------------------------------------------------------------------------