diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f492915 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Xcode +# +.DS_Store + +## SPM +.build/ +Packages/ +.swiftpm/ + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Gcc Patch +/*.gcno \ No newline at end of file diff --git a/FireworkVideoSample/FireworkVideoSample.xcodeproj/project.pbxproj b/FireworkVideoSample/FireworkVideoSample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..9b7b8fd --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample.xcodeproj/project.pbxproj @@ -0,0 +1,423 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 52; + objects = { + +/* Begin PBXBuildFile section */ + 7B8BD68E25BD667200BFFFB5 /* ChromaColorPicker in Frameworks */ = {isa = PBXBuildFile; productRef = 7B8BD68D25BD667200BFFFB5 /* ChromaColorPicker */; }; + 7B8BD69F25BD85C400BFFFB5 /* ColorPickers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B8BD69E25BD85C400BFFFB5 /* ColorPickers.swift */; }; + 7B8BD6A125BD973900BFFFB5 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B8BD6A025BD973900BFFFB5 /* Helpers.swift */; }; + 7BA940A925B9765300CF58D5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA940A825B9765300CF58D5 /* AppDelegate.swift */; }; + 7BA940AB25B9765300CF58D5 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA940AA25B9765300CF58D5 /* SceneDelegate.swift */; }; + 7BA940AD25B9765300CF58D5 /* VideoFeedExamplesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA940AC25B9765300CF58D5 /* VideoFeedExamplesViewController.swift */; }; + 7BA940B025B9765300CF58D5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7BA940AE25B9765300CF58D5 /* Main.storyboard */; }; + 7BA940B225B9765400CF58D5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7BA940B125B9765400CF58D5 /* Assets.xcassets */; }; + 7BA940B525B9765400CF58D5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7BA940B325B9765400CF58D5 /* LaunchScreen.storyboard */; }; + 7BA940C125B979D000CF58D5 /* VideoFeedCustomizationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA940BE25B979D000CF58D5 /* VideoFeedCustomizationViewController.swift */; }; + 7BA940C225B979D000CF58D5 /* VideoFeedLayoutsExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA940BF25B979D000CF58D5 /* VideoFeedLayoutsExampleViewController.swift */; }; + 7BA940C325B979D000CF58D5 /* TableViewEmbeddedVideoFeedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA940C025B979D000CF58D5 /* TableViewEmbeddedVideoFeedViewController.swift */; }; + 7BA940C625B979D900CF58D5 /* StoryboardVideoFeedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA940C525B979D900CF58D5 /* StoryboardVideoFeedViewController.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 7B8BD67925BD416400BFFFB5 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 7B8BD69E25BD85C400BFFFB5 /* ColorPickers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickers.swift; sourceTree = ""; }; + 7B8BD6A025BD973900BFFFB5 /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = ""; }; + 7BA940A525B9765300CF58D5 /* FireworkVideoSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FireworkVideoSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7BA940A825B9765300CF58D5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7BA940AA25B9765300CF58D5 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 7BA940AC25B9765300CF58D5 /* VideoFeedExamplesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoFeedExamplesViewController.swift; sourceTree = ""; }; + 7BA940AF25B9765300CF58D5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 7BA940B125B9765400CF58D5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 7BA940B425B9765400CF58D5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 7BA940B625B9765400CF58D5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 7BA940BE25B979D000CF58D5 /* VideoFeedCustomizationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoFeedCustomizationViewController.swift; sourceTree = ""; }; + 7BA940BF25B979D000CF58D5 /* VideoFeedLayoutsExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoFeedLayoutsExampleViewController.swift; sourceTree = ""; }; + 7BA940C025B979D000CF58D5 /* TableViewEmbeddedVideoFeedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewEmbeddedVideoFeedViewController.swift; sourceTree = ""; }; + 7BA940C525B979D900CF58D5 /* StoryboardVideoFeedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardVideoFeedViewController.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 7BA940A225B9765300CF58D5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7B8BD68E25BD667200BFFFB5 /* ChromaColorPicker in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 7B8BD82E25BD169200767F6A /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 7BA9409C25B9765300CF58D5 = { + isa = PBXGroup; + children = ( + 7BA940A725B9765300CF58D5 /* FireworkVideoSample */, + 7BA940A625B9765300CF58D5 /* Products */, + 7B8BD82E25BD169200767F6A /* Frameworks */, + ); + sourceTree = ""; + }; + 7BA940A625B9765300CF58D5 /* Products */ = { + isa = PBXGroup; + children = ( + 7BA940A525B9765300CF58D5 /* FireworkVideoSample.app */, + ); + name = Products; + sourceTree = ""; + }; + 7BA940A725B9765300CF58D5 /* FireworkVideoSample */ = { + isa = PBXGroup; + children = ( + 7BA940A825B9765300CF58D5 /* AppDelegate.swift */, + 7BA940AA25B9765300CF58D5 /* SceneDelegate.swift */, + 7BA940AC25B9765300CF58D5 /* VideoFeedExamplesViewController.swift */, + 7BA940BF25B979D000CF58D5 /* VideoFeedLayoutsExampleViewController.swift */, + 7BA940BE25B979D000CF58D5 /* VideoFeedCustomizationViewController.swift */, + 7BA940C025B979D000CF58D5 /* TableViewEmbeddedVideoFeedViewController.swift */, + 7BA940C525B979D900CF58D5 /* StoryboardVideoFeedViewController.swift */, + 7B8BD69E25BD85C400BFFFB5 /* ColorPickers.swift */, + 7B8BD6A025BD973900BFFFB5 /* Helpers.swift */, + 7BA940AE25B9765300CF58D5 /* Main.storyboard */, + 7BA940B125B9765400CF58D5 /* Assets.xcassets */, + 7BA940B325B9765400CF58D5 /* LaunchScreen.storyboard */, + 7BA940B625B9765400CF58D5 /* Info.plist */, + ); + path = FireworkVideoSample; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7BA940A425B9765300CF58D5 /* FireworkVideoSample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7BA940B925B9765400CF58D5 /* Build configuration list for PBXNativeTarget "FireworkVideoSample" */; + buildPhases = ( + 7BA940A125B9765300CF58D5 /* Sources */, + 7BA940A225B9765300CF58D5 /* Frameworks */, + 7BA940A325B9765300CF58D5 /* Resources */, + 7B8BD67925BD416400BFFFB5 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FireworkVideoSample; + packageProductDependencies = ( + 7B8BD68D25BD667200BFFFB5 /* ChromaColorPicker */, + ); + productName = FireworkVideoSample; + productReference = 7BA940A525B9765300CF58D5 /* FireworkVideoSample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7BA9409D25B9765300CF58D5 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1230; + LastUpgradeCheck = 1230; + ORGANIZATIONNAME = Firework; + TargetAttributes = { + 7BA940A425B9765300CF58D5 = { + CreatedOnToolsVersion = 12.3; + }; + }; + }; + buildConfigurationList = 7BA940A025B9765300CF58D5 /* Build configuration list for PBXProject "FireworkVideoSample" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7BA9409C25B9765300CF58D5; + packageReferences = ( + 7B8BD68C25BD667200BFFFB5 /* XCRemoteSwiftPackageReference "ChromaColorPicker" */, + ); + productRefGroup = 7BA940A625B9765300CF58D5 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7BA940A425B9765300CF58D5 /* FireworkVideoSample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7BA940A325B9765300CF58D5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7BA940B525B9765400CF58D5 /* LaunchScreen.storyboard in Resources */, + 7BA940B225B9765400CF58D5 /* Assets.xcassets in Resources */, + 7BA940B025B9765300CF58D5 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7BA940A125B9765300CF58D5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7BA940AD25B9765300CF58D5 /* VideoFeedExamplesViewController.swift in Sources */, + 7BA940A925B9765300CF58D5 /* AppDelegate.swift in Sources */, + 7BA940C125B979D000CF58D5 /* VideoFeedCustomizationViewController.swift in Sources */, + 7BA940AB25B9765300CF58D5 /* SceneDelegate.swift in Sources */, + 7B8BD6A125BD973900BFFFB5 /* Helpers.swift in Sources */, + 7BA940C625B979D900CF58D5 /* StoryboardVideoFeedViewController.swift in Sources */, + 7B8BD69F25BD85C400BFFFB5 /* ColorPickers.swift in Sources */, + 7BA940C225B979D000CF58D5 /* VideoFeedLayoutsExampleViewController.swift in Sources */, + 7BA940C325B979D000CF58D5 /* TableViewEmbeddedVideoFeedViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 7BA940AE25B9765300CF58D5 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 7BA940AF25B9765300CF58D5 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 7BA940B325B9765400CF58D5 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 7BA940B425B9765400CF58D5 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 7BA940B725B9765400CF58D5 /* 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + 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.0; + 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; + }; + 7BA940B825B9765400CF58D5 /* 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + 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.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7BA940BA25B9765400CF58D5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = Z24TE4EN73; + INFOPLIST_FILE = FireworkVideoSample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 0.1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.firework.FireworkVideoSample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7BA940BB25B9765400CF58D5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = Z24TE4EN73; + INFOPLIST_FILE = FireworkVideoSample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 0.1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.firework.FireworkVideoSample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7BA940A025B9765300CF58D5 /* Build configuration list for PBXProject "FireworkVideoSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7BA940B725B9765400CF58D5 /* Debug */, + 7BA940B825B9765400CF58D5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7BA940B925B9765400CF58D5 /* Build configuration list for PBXNativeTarget "FireworkVideoSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7BA940BA25B9765400CF58D5 /* Debug */, + 7BA940BB25B9765400CF58D5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 7B8BD68C25BD667200BFFFB5 /* XCRemoteSwiftPackageReference "ChromaColorPicker" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/joncardasis/ChromaColorPicker"; + requirement = { + branch = master; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 7B8BD68D25BD667200BFFFB5 /* ChromaColorPicker */ = { + isa = XCSwiftPackageProductDependency; + package = 7B8BD68C25BD667200BFFFB5 /* XCRemoteSwiftPackageReference "ChromaColorPicker" */; + productName = ChromaColorPicker; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 7BA9409D25B9765300CF58D5 /* Project object */; +} diff --git a/FireworkVideoSample/FireworkVideoSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/FireworkVideoSample/FireworkVideoSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/FireworkVideoSample/FireworkVideoSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/FireworkVideoSample/FireworkVideoSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/FireworkVideoSample/FireworkVideoSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/FireworkVideoSample/FireworkVideoSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..584a027 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "ChromaColorPicker", + "repositoryURL": "https://github.com/joncardasis/ChromaColorPicker", + "state": { + "branch": "master", + "revision": "4fa7a0b3cb779d97b771a5bf0de8927d6a37153f", + "version": null + } + } + ] + }, + "version": 1 +} diff --git a/FireworkVideoSample/FireworkVideoSample/AppDelegate.swift b/FireworkVideoSample/FireworkVideoSample/AppDelegate.swift new file mode 100644 index 0000000..a3f2f67 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/AppDelegate.swift @@ -0,0 +1,57 @@ +// +// AppDelegate.swift +// FireworkVideoSample +// +// Copyright © 2021 Firework. All rights reserved. +// + +import UIKit + +/// Add the dependency SDK +import FireworkVideo + +@main +class AppDelegate: UIResponder, UIApplicationDelegate, FireworkVideoSDKDelegate { + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + /// Initialize the SDK as soon as app launches. + FireworkVideoSDK.initializeSDK(delegate: self) + return true + } + + /// Implementing the FireworkVideoSDKDelegate allows + /// the SDK to notify your app if it was successfully + /// setup. + func fireworkVideoDidLoadSuccessfully() { + print("FireworkVideo loaded successfully.") + } + + func fireworkVideoDidLoadWith(error: FireworkVideoSDKError) { + switch error { + case .missingClientID: + print("FireworkVideo loaded with error due to missing client ID.") + case .authenticationFailure: + print("FireworkVideo loaded with error due to authentication failure.") + @unknown default: + break + } + } + + // 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/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AccentColor.colorset/Contents.json b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/Contents.json b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..12667b2 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,158 @@ +{ + "images" : [ + { + "filename" : "icon-20@2x-1.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "icon-20@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "icon-29@1x-1.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "icon-29@2x-1.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "icon-29@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "icon-40@2x-1.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "icon-40@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "icon-57@1x.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "57x57" + }, + { + "filename" : "icon-57@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "57x57" + }, + { + "filename" : "icon-60@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "icon-60@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "icon-20@1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "icon-20@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "icon-29@1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "icon-29@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "icon-40@1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "icon-40@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "icon-50@1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "50x50" + }, + { + "filename" : "icon-50@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "50x50" + }, + { + "filename" : "icon-72@1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "72x72" + }, + { + "filename" : "icon-72@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "72x72" + }, + { + "filename" : "icon-76@1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "icon-76@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "icon-83.5@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "MarketingIcon.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/MarketingIcon.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/MarketingIcon.png new file mode 100644 index 0000000..b837fe9 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/MarketingIcon.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-20@1x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-20@1x.png new file mode 100644 index 0000000..ded2864 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-20@1x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-20@2x-1.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-20@2x-1.png new file mode 100644 index 0000000..8312bd6 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-20@2x-1.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png new file mode 100644 index 0000000..8312bd6 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png new file mode 100644 index 0000000..7845ac5 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@1x-1.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@1x-1.png new file mode 100644 index 0000000..b9acac0 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@1x-1.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@1x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@1x.png new file mode 100644 index 0000000..b9acac0 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@1x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@2x-1.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@2x-1.png new file mode 100644 index 0000000..65aea5b Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@2x-1.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png new file mode 100644 index 0000000..65aea5b Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png new file mode 100644 index 0000000..d6728e5 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-40@1x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-40@1x.png new file mode 100644 index 0000000..15b0127 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-40@1x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-40@2x-1.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-40@2x-1.png new file mode 100644 index 0000000..d148f0a Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-40@2x-1.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png new file mode 100644 index 0000000..d148f0a Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png new file mode 100644 index 0000000..310a521 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-50@1x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-50@1x.png new file mode 100644 index 0000000..2726679 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-50@1x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-50@2x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-50@2x.png new file mode 100644 index 0000000..c45b62e Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-50@2x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-57@1x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-57@1x.png new file mode 100644 index 0000000..117d625 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-57@1x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-57@2x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-57@2x.png new file mode 100644 index 0000000..591224e Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-57@2x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png new file mode 100644 index 0000000..267354f Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png new file mode 100644 index 0000000..b4be7f4 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-72@1x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-72@1x.png new file mode 100644 index 0000000..481b55d Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-72@1x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png new file mode 100644 index 0000000..3c54f31 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-76@1x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-76@1x.png new file mode 100644 index 0000000..e103324 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-76@1x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png new file mode 100644 index 0000000..19d4919 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png new file mode 100644 index 0000000..228f7e8 Binary files /dev/null and b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ diff --git a/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/Contents.json b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FireworkVideoSample/FireworkVideoSample/Base.lproj/LaunchScreen.storyboard b/FireworkVideoSample/FireworkVideoSample/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..d30bca7 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FireworkVideoSample/FireworkVideoSample/Base.lproj/Main.storyboard b/FireworkVideoSample/FireworkVideoSample/Base.lproj/Main.storyboard new file mode 100644 index 0000000..ab092ad --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/Base.lproj/Main.storyboard @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FireworkVideoSample/FireworkVideoSample/ColorPickers.swift b/FireworkVideoSample/FireworkVideoSample/ColorPickers.swift new file mode 100644 index 0000000..1b6c894 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/ColorPickers.swift @@ -0,0 +1,115 @@ +// +// ColorPickers.swift +// FireworkVideoSample +// +// Copyright © 2021 Firework. All rights reserved. +// + +import UIKit +import ChromaColorPicker + +@available(iOS 14.0, *) +class ColorPickerDelegate: NSObject { + static let shared: ColorPickerDelegate = ColorPickerDelegate() + + var colorPicker: UIColorPickerViewController + var colorChangedHandler: ColorChangeHandler? + + override init() { + self.colorPicker = UIColorPickerViewController() + super.init() + self.colorPicker.delegate = self + self.colorPicker.view.backgroundColor = UIColor.secondarySystemBackground + } +} + +@available(iOS 14.0, *) +extension ColorPickerDelegate: UIColorPickerViewControllerDelegate { + func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) { + self.colorChangedHandler?(viewController.selectedColor) + } + + func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) { + self.colorChangedHandler?(viewController.selectedColor) + } +} + +typealias ColorChangeHandler = (UIColor) -> () + +class ChromaColorPickerHolder: UIViewController { + static let shared: ChromaColorPickerHolder = ChromaColorPickerHolder() + + var colorChangedHandler: ColorChangeHandler? + var colorSelectionFinishedHandler: ColorChangeHandler? + + var colorPicker: ChromaColorPicker + var brightnessSlider: ChromaBrightnessSlider + + override init(nibName nibNameOrNil: String?, + bundle nibBundleOrNil: Bundle?) { + self.colorPicker = ChromaColorPicker() + self.brightnessSlider = ChromaBrightnessSlider() + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + self.colorPicker.delegate = self + self.colorPicker.connect(self.brightnessSlider) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + + self.view.backgroundColor = UIColor.secondarySystemBackground + + self.colorPicker.translatesAutoresizingMaskIntoConstraints = false + self.view.addSubview(self.colorPicker) + self.colorPicker.leadingAnchor.constraint(equalTo:self.view.safeAreaLayoutGuide.leadingAnchor, + constant: 8.0).isActive = true + self.colorPicker.trailingAnchor.constraint(equalTo:self.view.safeAreaLayoutGuide.trailingAnchor, + constant: -8.0).isActive = true + self.colorPicker.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, + constant: 20.0).isActive = true + self.colorPicker.heightAnchor.constraint(equalTo: self.view.widthAnchor, + constant: -16.0).isActive = true + + self.brightnessSlider.translatesAutoresizingMaskIntoConstraints = false + self.view.addSubview(self.brightnessSlider) + self.brightnessSlider.leadingAnchor.constraint(equalTo:self.view.safeAreaLayoutGuide.leadingAnchor, + constant: 8.0).isActive = true + self.brightnessSlider.trailingAnchor.constraint(equalTo:self.view.safeAreaLayoutGuide.trailingAnchor, + constant: -8.0).isActive = true + self.brightnessSlider.widthAnchor.constraint(equalTo: self.colorPicker.widthAnchor).isActive = true + self.brightnessSlider.heightAnchor.constraint(equalToConstant: 30.0).isActive = true + self.brightnessSlider.topAnchor.constraint(equalTo: self.colorPicker.bottomAnchor, + constant: 20.0).isActive = true + } +} + +extension ChromaColorPickerHolder: ChromaColorPickerDelegate { + func colorPickerHandleDidChange(_ colorPicker: ChromaColorPicker, + handle: ChromaColorHandle, + to color: UIColor) { + self.colorChangedHandler?(color) + } +} + +func compatibleColorPicker(initialColor: UIColor, + colorChangedHandler: @escaping ColorChangeHandler) -> UIViewController { + if #available(iOS 14.0, *) { + ColorPickerDelegate.shared.colorChangedHandler = colorChangedHandler + let viewController = ColorPickerDelegate.shared.colorPicker + viewController.selectedColor = initialColor + return viewController + } else { + let viewController = ChromaColorPickerHolder() + viewController.colorPicker.addHandle(at: initialColor) + viewController.colorChangedHandler = colorChangedHandler + return viewController + } +} + +//func colorPickerTargetAction(handler: TargetAction.Handler) -> TargetAction { +// return +//} + diff --git a/FireworkVideoSample/FireworkVideoSample/Helpers.swift b/FireworkVideoSample/FireworkVideoSample/Helpers.swift new file mode 100644 index 0000000..4b6bcf3 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/Helpers.swift @@ -0,0 +1,48 @@ +// +// Helpers.swift +// FireworkVideoSample +// +// Copyright © 2021 Firework. All rights reserved. +// + +import UIKit + +final class TargetAction { + typealias Handler = () -> () + + let handler: Handler + + init(_ handler: @escaping Handler) { + self.handler = handler + } + + @objc func performAction() { + self.handler() + } +} + +final class SenderSuppliedTargetAction { + typealias Handler = (AnyObject) -> () + + let handler: Handler + + init(_ handler: @escaping Handler) { + self.handler = handler + } + @objc func performAction(_ sender: AnyObject) { + self.handler(sender) + } +} + +extension UILabel { + static func standardLabel(text: String, + font: UIFont = UIFont.preferredFont(forTextStyle: .callout), + textAlignment: NSTextAlignment = .center) -> UILabel { + let label = UILabel() + label.font = font + label.textAlignment = textAlignment + label.text = text + label.translatesAutoresizingMaskIntoConstraints = false + return label + } +} diff --git a/FireworkVideoSample/FireworkVideoSample/Info.plist b/FireworkVideoSample/FireworkVideoSample/Info.plist new file mode 100644 index 0000000..9330503 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/Info.plist @@ -0,0 +1,70 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + FireworkVideo Sample + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + 1 + FireworkVideoClientID + + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/FireworkVideoSample/FireworkVideoSample/SceneDelegate.swift b/FireworkVideoSample/FireworkVideoSample/SceneDelegate.swift new file mode 100644 index 0000000..10d4707 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/SceneDelegate.swift @@ -0,0 +1,52 @@ +// +// SceneDelegate.swift +// FireworkVideoSample +// +// Copyright © 2021 Firework. 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 necessarily 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/FireworkVideoSample/FireworkVideoSample/StoryboardVideoFeedViewController.swift b/FireworkVideoSample/FireworkVideoSample/StoryboardVideoFeedViewController.swift new file mode 100644 index 0000000..6734804 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/StoryboardVideoFeedViewController.swift @@ -0,0 +1,85 @@ +// +// StoryboardVideoFeedViewController.swift +// FireworkVideoSample +// +// Copyright © 2021 Firework. All rights reserved. +// + +import UIKit +import FireworkVideo + +class StoryboardVideoFeedViewController: UIViewController { + static let storyboardID = "StoryboardVideoFeedViewController" + + @IBOutlet weak var videoFeedView: UIView! + + /// Video Feed View Controller Initialized Here When it's View Controller + /// is intialized + var videoFeedViewController: VideoFeedViewController = VideoFeedViewController() + + override func viewDidLoad() { + super.viewDidLoad() + self.view.backgroundColor = UIColor.systemBackground + self.title = "Storyboard Video Feed" + + /// Configure layout, this uses the horizontal layout + /// That will display the video feed as a single row + let layout = VideoFeedHorizontalLayout() + layout.itemSpacing = 8 + layout.contentInsets = UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16) + self.videoFeedViewController.layout = layout + + /// Configuring appearance uses a model named View Configuration + + /// Copy the existing configuration, because it is a struct + /// changing the config variable will not affect the video feed until + /// it is set again, we'll do that later in this function. + var config = self.videoFeedViewController.viewConfiguration + + /// We set the background to a light gray color. + config.backgroundColor = UIColor.lightGray + + /// Configure the appearance of each video thumbnail in the feed + config.itemView.cornerRadius = 12 + + /// Configure title appearance + + /// Is the title visible or not? + config.itemView.title.isHidden = false + + /// Title text appearance including font, text color, and max number + /// of lines for long titles + config.itemView.title.font = UIFont.systemFont(ofSize: 12) + config.itemView.title.textColor = UIColor.white.withAlphaComponent(0.9) + config.itemView.title.numberOfLines = 2 + + /// Spacing between the end of the title text and the border of it's view + config.itemView.titleLayoutConfiguration.insets = UIEdgeInsets(top: 10, left: 4, bottom: 0, right: 4) + + /// Arrangement of the titles + config.itemView.titleLayoutConfiguration.titlePosition = .stacked + + /// When finished configuring the apperarance + /// Set back the viewConfiguration property + /// This will update the apperance of the video feed + self.videoFeedViewController.viewConfiguration = config + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + /// Insert the video feed + self.addChild(self.videoFeedViewController) + self.videoFeedView.addSubview(self.videoFeedViewController.view) + + /// Configure constraints to make sure video feed + /// size matches the video feed inside the storyboard + self.videoFeedViewController.view.translatesAutoresizingMaskIntoConstraints = false + self.videoFeedViewController.view.heightAnchor.constraint(equalTo: self.videoFeedView.heightAnchor).isActive = true + self.videoFeedViewController.view.widthAnchor.constraint(equalTo: self.videoFeedView.widthAnchor).isActive = true + + /// Notify the VideoFeedViewController that it's + /// been inserted into another view controller + self.videoFeedViewController.didMove(toParent: self) + } +} diff --git a/FireworkVideoSample/FireworkVideoSample/TableViewEmbeddedVideoFeedViewController.swift b/FireworkVideoSample/FireworkVideoSample/TableViewEmbeddedVideoFeedViewController.swift new file mode 100644 index 0000000..2bbacf7 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/TableViewEmbeddedVideoFeedViewController.swift @@ -0,0 +1,112 @@ +// +// TableViewEmbeddedVideoFeedViewController.swift +// FireworkVideoSample +// +// Copyright © 2021 Firework. All rights reserved. +// + +import UIKit +import FireworkVideo + + +class VideoFeedCell: UITableViewCell { + static let reuseIdentifier = "VideoFeedCellReuseIdentifier" + + var isSetup: Bool = false + + func setup(videoFeedViewController: VideoFeedViewController) { + self.contentView.addSubview(videoFeedViewController.view) + + videoFeedViewController.view.translatesAutoresizingMaskIntoConstraints = false + + self.contentView.safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: videoFeedViewController.view.leadingAnchor).isActive = true + self.contentView.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: videoFeedViewController.view.trailingAnchor).isActive = true + self.contentView.safeAreaLayoutGuide.topAnchor.constraint(equalTo: videoFeedViewController.view.topAnchor).isActive = true + self.contentView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: videoFeedViewController.view.bottomAnchor).isActive = true + + self.isSetup = true + } +} + +class TableViewEmbeddedVideoFeedViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { + lazy var videoFeedViewController: VideoFeedViewController = self.configuredFeedViewController() + + lazy var tableView: UITableView = self.configureTableView() + + override func viewDidLoad() { + super.viewDidLoad() + self.view.backgroundColor = UIColor.systemBackground + self.setup() + } + + private func setup() { + self.view.addSubview(self.tableView) + self.view.safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: self.tableView.leadingAnchor).isActive = true + self.view.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: self.tableView.trailingAnchor).isActive = true + self.view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: self.tableView.topAnchor).isActive = true + self.view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: self.tableView.bottomAnchor).isActive = true + } + + private func configuredFeedViewController() -> VideoFeedViewController { + let viewController = VideoFeedViewController() + let layout = VideoFeedHorizontalLayout() + layout.itemSpacing = 8 + layout.contentInsets = UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16) + viewController.layout = layout + var config = viewController.viewConfiguration + config.backgroundColor = UIColor.lightGray + config.itemView.cornerRadius = 12 + config.itemView.titleLayoutConfiguration.insets = UIEdgeInsets(top: 10, left: 4, bottom: 0, right: 4) + config.itemView.title.isHidden = false + config.itemView.title.font = UIFont.systemFont(ofSize: 12) + config.itemView.title.numberOfLines = 2 + config.itemView.title.textColor = UIColor.white.withAlphaComponent(0.9) + config.itemView.titleLayoutConfiguration.titlePosition = .stacked + viewController.viewConfiguration = config + return viewController + } + + private func configureTableView() -> UITableView { + let tableView = UITableView(frame: .zero, style: .grouped) + + tableView.register(VideoFeedCell.self, + forCellReuseIdentifier: VideoFeedCell.reuseIdentifier) + tableView.translatesAutoresizingMaskIntoConstraints = false + + tableView.delegate = self + tableView.dataSource = self + + return tableView + } + + + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 2 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + + + if indexPath.row == 0 { + let cell = UITableViewCell(style: .subtitle, + reuseIdentifier: "") + cell.textLabel?.text = NSLocalizedString("Embed the Horizontal Layout in a Table View", + comment: "") + return cell + } else { + let cell = tableView.dequeueReusableCell(withIdentifier: VideoFeedCell.reuseIdentifier, + for: indexPath) as! VideoFeedCell + + if !cell.isSetup { + cell.setup(videoFeedViewController: self.videoFeedViewController) + } + + return cell + } + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return indexPath.row == 0 ? 75.0 : 280.0 + } +} diff --git a/FireworkVideoSample/FireworkVideoSample/VideoFeedCustomizationViewController.swift b/FireworkVideoSample/FireworkVideoSample/VideoFeedCustomizationViewController.swift new file mode 100644 index 0000000..35d13d6 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/VideoFeedCustomizationViewController.swift @@ -0,0 +1,193 @@ +// +// ProgrammaticCustomizationViewController.swift +// FireworkVideoSample +// +// Copyright © 2021 Firework. All rights reserved. +// + +import UIKit +import FireworkVideo + +/// Example of a customization panel that can be used +/// to modify the apperance of the video feed at runtime +class VideoFeedCustomizationViewController: UIViewController { + + var embeddedVideoFeedViewController: VideoFeedViewController! + + /// This wraps get and set calls to the viewConfiguration property + /// of the embedded view controller + var videoFeedContentConfiguration: VideoFeedContentConfiguration! { + get { + return self.embeddedVideoFeedViewController.viewConfiguration + } + set(newValue) { + self.embeddedVideoFeedViewController.viewConfiguration = newValue + } + } + + /// Controls that change apperance, along with helpers + var colorAttributesSegmentedControl: UISegmentedControl! + var selectedColorPreview: UIView! + var customizationControlsStackView: UIStackView! + + var selectedColor: UIColor = .lightGray + var targetActions: [TargetAction] = [] + var senderSuppliedTargetActions: [SenderSuppliedTargetAction] = [] + + /// The video feed view controller is embedded when view first loads + override func viewDidLoad() { + super.viewDidLoad() + + self.view.backgroundColor = UIColor.secondarySystemBackground + + self.embeddedVideoFeedViewController = VideoFeedLayoutTypes.horizontalViewController + self.setupVideoFeed(videoFeedViewController: self.embeddedVideoFeedViewController, + topSpacing: 20.0, + fullHeight: false) + self.setupCustomizationControls() + } + + private func setupColorAttributesSegmentedControl() -> UISegmentedControl { + let colorSegmentedControl = UISegmentedControl() + colorSegmentedControl.translatesAutoresizingMaskIntoConstraints = false + colorSegmentedControl.insertSegment(withTitle: "Background Color", + at: 0, + animated: false) + colorSegmentedControl.insertSegment(withTitle: "Video Title Text Color", + at: 1, + animated: false) + return colorSegmentedControl + } + + private func setupSelectedColorPreview() -> UIView { + let selectedColorPreview = UIView() + selectedColorPreview.translatesAutoresizingMaskIntoConstraints = false + selectedColorPreview.heightAnchor.constraint(equalToConstant: 50.0).isActive = true + selectedColorPreview.layer.cornerRadius = 10.0 + return selectedColorPreview + } + + private func setupPickDifferentColorButton() -> UIButton { + let pickDifferentColorButton = UIButton() + pickDifferentColorButton.setTitle("Pick Different Color", for: .normal) + pickDifferentColorButton.setTitleColor(.systemBlue, for: .normal) + pickDifferentColorButton.setTitleColor(UIColor.systemBlue.withAlphaComponent(0.75), for: .highlighted) + return pickDifferentColorButton + } + + private func setupFontSizePickingSlider() -> UISlider { + let fontSizePickingSlider = UISlider() + fontSizePickingSlider.translatesAutoresizingMaskIntoConstraints = false + fontSizePickingSlider.minimumValue = 6.0 + fontSizePickingSlider.maximumValue = 24.0 + return fontSizePickingSlider + } + + private func setupCustomizationControls() { + let colorSegmentExplanation = UILabel.standardLabel(text: NSLocalizedString("Pick a Color Attribute to Change", + comment: "")) + self.colorAttributesSegmentedControl = self.setupColorAttributesSegmentedControl() + self.colorAttributesSegmentedControl.selectedSegmentIndex = 0 + let colorSegmentTargetAction = SenderSuppliedTargetAction { (sender) in + if let segmentedControl = sender as? UISegmentedControl { + self.updateSelected(segmentIndex: segmentedControl.selectedSegmentIndex) + } + } + self.senderSuppliedTargetActions.append(colorSegmentTargetAction) + + self.colorAttributesSegmentedControl.addTarget(colorSegmentTargetAction, + action: #selector(SenderSuppliedTargetAction.performAction(_:)), + for: .valueChanged) + + let colorSelectionLabel = UILabel.standardLabel(text: NSLocalizedString("Current Color Selected", + comment: "")) + self.selectedColorPreview = self.setupSelectedColorPreview() + selectedColorPreview.backgroundColor = self.selectedColor + + + let targetAction = TargetAction { + let viewController = compatibleColorPicker(initialColor: self.selectedColor) { (color) in + self.selectedColor = color + self.updateSelectedColor() + } + self.navigationController?.pushViewController(viewController, + animated: true) + } + self.targetActions.append(targetAction) + let pickDifferentColorButton = self.setupPickDifferentColorButton() + pickDifferentColorButton.addTarget(targetAction, + action: #selector(TargetAction.performAction), + for: .touchUpInside) + + let fontSizeSlider = self.setupFontSizePickingSlider() + fontSizeSlider.value = Float(self.videoFeedContentConfiguration.itemView.title.font.pointSize) + let sliderTargetAction = SenderSuppliedTargetAction { sender in + if let s = sender as? UISlider { + var configuration = self.videoFeedContentConfiguration! + configuration.itemView.title.font = UIFont.preferredFont(forTextStyle: .body).withSize(CGFloat(s.value)) + self.videoFeedContentConfiguration = configuration + } + } + self.senderSuppliedTargetActions.append(sliderTargetAction) + fontSizeSlider.addTarget(sliderTargetAction, + action: #selector(SenderSuppliedTargetAction.performAction(_:)), + for: .valueChanged) + + self.customizationControlsStackView = UIStackView(arrangedSubviews: [ + colorSegmentExplanation, + self.colorAttributesSegmentedControl, + pickDifferentColorButton, + colorSelectionLabel, + selectedColorPreview, + UILabel.standardLabel(text: NSLocalizedString("Drag Slider to Change Title Font Size", + comment: "")), + fontSizeSlider, + ]) + + self.customizationControlsStackView.translatesAutoresizingMaskIntoConstraints = false + self.customizationControlsStackView.spacing = 10.0 + self.customizationControlsStackView.axis = .vertical + self.customizationControlsStackView.distribution = .fillProportionally + + self.view.addSubview(self.customizationControlsStackView) + self.customizationControlsStackView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, + constant: 8.0).isActive = true + self.customizationControlsStackView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, + constant: -8.0).isActive = true + self.customizationControlsStackView.topAnchor.constraint(equalTo: self.embeddedVideoFeedViewController.view.bottomAnchor, + constant: 16.0).isActive = true + + self.updateSelectedColor() + } + + /// Handler methods for controls + func updateSelected(segmentIndex: Int) { + switch segmentIndex { + case 0: + self.selectedColor = self.videoFeedContentConfiguration.backgroundColor! + self.selectedColorPreview.backgroundColor = self.selectedColor + case 1: + self.selectedColor = self.videoFeedContentConfiguration.itemView.title.textColor + self.selectedColorPreview.backgroundColor = self.selectedColor + default: + break + } + } + + func updateSelectedColor() { + self.selectedColorPreview.backgroundColor = self.selectedColor + + switch self.colorAttributesSegmentedControl.selectedSegmentIndex { + case 0: + self.videoFeedContentConfiguration.backgroundColor = self.selectedColor + break + case 1: + var configuration = self.videoFeedContentConfiguration! + configuration.itemView.title.textColor = self.selectedColor + self.videoFeedContentConfiguration = configuration + break + default: + break + } + } +} diff --git a/FireworkVideoSample/FireworkVideoSample/VideoFeedExamplesViewController.swift b/FireworkVideoSample/FireworkVideoSample/VideoFeedExamplesViewController.swift new file mode 100644 index 0000000..ca7256f --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/VideoFeedExamplesViewController.swift @@ -0,0 +1,40 @@ +// +// VideoFeedExamplesViewController.swift +// FireworkVideoSample +// +// Copyright © 2021 Firework. All rights reserved. +// + +import UIKit + +class VideoFeedExamplesViewController: UITableViewController { + + override func viewDidLoad() { + super.viewDidLoad() + self.title = "Firework Video Sample App" + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + if let viewController = self.viewController(at: indexPath) { + self.navigationController?.pushViewController(viewController, + animated: true) + } + } + + func viewController(at selectedIndexPath:IndexPath) -> UIViewController? { + if selectedIndexPath.section == 0 && selectedIndexPath.row == 0 { + return VideoFeedLayoutsExampleViewController() + } else if selectedIndexPath.section == 0 && selectedIndexPath.row == 1 { + /// A grid view controller is configured and returned in this method + let gridViewController = VideoFeedLayoutTypes.configureGridVideoFeedViewController() + return gridViewController + } else if selectedIndexPath.section == 0 && selectedIndexPath.row == 2 { + return VideoFeedCustomizationViewController() + } else if selectedIndexPath.section == 0 && selectedIndexPath.row == 3 { + return TableViewEmbeddedVideoFeedViewController() + } + + return nil + } +} + diff --git a/FireworkVideoSample/FireworkVideoSample/VideoFeedLayoutsExampleViewController.swift b/FireworkVideoSample/FireworkVideoSample/VideoFeedLayoutsExampleViewController.swift new file mode 100644 index 0000000..2883711 --- /dev/null +++ b/FireworkVideoSample/FireworkVideoSample/VideoFeedLayoutsExampleViewController.swift @@ -0,0 +1,199 @@ +// +// VideoFeedLayoutsExampleViewController.swift +// FireworkVideoSample +// +// Copyright © 2021 Firework. All rights reserved. +// + +import UIKit +import FireworkVideo + +/// Examples of setting up different video feed layouts and view configurations +class VideoFeedLayoutTypes { + static var horizontalViewController: VideoFeedViewController { + return VideoFeedLayoutTypes.configureHorizontalVideoFeedViewController() + } + + static var verticalViewController: VideoFeedViewController { + return VideoFeedLayoutTypes.configureVerticalVideoFeedViewController() + } + + static var gridViewController: VideoFeedViewController { + return VideoFeedLayoutTypes.configureGridVideoFeedViewController() + } + + static func configureHorizontalVideoFeedViewController() -> VideoFeedViewController { + let vc = VideoFeedViewController() + let layout = VideoFeedHorizontalLayout() + layout.itemSpacing = 8 + layout.contentInsets = UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16) + vc.layout = layout + var config = vc.viewConfiguration + config.backgroundColor = UIColor.systemGray + config.itemView.cornerRadius = 12 + config.itemView.titleLayoutConfiguration.insets = UIEdgeInsets(top: 10, left: 4, bottom: 0, right: 4) + config.itemView.title.isHidden = false + config.itemView.title.font = UIFont.preferredFont(forTextStyle: .body).withSize(12.0) + config.itemView.title.numberOfLines = 2 + config.itemView.title.textColor = UIColor.white.withAlphaComponent(0.9) + config.itemView.titleLayoutConfiguration.titlePosition = .stacked + vc.viewConfiguration = config + return vc + } + + static func configureVerticalVideoFeedViewController() -> VideoFeedViewController { + let verticalVC = VideoFeedViewController() + let layout = VideoFeedGridLayout() + layout.numberOfColumns = 1 + layout.contentInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) + layout.itemSpacing = 20 + verticalVC.layout = layout + var config = verticalVC.viewConfiguration + config.backgroundColor = .white + verticalVC.viewConfiguration = config + return verticalVC + } + + static func configureGridVideoFeedViewController() -> VideoFeedViewController { + let gridVC = VideoFeedViewController() + gridVC.view.backgroundColor = .systemBackground + let layout = VideoFeedGridLayout() + layout.numberOfColumns = 3 + layout.contentInsets = UIEdgeInsets(top: 16, left: 8, bottom: 16, right: 8) + gridVC.layout = layout + var config = gridVC.viewConfiguration + config.backgroundColor = .white + gridVC.viewConfiguration.playerView.videoCompleteAction = .loop + gridVC.viewConfiguration = config + return gridVC + } +} + +/// Extension showing how to insert and remove video feeds +/// from a container view controller +extension UIViewController { + func removeVideoFeed(videoFeedViewController: VideoFeedViewController) { + videoFeedViewController.willMove(toParent: nil) + videoFeedViewController.view.removeFromSuperview() + videoFeedViewController.removeFromParent() + } + + /// In addition to embedding the view feed, this method sets up constraints + func setupVideoFeed(videoFeedViewController: VideoFeedViewController, + topSpacing: CGFloat = 60.0, + fullHeight: Bool = true) { + self.addChild(videoFeedViewController) + self.view.addSubview(videoFeedViewController.view) + videoFeedViewController.view.translatesAutoresizingMaskIntoConstraints = false + + videoFeedViewController.view.leadingAnchor.constraint(equalTo:self.view.safeAreaLayoutGuide.leadingAnchor).isActive = true + videoFeedViewController.view.trailingAnchor.constraint(equalTo:self.view.safeAreaLayoutGuide.trailingAnchor).isActive = true + videoFeedViewController.view.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, + constant: topSpacing).isActive = true + + if fullHeight { + videoFeedViewController.view.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, + constant: -topSpacing).isActive = true + } else { + videoFeedViewController.view.heightAnchor.constraint(equalToConstant: 280.0).isActive = true + } + + videoFeedViewController.didMove(toParent: self) + } +} + +/// Use this along with VideoFeedLayoutTypes to see how different layouts can be implemented +/// and embedded into a container view controller +class VideoFeedLayoutsExampleViewController: UIViewController { + + var embeddedVideoFeedViewController:VideoFeedViewController! + var layoutPicker: UISegmentedControl! + + var targetAction: SenderSuppliedTargetAction? + + override func viewDidLoad() { + super.viewDidLoad() + self.view.backgroundColor = UIColor.secondarySystemBackground + self.embeddedVideoFeedViewController = VideoFeedLayoutTypes.horizontalViewController + self.setupLayoutPicker() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.setupVideoFeed(videoFeedViewController: self.embeddedVideoFeedViewController, + fullHeight: false) + } + + override func viewDidDisappear(_ animated: Bool) { + self.removeVideoFeed(videoFeedViewController: self.embeddedVideoFeedViewController) + super.viewDidDisappear(animated) + } + + private func setupLayoutPicker() { + self.layoutPicker = UISegmentedControl() + + self.layoutPicker.insertSegment(withTitle: "Horizontal", + at: 0, + animated: false) + self.layoutPicker.insertSegment(withTitle: "Vertical", + at: 1, + animated: false) + self.layoutPicker.insertSegment(withTitle: "Grid", + at: 2, + animated: false) + self.layoutPicker.selectedSegmentIndex = 0 + + let targetAction = SenderSuppliedTargetAction { sender in + if let segmentedControl = sender as? UISegmentedControl { + self.removeVideoFeed(videoFeedViewController: self.embeddedVideoFeedViewController) + + switch segmentedControl.selectedSegmentIndex { + case 0: + self.embeddedVideoFeedViewController = VideoFeedLayoutTypes.horizontalViewController + self.setupVideoFeed(videoFeedViewController: self.embeddedVideoFeedViewController, + fullHeight: false) + case 1: + self.embeddedVideoFeedViewController = VideoFeedLayoutTypes.verticalViewController + self.setupVideoFeed(videoFeedViewController: self.embeddedVideoFeedViewController) + case 2: + self.embeddedVideoFeedViewController = VideoFeedLayoutTypes.gridViewController + self.setupVideoFeed(videoFeedViewController: self.embeddedVideoFeedViewController) + default: + break + } + + + } + } + self.layoutPicker.addTarget(targetAction, + action: #selector(SenderSuppliedTargetAction.performAction(_:)), + for: .valueChanged) + self.targetAction = targetAction + + self.layoutPicker.tintColor = UIColor.secondarySystemGroupedBackground + self.layoutPicker.translatesAutoresizingMaskIntoConstraints = false + self.view.addSubview(self.layoutPicker) + + self.layoutPicker.leadingAnchor.constraint(equalTo:self.view.safeAreaLayoutGuide.leadingAnchor, + constant: 8.0).isActive = true + self.layoutPicker.trailingAnchor.constraint(equalTo:self.view.safeAreaLayoutGuide.trailingAnchor, + constant: -8.0).isActive = true + self.layoutPicker.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, + constant: 20.0).isActive = true + } + + private func configureVerticalVideoFeedLayout() -> VideoFeedGridLayout { + let layout = VideoFeedGridLayout() + layout.numberOfColumns = 1 + layout.contentInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) + layout.itemSpacing = 20 + return layout + } + + private func configureGridVideoFeedLayout() -> VideoFeedGridLayout { + let layout = VideoFeedGridLayout() + layout.numberOfColumns = 3 + layout.contentInsets = UIEdgeInsets(top: 16, left: 8, bottom: 16, right: 8) + return layout + } +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..d7a44a0 --- /dev/null +++ b/Package.swift @@ -0,0 +1,22 @@ +// swift-tools-version:5.3 + +import PackageDescription + +let package = Package( + name: "FireworkVideo", + platforms: [ + .iOS(.v12), + ], + products: [ + .library(name: "FireworkVideo", + targets: ["FireworkVideo"]) + ], + dependencies: [ + ], + targets: [ + .binaryTarget(name: "FireworkVideo", + url: "https://www.github.com/loopsocial/firework_ios_sdk/releases/download/v0.1.0/FireworkVideo-v0.1.0.xcframework.zip", + checksum: "e27cbdd8f285b65a111221128fd710d62a56ee1d0f4883867c0f4d2d72a23270"), + ] + +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..cbe1792 --- /dev/null +++ b/README.md @@ -0,0 +1,222 @@ +# FireworkVideo + +## About + +FireworkVideo is a library to integrate video feeds from Firework - a short form video platform - into your iOS app. + +## Setup Prerequisites + +To integrate FireworkVideo into your application, you have to register your application with Firework platform and get unique FireworkVideo client ID. To get the client ID + +- [X] Provide your application's bundle identifier or package name to the business team / engineering team you are co-ordinating with. +- [X] We will email you the client ID, follow the setup steps below under Firework Client ID to include it in your app. + +The client ID is used to authenticate your application with the server. Authentication will fail if you use wrong client ID. + +## Requirements + +FireworkVideo is compatible with: + + - iOS 12 or greater. + - Xcode 12.3 or greater. + - Swift 5.3 or greater. + +## How to add FireworkVideo to your Xcode project? + +FireworkVideo can be added as a Swift binary package using Swift Package Manager or it can be imported manually as framework. Instructions for both installation methods are below. + +### Importing Using Swift Package Manager + +In your Xcode project, select File > Swift Packages > Add Package Dependency and enter the following URL: `https://www.github.com/loopsocial/firework_ios_sdk/` + +> If you are new to Xcode's Swift Pacakage Manager integration, please refer to Apple's documentation on [Adding a Package Dependency to Your App](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app) + +Select Version > Up to Next Major > 0.1.0. + +### Importing FireworkVideo Framework Manually + +* Clone the SDK repo located at `https://www.github.com/loopsocial/firework_ios_sdk/` + +* Download the [SDK binary](https://www.github.com/loopsocial/firework_ios_sdk/releases/download/v0.1.0/FireworkVideo-v0.1.0.xcframework.zip) and unzip if needed. + +* Drag the unzipped `FireworkVideo.xcframework` into the Xcode project navigator and drop it at root of your project. Make sure `Copy items if needed` checkbox is selected in the confirmation dialog. Check to make sure your project directory now has `FireworkVideo.xcframework` in it and it is visible and linked in your Xcode project navigator. + +* In your apps Project pane select your app Target > Build Phases, click the + button, and add a Copy Files step. Select `Frameworks` as the destination and click the + within the Copy Files step and select FireworkVideo.xcframework. Your Copy Files step should look like below. + +![Copy Files Step](https://github.com/loopsocial/firework_ios_sdk/blob/main/Resources/manual_installation_copy_files_step.png?raw=true) + +### Firework Client ID + +Include the client ID in your app `Info.plist` file using the key `FireworkVideoClientID`, see image below. If the client ID is not included or is included under a differently spelled key, the SDK will return an error. See Troubleshooting for more details. + +![App Setup Info Plist](https://github.com/loopsocial/firework_ios_sdk/blob/main/Resources/app_setup_info_plist.png?raw=true) + +### Using FireworkVideo + +#### Initialization + +Before using any components video components are used you must initialize `FireworkVideo`. + +Start by importing the SDK into your App Delegate. Then initialize the Firework Video SDK in the App Delegate method `application(:, didFinishLaunchingWithOptions:) -> Bool`. + +``` +import UIKit + +// Add the dependency SDK +import FireworkVideo + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + // Initialize the SDK to start using + FireworkVideoSDK.initializeSDK() + + return true + } + + // ... // +} +``` +#### Displaying the Video Feed + +Displaying a video feed can be done by using a `VideoFeedViewController` either programmatically or in a storyboard. + +##### Programmatic + + 1. Import FireworkVideo + 2. Create a new `VideoFeedViewController` + 3. Present, show or embed the instantiated `VideoFeedViewController` + +``` +import FireworkVideo + +class ViewController: UIViewController { + + func addFeedToTabBarController() { + let feedVC = VideoFeedViewController() + feedVC.tabBarItem = UITabBarItem(title: "Videos", image: nil, selectedImage: nil) + self.tabBarController?.viewControllers?.append(feedVC) + } + + func pushFeedOnNavigationController() { + let feedVC = VideoFeedViewController() + self.navigationController?.pushViewController(feedVC, animated: true) + } + + func embedFeedInViewController() { + let feedVC = VideoFeedViewController() + self.addChild(feedVC) + self.view.addSubview(feedVC.view) + feedVC.view.frame = self.view.bounds + feedVC.willMove(toParent: self) + } + +} +``` + +##### Storyboard + +Adding a `VideoFeedViewController` to a storyboard is easy but has limited support. Therefore, layout and customizations can only be made progrgammatically. Both layout and customizations changes can be made at anytime. This allows you to add a `VideoFeedViewController` via storyboard then make adjustments programmatically. + + 1. Open storyboard + 2. Add and select a `UIViewController` + 3. In the Inspector pane, select the Identity inspector + 4. Add `VideoFeedViewController` as the Custom Class + 5. Make sure the Module is set to `FireworkVideo` + +![Storyboard Setup](https://github.com/loopsocial/firework_ios_sdk/blob/main/Resources/storyboard_setup.png?raw=true) + +#### Layouts + +There are two concrete layout types `VideoFeedHorizontalLayout` and `VideoFeedGridLayout`. A layout can be specified at time of instantiating a `let feedVC = VideoFeedViewController(layout: VideoFeedGridLayout())`. Or the layout can be set later by setting the layout property of the video feed view controller; `feedVC.layout = VideoFeedHorizontalLayout()`. + +##### Horizontal Layout + +The `VideoFeedHorizontalLayout` is a layout that has a single row and scrolls horizontally. The row will fill the available height. + +> A `VideoFeedHorizontalLayout` is best used by embedding in another view controller. + +##### Grid Layout + +The `VideoFeedGridLayout` is a layout that provides a vertical scrolling feed that has both columns and rows. The width of the items in the grid are calculated based on the `numberOfColumns` property in the `VideoFeedGridLayout`. + +> Note: Only changing a layout property does not change the `VideoFeedViewController` layout. To update the `VideoFeedViewController` layout you must set the `layout` property after making the layout changes. + +#### Feed Customizations + +There are many view customizations that are exposed and can be accessed by the `viewConfiguration` of a `VideoFeedViewController`. All view configurations are value types which means changing the properties will not update the view state. To update the view state, simply set the `viewConfiguration` with the updated view configuration. + +``` +let feedVC = VideoFeedViewController() + +// Gets the default configuration +var config = feedVC.viewConfiguration + +// Sets the feed background to white +config.backgroundColor = .white + +// Gets the feed item view configuration. This applies to all items in the feed. +var itemConfig = config.itemView + +// Sets the corner radius to 4 +itemConfig.cornerRadius = 4 +// Sets the title layout insets to 0 +itemConfig.titleLayoutConfiguration.insets = .zero +// Sets the title position to stacked +itemConfig.titleLayoutConfiguration.titlePosition = .stacked +// Sets the title to System 18 +itemConfig.title.font = .systemFont(ofSize: 18) +// Sets the title number of lines to 1 +itemConfig.title.numberOfLines = 1 +// Sets the title color to black +itemConfig.title.textColor = .black +// Sets the title is hidden to false +itemConfig.title.isHidden = false + +// Updates the title configuration +config.itemView = itemConfig + +// Must set the viewConfiguration property to apply the changes +feedVC.viewConfiguration = config +``` + +### Troubleshooting + +`FireworkVideoSDK.initializeSDK` accepts an optional `delegate` parameter that can receive any errors the SDK outputs during setup. This delegate can be any class that conforms to the `FireworkVideoSDKDelegate` protocol. See example code below that uses `AppDelegate` to print any errors to console. + +``` +import UIKit + +/// Add the dependency SDK +import FireworkVideo + +@main +class AppDelegate: UIResponder, UIApplicationDelegate, FireworkVideoSDKDelegate { + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + FireworkVideoSDK.initializeSDK(delegate: self) + return true + } + + func fireworkVideoDidLoadSuccessfully() { + print("FireworkVideo loaded successfully.") + } + + func fireworkVideoDidLoadWith(error: FireworkVideoSDKError) { + switch error { + case .missingClientID: + print("FireworkVideo loaded with error due to missing FireworkVideo client ID.") + case .authenticationFailure: + print("FireworkVideo loaded with error due to authentication failure.") + @unknown default: + break + } + } +``` + +## API Changes + +**You're strongly encouraged you to use the latest available FireworkVideo version. Versions prior to 1.0 may contain breaking API changes and behaviors. Please provide feedback and report any bugs to the business and engineering team you're working with.** diff --git a/Resources/app_setup_info_plist.png b/Resources/app_setup_info_plist.png new file mode 100644 index 0000000..667977f Binary files /dev/null and b/Resources/app_setup_info_plist.png differ diff --git a/Resources/manual_installation_copy_files_step.png b/Resources/manual_installation_copy_files_step.png new file mode 100644 index 0000000..5ca762a Binary files /dev/null and b/Resources/manual_installation_copy_files_step.png differ diff --git a/Resources/storyboard_setup.png b/Resources/storyboard_setup.png new file mode 100644 index 0000000..e61044a Binary files /dev/null and b/Resources/storyboard_setup.png differ