├── Images └── thumbnail.png ├── UltimateMenu ├── Resources │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── YourAppIcon.imageset │ │ │ ├── financialIndigoReversed.png │ │ │ └── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── SceneDelegate.swift │ └── Base.lproj │ │ └── LaunchScreen.storyboard ├── Helpers │ ├── UIApplicationExtension.swift │ └── UIDeviceExtension.swift ├── Views │ ├── View Controllers │ │ ├── HomeViewController.swift │ │ └── MenuTableViewController.swift │ └── Storyboards │ │ └── Base.lproj │ │ └── Main.storyboard └── Info.plist ├── UltimateMenu.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ └── DavidJacobsen.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcuserdata │ └── DavidJacobsen.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── project.pbxproj └── README.md /Images/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davejacobsen/UltimateMenuScreen/HEAD/Images/thumbnail.png -------------------------------------------------------------------------------- /UltimateMenu/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /UltimateMenu.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /UltimateMenu/Resources/Assets.xcassets/YourAppIcon.imageset/financialIndigoReversed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davejacobsen/UltimateMenuScreen/HEAD/UltimateMenu/Resources/Assets.xcassets/YourAppIcon.imageset/financialIndigoReversed.png -------------------------------------------------------------------------------- /UltimateMenu/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /UltimateMenu.xcodeproj/project.xcworkspace/xcuserdata/DavidJacobsen.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davejacobsen/UltimateMenuScreen/HEAD/UltimateMenu.xcodeproj/project.xcworkspace/xcuserdata/DavidJacobsen.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /UltimateMenu.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /UltimateMenu/Helpers/UIApplicationExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIApplicationExtension.swift 3 | // UltimateMenu 4 | // 5 | // Created by David on 3/7/21. 6 | // 7 | 8 | import UIKit 9 | 10 | /// Used to display the app version in the menu and gets attached to feedback emails 11 | 12 | extension UIApplication { 13 | static var appVersion: String? { 14 | return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /UltimateMenu/Resources/Assets.xcassets/YourAppIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "financialIndigoReversed.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UltimateMenu.xcodeproj/xcuserdata/DavidJacobsen.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | UltimateMenu.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UltimateMenuScreen 2 | 3 | 4 | 5 | Looking to improve the look and functionality of you own iOS apps? This free ultimate iOS app menu template contains a bunch of things that can be added to any iOS app to improve how professional your app looks. 6 | 7 | [YouTube Video Demo + Explanation](https://www.youtube.com/watch?v=ZQn4jME9i94) 8 | 9 | --- 10 | 11 | ## Project Includes: 12 | 13 | - Dark Mode 14 | 15 | - App Rating Launch 16 | 17 | - Share Sheets 18 | 19 | - In App Support/Feedback Email 20 | 21 | - App Version Section 22 | 23 | - Wildcard Settings Section 24 | -------------------------------------------------------------------------------- /UltimateMenu/Resources/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // UltimateMenu 4 | // 5 | // Created by David on 3/7/21. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /UltimateMenu/Views/View Controllers/HomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewController.swift 3 | // UltimateMenu 4 | // 5 | // Created by David on 3/7/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class HomeViewController: UIViewController { 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | 15 | setUpNavBar() 16 | setAppearance() 17 | 18 | } 19 | 20 | override func viewWillAppear(_ animated: Bool) { 21 | 22 | /// must be called to update the appearance when it is changed in the menu since viewDidLoad is only called once 23 | setAppearance() 24 | } 25 | 26 | func setUpNavBar() { 27 | 28 | /// Makes the navigation controller background clear 29 | navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default) 30 | navigationController?.navigationBar.shadowImage = UIImage() 31 | navigationController?.navigationBar.isTranslucent = true 32 | navigationController?.view.backgroundColor = UIColor.clear 33 | } 34 | 35 | func setAppearance() { 36 | 37 | /// makes sure this view controller's appearance is synced with the user default value set by the segmented control 38 | let defaults = UserDefaults.standard 39 | let appearanceSelection = defaults.integer(forKey: "appearanceSelection") 40 | 41 | if appearanceSelection == 0 { 42 | overrideUserInterfaceStyle = .unspecified 43 | } else if appearanceSelection == 1 { 44 | overrideUserInterfaceStyle = .light 45 | } else { 46 | overrideUserInterfaceStyle = .dark 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /UltimateMenu/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /UltimateMenu/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UIApplicationSupportsIndirectInputEvents 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /UltimateMenu/Resources/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // UltimateMenu 4 | // 5 | // Created by David on 3/7/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /UltimateMenu/Resources/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /UltimateMenu/Helpers/UIDeviceExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIDeviceExtension.swift 3 | // UltimateMenu 4 | // 5 | // Created by David on 3/7/21. 6 | // 7 | 8 | import SystemConfiguration 9 | import UIKit 10 | 11 | public extension UIDevice { 12 | static let modelName: String = { 13 | var systemInfo = utsname() 14 | uname(&systemInfo) 15 | let machineMirror = Mirror(reflecting: systemInfo.machine) 16 | let identifier = machineMirror.children.reduce("") { identifier, element in 17 | guard let value = element.value as? Int8, value != 0 else { return identifier } 18 | return identifier + String(UnicodeScalar(UInt8(value))) 19 | } 20 | 21 | func mapToDevice(identifier: String) -> String { 22 | #if os(iOS) 23 | switch identifier { 24 | case "iPod5,1": return "iPod Touch 5" 25 | case "iPod7,1": return "iPod Touch 6" 26 | case "iPhone3,1", "iPhone3,2", "iPhone3,3": return "iPhone 4" 27 | case "iPhone4,1": return "iPhone 4s" 28 | case "iPhone5,1", "iPhone5,2": return "iPhone 5" 29 | case "iPhone5,3", "iPhone5,4": return "iPhone 5c" 30 | case "iPhone6,1", "iPhone6,2": return "iPhone 5s" 31 | case "iPhone7,2": return "iPhone 6" 32 | case "iPhone7,1": return "iPhone 6 Plus" 33 | case "iPhone8,1": return "iPhone 6s" 34 | case "iPhone8,2": return "iPhone 6s Plus" 35 | case "iPhone9,1", "iPhone9,3": return "iPhone 7" 36 | case "iPhone9,2", "iPhone9,4": return "iPhone 7 Plus" 37 | case "iPhone8,4": return "iPhone SE" 38 | case "iPhone10,1", "iPhone10,4": return "iPhone 8" 39 | case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus" 40 | case "iPhone10,3", "iPhone10,6": return "iPhone X" 41 | case "iPhone11,2": return "iPhone XS" 42 | case "iPhone11,4", "iPhone11,6": return "iPhone XS Max" 43 | case "iPhone11,8": return "iPhone XR" 44 | case "iPhone12,1": return "iPhone 11" 45 | case "iPhone12,3": return "iPhone 11 Pr" 46 | case "iPhone12,5": return "iPhone 11 Pro Max" 47 | case "iPhone12,8": return "iPhone SE (2nd Gen)" 48 | case "iPhone13,1": return "iPhone 12 Mini" 49 | case "iPhone13,2": return "iPhone 12" 50 | case "iPhone13,3": return "iPhone 12 Pro" 51 | case "iPhone13,4": return "iPhone 12 Pro Max" 52 | case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":return "iPad 2" 53 | case "iPad3,1", "iPad3,2", "iPad3,3": return "iPad 3" 54 | case "iPad3,4", "iPad3,5", "iPad3,6": return "iPad 4" 55 | case "iPad4,1", "iPad4,2", "iPad4,3": return "iPad Air" 56 | case "iPad5,3", "iPad5,4": return "iPad Air 2" 57 | case "iPad6,11", "iPad6,12": return "iPad 5" 58 | case "iPad7,5", "iPad7,6": return "iPad 6" 59 | case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad Mini" 60 | case "iPad4,4", "iPad4,5", "iPad4,6": return "iPad Mini 2" 61 | case "iPad4,7", "iPad4,8", "iPad4,9": return "iPad Mini 3" 62 | case "iPad5,1", "iPad5,2": return "iPad Mini 4" 63 | case "iPad6,3", "iPad6,4": return "iPad Pro (9.7-inch)" 64 | case "iPad6,7", "iPad6,8": return "iPad Pro (12.9-inch)" 65 | case "iPad7,1", "iPad7,2": return "iPad Pro (12.9-inch) (2nd generation)" 66 | case "iPad7,3", "iPad7,4": return "iPad Pro (10.5-inch)" 67 | case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4":return "iPad Pro (11-inch)" 68 | case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8":return "iPad Pro (12.9-inch) (3rd generation)" 69 | case "AppleTV5,3": return "Apple TV" 70 | case "AppleTV6,2": return "Apple TV 4K" 71 | case "AudioAccessory1,1": return "HomePod" 72 | case "i386", "x86_64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "iOS"))" 73 | default: return identifier 74 | } 75 | #elseif os(tvOS) 76 | switch identifier { 77 | case "AppleTV5,3": return "Apple TV 4" 78 | case "AppleTV6,2": return "Apple TV 4K" 79 | case "i386", "x86_64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "tvOS"))" 80 | default: return identifier 81 | } 82 | #endif 83 | } 84 | 85 | return mapToDevice(identifier: identifier) 86 | }() 87 | } 88 | -------------------------------------------------------------------------------- /UltimateMenu/Views/View Controllers/MenuTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuTableViewController.swift 3 | // UltimateMenu 4 | // 5 | // Created by David on 3/7/21. 6 | // 7 | 8 | import UIKit 9 | import MessageUI 10 | import StoreKit 11 | import SafariServices 12 | 13 | class MenuTableViewController: UITableViewController, SFSafariViewControllerDelegate { 14 | 15 | @IBOutlet weak var versionLabel: UILabel! 16 | @IBOutlet weak var appearanceSegmentedControl: UISegmentedControl! 17 | 18 | /// CUSTOMIZE TO YOUR OWN APP/LINKS 19 | let appStoreURLStringForRating = "itms-apps://apple.com/app/id1534974973" 20 | let appStoreURLStringForShareSheet = "https://apps.apple.com/us/app/id1534974973" 21 | let twitterURLString = "https://twitter.com/davejacobseniOS" 22 | let supportEmail = "marketcapp.app@gmail.com" 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | if let appVersion = UIApplication.appVersion { 28 | versionLabel.text = "Your App version \(appVersion)" 29 | } 30 | 31 | /// delete this if your wildcard settings make all the content take up more than the smallest iPhone screen size 32 | tableView.isScrollEnabled = false 33 | } 34 | 35 | override func viewWillAppear(_ animated: Bool) { 36 | super.viewWillAppear(true) 37 | 38 | /// This will makes sure when the user hits this screen, that the appearance is synced to their device settings 39 | setAppearance() 40 | } 41 | 42 | func setAppearance() { 43 | 44 | let defaults = UserDefaults.standard 45 | let appearanceSelection = defaults.integer(forKey: "appearanceSelection") 46 | appearanceSegmentedControl.selectedSegmentIndex = appearanceSelection 47 | 48 | if appearanceSelection == 0 { 49 | overrideUserInterfaceStyle = .unspecified 50 | } else if appearanceSelection == 1 { 51 | overrideUserInterfaceStyle = .light 52 | } else { 53 | overrideUserInterfaceStyle = .dark 54 | } 55 | } 56 | 57 | @IBAction func appearanceValueChanged(_ sender: Any) { 58 | 59 | let defaults = UserDefaults.standard 60 | 61 | if appearanceSegmentedControl.selectedSegmentIndex == 0 { 62 | overrideUserInterfaceStyle = .unspecified 63 | defaults.setValue(0, forKey: "appearanceSelection") 64 | } else if appearanceSegmentedControl.selectedSegmentIndex == 1 { 65 | overrideUserInterfaceStyle = .light 66 | defaults.setValue(1, forKey: "appearanceSelection") 67 | } else if appearanceSegmentedControl.selectedSegmentIndex == 2 { 68 | overrideUserInterfaceStyle = .dark 69 | defaults.setValue(2, forKey: "appearanceSelection") 70 | } else { 71 | print("selection error") 72 | } 73 | } 74 | 75 | // MARK: - Table view data source 76 | 77 | override func numberOfSections(in tableView: UITableView) -> Int { 78 | return 4 79 | } 80 | 81 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 82 | 83 | switch section { 84 | case 0: return 1 85 | case 1: return 2 86 | case 2: return 3 87 | case 3: return 1 88 | default: return 0 89 | } 90 | } 91 | 92 | override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 93 | 94 | /// Set to your own styling preferences 95 | 96 | switch section { 97 | case 0: return 35 98 | case 1: return 35 99 | case 2: return 15 100 | case 3: return 6 101 | default: return 0 102 | } 103 | } 104 | 105 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 106 | 107 | switch indexPath { 108 | case [1, 1]: launchHelpfulLink() 109 | case [2, 0]: promptRating() 110 | tableView.deselectRow(at: indexPath, animated: true) 111 | case [2, 1]: launchShareSheet() 112 | tableView.deselectRow(at: indexPath, animated: true) 113 | case [2, 2]: composeShareEmail() 114 | tableView.deselectRow(at: indexPath, animated: true) 115 | default: print("no class function triggered for index path: \(indexPath)") 116 | } 117 | } 118 | 119 | @IBAction func yourAppsSwitchValueChanged(_ sender: Any) { 120 | print("Your Apps switch value changed") 121 | } 122 | 123 | func launchHelpfulLink() { 124 | let urlString = twitterURLString 125 | 126 | if let url = URL(string: urlString) { 127 | let vc = SFSafariViewController(url: url) 128 | vc.delegate = self 129 | 130 | present(vc, animated: true) 131 | } 132 | } 133 | 134 | func promptRating() { 135 | if let url = URL(string: appStoreURLStringForRating) { 136 | UIApplication.shared.open(url) 137 | } else { 138 | print("error with app store URL") 139 | } 140 | } 141 | 142 | func launchShareSheet() { 143 | if let appURL = NSURL(string: appStoreURLStringForShareSheet) { 144 | 145 | let objectsToShare: [Any] = [appURL] 146 | let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil) 147 | 148 | activityVC.excludedActivityTypes = [UIActivity.ActivityType.addToReadingList] 149 | activityVC.popoverPresentationController?.sourceView = tableView 150 | 151 | self.present(activityVC, animated: true, completion: nil) 152 | } 153 | } 154 | 155 | @IBAction func twitterHandleTapped(_ sender: Any) { 156 | let urlString = twitterURLString 157 | 158 | if let url = URL(string: urlString) { 159 | let vc = SFSafariViewController(url: url) 160 | vc.delegate = self 161 | 162 | present(vc, animated: true) 163 | } 164 | } 165 | } 166 | 167 | extension MenuTableViewController: MFMailComposeViewControllerDelegate { 168 | 169 | func composeShareEmail() { 170 | let mailComposeViewController = configuredMailComposeViewController() 171 | if MFMailComposeViewController.canSendMail() { 172 | self.present(mailComposeViewController, animated: true, completion: nil) 173 | } else { 174 | showSendMailErrorAlert() 175 | } 176 | } 177 | 178 | func configuredMailComposeViewController() -> MFMailComposeViewController { 179 | 180 | let messageBody: String 181 | let deviceModelName = UIDevice.modelName 182 | let iOSVersion = UIDevice.current.systemVersion 183 | let topDivider = "------- Developer Info -------" 184 | let divider = "------------------------------" 185 | 186 | if let appVersion = UIApplication.appVersion { 187 | 188 | messageBody = "\n\n\n\n\(topDivider)\nApp version: \(appVersion)\nDevice model: \(deviceModelName)\niOS version: \(iOSVersion)\n\(divider)" 189 | } else { 190 | messageBody = "\n\n\n\n\(topDivider)\nDevice model: \(deviceModelName)\niOS version: \(iOSVersion)\n\(divider)" 191 | } 192 | 193 | let mailComposerVC = MFMailComposeViewController() 194 | mailComposerVC.mailComposeDelegate = self 195 | mailComposerVC.setToRecipients([supportEmail]) 196 | mailComposerVC.setSubject("Your App Feedback") 197 | mailComposerVC.setMessageBody(messageBody, isHTML: false) 198 | return mailComposerVC 199 | } 200 | 201 | /// This alert gets shown if the device is a simulator, doesn't have Apple mail set up, or if mail in not available due to connectivity issues. 202 | func showSendMailErrorAlert() { 203 | let sendMailErrorAlert = UIAlertController(title: "Could Not Send Email", message: "Your device could not send email. Please check email configuration and internet connection and try again.", preferredStyle: .alert) 204 | sendMailErrorAlert.addAction(UIAlertAction(title: "Okay", style: .cancel, handler: nil)) 205 | self.present(sendMailErrorAlert, animated: true, completion: nil) 206 | } 207 | 208 | func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { 209 | controller.dismiss(animated: true, completion: nil) 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /UltimateMenu.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D58AD05725F555F6001E4A88 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58AD05625F555F6001E4A88 /* AppDelegate.swift */; }; 11 | D58AD05925F555F6001E4A88 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58AD05825F555F6001E4A88 /* SceneDelegate.swift */; }; 12 | D58AD05E25F555F6001E4A88 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D58AD05C25F555F6001E4A88 /* Main.storyboard */; }; 13 | D58AD06025F555F8001E4A88 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D58AD05F25F555F8001E4A88 /* Assets.xcassets */; }; 14 | D58AD06325F555F8001E4A88 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D58AD06125F555F8001E4A88 /* LaunchScreen.storyboard */; }; 15 | D58AD07325F556E4001E4A88 /* UIApplicationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58AD07225F556E4001E4A88 /* UIApplicationExtension.swift */; }; 16 | D58AD07725F55701001E4A88 /* UIDeviceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58AD07625F55701001E4A88 /* UIDeviceExtension.swift */; }; 17 | D58AD07A25F5578D001E4A88 /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58AD07925F5578D001E4A88 /* HomeViewController.swift */; }; 18 | D58AD07D25F557B0001E4A88 /* MenuTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58AD07C25F557B0001E4A88 /* MenuTableViewController.swift */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | D58AD05325F555F6001E4A88 /* UltimateMenu.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UltimateMenu.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | D58AD05625F555F6001E4A88 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 24 | D58AD05825F555F6001E4A88 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 25 | D58AD05D25F555F6001E4A88 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 26 | D58AD05F25F555F8001E4A88 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 27 | D58AD06225F555F8001E4A88 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 28 | D58AD06425F555F8001E4A88 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | D58AD07225F556E4001E4A88 /* UIApplicationExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplicationExtension.swift; sourceTree = ""; }; 30 | D58AD07625F55701001E4A88 /* UIDeviceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDeviceExtension.swift; sourceTree = ""; }; 31 | D58AD07925F5578D001E4A88 /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = ""; }; 32 | D58AD07C25F557B0001E4A88 /* MenuTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuTableViewController.swift; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | D58AD05025F555F6001E4A88 /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | D58AD04A25F555F6001E4A88 = { 47 | isa = PBXGroup; 48 | children = ( 49 | D58AD05525F555F6001E4A88 /* UltimateMenu */, 50 | D58AD05425F555F6001E4A88 /* Products */, 51 | ); 52 | sourceTree = ""; 53 | }; 54 | D58AD05425F555F6001E4A88 /* Products */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | D58AD05325F555F6001E4A88 /* UltimateMenu.app */, 58 | ); 59 | name = Products; 60 | sourceTree = ""; 61 | }; 62 | D58AD05525F555F6001E4A88 /* UltimateMenu */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | D58AD06B25F5563F001E4A88 /* Views */, 66 | D58AD07025F55692001E4A88 /* Helpers */, 67 | D58AD06F25F5568B001E4A88 /* Resources */, 68 | D58AD06425F555F8001E4A88 /* Info.plist */, 69 | ); 70 | path = UltimateMenu; 71 | sourceTree = ""; 72 | }; 73 | D58AD06B25F5563F001E4A88 /* Views */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | D58AD06D25F5566A001E4A88 /* Storyboards */, 77 | D58AD06C25F55654001E4A88 /* View Controllers */, 78 | ); 79 | path = Views; 80 | sourceTree = ""; 81 | }; 82 | D58AD06C25F55654001E4A88 /* View Controllers */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | D58AD07925F5578D001E4A88 /* HomeViewController.swift */, 86 | D58AD07C25F557B0001E4A88 /* MenuTableViewController.swift */, 87 | ); 88 | path = "View Controllers"; 89 | sourceTree = ""; 90 | }; 91 | D58AD06D25F5566A001E4A88 /* Storyboards */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | D58AD05C25F555F6001E4A88 /* Main.storyboard */, 95 | ); 96 | path = Storyboards; 97 | sourceTree = ""; 98 | }; 99 | D58AD06F25F5568B001E4A88 /* Resources */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | D58AD05625F555F6001E4A88 /* AppDelegate.swift */, 103 | D58AD05825F555F6001E4A88 /* SceneDelegate.swift */, 104 | D58AD05F25F555F8001E4A88 /* Assets.xcassets */, 105 | D58AD06125F555F8001E4A88 /* LaunchScreen.storyboard */, 106 | ); 107 | path = Resources; 108 | sourceTree = ""; 109 | }; 110 | D58AD07025F55692001E4A88 /* Helpers */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | D58AD07225F556E4001E4A88 /* UIApplicationExtension.swift */, 114 | D58AD07625F55701001E4A88 /* UIDeviceExtension.swift */, 115 | ); 116 | path = Helpers; 117 | sourceTree = ""; 118 | }; 119 | /* End PBXGroup section */ 120 | 121 | /* Begin PBXNativeTarget section */ 122 | D58AD05225F555F6001E4A88 /* UltimateMenu */ = { 123 | isa = PBXNativeTarget; 124 | buildConfigurationList = D58AD06725F555F8001E4A88 /* Build configuration list for PBXNativeTarget "UltimateMenu" */; 125 | buildPhases = ( 126 | D58AD04F25F555F6001E4A88 /* Sources */, 127 | D58AD05025F555F6001E4A88 /* Frameworks */, 128 | D58AD05125F555F6001E4A88 /* Resources */, 129 | ); 130 | buildRules = ( 131 | ); 132 | dependencies = ( 133 | ); 134 | name = UltimateMenu; 135 | productName = UltimateMenu; 136 | productReference = D58AD05325F555F6001E4A88 /* UltimateMenu.app */; 137 | productType = "com.apple.product-type.application"; 138 | }; 139 | /* End PBXNativeTarget section */ 140 | 141 | /* Begin PBXProject section */ 142 | D58AD04B25F555F6001E4A88 /* Project object */ = { 143 | isa = PBXProject; 144 | attributes = { 145 | LastSwiftUpdateCheck = 1240; 146 | LastUpgradeCheck = 1240; 147 | TargetAttributes = { 148 | D58AD05225F555F6001E4A88 = { 149 | CreatedOnToolsVersion = 12.4; 150 | }; 151 | }; 152 | }; 153 | buildConfigurationList = D58AD04E25F555F6001E4A88 /* Build configuration list for PBXProject "UltimateMenu" */; 154 | compatibilityVersion = "Xcode 9.3"; 155 | developmentRegion = en; 156 | hasScannedForEncodings = 0; 157 | knownRegions = ( 158 | en, 159 | Base, 160 | ); 161 | mainGroup = D58AD04A25F555F6001E4A88; 162 | productRefGroup = D58AD05425F555F6001E4A88 /* Products */; 163 | projectDirPath = ""; 164 | projectRoot = ""; 165 | targets = ( 166 | D58AD05225F555F6001E4A88 /* UltimateMenu */, 167 | ); 168 | }; 169 | /* End PBXProject section */ 170 | 171 | /* Begin PBXResourcesBuildPhase section */ 172 | D58AD05125F555F6001E4A88 /* Resources */ = { 173 | isa = PBXResourcesBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | D58AD06325F555F8001E4A88 /* LaunchScreen.storyboard in Resources */, 177 | D58AD06025F555F8001E4A88 /* Assets.xcassets in Resources */, 178 | D58AD05E25F555F6001E4A88 /* Main.storyboard in Resources */, 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | }; 182 | /* End PBXResourcesBuildPhase section */ 183 | 184 | /* Begin PBXSourcesBuildPhase section */ 185 | D58AD04F25F555F6001E4A88 /* Sources */ = { 186 | isa = PBXSourcesBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | D58AD07A25F5578D001E4A88 /* HomeViewController.swift in Sources */, 190 | D58AD07325F556E4001E4A88 /* UIApplicationExtension.swift in Sources */, 191 | D58AD07D25F557B0001E4A88 /* MenuTableViewController.swift in Sources */, 192 | D58AD07725F55701001E4A88 /* UIDeviceExtension.swift in Sources */, 193 | D58AD05725F555F6001E4A88 /* AppDelegate.swift in Sources */, 194 | D58AD05925F555F6001E4A88 /* SceneDelegate.swift in Sources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXSourcesBuildPhase section */ 199 | 200 | /* Begin PBXVariantGroup section */ 201 | D58AD05C25F555F6001E4A88 /* Main.storyboard */ = { 202 | isa = PBXVariantGroup; 203 | children = ( 204 | D58AD05D25F555F6001E4A88 /* Base */, 205 | ); 206 | name = Main.storyboard; 207 | sourceTree = ""; 208 | }; 209 | D58AD06125F555F8001E4A88 /* LaunchScreen.storyboard */ = { 210 | isa = PBXVariantGroup; 211 | children = ( 212 | D58AD06225F555F8001E4A88 /* Base */, 213 | ); 214 | name = LaunchScreen.storyboard; 215 | sourceTree = ""; 216 | }; 217 | /* End PBXVariantGroup section */ 218 | 219 | /* Begin XCBuildConfiguration section */ 220 | D58AD06525F555F8001E4A88 /* Debug */ = { 221 | isa = XCBuildConfiguration; 222 | buildSettings = { 223 | ALWAYS_SEARCH_USER_PATHS = NO; 224 | CLANG_ANALYZER_NONNULL = YES; 225 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 226 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 227 | CLANG_CXX_LIBRARY = "libc++"; 228 | CLANG_ENABLE_MODULES = YES; 229 | CLANG_ENABLE_OBJC_ARC = YES; 230 | CLANG_ENABLE_OBJC_WEAK = YES; 231 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 232 | CLANG_WARN_BOOL_CONVERSION = YES; 233 | CLANG_WARN_COMMA = YES; 234 | CLANG_WARN_CONSTANT_CONVERSION = YES; 235 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 236 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 237 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 238 | CLANG_WARN_EMPTY_BODY = YES; 239 | CLANG_WARN_ENUM_CONVERSION = YES; 240 | CLANG_WARN_INFINITE_RECURSION = YES; 241 | CLANG_WARN_INT_CONVERSION = YES; 242 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 243 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 244 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 245 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 246 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 247 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 248 | CLANG_WARN_STRICT_PROTOTYPES = YES; 249 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 250 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 251 | CLANG_WARN_UNREACHABLE_CODE = YES; 252 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 253 | COPY_PHASE_STRIP = NO; 254 | DEBUG_INFORMATION_FORMAT = dwarf; 255 | ENABLE_STRICT_OBJC_MSGSEND = YES; 256 | ENABLE_TESTABILITY = YES; 257 | GCC_C_LANGUAGE_STANDARD = gnu11; 258 | GCC_DYNAMIC_NO_PIC = NO; 259 | GCC_NO_COMMON_BLOCKS = YES; 260 | GCC_OPTIMIZATION_LEVEL = 0; 261 | GCC_PREPROCESSOR_DEFINITIONS = ( 262 | "DEBUG=1", 263 | "$(inherited)", 264 | ); 265 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 266 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 267 | GCC_WARN_UNDECLARED_SELECTOR = YES; 268 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 269 | GCC_WARN_UNUSED_FUNCTION = YES; 270 | GCC_WARN_UNUSED_VARIABLE = YES; 271 | IPHONEOS_DEPLOYMENT_TARGET = 14.4; 272 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 273 | MTL_FAST_MATH = YES; 274 | ONLY_ACTIVE_ARCH = YES; 275 | SDKROOT = iphoneos; 276 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 277 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 278 | }; 279 | name = Debug; 280 | }; 281 | D58AD06625F555F8001E4A88 /* Release */ = { 282 | isa = XCBuildConfiguration; 283 | buildSettings = { 284 | ALWAYS_SEARCH_USER_PATHS = NO; 285 | CLANG_ANALYZER_NONNULL = YES; 286 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 287 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 288 | CLANG_CXX_LIBRARY = "libc++"; 289 | CLANG_ENABLE_MODULES = YES; 290 | CLANG_ENABLE_OBJC_ARC = YES; 291 | CLANG_ENABLE_OBJC_WEAK = YES; 292 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 293 | CLANG_WARN_BOOL_CONVERSION = YES; 294 | CLANG_WARN_COMMA = YES; 295 | CLANG_WARN_CONSTANT_CONVERSION = YES; 296 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 297 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 298 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 299 | CLANG_WARN_EMPTY_BODY = YES; 300 | CLANG_WARN_ENUM_CONVERSION = YES; 301 | CLANG_WARN_INFINITE_RECURSION = YES; 302 | CLANG_WARN_INT_CONVERSION = YES; 303 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 304 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 305 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 306 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 307 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 308 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 309 | CLANG_WARN_STRICT_PROTOTYPES = YES; 310 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 311 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 312 | CLANG_WARN_UNREACHABLE_CODE = YES; 313 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 314 | COPY_PHASE_STRIP = NO; 315 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 316 | ENABLE_NS_ASSERTIONS = NO; 317 | ENABLE_STRICT_OBJC_MSGSEND = YES; 318 | GCC_C_LANGUAGE_STANDARD = gnu11; 319 | GCC_NO_COMMON_BLOCKS = YES; 320 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 321 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 322 | GCC_WARN_UNDECLARED_SELECTOR = YES; 323 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 324 | GCC_WARN_UNUSED_FUNCTION = YES; 325 | GCC_WARN_UNUSED_VARIABLE = YES; 326 | IPHONEOS_DEPLOYMENT_TARGET = 14.4; 327 | MTL_ENABLE_DEBUG_INFO = NO; 328 | MTL_FAST_MATH = YES; 329 | SDKROOT = iphoneos; 330 | SWIFT_COMPILATION_MODE = wholemodule; 331 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 332 | VALIDATE_PRODUCT = YES; 333 | }; 334 | name = Release; 335 | }; 336 | D58AD06825F555F8001E4A88 /* Debug */ = { 337 | isa = XCBuildConfiguration; 338 | buildSettings = { 339 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 340 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 341 | CODE_SIGN_STYLE = Automatic; 342 | DEVELOPMENT_TEAM = 9U46PJ4YSS; 343 | INFOPLIST_FILE = UltimateMenu/Info.plist; 344 | LD_RUNPATH_SEARCH_PATHS = ( 345 | "$(inherited)", 346 | "@executable_path/Frameworks", 347 | ); 348 | PRODUCT_BUNDLE_IDENTIFIER = com.ddjacobsen17.UltimateMenu; 349 | PRODUCT_NAME = "$(TARGET_NAME)"; 350 | SWIFT_VERSION = 5.0; 351 | TARGETED_DEVICE_FAMILY = 1; 352 | }; 353 | name = Debug; 354 | }; 355 | D58AD06925F555F8001E4A88 /* Release */ = { 356 | isa = XCBuildConfiguration; 357 | buildSettings = { 358 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 359 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 360 | CODE_SIGN_STYLE = Automatic; 361 | DEVELOPMENT_TEAM = 9U46PJ4YSS; 362 | INFOPLIST_FILE = UltimateMenu/Info.plist; 363 | LD_RUNPATH_SEARCH_PATHS = ( 364 | "$(inherited)", 365 | "@executable_path/Frameworks", 366 | ); 367 | PRODUCT_BUNDLE_IDENTIFIER = com.ddjacobsen17.UltimateMenu; 368 | PRODUCT_NAME = "$(TARGET_NAME)"; 369 | SWIFT_VERSION = 5.0; 370 | TARGETED_DEVICE_FAMILY = 1; 371 | }; 372 | name = Release; 373 | }; 374 | /* End XCBuildConfiguration section */ 375 | 376 | /* Begin XCConfigurationList section */ 377 | D58AD04E25F555F6001E4A88 /* Build configuration list for PBXProject "UltimateMenu" */ = { 378 | isa = XCConfigurationList; 379 | buildConfigurations = ( 380 | D58AD06525F555F8001E4A88 /* Debug */, 381 | D58AD06625F555F8001E4A88 /* Release */, 382 | ); 383 | defaultConfigurationIsVisible = 0; 384 | defaultConfigurationName = Release; 385 | }; 386 | D58AD06725F555F8001E4A88 /* Build configuration list for PBXNativeTarget "UltimateMenu" */ = { 387 | isa = XCConfigurationList; 388 | buildConfigurations = ( 389 | D58AD06825F555F8001E4A88 /* Debug */, 390 | D58AD06925F555F8001E4A88 /* Release */, 391 | ); 392 | defaultConfigurationIsVisible = 0; 393 | defaultConfigurationName = Release; 394 | }; 395 | /* End XCConfigurationList section */ 396 | }; 397 | rootObject = D58AD04B25F555F6001E4A88 /* Project object */; 398 | } 399 | -------------------------------------------------------------------------------- /UltimateMenu/Views/Storyboards/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 265 | 266 | 267 | 268 | 269 | 275 | 285 | 286 | 287 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | --------------------------------------------------------------------------------