From 93338a66cb00509d8a3dc737d0ee7484be018a88 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Sun, 19 Jul 2020 19:02:09 -0600 Subject: [PATCH 1/7] Added project files --- .../project.pbxproj | 341 ++++++++++++++++++ .../AppDelegate.swift | 37 ++ .../AppIcon.appiconset/Contents.json | 98 +++++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 ++ .../Base.lproj/Main.storyboard | 24 ++ .../Experiences-Sprint-Challenge/Info.plist | 64 ++++ .../SceneDelegate.swift | 53 +++ .../ViewController.swift | 20 + 9 files changed, 668 insertions(+) create mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge.xcodeproj/project.pbxproj create mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/AppDelegate.swift create mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/Contents.json create mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/LaunchScreen.storyboard create mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard create mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist create mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/SceneDelegate.swift create mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/ViewController.swift diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge.xcodeproj/project.pbxproj b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge.xcodeproj/project.pbxproj new file mode 100644 index 00000000..e306071d --- /dev/null +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge.xcodeproj/project.pbxproj @@ -0,0 +1,341 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + C9AE226624C5247C003290A0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9AE226524C5247C003290A0 /* AppDelegate.swift */; }; + C9AE226824C5247C003290A0 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9AE226724C5247C003290A0 /* SceneDelegate.swift */; }; + C9AE226A24C5247C003290A0 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9AE226924C5247C003290A0 /* ViewController.swift */; }; + C9AE226D24C5247C003290A0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C9AE226B24C5247C003290A0 /* Main.storyboard */; }; + C9AE226F24C52481003290A0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C9AE226E24C52481003290A0 /* Assets.xcassets */; }; + C9AE227224C52481003290A0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C9AE227024C52481003290A0 /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + C9AE226224C5247C003290A0 /* Experiences-Sprint-Challenge.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Experiences-Sprint-Challenge.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + C9AE226524C5247C003290A0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + C9AE226724C5247C003290A0 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + C9AE226924C5247C003290A0 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + C9AE226C24C5247C003290A0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + C9AE226E24C52481003290A0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + C9AE227124C52481003290A0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + C9AE227324C52481003290A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + C9AE225F24C5247C003290A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + C9AE225924C5247C003290A0 = { + isa = PBXGroup; + children = ( + C9AE226424C5247C003290A0 /* Experiences-Sprint-Challenge */, + C9AE226324C5247C003290A0 /* Products */, + ); + sourceTree = ""; + }; + C9AE226324C5247C003290A0 /* Products */ = { + isa = PBXGroup; + children = ( + C9AE226224C5247C003290A0 /* Experiences-Sprint-Challenge.app */, + ); + name = Products; + sourceTree = ""; + }; + C9AE226424C5247C003290A0 /* Experiences-Sprint-Challenge */ = { + isa = PBXGroup; + children = ( + C9AE226524C5247C003290A0 /* AppDelegate.swift */, + C9AE226724C5247C003290A0 /* SceneDelegate.swift */, + C9AE226924C5247C003290A0 /* ViewController.swift */, + C9AE226B24C5247C003290A0 /* Main.storyboard */, + C9AE226E24C52481003290A0 /* Assets.xcassets */, + C9AE227024C52481003290A0 /* LaunchScreen.storyboard */, + C9AE227324C52481003290A0 /* Info.plist */, + ); + path = "Experiences-Sprint-Challenge"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + C9AE226124C5247C003290A0 /* Experiences-Sprint-Challenge */ = { + isa = PBXNativeTarget; + buildConfigurationList = C9AE227624C52481003290A0 /* Build configuration list for PBXNativeTarget "Experiences-Sprint-Challenge" */; + buildPhases = ( + C9AE225E24C5247C003290A0 /* Sources */, + C9AE225F24C5247C003290A0 /* Frameworks */, + C9AE226024C5247C003290A0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Experiences-Sprint-Challenge"; + productName = "Experiences-Sprint-Challenge"; + productReference = C9AE226224C5247C003290A0 /* Experiences-Sprint-Challenge.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C9AE225A24C5247C003290A0 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1140; + LastUpgradeCheck = 1140; + ORGANIZATIONNAME = "Matthew Martindale"; + TargetAttributes = { + C9AE226124C5247C003290A0 = { + CreatedOnToolsVersion = 11.4.1; + }; + }; + }; + buildConfigurationList = C9AE225D24C5247C003290A0 /* Build configuration list for PBXProject "Experiences-Sprint-Challenge" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = C9AE225924C5247C003290A0; + productRefGroup = C9AE226324C5247C003290A0 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C9AE226124C5247C003290A0 /* Experiences-Sprint-Challenge */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + C9AE226024C5247C003290A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C9AE227224C52481003290A0 /* LaunchScreen.storyboard in Resources */, + C9AE226F24C52481003290A0 /* Assets.xcassets in Resources */, + C9AE226D24C5247C003290A0 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C9AE225E24C5247C003290A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C9AE226A24C5247C003290A0 /* ViewController.swift in Sources */, + C9AE226624C5247C003290A0 /* AppDelegate.swift in Sources */, + C9AE226824C5247C003290A0 /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + C9AE226B24C5247C003290A0 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + C9AE226C24C5247C003290A0 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + C9AE227024C52481003290A0 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + C9AE227124C52481003290A0 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + C9AE227424C52481003290A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + C9AE227524C52481003290A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + C9AE227724C52481003290A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Experiences-Sprint-Challenge/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.mattmartindale.Experiences-Sprint-Challenge"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + C9AE227824C52481003290A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Experiences-Sprint-Challenge/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.mattmartindale.Experiences-Sprint-Challenge"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C9AE225D24C5247C003290A0 /* Build configuration list for PBXProject "Experiences-Sprint-Challenge" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C9AE227424C52481003290A0 /* Debug */, + C9AE227524C52481003290A0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C9AE227624C52481003290A0 /* Build configuration list for PBXNativeTarget "Experiences-Sprint-Challenge" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C9AE227724C52481003290A0 /* Debug */, + C9AE227824C52481003290A0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = C9AE225A24C5247C003290A0 /* Project object */; +} diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/AppDelegate.swift b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/AppDelegate.swift new file mode 100644 index 00000000..c861fd42 --- /dev/null +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/AppDelegate.swift @@ -0,0 +1,37 @@ +// +// AppDelegate.swift +// Experiences-Sprint-Challenge +// +// Created by Matthew Martindale on 7/19/20. +// Copyright © 2020 Matthew Martindale. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/AppIcon.appiconset/Contents.json b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..9221b9bb --- /dev/null +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/Contents.json b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/LaunchScreen.storyboard b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard new file mode 100644 index 00000000..25a76385 --- /dev/null +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist new file mode 100644 index 00000000..2a3483c0 --- /dev/null +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist @@ -0,0 +1,64 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/SceneDelegate.swift b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/SceneDelegate.swift new file mode 100644 index 00000000..54cf3cf2 --- /dev/null +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/SceneDelegate.swift @@ -0,0 +1,53 @@ +// +// SceneDelegate.swift +// Experiences-Sprint-Challenge +// +// Created by Matthew Martindale on 7/19/20. +// Copyright © 2020 Matthew Martindale. All rights reserved. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let _ = (scene as? UIWindowScene) else { return } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/ViewController.swift b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/ViewController.swift new file mode 100644 index 00000000..7657f353 --- /dev/null +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/ViewController.swift @@ -0,0 +1,20 @@ +// +// ViewController.swift +// Experiences-Sprint-Challenge +// +// Created by Matthew Martindale on 7/19/20. +// Copyright © 2020 Matthew Martindale. All rights reserved. +// + +import UIKit + +class ViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + + +} + From a673881a7ebf092ac11c99603640927582901e64 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Sun, 19 Jul 2020 19:16:25 -0600 Subject: [PATCH 2/7] Added mapView and addExperience button --- .../project.pbxproj | 32 +++++++++++--- .../Base.lproj/Main.storyboard | 43 ++++++++++++++++--- .../Experiences-Sprint-Challenge/Info.plist | 2 + .../Models/Experience.swift | 32 ++++++++++++++ .../View Controllers/MapViewController.swift | 42 ++++++++++++++++++ .../ViewController.swift | 20 --------- 6 files changed, 139 insertions(+), 32 deletions(-) create mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Models/Experience.swift create mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/MapViewController.swift delete mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/ViewController.swift diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge.xcodeproj/project.pbxproj b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge.xcodeproj/project.pbxproj index e306071d..ccb14017 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge.xcodeproj/project.pbxproj +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge.xcodeproj/project.pbxproj @@ -9,21 +9,23 @@ /* Begin PBXBuildFile section */ C9AE226624C5247C003290A0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9AE226524C5247C003290A0 /* AppDelegate.swift */; }; C9AE226824C5247C003290A0 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9AE226724C5247C003290A0 /* SceneDelegate.swift */; }; - C9AE226A24C5247C003290A0 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9AE226924C5247C003290A0 /* ViewController.swift */; }; C9AE226D24C5247C003290A0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C9AE226B24C5247C003290A0 /* Main.storyboard */; }; C9AE226F24C52481003290A0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C9AE226E24C52481003290A0 /* Assets.xcassets */; }; C9AE227224C52481003290A0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C9AE227024C52481003290A0 /* LaunchScreen.storyboard */; }; + C9AE227C24C52544003290A0 /* Experience.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9AE227B24C52544003290A0 /* Experience.swift */; }; + C9AE227E24C525F4003290A0 /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9AE227D24C525F4003290A0 /* MapViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ C9AE226224C5247C003290A0 /* Experiences-Sprint-Challenge.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Experiences-Sprint-Challenge.app"; sourceTree = BUILT_PRODUCTS_DIR; }; C9AE226524C5247C003290A0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C9AE226724C5247C003290A0 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - C9AE226924C5247C003290A0 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; C9AE226C24C5247C003290A0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; C9AE226E24C52481003290A0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; C9AE227124C52481003290A0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; C9AE227324C52481003290A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C9AE227B24C52544003290A0 /* Experience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Experience.swift; sourceTree = ""; }; + C9AE227D24C525F4003290A0 /* MapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -56,17 +58,34 @@ C9AE226424C5247C003290A0 /* Experiences-Sprint-Challenge */ = { isa = PBXGroup; children = ( + C9AE227324C52481003290A0 /* Info.plist */, C9AE226524C5247C003290A0 /* AppDelegate.swift */, C9AE226724C5247C003290A0 /* SceneDelegate.swift */, - C9AE226924C5247C003290A0 /* ViewController.swift */, - C9AE226B24C5247C003290A0 /* Main.storyboard */, C9AE226E24C52481003290A0 /* Assets.xcassets */, C9AE227024C52481003290A0 /* LaunchScreen.storyboard */, - C9AE227324C52481003290A0 /* Info.plist */, + C9AE226B24C5247C003290A0 /* Main.storyboard */, + C9AE227A24C52534003290A0 /* Models */, + C9AE227924C52528003290A0 /* View Controllers */, ); path = "Experiences-Sprint-Challenge"; sourceTree = ""; }; + C9AE227924C52528003290A0 /* View Controllers */ = { + isa = PBXGroup; + children = ( + C9AE227D24C525F4003290A0 /* MapViewController.swift */, + ); + path = "View Controllers"; + sourceTree = ""; + }; + C9AE227A24C52534003290A0 /* Models */ = { + isa = PBXGroup; + children = ( + C9AE227B24C52544003290A0 /* Experience.swift */, + ); + path = Models; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -138,9 +157,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C9AE226A24C5247C003290A0 /* ViewController.swift in Sources */, C9AE226624C5247C003290A0 /* AppDelegate.swift in Sources */, C9AE226824C5247C003290A0 /* SceneDelegate.swift in Sources */, + C9AE227C24C52544003290A0 /* Experience.swift in Sources */, + C9AE227E24C525F4003290A0 /* MapViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard index 25a76385..bc091857 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard @@ -1,24 +1,55 @@ - + + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist index 2a3483c0..85cc3cfe 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist @@ -60,5 +60,7 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + NSLocationWhenInUseUsageDescription + $(PRODUCT_NAME) uses your location to show where you are on the map diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Models/Experience.swift b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Models/Experience.swift new file mode 100644 index 00000000..1b0aa077 --- /dev/null +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Models/Experience.swift @@ -0,0 +1,32 @@ +// +// Experience.swift +// Experiences-Sprint-Challenge +// +// Created by Matthew Martindale on 7/19/20. +// Copyright © 2020 Matthew Martindale. All rights reserved. +// + +import UIKit +import MapKit + +class Experience: NSObject { + var title: String? + var image: UIImage? + var audioURL: URL? + var latitude: Double + var longitude: Double + + init(title: String? = "New Experience", image: UIImage?, audioURL: URL?, latitude: Double, longitude: Double) { + self.title = title + self.image = image + self.audioURL = audioURL + self.latitude = latitude + self.longitude = longitude + } +} + +extension Experience: MKAnnotation { + var coordinate: CLLocationCoordinate2D { + CLLocationCoordinate2D(latitude: latitude, longitude: longitude) + } +} diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/MapViewController.swift b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/MapViewController.swift new file mode 100644 index 00000000..dbd26b0e --- /dev/null +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/MapViewController.swift @@ -0,0 +1,42 @@ +// +// MapViewController.swift +// Experiences-Sprint-Challenge +// +// Created by Matthew Martindale on 7/19/20. +// Copyright © 2020 Matthew Martindale. All rights reserved. +// + +import UIKit +import MapKit + +class MapViewController: UIViewController { + + // MARK: - IBOutlets + @IBOutlet weak var mapView: MKMapView! + + // MARK: - Properties + private let locationManager = CLLocationManager() + private var userTrackingButton: MKUserTrackingButton! + + override func viewDidLoad() { + super.viewDidLoad() + locationManager.requestWhenInUseAuthorization() + setupUserTracking() + } + + private func setupUserTracking() { + userTrackingButton = MKUserTrackingButton(mapView: mapView) + userTrackingButton.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(userTrackingButton) + + NSLayoutConstraint.activate([ + userTrackingButton.trailingAnchor.constraint(equalTo: mapView.trailingAnchor, constant: -30), + mapView.bottomAnchor.constraint(equalTo: userTrackingButton.bottomAnchor, constant: 30) + ]) + } + + @IBAction func addExperienceButtonTapped(_ sender: UIBarButtonItem) { + + } + +} diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/ViewController.swift b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/ViewController.swift deleted file mode 100644 index 7657f353..00000000 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/ViewController.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// ViewController.swift -// Experiences-Sprint-Challenge -// -// Created by Matthew Martindale on 7/19/20. -// Copyright © 2020 Matthew Martindale. All rights reserved. -// - -import UIKit - -class ViewController: UIViewController { - - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view. - } - - -} - From 375366f362b837a1bec7950d159e9de6c7801ec0 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Sun, 19 Jul 2020 19:36:10 -0600 Subject: [PATCH 3/7] Added addExperience VC and setup views --- .../project.pbxproj | 4 + .../Base.lproj/Main.storyboard | 142 +++++++++++++++--- .../AddExperienceViewController.swift | 36 +++++ 3 files changed, 162 insertions(+), 20 deletions(-) create mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge.xcodeproj/project.pbxproj b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge.xcodeproj/project.pbxproj index ccb14017..b4da797f 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge.xcodeproj/project.pbxproj +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ C9AE227224C52481003290A0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C9AE227024C52481003290A0 /* LaunchScreen.storyboard */; }; C9AE227C24C52544003290A0 /* Experience.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9AE227B24C52544003290A0 /* Experience.swift */; }; C9AE227E24C525F4003290A0 /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9AE227D24C525F4003290A0 /* MapViewController.swift */; }; + C9AE228024C5285E003290A0 /* AddExperienceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9AE227F24C5285E003290A0 /* AddExperienceViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -26,6 +27,7 @@ C9AE227324C52481003290A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C9AE227B24C52544003290A0 /* Experience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Experience.swift; sourceTree = ""; }; C9AE227D24C525F4003290A0 /* MapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewController.swift; sourceTree = ""; }; + C9AE227F24C5285E003290A0 /* AddExperienceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddExperienceViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -74,6 +76,7 @@ isa = PBXGroup; children = ( C9AE227D24C525F4003290A0 /* MapViewController.swift */, + C9AE227F24C5285E003290A0 /* AddExperienceViewController.swift */, ); path = "View Controllers"; sourceTree = ""; @@ -160,6 +163,7 @@ C9AE226624C5247C003290A0 /* AppDelegate.swift in Sources */, C9AE226824C5247C003290A0 /* SceneDelegate.swift in Sources */, C9AE227C24C52544003290A0 /* Experience.swift in Sources */, + C9AE228024C5285E003290A0 /* AddExperienceViewController.swift in Sources */, C9AE227E24C525F4003290A0 /* MapViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard index bc091857..a2857e1e 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard @@ -1,5 +1,5 @@ - + @@ -7,7 +7,7 @@ - + @@ -16,40 +16,142 @@ - + - - - - - - - - - - - - - - - + - + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift new file mode 100644 index 00000000..2b265955 --- /dev/null +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift @@ -0,0 +1,36 @@ +// +// AddExperienceViewController.swift +// Experiences-Sprint-Challenge +// +// Created by Matthew Martindale on 7/19/20. +// Copyright © 2020 Matthew Martindale. All rights reserved. +// + +import UIKit + +class AddExperienceViewController: UIViewController { + + @IBOutlet weak var titleTextField: UITextField! + @IBOutlet weak var imageView: UIImageView! + @IBOutlet weak var photoButton: UIButton! + @IBOutlet weak var micButton: UIButton! + + override func viewDidLoad() { + super.viewDidLoad() + + setupViews() + } + + private func setupViews() { + let configuration = UIImage.SymbolConfiguration(pointSize: 50) + + // setup photoImage + let photoImage = UIImage(systemName: "photo", withConfiguration: configuration) + photoButton.setImage(photoImage, for: .normal) + + // setup micImage + let micImage = UIImage(systemName: "mic.fill", withConfiguration: configuration) + micButton.setImage(micImage, for: .normal) + } + +} From 9df877d6695d7f6671ca4af1195a3c7fbfc756a6 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Sun, 19 Jul 2020 19:55:20 -0600 Subject: [PATCH 4/7] Add photo successfully selects image and adds saturation filter --- .../NoImage.imageset/Contents.json | 21 +++++++ .../NoImage.imageset/noImage.png | Bin 0 -> 16243 bytes .../Base.lproj/Main.storyboard | 15 +++-- .../Experiences-Sprint-Challenge/Info.plist | 4 ++ .../AddExperienceViewController.swift | 55 ++++++++++++++++++ 5 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/NoImage.imageset/Contents.json create mode 100644 Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/NoImage.imageset/noImage.png diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/NoImage.imageset/Contents.json b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/NoImage.imageset/Contents.json new file mode 100644 index 00000000..19389fb1 --- /dev/null +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/NoImage.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "noImage.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/NoImage.imageset/noImage.png b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Assets.xcassets/NoImage.imageset/noImage.png new file mode 100644 index 0000000000000000000000000000000000000000..f45b4ae75e1742eb2500aaa465c259fb71747749 GIT binary patch literal 16243 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

J@Lx$Yka$nN~oeFIQD6~~t&9YsgySZwq~ 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 literal 0 HcmV?d00001 diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard index a2857e1e..731a6a71 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard @@ -53,7 +53,7 @@ - + - + @@ -86,6 +86,9 @@ + + + - - + + @@ -151,6 +157,7 @@ + diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist index 85cc3cfe..3ecc41f8 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist @@ -60,6 +60,10 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + NSPhotoLibraryUsageDescription + $(PRODUCT_NAME) needs access to your photo library to filter images + NSPhotoLibraryAddUsageDescription + $(PRODUCT_NAME) needs access to your photo library to save images NSLocationWhenInUseUsageDescription $(PRODUCT_NAME) uses your location to show where you are on the map diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift index 2b265955..d43d0392 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift @@ -7,14 +7,22 @@ // import UIKit +import CoreImage +import CoreImage.CIFilterBuiltins class AddExperienceViewController: UIViewController { + // MARK: - IBOutlets @IBOutlet weak var titleTextField: UITextField! @IBOutlet weak var imageView: UIImageView! @IBOutlet weak var photoButton: UIButton! @IBOutlet weak var micButton: UIButton! + // MARK: - Properties + private let context = CIContext() + private let colorControlFilter = CIFilter.colorControls() + + // MARK: - View Controller Lifecycles override func viewDidLoad() { super.viewDidLoad() @@ -33,4 +41,51 @@ class AddExperienceViewController: UIViewController { micButton.setImage(micImage, for: .normal) } + // MARK: - Methods + private func presentImagePickerController() { + guard UIImagePickerController.isSourceTypeAvailable(.photoLibrary) else { + print("The photo library is not available") + return + } + + let imagePicker = UIImagePickerController() + imagePicker.sourceType = .photoLibrary + imagePicker.delegate = self + + present(imagePicker, animated: true, completion: nil) + } + + private func saturateImage(_ inputImage: CIImage) -> UIImage? { + colorControlFilter.inputImage = inputImage + colorControlFilter.saturation = 2.0 + + guard let outputImage = colorControlFilter.outputImage else { return nil} + + return UIImage(ciImage: outputImage) + } + + // MARK: - IBActions + @IBAction func imageButtonTapped(_ sender: UIButton) { + presentImagePickerController() + } + + @IBAction func recordButtonTapped(_ sender: UIButton) { + } + +} + +extension AddExperienceViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { + func imagePickerController(_ picker: UIImagePickerController, + didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { + guard let uiImage = info[.originalImage] as? UIImage else { return } + guard let coreImage = CIImage(image: uiImage) else { return } + guard let image = saturateImage(coreImage) else { return } + imageView.image = image + + picker.dismiss(animated: true, completion: nil) + } + + func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { + picker.dismiss(animated: true, completion: nil) + } } From 20e3bf45360343ac466be09273803653cb597f36 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Sun, 19 Jul 2020 20:03:01 -0600 Subject: [PATCH 5/7] UI updates --- .../Base.lproj/Main.storyboard | 40 ++++++++++--------- .../AddExperienceViewController.swift | 4 +- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard index 731a6a71..9e4ffb0d 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard @@ -52,8 +52,8 @@ - - + + - + + - - + + + + + - - - + + - - - - - + + diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift index d43d0392..07894693 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift @@ -30,15 +30,17 @@ class AddExperienceViewController: UIViewController { } private func setupViews() { - let configuration = UIImage.SymbolConfiguration(pointSize: 50) + let configuration = UIImage.SymbolConfiguration(pointSize: 40) // setup photoImage let photoImage = UIImage(systemName: "photo", withConfiguration: configuration) photoButton.setImage(photoImage, for: .normal) + photoButton.layer.cornerRadius = 12 // setup micImage let micImage = UIImage(systemName: "mic.fill", withConfiguration: configuration) micButton.setImage(micImage, for: .normal) + micButton.layer.cornerRadius = 12 } // MARK: - Methods From b684b366f77b27e536b21d94c7bb2e692cc3893e Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Sun, 19 Jul 2020 20:36:22 -0600 Subject: [PATCH 6/7] Added recording session and mic updates if recording is in session or not --- .../Base.lproj/Main.storyboard | 1 + .../Experiences-Sprint-Challenge/Info.plist | 2 + .../AddExperienceViewController.swift | 116 +++++++++++++++++- 3 files changed, 118 insertions(+), 1 deletion(-) diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard index 9e4ffb0d..d42f1df9 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard @@ -132,6 +132,7 @@ + diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist index 3ecc41f8..d08a546c 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Info.plist @@ -64,6 +64,8 @@ $(PRODUCT_NAME) needs access to your photo library to filter images NSPhotoLibraryAddUsageDescription $(PRODUCT_NAME) needs access to your photo library to save images + NSMicrophoneUsageDescription + $(PRODUCT_NAME) uses the microphone to record audio NSLocationWhenInUseUsageDescription $(PRODUCT_NAME) uses your location to show where you are on the map diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift index 07894693..e953b0d1 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift @@ -9,6 +9,7 @@ import UIKit import CoreImage import CoreImage.CIFilterBuiltins +import AVFoundation class AddExperienceViewController: UIViewController { @@ -17,11 +18,16 @@ class AddExperienceViewController: UIViewController { @IBOutlet weak var imageView: UIImageView! @IBOutlet weak var photoButton: UIButton! @IBOutlet weak var micButton: UIButton! + @IBOutlet weak var saveButton: UIBarButtonItem! // MARK: - Properties private let context = CIContext() private let colorControlFilter = CIFilter.colorControls() - + private var audioRecorder: AVAudioRecorder? + private var recordingURL: URL? + var isRecording: Bool { + audioRecorder?.isRecording ?? false + } // MARK: - View Controller Lifecycles override func viewDidLoad() { super.viewDidLoad() @@ -44,6 +50,21 @@ class AddExperienceViewController: UIViewController { } // MARK: - Methods + func updateViews() { + photoButton.isEnabled = !isRecording + saveButton.isEnabled = !isRecording + + if isRecording { + micButton.isSelected = true + let configuration = UIImage.SymbolConfiguration(pointSize: 40) + let stopRecordingImage = UIImage(systemName: "stop.circle", withConfiguration: configuration) + let redStopRecording = stopRecordingImage?.withTintColor(.systemRed) + micButton.setImage(redStopRecording, for: .selected) + } else if !isRecording { + micButton.isSelected = false + } + } + private func presentImagePickerController() { guard UIImagePickerController.isSourceTypeAvailable(.photoLibrary) else { print("The photo library is not available") @@ -66,12 +87,90 @@ class AddExperienceViewController: UIViewController { return UIImage(ciImage: outputImage) } + func prepareAudioSession() throws { + let session = AVAudioSession.sharedInstance() + try session.setCategory(.playAndRecord, options: [.defaultToSpeaker]) + try session.setActive(true, options: []) // can fail if on a phone call, for instance + } + + func requestPermissionOrStartRecording() { + switch AVAudioSession.sharedInstance().recordPermission { + case .undetermined: + AVAudioSession.sharedInstance().requestRecordPermission { granted in + guard granted == true else { + print("We need microphone access") + return + } + + print("Recording permission has been granted!") + // NOTE: Invite the user to tap record again, since we just interrupted them, and they may not have been ready to record + } + case .denied: + print("Microphone access has been blocked.") + + let alertController = UIAlertController(title: "Microphone Access Denied", message: "Please allow this app to access your Microphone.", preferredStyle: .alert) + + alertController.addAction(UIAlertAction(title: "Open Settings", style: .default) { (_) in + UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!) + }) + + alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil)) + + present(alertController, animated: true, completion: nil) + case .granted: + startRecording() + @unknown default: + break + } + } + + func createNewRecordingURL() -> URL { + let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! + + let name = ISO8601DateFormatter.string(from: Date(), timeZone: .current, formatOptions: .withInternetDateTime) + let file = documents.appendingPathComponent(name, isDirectory: false).appendingPathExtension("caf") + + return file + } + + func startRecording() { + do { + try prepareAudioSession() + } catch { + print("Cannot record audio: \(error)") + return + } + + recordingURL = createNewRecordingURL() + + let format = AVAudioFormat(standardFormatWithSampleRate: 44_100, channels: 1)! + + do { + audioRecorder = try AVAudioRecorder(url: recordingURL!, format: format) + audioRecorder?.delegate = self + audioRecorder?.record() + updateViews() + } catch { + preconditionFailure("The audio recorder could not be created with \(recordingURL!) and \(format): \(error)") + } + } + + func stopRecording() { + audioRecorder?.stop() + updateViews() + } + // MARK: - IBActions @IBAction func imageButtonTapped(_ sender: UIButton) { presentImagePickerController() } @IBAction func recordButtonTapped(_ sender: UIButton) { + if isRecording { + stopRecording() + } else { + requestPermissionOrStartRecording() + } } } @@ -90,4 +189,19 @@ extension AddExperienceViewController: UIImagePickerControllerDelegate, UINaviga func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true, completion: nil) } + +} + +extension AddExperienceViewController: AVAudioRecorderDelegate { + func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { + if let recordingURL = recordingURL { + print("Successfully recorded message: \(recordingURL)") + } + } + + func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder, error: Error?) { + if let error = error { + print("Error Audio Recording: \(error)") + } + } } From 5c37074c101c5ddfc2fcf00ce068bc2698cd1972 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Sun, 19 Jul 2020 21:00:01 -0600 Subject: [PATCH 7/7] MVP - App saves new Experience and adds annotation to mapView --- .../Base.lproj/Main.storyboard | 18 +++++++++++++++++- .../AddExperienceViewController.swift | 18 ++++++++++++++++-- .../View Controllers/MapViewController.swift | 15 ++++++++++++++- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard index d42f1df9..a5904463 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/Base.lproj/Main.storyboard @@ -109,29 +109,44 @@ + + + - + + + + + + @@ -161,6 +176,7 @@ + diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift index e953b0d1..a91d07ce 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/AddExperienceViewController.swift @@ -10,6 +10,7 @@ import UIKit import CoreImage import CoreImage.CIFilterBuiltins import AVFoundation +import MapKit class AddExperienceViewController: UIViewController { @@ -19,8 +20,12 @@ class AddExperienceViewController: UIViewController { @IBOutlet weak var photoButton: UIButton! @IBOutlet weak var micButton: UIButton! @IBOutlet weak var saveButton: UIBarButtonItem! + @IBOutlet weak var recordingSuccessful: UIImageView! // MARK: - Properties + var mapView: MKMapView? + var latitude: Double? + var longitude: Double? private let context = CIContext() private let colorControlFilter = CIFilter.colorControls() private var audioRecorder: AVAudioRecorder? @@ -173,6 +178,15 @@ class AddExperienceViewController: UIViewController { } } + @IBAction func saveButtonTapped(_ sender: UIBarButtonItem) { + guard let latitude = latitude, + let longitude = longitude else { return } + let newExperience = Experience(title: titleTextField.text, image: imageView.image, audioURL: recordingURL, latitude: latitude, longitude: longitude) + mapView?.addAnnotation(newExperience) + + navigationController?.popToRootViewController(animated: true) + } + } extension AddExperienceViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { @@ -194,8 +208,8 @@ extension AddExperienceViewController: UIImagePickerControllerDelegate, UINaviga extension AddExperienceViewController: AVAudioRecorderDelegate { func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { - if let recordingURL = recordingURL { - print("Successfully recorded message: \(recordingURL)") + if recordingURL != nil { + recordingSuccessful.isHidden = false } } diff --git a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/MapViewController.swift b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/MapViewController.swift index dbd26b0e..79aab05b 100644 --- a/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/MapViewController.swift +++ b/Experiences-Sprint-Challenge/Experiences-Sprint-Challenge/View Controllers/MapViewController.swift @@ -17,6 +17,8 @@ class MapViewController: UIViewController { // MARK: - Properties private let locationManager = CLLocationManager() private var userTrackingButton: MKUserTrackingButton! + var latitude: Double? + var longitude: Double? override func viewDidLoad() { super.viewDidLoad() @@ -36,7 +38,18 @@ class MapViewController: UIViewController { } @IBAction func addExperienceButtonTapped(_ sender: UIBarButtonItem) { - + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if segue.identifier == "AddExperienceSegue" { + let addExperienceVC = segue.destination as! AddExperienceViewController + guard let location = locationManager.location?.coordinate else { return } + latitude = location.latitude + longitude = location.longitude + addExperienceVC.latitude = latitude + addExperienceVC.longitude = longitude + addExperienceVC.mapView = mapView + } } }