From df595603a25a3a30e766c1a472a9b1ff42f2a26f Mon Sep 17 00:00:00 2001 From: UXSDK Core Team Date: Wed, 16 Oct 2019 14:01:29 -0700 Subject: [PATCH] Updating to beta 1.1 code --- DJI-UXSDK-iOS-Beta-Communication.podspec | 2 +- DJI-UXSDK-iOS-Beta-Core.podspec | 8 +- DJI-UXSDK-iOS-Beta-Widgets.podspec | 10 +- DJI-UXSDK-iOS-Beta.podspec | 10 +- .../NSObject+DUXBetaCommand.h | 24 +- .../NSObject+DUXBetaMapping.h | 24 +- .../NSObject+DUXBetaMapping.m | 24 +- .../NSObject+DUXBetaRKVOExtension.h | 24 +- .../NSObject+DUXBetaRKVOExtension.m | 24 +- .../VisionWidget/DUXBetaVisionWidget.h | 24 +- .../VisionWidget/DUXBetaVisionWidget.m | 24 +- UXSDKBetaSample/Podfile | 11 +- .../UXSDKBetaSample.xcodeproj/project.pbxproj | 60 +- .../UXSDKBetaSample/AppDelegate.swift | 3 +- .../Aircraft.imageset/Aircraft@1x.png | Bin 0 -> 1700 bytes .../Aircraft.imageset/Aircraft@2x.png | Bin 0 -> 4445 bytes .../Aircraft.imageset/Aircraft@3x.png | Bin 0 -> 8028 bytes .../Aircraft.imageset/Contents.json | 23 + .../HomePoint.imageset/Contents.json | 23 + .../HomePoint.imageset/HomePoint@1x.png | Bin 0 -> 2540 bytes .../HomePoint.imageset/HomePoint@2x.png | Bin 0 -> 2540 bytes .../HomePoint.imageset/HomePoint@3x.png | Bin 0 -> 3798 bytes .../Base.lproj/Main.storyboard | 484 +++++++++++++++- .../CustomMapViewController.swift | 516 ++++++++++++++++++ UXSDKBetaSample/UXSDKBetaSample/Helpers.swift | 73 +++ UXSDKBetaSample/UXSDKBetaSample/Info.plist | 4 +- .../UXSDKBetaSample/MainViewController.swift | 20 +- .../ProductCommunicationService.swift | 283 ++++++++++ .../SingleWidgetViewController.swift | 196 +++++-- .../WidgetsListViewController.swift | 66 ++- 30 files changed, 1808 insertions(+), 152 deletions(-) create mode 100644 UXSDKBetaSample/UXSDKBetaSample/Assets.xcassets/Aircraft.imageset/Aircraft@1x.png create mode 100644 UXSDKBetaSample/UXSDKBetaSample/Assets.xcassets/Aircraft.imageset/Aircraft@2x.png create mode 100644 UXSDKBetaSample/UXSDKBetaSample/Assets.xcassets/Aircraft.imageset/Aircraft@3x.png create mode 100644 UXSDKBetaSample/UXSDKBetaSample/Assets.xcassets/Aircraft.imageset/Contents.json create mode 100644 UXSDKBetaSample/UXSDKBetaSample/Assets.xcassets/HomePoint.imageset/Contents.json create mode 100644 UXSDKBetaSample/UXSDKBetaSample/Assets.xcassets/HomePoint.imageset/HomePoint@1x.png create mode 100644 UXSDKBetaSample/UXSDKBetaSample/Assets.xcassets/HomePoint.imageset/HomePoint@2x.png create mode 100644 UXSDKBetaSample/UXSDKBetaSample/Assets.xcassets/HomePoint.imageset/HomePoint@3x.png create mode 100644 UXSDKBetaSample/UXSDKBetaSample/CustomMapViewController.swift create mode 100644 UXSDKBetaSample/UXSDKBetaSample/Helpers.swift create mode 100644 UXSDKBetaSample/UXSDKBetaSample/Services/ProductCommunicationService.swift diff --git a/DJI-UXSDK-iOS-Beta-Communication.podspec b/DJI-UXSDK-iOS-Beta-Communication.podspec index 5012157..aab12a2 100644 --- a/DJI-UXSDK-iOS-Beta-Communication.podspec +++ b/DJI-UXSDK-iOS-Beta-Communication.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'DJI-UXSDK-iOS-Beta-Communication' - s.version = '0.1.0' + s.version = '0.1.1' s.license = 'MIT' s.summary = 'Intra-framework, and system communication infrastructure for DJI iOS UX SDK.' s.homepage = 'https://github.com/dji-sdk/Mobile-UXSDK-Beta-iOS' diff --git a/DJI-UXSDK-iOS-Beta-Core.podspec b/DJI-UXSDK-iOS-Beta-Core.podspec index 3b2a5f0..9f7db36 100644 --- a/DJI-UXSDK-iOS-Beta-Core.podspec +++ b/DJI-UXSDK-iOS-Beta-Core.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'DJI-UXSDK-iOS-Beta-Core' - s.version = '0.1.0' + s.version = '0.1.1' s.license = 'MIT' s.summary = 'Core utilities for DJI iOS UX SDK.' s.homepage = 'https://github.com/dji-sdk/Mobile-UXSDK-Beta-iOS' @@ -15,6 +15,6 @@ Pod::Spec.new do |s| s.cocoapods_version = '>= 1.7.1' s.source_files = 'DJIUXSDKCore/**/*.{h,m}' s.public_header_files = 'DJIUXSDKCore/**/*.h' - s.dependency 'DJI-SDK-iOS', '4.10' - s.dependency 'DJI-UXSDK-iOS-Beta-Communication', '0.1.0' -end \ No newline at end of file + s.dependency 'DJI-SDK-iOS', '4.11' + s.dependency 'DJI-UXSDK-iOS-Beta-Communication', '0.1.1' +end diff --git a/DJI-UXSDK-iOS-Beta-Widgets.podspec b/DJI-UXSDK-iOS-Beta-Widgets.podspec index 0949339..150247e 100644 --- a/DJI-UXSDK-iOS-Beta-Widgets.podspec +++ b/DJI-UXSDK-iOS-Beta-Widgets.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'DJI-UXSDK-iOS-Beta-Widgets' - s.version = '0.1.0' + s.version = '0.1.1' s.license = 'MIT' s.summary = 'A collection of widget, widget model, and related helpers for DJI iOS UX SDK.' s.homepage = 'https://github.com/dji-sdk/Mobile-UXSDK-Beta-iOS' @@ -15,7 +15,7 @@ Pod::Spec.new do |s| s.cocoapods_version = '>= 1.7.1' s.source_files = 'DJIUXSDKWidgets/**/*.{h,m}' s.resource_bundle = { 'DUXBetaAssets' => 'DJIUXSDKWidgets/**/*.xcassets' } - s.dependency 'DJI-SDK-iOS', '4.10' - s.dependency 'DJI-UXSDK-iOS-Beta-Core', '0.1.0' - s.dependency 'DJI-UXSDK-iOS-Beta-Communication', '0.1.0' -end \ No newline at end of file + s.dependency 'DJI-SDK-iOS', '4.11' + s.dependency 'DJI-UXSDK-iOS-Beta-Core', '0.1.1' + s.dependency 'DJI-UXSDK-iOS-Beta-Communication', '0.1.1' +end diff --git a/DJI-UXSDK-iOS-Beta.podspec b/DJI-UXSDK-iOS-Beta.podspec index a5666f8..6b94570 100644 --- a/DJI-UXSDK-iOS-Beta.podspec +++ b/DJI-UXSDK-iOS-Beta.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'DJI-UXSDK-iOS-Beta' - s.version = '0.1.0' + s.version = '0.1.1' s.license = 'MIT' s.summary = 'DJI iOS UX SDK' s.homepage = 'https://github.com/dji-sdk/Mobile-UXSDK-Beta-iOS' @@ -14,7 +14,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { 'ENABLE_BITCODE' => 'NO', 'DEFINES_MODULE' => 'YES', 'SWIFT_OBJC_BRIDGING_HEADER' => '$(PODS_TARGET_SRCROOT)/'} s.cocoapods_version = '>= 1.7.1' s.source_files = 'DJIUXSDKBeta/**/*.{h,m}' - s.dependency 'DJI-UXSDK-iOS-Beta-Core', '0.1.0' - s.dependency 'DJI-UXSDK-iOS-Beta-Communication', '0.1.0' - s.dependency 'DJI-UXSDK-iOS-Beta-Widgets', '0.1.0' -end \ No newline at end of file + s.dependency 'DJI-UXSDK-iOS-Beta-Core', '0.1.1' + s.dependency 'DJI-UXSDK-iOS-Beta-Communication', '0.1.1' + s.dependency 'DJI-UXSDK-iOS-Beta-Widgets', '0.1.1' +end diff --git a/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaCommand.h b/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaCommand.h index dc7dc76..977393b 100644 --- a/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaCommand.h +++ b/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaCommand.h @@ -1,8 +1,28 @@ // // NSObject+DUXBetaCommand.h // DJIUXSDK -// -// Copyright © 2018-2019 DJI. All rights reserved. +// +// MIT License +// +// Copyright © 2018-2019 DJI +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // #import diff --git a/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaMapping.h b/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaMapping.h index 8a79249..916c255 100644 --- a/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaMapping.h +++ b/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaMapping.h @@ -1,8 +1,28 @@ // // NSObject+DUXBetaMapping.h // DJIUXSDK -// -// Copyright © 2018-2019 DJI. All rights reserved. +// +// MIT License +// +// Copyright © 2018-2019 DJI +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // #import diff --git a/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaMapping.m b/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaMapping.m index 9b00c9e..d4ef7ce 100644 --- a/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaMapping.m +++ b/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaMapping.m @@ -1,8 +1,28 @@ // // NSObject+DUXBetaMapping.m // DJIUXSDK -// -// Copyright © 2018-2019 DJI. All rights reserved. +// +// MIT License +// +// Copyright © 2018-2019 DJI +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // #import "NSObject+DUXBetaMapping.h" diff --git a/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaRKVOExtension.h b/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaRKVOExtension.h index a0850a9..dd02ac4 100644 --- a/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaRKVOExtension.h +++ b/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaRKVOExtension.h @@ -1,8 +1,28 @@ // // NSObject+DUXBetaRKVOExtension.h // DJIUXSDK -// -// Copyright © 2018-2019 DJI. All rights reserved. +// +// MIT License +// +// Copyright © 2018-2019 DJI +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // diff --git a/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaRKVOExtension.m b/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaRKVOExtension.m index 8413b09..971794d 100644 --- a/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaRKVOExtension.m +++ b/DJIUXSDKCore/BindingConvenienceCategories/NSObject+DUXBetaRKVOExtension.m @@ -1,8 +1,28 @@ // // NSObject+DUXBetaRKVOExtension.m // DJIUXSDK -// -// Copyright © 2018-2019 DJI. All rights reserved. +// +// MIT License +// +// Copyright © 2018-2019 DJI +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // #import "NSObject+DUXBetaRKVOExtension.h" diff --git a/DJIUXSDKWidgets/VisionWidget/DUXBetaVisionWidget.h b/DJIUXSDKWidgets/VisionWidget/DUXBetaVisionWidget.h index cefc3d9..93c8742 100644 --- a/DJIUXSDKWidgets/VisionWidget/DUXBetaVisionWidget.h +++ b/DJIUXSDKWidgets/VisionWidget/DUXBetaVisionWidget.h @@ -1,8 +1,28 @@ // // DUXBetaVisionWidget.h // DJIUXSDK -// -// Copyright © 2018-2019 DJI. All rights reserved. +// +// MIT License +// +// Copyright © 2018-2019 DJI +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // #import diff --git a/DJIUXSDKWidgets/VisionWidget/DUXBetaVisionWidget.m b/DJIUXSDKWidgets/VisionWidget/DUXBetaVisionWidget.m index c85873e..5e0660f 100644 --- a/DJIUXSDKWidgets/VisionWidget/DUXBetaVisionWidget.m +++ b/DJIUXSDKWidgets/VisionWidget/DUXBetaVisionWidget.m @@ -1,8 +1,28 @@ // // DUXBetaVisionWidget.m // DJIUXSDK -// -// Copyright © 2018-2019 DJI. All rights reserved. +// +// MIT License +// +// Copyright © 2018-2019 DJI +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // #import "DUXBetaVisionWidget.h" diff --git a/UXSDKBetaSample/Podfile b/UXSDKBetaSample/Podfile index 5f88091..6d0367a 100644 --- a/UXSDKBetaSample/Podfile +++ b/UXSDKBetaSample/Podfile @@ -2,8 +2,9 @@ platform :ios, '11.0' target 'UXSDKBetaSample' do use_frameworks! - pod 'DJI-SDK-iOS', '~> 4.10' - pod 'DJI-UXSDK-iOS', '~> 4.10' - pod 'DJIWidget', '~> 1.5' - pod 'DJI-UXSDK-iOS-Beta', '~> 0.1.0' -end \ No newline at end of file + pod 'DJI-SDK-iOS', '~> 4.11' + pod 'DJI-UXSDK-iOS', '~> 4.11' + pod 'DJIWidget', '~> 1.6.1' + pod 'DJI-UXSDK-iOS-Beta', '~> 0.1.1' + pod 'iOS-Color-Picker' +end diff --git a/UXSDKBetaSample/UXSDKBetaSample.xcodeproj/project.pbxproj b/UXSDKBetaSample/UXSDKBetaSample.xcodeproj/project.pbxproj index fdc7256..d2a9ce1 100644 --- a/UXSDKBetaSample/UXSDKBetaSample.xcodeproj/project.pbxproj +++ b/UXSDKBetaSample/UXSDKBetaSample.xcodeproj/project.pbxproj @@ -15,11 +15,13 @@ 537AA97D2265344300905FCC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537AA97C2265344200905FCC /* AppDelegate.swift */; }; 537AA991226534AA00905FCC /* SingleWidgetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537AA98A226534AA00905FCC /* SingleWidgetViewController.swift */; }; 537AA992226534AA00905FCC /* LogCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537AA98C226534AA00905FCC /* LogCenter.swift */; }; - 537AA993226534AA00905FCC /* ProductCommunicationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537AA98D226534AA00905FCC /* ProductCommunicationManager.swift */; }; + 537AA993226534AA00905FCC /* ProductCommunicationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537AA98D226534AA00905FCC /* ProductCommunicationService.swift */; }; 537AA994226534AA00905FCC /* LogEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537AA98E226534AA00905FCC /* LogEntry.swift */; }; 537AA995226534AA00905FCC /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537AA98F226534AA00905FCC /* MainViewController.swift */; }; 537AA996226534AA00905FCC /* WidgetsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537AA990226534AA00905FCC /* WidgetsListViewController.swift */; }; - A884E0421F33C978D173A425 /* Pods_UXSDKBetaSample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB9CD7396257BA97511570C0 /* Pods_UXSDKBetaSample.framework */; }; + 53A5E36D2344013700313B15 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A5E36C2344013700313B15 /* Helpers.swift */; }; + 53A5E37223441CC800313B15 /* CustomMapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A5E37123441CC800313B15 /* CustomMapViewController.swift */; }; + C97EF7DCF3F3C81B3B3AB4D9 /* Pods_UXSDKBetaSample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A026F0CA3230C6AC37EB63EE /* Pods_UXSDKBetaSample.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -33,8 +35,6 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 06CD3653D82C9464D05BAE55 /* Pods-UXSDKBetaSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UXSDKBetaSample.debug.xcconfig"; path = "Target Support Files/Pods-UXSDKBetaSample/Pods-UXSDKBetaSample.debug.xcconfig"; sourceTree = ""; }; - 4A34B7F60322694B7B3C0C36 /* Pods-UXSDKBetaSample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UXSDKBetaSample.release.xcconfig"; path = "Target Support Files/Pods-UXSDKBetaSample/Pods-UXSDKBetaSample.release.xcconfig"; sourceTree = ""; }; 536C4BA122652AA8008E370B /* UXSDKBetaSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UXSDKBetaSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 536C4BAD22652AAA008E370B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 536C4BB222652AAA008E370B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -49,11 +49,15 @@ 537AA97C2265344200905FCC /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 537AA98A226534AA00905FCC /* SingleWidgetViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleWidgetViewController.swift; sourceTree = ""; }; 537AA98C226534AA00905FCC /* LogCenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogCenter.swift; sourceTree = ""; }; - 537AA98D226534AA00905FCC /* ProductCommunicationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProductCommunicationManager.swift; sourceTree = ""; }; + 537AA98D226534AA00905FCC /* ProductCommunicationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProductCommunicationService.swift; sourceTree = ""; }; 537AA98E226534AA00905FCC /* LogEntry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogEntry.swift; sourceTree = ""; }; 537AA98F226534AA00905FCC /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 537AA990226534AA00905FCC /* WidgetsListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetsListViewController.swift; sourceTree = ""; }; - FB9CD7396257BA97511570C0 /* Pods_UXSDKBetaSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_UXSDKBetaSample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 53A5E36C2344013700313B15 /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = ""; }; + 53A5E37123441CC800313B15 /* CustomMapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomMapViewController.swift; sourceTree = ""; }; + 7D71737CF97A8704C6281A3B /* Pods-UXSDKBetaSample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UXSDKBetaSample.release.xcconfig"; path = "Target Support Files/Pods-UXSDKBetaSample/Pods-UXSDKBetaSample.release.xcconfig"; sourceTree = ""; }; + A026F0CA3230C6AC37EB63EE /* Pods_UXSDKBetaSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_UXSDKBetaSample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B7D7223DB74FFB59D2B7B25A /* Pods-UXSDKBetaSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UXSDKBetaSample.debug.xcconfig"; path = "Target Support Files/Pods-UXSDKBetaSample/Pods-UXSDKBetaSample.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -61,7 +65,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - A884E0421F33C978D173A425 /* Pods_UXSDKBetaSample.framework in Frameworks */, + C97EF7DCF3F3C81B3B3AB4D9 /* Pods_UXSDKBetaSample.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -75,11 +79,19 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 32A8964571722FADB1A813A3 /* Frameworks */ = { + isa = PBXGroup; + children = ( + A026F0CA3230C6AC37EB63EE /* Pods_UXSDKBetaSample.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 3A5F261302AC9ED5A40BF4DA /* Pods */ = { isa = PBXGroup; children = ( - 06CD3653D82C9464D05BAE55 /* Pods-UXSDKBetaSample.debug.xcconfig */, - 4A34B7F60322694B7B3C0C36 /* Pods-UXSDKBetaSample.release.xcconfig */, + B7D7223DB74FFB59D2B7B25A /* Pods-UXSDKBetaSample.debug.xcconfig */, + 7D71737CF97A8704C6281A3B /* Pods-UXSDKBetaSample.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -92,7 +104,7 @@ 536C4BBC22652AAA008E370B /* UXSDKSampleTests */, 536C4BA222652AA8008E370B /* Products */, 3A5F261302AC9ED5A40BF4DA /* Pods */, - B0861A724F685D4965C33316 /* Frameworks */, + 32A8964571722FADB1A813A3 /* Frameworks */, ); sourceTree = ""; }; @@ -114,6 +126,8 @@ 537AA98F226534AA00905FCC /* MainViewController.swift */, 537AA98B226534AA00905FCC /* Services */, 537AA98A226534AA00905FCC /* SingleWidgetViewController.swift */, + 53A5E36C2344013700313B15 /* Helpers.swift */, + 53A5E37123441CC800313B15 /* CustomMapViewController.swift */, 537AA990226534AA00905FCC /* WidgetsListViewController.swift */, 536C4BAD22652AAA008E370B /* Assets.xcassets */, 536C4BB222652AAA008E370B /* Info.plist */, @@ -144,20 +158,12 @@ isa = PBXGroup; children = ( 537AA98C226534AA00905FCC /* LogCenter.swift */, - 537AA98D226534AA00905FCC /* ProductCommunicationManager.swift */, + 537AA98D226534AA00905FCC /* ProductCommunicationService.swift */, 537AA98E226534AA00905FCC /* LogEntry.swift */, ); path = Services; sourceTree = ""; }; - B0861A724F685D4965C33316 /* Frameworks */ = { - isa = PBXGroup; - children = ( - FB9CD7396257BA97511570C0 /* Pods_UXSDKBetaSample.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -165,11 +171,11 @@ isa = PBXNativeTarget; buildConfigurationList = 536C4BC222652AAA008E370B /* Build configuration list for PBXNativeTarget "UXSDKBetaSample" */; buildPhases = ( - 725353F468B480DD15748481 /* [CP] Check Pods Manifest.lock */, + 7B767114A4EE26EDA22879A5 /* [CP] Check Pods Manifest.lock */, 536C4B9D22652AA8008E370B /* Sources */, 536C4B9E22652AA8008E370B /* Frameworks */, 536C4B9F22652AA8008E370B /* Resources */, - B72324519921484489A4BB7B /* [CP] Embed Pods Frameworks */, + C20598BE7C6A4A45B8CB1407 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -257,7 +263,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 725353F468B480DD15748481 /* [CP] Check Pods Manifest.lock */ = { + 7B767114A4EE26EDA22879A5 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -279,7 +285,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - B72324519921484489A4BB7B /* [CP] Embed Pods Frameworks */ = { + C20598BE7C6A4A45B8CB1407 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -305,8 +311,10 @@ files = ( 5379933D22AB14A5006FEE71 /* CustomSplitViewController.swift in Sources */, 537AA995226534AA00905FCC /* MainViewController.swift in Sources */, - 537AA993226534AA00905FCC /* ProductCommunicationManager.swift in Sources */, + 537AA993226534AA00905FCC /* ProductCommunicationService.swift in Sources */, 537AA991226534AA00905FCC /* SingleWidgetViewController.swift in Sources */, + 53A5E37223441CC800313B15 /* CustomMapViewController.swift in Sources */, + 53A5E36D2344013700313B15 /* Helpers.swift in Sources */, 537AA992226534AA00905FCC /* LogCenter.swift in Sources */, 537AA996226534AA00905FCC /* WidgetsListViewController.swift in Sources */, 537AA97D2265344300905FCC /* AppDelegate.swift in Sources */, @@ -472,7 +480,7 @@ }; 536C4BC322652AAA008E370B /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 06CD3653D82C9464D05BAE55 /* Pods-UXSDKBetaSample.debug.xcconfig */; + baseConfigurationReference = B7D7223DB74FFB59D2B7B25A /* Pods-UXSDKBetaSample.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -501,7 +509,7 @@ }; 536C4BC422652AAA008E370B /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4A34B7F60322694B7B3C0C36 /* Pods-UXSDKBetaSample.release.xcconfig */; + baseConfigurationReference = 7D71737CF97A8704C6281A3B /* Pods-UXSDKBetaSample.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; diff --git a/UXSDKBetaSample/UXSDKBetaSample/AppDelegate.swift b/UXSDKBetaSample/UXSDKBetaSample/AppDelegate.swift index f86a2bb..95f9809 100644 --- a/UXSDKBetaSample/UXSDKBetaSample/AppDelegate.swift +++ b/UXSDKBetaSample/UXSDKBetaSample/AppDelegate.swift @@ -29,10 +29,9 @@ import UIKit class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - public var productCommManager = ProductCommunicationManager() func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - self.productCommManager.registerWithProduct() + ProductCommunicationService.shared.registerWithProduct() return true } diff --git a/UXSDKBetaSample/UXSDKBetaSample/Assets.xcassets/Aircraft.imageset/Aircraft@1x.png b/UXSDKBetaSample/UXSDKBetaSample/Assets.xcassets/Aircraft.imageset/Aircraft@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..22bf3e6db539ec510db349557413711bcedb1842 GIT binary patch literal 1700 zcmV;V23z@wP)P001Ef1^@s6EWiMx00001b5ch_0Itp) z=>Px*UP(kjR7ef2R%vWiMHId>bMJk9TUWXOEw(Ie2?njDD4`K>0Zl7gX}}1H!5<_- zf(BEA{*Vy<_(KFOA<=}!7-L)j!ALZXf`!IHiw$a_mZqkq1xo40~rqCGG!vMiQE6Qv!%5v8CntG{|o}xP9W)22AI5u;=ds{m4KbYqY@{; z^5x5U=*5sedSjH2^#`~$)<^ObC{7?NYO-Pdj-3=uWJAi3!gOx4Ft=^L#e|_*BGqGM z&(rMS=fUWiGiNLqyDMOekF85KgN8pgI4rCYmD@&yS%$@wF-l3;jG4?S-xeD2G22;5 zjT0DFpK56-9#e!y!WY*6I7I&AmB>t4cTHDh?mM$*+!dD2!%U1tm|!N^6JnOl1g%gu z2%1oBgEJD2Ll>6tsisI&V}E^gbAVK!>m=pAc;))H7p>_pC5>pgNm-^ns%na^S}}_o z(WnrX$_ypO6$-``p%~n@R8?41fn|9VQ}^hm?Po^hTDYt4s}|%DCr+FbO|BhpHQ%Pj z=-jKPFfUJ4G$mbVYP!PhOwL8twuM_(aE)hiAu_2=ncO4NRgan`{Awx%@^!v<`BvQf zJ;oL;fpb5jgQGmTaGLGS56-7X_z3~hNn{oaChGpw50>rQHV_@Y(;8}z**f!3Iv(Lw zb?y^dxNW%o+QCb`q>oB)=}rNtM3Qq!w6R;>INNL8HSbU*Ni2aUQ|2j5XK9-YD;9f% z?p-^3eZ42X`x%Y!s}k@lVE+sMvIaNNg~N=P0b z09lRk=k5#VX%abA6WlPXd>)h(+h*1K4xV~?+R}$KzsINgH2>||p87~65~EWD0c2;0 zy?-UL8Y}$AU}W&8o4*A8fyC0}MVT;Qj9VvPbeYp2V@AuocZ{Oe?{9`BW$Hi0I1qD>65p;sLva)hDt#J~zKx>eFthToH zXiiQ}MSgxh^kF9fKMy1*c?hY5{L<3W7pPrP+eNLdt!L?M8Q~E^;ppgSTT@fhtHe(y zBoh({xW9NHIF%&JM4O=K+p}lSY9R!&oJ_bERAh8^c79z@P_Tt~*Z~OO_B&yNAW(o) zkE*JHl9G}#;wKPe&Tk67B?tTW??2}E`-=}BK0J?P?jZ3>+zt@HOTZ*8IB?*=o31X! z+3|KQC7t<%JYZm8;D?@`o+HF(6VPh-K|m*vEDIa(sKRMgd3pI7;-?TrgDw)1Z4r%) zjYl&xGuIav7vsgoz`zguO$j1fl~7hzmXnv4x0sY3M9@X1j>AoxHZ@q5HFoIGp>^Xj z*){}m0?Ew$B*-ExJa_KgC-MGFVrZI%>g(%2-nen&^Q4Oj10K6_<;syzD0GQ<+ztGo zDpV8?f=!T+OzPb}9PVCCa$XL2#$Bs%;vZ|}A zQ4Q`UiGWI-E5RRZAe?pl`0<_6qg?4?yH1}zT|*z{M$&n_sHkX3U0vO7lGh_xG#VYC zND)hi@lLU@#biJC*m9mkxl zTep^y-n-PQ7TB4l8Lp_PD5P^41os!8D%?prjpBz?&C4|V==H;d0zxhU?^ikjBMPJu z@cg4Sd4#z%_Rmr!@7L7Sl+YeYJMF@y04NFf0wI9ufD9iTzHGczSe*}$R%wSU0rxcF zHGqDU02>pBAUw?w$kpj1?WI!~f_FdRGC8aWByEU||3W%8Pyory+$$Wq0X=sR?A`j( um(-KG_x|mKl*p}gPcqf1l;3ZCp#9&`s=wq{qxA*=0000r5w literal 0 HcmV?d00001 diff --git a/UXSDKBetaSample/UXSDKBetaSample/Assets.xcassets/Aircraft.imageset/Aircraft@2x.png b/UXSDKBetaSample/UXSDKBetaSample/Assets.xcassets/Aircraft.imageset/Aircraft@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9343090a55269d64907543baf09c0e101e896514 GIT binary patch literal 4445 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 M07*qoM6N<$f?EAh*Z=?k literal 0 HcmV?d00001 diff --git a/UXSDKBetaSample/UXSDKBetaSample/Base.lproj/Main.storyboard b/UXSDKBetaSample/UXSDKBetaSample/Base.lproj/Main.storyboard index 2e61745..949cedd 100644 --- a/UXSDKBetaSample/UXSDKBetaSample/Base.lproj/Main.storyboard +++ b/UXSDKBetaSample/UXSDKBetaSample/Base.lproj/Main.storyboard @@ -1,11 +1,9 @@ - - - - + + - + @@ -15,7 +13,7 @@ - + @@ -35,7 +33,7 @@ - + @@ -178,7 +176,6 @@ - @@ -202,10 +199,10 @@ - + - + @@ -258,10 +255,10 @@ - + - + - - + @@ -515,7 +512,7 @@ - + @@ -545,7 +542,7 @@ - + @@ -559,7 +556,7 @@ - + @@ -569,5 +566,448 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UXSDKBetaSample/UXSDKBetaSample/CustomMapViewController.swift b/UXSDKBetaSample/UXSDKBetaSample/CustomMapViewController.swift new file mode 100644 index 0000000..c49e2e5 --- /dev/null +++ b/UXSDKBetaSample/UXSDKBetaSample/CustomMapViewController.swift @@ -0,0 +1,516 @@ +// +// WidgetsListViewController.swift +// DJIUXSDK +// +// Copyright © 2018-2019 DJI +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import UIKit +import DJIUXSDKBeta +import iOS_Color_Picker + +class CustomMapViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, FCColorPickerViewControllerDelegate { + + var mapWidget:DUXBetaMapWidget? + + @IBOutlet weak var mainStackView: UIStackView? + + @IBOutlet weak var widthSlider:UISlider? + @IBOutlet weak var alphaSlider:UISlider? + @IBOutlet weak var widthLabel:UILabel? + @IBOutlet weak var alphaLabel:UILabel? + + @IBOutlet weak var lockAircraftCameraSwitch:UISwitch? + @IBOutlet weak var lockHomeCameraSwitch:UISwitch? + @IBOutlet weak var showLegendSwitch:UISwitch? + + @IBOutlet weak var flyZoneDisplaySwitch:UISwitch? + + @IBOutlet weak var colorSelectionPickerView:UIPickerView? + @IBOutlet weak var widthSelectionPickerView:UIPickerView? + @IBOutlet weak var alphaSelectionPickerView:UIPickerView? + @IBOutlet weak var visibleFlyZoneSelectionPickerView:UIPickerView? + + var colorPickerCurrentlySelectedRow: Int = 0 + var widthPickerCurrentlySelectedRow: Int = 0 + var alphaPickerCurrentlySelectedRow: Int = 0 + var visibleFlyZonesPickerCurrentlySelectedRow: Int = 0 + + @IBOutlet weak var replaceIconSegmentedView: UISegmentedControl! + @IBOutlet weak var replaceIconBlurView: UIVisualEffectView! + @IBOutlet weak var replaceIconImageView: UIImageView! + + override func viewDidLoad() { + super.viewDidLoad() + self.widthSlider?.minimumValue = 0.5 + self.widthSlider?.maximumValue = 10.0 + + self.mapWidget!.showDirectionToHome = true + self.replaceIconBlurView.layer.cornerRadius = 7.0 + self.replaceIconBlurView.layer.masksToBounds = true + self.mapWidget!.visibleFlyZones = [] + + self.mainStackView?.isLayoutMarginsRelativeArrangement = true + self.mainStackView?.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 0, + leading: 10, + bottom: 0, + trailing: 10) + + if let selectedColorSelectionPickerViewRow = self.colorSelectionPickerView?.selectedRow(inComponent: 0) { + self.colorPickerCurrentlySelectedRow = selectedColorSelectionPickerViewRow + } + + if let selectedAlphaSelectionPickerViewRow = self.alphaSelectionPickerView?.selectedRow(inComponent: 0) { + self.alphaPickerCurrentlySelectedRow = selectedAlphaSelectionPickerViewRow + } + + if let selectedWidthSelectionPickerViewRow = self.widthSelectionPickerView?.selectedRow(inComponent: 0) { + self.widthPickerCurrentlySelectedRow = selectedWidthSelectionPickerViewRow + } + + if let selectedVisibleFlyZonesSelectionPickerViewRow = self.visibleFlyZoneSelectionPickerView?.selectedRow(inComponent: 0) { + self.visibleFlyZonesPickerCurrentlySelectedRow = selectedVisibleFlyZonesSelectionPickerViewRow + } + self.replaceIconImageView.image = #imageLiteral(resourceName: "Aircraft") + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.updateAlpha() + self.updateWidth() + self.updateFlyZones() + + if let mapWidget = self.mapWidget { + self.showLegendSwitch?.isOn = mapWidget.showFlyZoneLegend + } + } + + func updateAlpha() { + if self.alphaPickerCurrentlySelectedRow == 0 { + self.alphaSlider?.setValue(Float(self.mapWidget!.flyZoneOverlayAlpha(for: .restricted)), animated: false) + self.alphaLabel?.text = "\(self.mapWidget!.flyZoneOverlayAlpha(for: .restricted))" + } else if self.alphaPickerCurrentlySelectedRow == 1 { + self.alphaSlider?.setValue(Float(self.mapWidget!.flyZoneOverlayAlpha(for: .authorization)), animated: false) + self.alphaLabel?.text = "\(self.mapWidget!.flyZoneOverlayAlpha(for: .authorization))" + } else if self.alphaPickerCurrentlySelectedRow == 2 { + self.alphaSlider?.setValue(Float(self.mapWidget!.flyZoneOverlayAlpha(for: .enhancedWarning)), animated: false) + self.alphaLabel?.text = "\(self.mapWidget!.flyZoneOverlayAlpha(for: .enhancedWarning))" + } else if self.alphaPickerCurrentlySelectedRow == 3 { + self.alphaSlider?.setValue(Float(self.mapWidget!.flyZoneOverlayAlpha(for: .warning)), animated: false) + self.alphaLabel?.text = "\(self.mapWidget!.flyZoneOverlayAlpha(for: .warning))" + } else if self.alphaPickerCurrentlySelectedRow == 4 { + self.alphaSlider?.setValue(Float(self.mapWidget!.unlockedFlyZoneOverlayAlpha), animated: false) + self.alphaLabel?.text = "\(self.mapWidget!.unlockedFlyZoneOverlayAlpha)" + } else if self.alphaPickerCurrentlySelectedRow == 5 { + self.alphaSlider?.setValue(Float(self.mapWidget!.maximumHeightFlyZoneOverlayAlpha), animated: false) + self.alphaLabel?.text = "\(self.mapWidget!.maximumHeightFlyZoneOverlayAlpha)" + } else if self.alphaPickerCurrentlySelectedRow == 6 { + self.alphaSlider?.setValue(Float(self.mapWidget!.customUnlockFlyZoneOverlayAlpha), animated: false) + self.alphaLabel?.text = "\(self.mapWidget!.customUnlockFlyZoneOverlayAlpha)" + } else if self.alphaPickerCurrentlySelectedRow == 7 { + self.alphaSlider?.setValue(Float(self.mapWidget!.customUnlockFlyZoneSentToAircraftOverlayAlpha), animated: false) + self.alphaLabel?.text = "\(self.mapWidget!.customUnlockFlyZoneSentToAircraftOverlayAlpha)" + } else if self.alphaPickerCurrentlySelectedRow == 8 { + self.alphaSlider?.setValue(Float(self.mapWidget!.customUnlockFlyZoneEnabledOverlayAlpha), animated: false) + self.alphaLabel?.text = "\(self.mapWidget!.customUnlockFlyZoneEnabledOverlayAlpha)" + } + } + + func updateFlyZones() { + var visibleFlyZones:DUXBetaMapVisibleFlyZones = [] + + if self.visibleFlyZonesPickerCurrentlySelectedRow == 0 { + visibleFlyZones.insert(DUXBetaMapVisibleFlyZones.restricted) + } else if self.visibleFlyZonesPickerCurrentlySelectedRow == 1 { + visibleFlyZones.insert(DUXBetaMapVisibleFlyZones.authorization) + } else if self.visibleFlyZonesPickerCurrentlySelectedRow == 2 { + visibleFlyZones.insert(DUXBetaMapVisibleFlyZones.enhancedWarning) + } else if self.visibleFlyZonesPickerCurrentlySelectedRow == 3 { + visibleFlyZones.insert(DUXBetaMapVisibleFlyZones.warning) + } + + self.flyZoneDisplaySwitch?.setOn((self.mapWidget!.visibleFlyZones.contains(visibleFlyZones)), animated: true) + } + + func updateWidth() { + if self.widthPickerCurrentlySelectedRow == 0 { + self.widthSlider?.setValue(Float(self.mapWidget!.flyZoneOverlayBorderWidth), animated: false) + self.widthLabel?.text = "\(self.mapWidget!.flyZoneOverlayBorderWidth)" + } else if self.widthPickerCurrentlySelectedRow == 1 { + self.widthSlider?.setValue(Float(self.mapWidget!.flightPathStrokeWidth), animated: false) + self.widthLabel?.text = "\(self.mapWidget!.flightPathStrokeWidth)" + } else if self.widthPickerCurrentlySelectedRow == 2 { + self.widthSlider?.setValue(Float(self.mapWidget!.directionToHomeStrokeWidth), animated: false) + self.widthLabel?.text = "\(self.mapWidget!.directionToHomeStrokeWidth)" + } + } + + @IBAction func widthSliderControlChanged(sender: UISlider) { + let sliderValue = CGFloat(sender.value) + self.widthLabel?.text = "\(sliderValue)" + + if self.widthPickerCurrentlySelectedRow == 0 { + self.mapWidget!.flyZoneOverlayBorderWidth = sliderValue + } else if self.widthPickerCurrentlySelectedRow == 1 { + self.mapWidget!.flightPathStrokeWidth = sliderValue + } else if self.widthPickerCurrentlySelectedRow == 2 { + self.mapWidget!.directionToHomeStrokeWidth = sliderValue + } + } + + @IBAction func alphaSliderControlChanged(sender: UISlider) { + let sliderValue = CGFloat(sender.value) + self.alphaLabel?.text = "\(sliderValue)" + + if self.alphaPickerCurrentlySelectedRow == 0 { + self.mapWidget!.setFlyZoneOverlayAlpha(sliderValue, for: .restricted) + } else if self.alphaPickerCurrentlySelectedRow == 1 { + self.mapWidget!.setFlyZoneOverlayAlpha(sliderValue, for: .authorization) + } else if self.alphaPickerCurrentlySelectedRow == 2 { + self.mapWidget!.setFlyZoneOverlayAlpha(sliderValue, for: .enhancedWarning) + } else if self.alphaPickerCurrentlySelectedRow == 3 { + self.mapWidget!.setFlyZoneOverlayAlpha(sliderValue, for: .warning) + } else if self.alphaPickerCurrentlySelectedRow == 4 { + self.mapWidget!.unlockedFlyZoneOverlayAlpha = sliderValue + } else if self.alphaPickerCurrentlySelectedRow == 5 { + self.mapWidget!.maximumHeightFlyZoneOverlayAlpha = sliderValue + } else if self.alphaPickerCurrentlySelectedRow == 6 { + self.mapWidget!.customUnlockFlyZoneOverlayAlpha = sliderValue + } else if self.alphaPickerCurrentlySelectedRow == 7 { + self.mapWidget!.customUnlockFlyZoneSentToAircraftOverlayAlpha = sliderValue + } else if self.alphaPickerCurrentlySelectedRow == 8 { + self.mapWidget!.customUnlockFlyZoneEnabledOverlayAlpha = sliderValue + } + } + + @IBAction func flyZoneDisplaySwitchValueChanged(sender: UISwitch) { + let isOn = sender.isOn + var visibleFlyZones:DUXBetaMapVisibleFlyZones = [] + + if self.visibleFlyZonesPickerCurrentlySelectedRow == 0 { + visibleFlyZones.insert(DUXBetaMapVisibleFlyZones.restricted) + } else if self.visibleFlyZonesPickerCurrentlySelectedRow == 1 { + visibleFlyZones.insert(DUXBetaMapVisibleFlyZones.authorization) + } else if self.visibleFlyZonesPickerCurrentlySelectedRow == 2 { + visibleFlyZones.insert(DUXBetaMapVisibleFlyZones.enhancedWarning) + } else if self.visibleFlyZonesPickerCurrentlySelectedRow == 3 { + visibleFlyZones.insert(DUXBetaMapVisibleFlyZones.warning) + } + + if isOn { + self.mapWidget!.visibleFlyZones.insert(visibleFlyZones) + } else { + self.mapWidget!.visibleFlyZones.remove(visibleFlyZones) + } + + self.updateFlyZones() + } + + func updateMapWith(color:UIColor) { + if colorPickerCurrentlySelectedRow == 0 { + self.mapWidget!.setFlyZoneOverlayColor(color, for: .restricted) + } else if colorPickerCurrentlySelectedRow == 1 { + self.mapWidget!.setFlyZoneOverlayColor(color, for: .authorization) + } else if colorPickerCurrentlySelectedRow == 2 { + self.mapWidget!.setFlyZoneOverlayColor(color, for: .enhancedWarning) + } else if colorPickerCurrentlySelectedRow == 3 { + self.mapWidget!.setFlyZoneOverlayColor(color, for: .warning) + } else if colorPickerCurrentlySelectedRow == 4 { + self.mapWidget!.unlockedFlyZoneOverlayColor = color + } else if colorPickerCurrentlySelectedRow == 5 { + self.mapWidget!.maximumHeightFlyZoneOverlayColor = color + } else if colorPickerCurrentlySelectedRow == 6 { + self.mapWidget!.flightPathStrokeColor = color + } else if colorPickerCurrentlySelectedRow == 7 { + self.mapWidget!.directionToHomeStrokeColor = color + } else if colorPickerCurrentlySelectedRow == 8 { + self.mapWidget!.customUnlockFlyZoneOverlayColor = color + } else if self.colorPickerCurrentlySelectedRow == 9 { + self.mapWidget!.customUnlockFlyZoneSentToAircraftOverlayColor = color + } else if self.colorPickerCurrentlySelectedRow == 10 { + self.mapWidget!.customUnlockFlyZoneEnabledOverlayColor = color + } + } + + @IBAction func close(_ sender: UIButton) { + self.willMove(toParent: nil) + self.view.removeFromSuperview() + self.removeFromParent() + } + + //MARK - FCColorPickerViewControllerDelegate + + func colorPickerViewController(_ colorPicker: FCColorPickerViewController, didSelect color: UIColor) { + self.updateMapWith(color: color) + self.presentedViewController?.dismiss(animated: true, completion: nil) + } + + func colorPickerViewControllerDidCancel(_ colorPicker: FCColorPickerViewController) { + self.presentedViewController?.dismiss(animated: true, completion: nil) + } + + func currentlySelectedColorFor(row:Int) -> UIColor? { + if colorPickerCurrentlySelectedRow == 0 { + return self.mapWidget!.flyZoneOverlayColor(for: .restricted) + } else if colorPickerCurrentlySelectedRow == 1 { + return self.mapWidget!.flyZoneOverlayColor(for: .authorization) + } else if colorPickerCurrentlySelectedRow == 2 { + return self.mapWidget!.flyZoneOverlayColor(for: .enhancedWarning) + } else if colorPickerCurrentlySelectedRow == 3 { + return self.mapWidget!.flyZoneOverlayColor(for: .warning) + } else if colorPickerCurrentlySelectedRow == 4 { + return self.mapWidget!.unlockedFlyZoneOverlayColor + } else if colorPickerCurrentlySelectedRow == 5 { + return self.mapWidget!.maximumHeightFlyZoneOverlayColor + } else if colorPickerCurrentlySelectedRow == 6 { + return self.mapWidget!.flightPathStrokeColor + } else if colorPickerCurrentlySelectedRow == 7 { + return self.mapWidget!.directionToHomeStrokeColor + } else if colorPickerCurrentlySelectedRow == 8 { + return self.mapWidget!.customUnlockFlyZoneOverlayColor + } else if self.colorPickerCurrentlySelectedRow == 9 { + return self.mapWidget!.customUnlockFlyZoneSentToAircraftOverlayColor + } else if self.colorPickerCurrentlySelectedRow == 10 { + return self.mapWidget!.customUnlockFlyZoneEnabledOverlayColor + } else { + return nil + } + } + + @IBAction func toggleColor() { + if let currentlySelectedColorForPickerTarget = self.currentlySelectedColorFor(row:self.colorPickerCurrentlySelectedRow) { + let colorPicker = FCColorPickerViewController.colorPicker(with: currentlySelectedColorForPickerTarget, delegate: self) + self.present(colorPicker, animated: true, completion: nil) + } else { + self.updateMapWith(color: UIColor.cyan) + } + } + + @IBAction func directionToHomeValueChanged(_ sender: UISwitch) { + self.mapWidget!.showDirectionToHome = sender.isOn + } + + @IBAction func lockAircraftCameraValueChanged(_ sender: UISwitch) { + self.mapWidget!.isMapCameraLockedOnAircraft = sender.isOn + if (sender.isOn) { + self.lockHomeCameraSwitch?.setOn(false, animated: true) + } + } + + @IBAction func lockHomePointCameraValueChanged(_ sender: UISwitch) { + self.mapWidget!.isMapCameraLockedOnHomePoint = sender.isOn + if (sender.isOn) { + self.lockAircraftCameraSwitch?.setOn(false, animated: true) + } + } + + @IBAction func showFlightPathValueChanged(_ sender: UISwitch) { + self.mapWidget!.showFlightPath = sender.isOn + } + + @IBAction func showHomePointValueChanged(_ sender: UISwitch) { + self.mapWidget!.showHomeAnnotation = sender.isOn + } + + @IBAction func tapToUnlockEnabledValueChanged(_ sender: UISwitch) { + self.mapWidget!.tapToUnlockEnabled = sender.isOn + } + + @IBAction func showLegendValueChanged(_ sender: UISwitch) { + self.mapWidget!.showFlyZoneLegend = sender.isOn + } + + @IBAction func showCustomUnlockZonesValueChanged(_ sender: UISwitch) { + self.mapWidget!.showCustomUnlockZones = sender.isOn + } + + @IBAction func showDJIAccountLoginIndicator(_ sender: UISwitch) { + self.mapWidget!.showDJIAccountLoginIndicator = sender.isOn + } + + @IBAction func replaceIconButtonPressed(_ sender: UIButton) { + if self.replaceIconSegmentedView.selectedSegmentIndex == 0 { + self.mapWidget!.changeAnnotation(of: .aircraft, toCustomImage: self.replaceIconImageView.image!, withCenterOffset: CGPoint(x: -8.75, y: -27.3)); + } else if self.replaceIconSegmentedView.selectedSegmentIndex == 1 { + self.mapWidget!.changeAnnotation(of: .home, toCustomImage: self.replaceIconImageView.image!, withCenterOffset: CGPoint(x: -8, y: -15)); + } else if self.replaceIconSegmentedView.selectedSegmentIndex == 2 { + self.mapWidget!.changeAnnotation(of: .eligibleFlyZones, toCustomImage: self.replaceIconImageView.image!, withCenterOffset: CGPoint(x: 8, y: -15)); + } else if self.replaceIconSegmentedView.selectedSegmentIndex == 3 { + self.mapWidget!.changeAnnotation(of: .unlockedFlyZones, toCustomImage: self.replaceIconImageView.image!, withCenterOffset: CGPoint(x: 8, y: -15)); + } else if self.replaceIconSegmentedView.selectedSegmentIndex == 4 { + self.mapWidget!.changeAnnotation(of: .customUnlockedFlyZones, toCustomImage: self.replaceIconImageView.image!, withCenterOffset: CGPoint(x: 8, y: -15)); + } + } + + @IBAction func replaceIconValueChanged(_ sender: UISegmentedControl) { + if sender.selectedSegmentIndex == 0 { + self.replaceIconImageView.image = #imageLiteral(resourceName: "Aircraft") + } else { + self.replaceIconImageView.image = #imageLiteral(resourceName: "HomePoint") + } + } + + //MARK: - UIPickerViewDataSource + + func numberOfComponents(in pickerView: UIPickerView) -> Int { + return 1 + } + + func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + if pickerView == self.colorSelectionPickerView { + return 11 + } else if pickerView == self.widthSelectionPickerView { + return 3 + } else if pickerView == self.alphaSelectionPickerView { + return 9 + } else if pickerView == self.visibleFlyZoneSelectionPickerView { + return 4 + } else { + return 0 + } + } + + //MARK: - UIPickerViewDelegate + + public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + if pickerView == self.colorSelectionPickerView { + return self.colorSelectionTitleForRow(row: row, forComponent: component) + } else if pickerView == self.widthSelectionPickerView { + return self.widthSelectionTitleForRow(row: row, forComponent: component) + } else if pickerView == self.alphaSelectionPickerView { + return self.alphaSelectionTitleForRow(row: row, forComponent: component) + } else if pickerView == self.visibleFlyZoneSelectionPickerView { + return self.visibleFlyZoneSelectionTitleForRow(row: row, forComponent: component) + } else { + return nil + } + } + + func colorSelectionTitleForRow(row: Int, forComponent component:Int) -> String? { + if row == 0 { + return "Restricted Overlay Color" + } else if row == 1 { + return "Authorization Overlay Color" + } else if row == 2 { + return "Enhanced Warning Overlay Color" + } else if row == 3 { + return "Warning Overlay Color" + } else if row == 4 { + return "Self Unlocked Overlay Color" + } else if row == 5 { + return "Max Height Overlay Color" + } else if row == 6 { + return "Flight Path Color" + } else if row == 7 { + return "Direction to Home Color" + } else if row == 8 { + return "Custom Unlock Not Sent Not Enabled Color" + } else if row == 9 { + return "Custom Unlock Sent Not Enabled Color" + } else if row == 10 { + return "Custom Unlock Sent Enabled Color" + } else { + return "" + } + } + + func widthSelectionTitleForRow(row: Int, forComponent component:Int) -> String? { + if row == 0 { + return "Overlay Border Width" + } else if row == 1 { + return "Flight Path Stroke Width" + } else if row == 2 { + return "Direction to Home Stroke Width" + } else { + return "" + } + } + + func alphaSelectionTitleForRow(row: Int, forComponent component:Int) -> String? { + if row == 0 { + return "Restricted Overlay Alpha" + } else if row == 1 { + return "Authorization Overlay Alpha" + } else if row == 2 { + return "Enhanced Warning Overlay Alpha" + } else if row == 3 { + return "Warning Overlay Alpha" + } else if row == 4 { + return "Self Unlocked Overlay Alpha" + } else if row == 5 { + return "Max Height Overlay Alpha" + } else if row == 6 { + return "Custom Unlock Not Sent Not Enabled Alpha" + } else if row == 7 { + return "Custom Unlock Sent Not Enabled Alpha" + } else if row == 8 { + return "Custom Unlock Sent Enabled Alpha" + } else { + return "" + } + } + + func visibleFlyZoneSelectionTitleForRow(row: Int, forComponent component:Int) -> String? { + if row == 0 { + return "Restricted" + } else if row == 1 { + return "Authorization" + } else if row == 2 { + return "Enhanced Warning" + } else if row == 3 { + return "Warning" + } else { + return "" + } + } + + public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + if pickerView == self.colorSelectionPickerView { + self.didSelectColor(row: row, inComponent: component) + } else if pickerView == self.widthSelectionPickerView { + self.didSelectAlpha(row: row, inComponent: component) + } else if pickerView == self.alphaSelectionPickerView { + self.didSelectWidth(row: row, inComponent: component) + } else if pickerView == self.visibleFlyZoneSelectionPickerView { + self.didSelectVisibleFlyZone(row: row, inComponent: component) + } + } + + func didSelectColor(row: Int, inComponent component: Int) { + self.colorPickerCurrentlySelectedRow = row + } + + func didSelectWidth(row: Int, inComponent component: Int) { + self.widthPickerCurrentlySelectedRow = row + self.updateWidth() + } + + func didSelectAlpha(row: Int, inComponent component: Int) { + self.alphaPickerCurrentlySelectedRow = row + self.updateAlpha() + } + + func didSelectVisibleFlyZone(row: Int, inComponent component: Int) { + self.visibleFlyZonesPickerCurrentlySelectedRow = row + self.updateFlyZones() + } +} + diff --git a/UXSDKBetaSample/UXSDKBetaSample/Helpers.swift b/UXSDKBetaSample/UXSDKBetaSample/Helpers.swift new file mode 100644 index 0000000..e2b8250 --- /dev/null +++ b/UXSDKBetaSample/UXSDKBetaSample/Helpers.swift @@ -0,0 +1,73 @@ +// +// WidgetsListViewController.swift +// DJIUXSDK +// +// Copyright © 2018-2019 DJI +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import UIKit + +extension UIControl { + func connect(controlAction:ControlAction, for event:UIControl.Event) { + self.addTarget(controlAction, + action: #selector(ControlAction.performAction(_:)), + for: event) + } + + func connect(controlAction:ControlAction, for events:[UIControl.Event]) { + for event in events { + self.addTarget(controlAction, + action: #selector(ControlAction.performAction(_:)), + for: event) + } + } + + func connect(action: @escaping ControlActionClosure, for event:UIControl.Event) -> ControlAction { + let controlAction = ControlAction(action) + + self.connect(controlAction: controlAction, + for: event) + + return controlAction + } + + func connect(action: @escaping ControlActionClosure, for events:[UIControl.Event]) -> ControlAction { + let controlAction = ControlAction(action) + + self.connect(controlAction: controlAction, + for: events) + + return controlAction + } +} + +typealias ControlActionClosure = () -> Void + +public final class ControlAction { + let action: ControlActionClosure + init(_ action: @escaping ControlActionClosure) { + self.action = action + } + + @objc func performAction(_ sender:Any) { + action() + } +} diff --git a/UXSDKBetaSample/UXSDKBetaSample/Info.plist b/UXSDKBetaSample/UXSDKBetaSample/Info.plist index fd0866d..2f89e95 100644 --- a/UXSDKBetaSample/UXSDKBetaSample/Info.plist +++ b/UXSDKBetaSample/UXSDKBetaSample/Info.plist @@ -21,9 +21,11 @@ CFBundleVersion 1 DJISDKAppKey - Insert your DJI SDK app key here + Replace_With_Your_DJI_App_Key LSRequiresIPhoneOS + NSBluetoothAlwaysUsageDescription + Bluetooth Use is Required for Mobile SDK Osmo Support NSLocationAlwaysAndWhenInUseUsageDescription Location Use is Required for Map Functionality NSLocationWhenInUseUsageDescription diff --git a/UXSDKBetaSample/UXSDKBetaSample/MainViewController.swift b/UXSDKBetaSample/UXSDKBetaSample/MainViewController.swift index ed73c75..ef4e3c6 100644 --- a/UXSDKBetaSample/UXSDKBetaSample/MainViewController.swift +++ b/UXSDKBetaSample/UXSDKBetaSample/MainViewController.swift @@ -44,7 +44,7 @@ class MainViewController: UIViewController, UITextFieldDelegate, LogCenterListen override func viewDidLoad() { super.viewDidLoad() - NotificationCenter.default.addObserver(self, selector: #selector(productCommunicationDidChange), name: Notification.Name(rawValue: "ProductCommunicationManagerStateDidChange"), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(productCommunicationDidChange), name: Notification.Name(rawValue: "ProductCommunicationServiceStateDidChange"), object: nil) self.bridgeIDField.delegate = self LogCenter.default.add(listener: self) } @@ -52,21 +52,21 @@ class MainViewController: UIViewController, UITextFieldDelegate, LogCenterListen override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.version.text = "\(DUXBetaSDKAttributes.sdkVersion())-\(DUXBetaSDKAttributes.sdkBuildNumber())" - self.bridgeIDField.text = self.appDelegate.productCommManager.bridgeAppIP - self.useBridgeSwitch.isOn = self.appDelegate.productCommManager.useBridge + self.bridgeIDField.text = ProductCommunicationService.shared.bridgeAppIP + self.useBridgeSwitch.isOn = ProductCommunicationService.shared.useBridge } @IBAction func registerAction() { - self.appDelegate.productCommManager.registerWithProduct() + ProductCommunicationService.shared.registerWithProduct() } @IBAction func connectAction() { - self.appDelegate.productCommManager.connectToProduct() + ProductCommunicationService.shared.connectToProduct() } @IBAction func useBridgeAction(_ sender: UISwitch) { - self.appDelegate.productCommManager.useBridge = sender.isOn - self.appDelegate.productCommManager.disconnectProduct() + ProductCommunicationService.shared.useBridge = sender.isOn + ProductCommunicationService.shared.productDisconnected() } @IBAction func pushWidgetList() { @@ -86,7 +86,7 @@ class MainViewController: UIViewController, UITextFieldDelegate, LogCenterListen } @objc func productCommunicationDidChange() { - if self.appDelegate.productCommManager.registered { + if ProductCommunicationService.shared.registered { self.registered.text = "YES" self.register.isHidden = true } else { @@ -94,7 +94,7 @@ class MainViewController: UIViewController, UITextFieldDelegate, LogCenterListen self.register.isHidden = false } - if self.appDelegate.productCommManager.connected { + if ProductCommunicationService.shared.connected { self.connected.text = "YES" self.connect.isHidden = true @@ -132,7 +132,7 @@ class MainViewController: UIViewController, UITextFieldDelegate, LogCenterListen func textFieldDidEndEditing(_ textField: UITextField) { if textField == self.bridgeIDField { - self.appDelegate.productCommManager.bridgeAppIP = textField.text! + ProductCommunicationService.shared.bridgeAppIP = textField.text! } } diff --git a/UXSDKBetaSample/UXSDKBetaSample/Services/ProductCommunicationService.swift b/UXSDKBetaSample/UXSDKBetaSample/Services/ProductCommunicationService.swift new file mode 100644 index 0000000..5579712 --- /dev/null +++ b/UXSDKBetaSample/UXSDKBetaSample/Services/ProductCommunicationService.swift @@ -0,0 +1,283 @@ +// +// ProductCommunicationService.swift +// DJIUXSDK +// +// Copyright © 2018-2019 DJI +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import UIKit +import DJISDK + +// +// ProductCommunicationService.swift +// UXSDK Sample +// +// Copyright © 2016 DJI. All rights reserved. +// + +import UIKit +import DJISDK + +let ProductCommunicationServiceStateDidChange = "ProductCommunicationServiceStateDidChange" + +// Automatically set default to bridge when on iOS simulator +func defaultUseBridgeSetting() -> Bool { + #if arch(i386) || arch(x86_64) + return true + #else + return false + #endif +} + +func postNotificationNamed(_ rawStringName:String, + dispatchOntoMainQueue:Bool = false, + notificationCenter:NotificationCenter = NotificationCenter.default) { + let post = { + notificationCenter.post(Notification(name: Notification.Name(rawValue: rawStringName))) + } + + if dispatchOntoMainQueue { + DispatchQueue.main.async { + post() + } + } else { + post() + } +} + +public let FligntControllerSimulatorDidStart = "FligntControllerSimulatorDidStart" +public let FligntControllerSimulatorDidStop = "FligntControllerSimulatorDidStop" + +class SimulatorControl: NSObject { + var isSimulatorActive:Bool = false { + didSet { + if self.isSimulatorActive { + postNotificationNamed(FligntControllerSimulatorDidStart, dispatchOntoMainQueue: true) + } else { + postNotificationNamed(FligntControllerSimulatorDidStop, dispatchOntoMainQueue: true) + } + } + } + + func startListeningOnProductState() { + let isSimulatorActiveKey = DJIFlightControllerKey(param: DJIFlightControllerParamIsSimulatorActive)! + + if let active = DJISDKManager.keyManager()?.getValueFor(isSimulatorActiveKey)?.boolValue { + self.isSimulatorActive = active + } + + DJISDKManager.keyManager()?.startListeningForChanges(on: isSimulatorActiveKey, + withListener: self, + andUpdate: { (updatedValue:DJIKeyedValue?, priorValue:DJIKeyedValue?) in + if let isSimulatorActive = updatedValue?.boolValue { + self.isSimulatorActive = isSimulatorActive + } + }) + + DJISDKManager.keyManager()?.getValueFor(isSimulatorActiveKey, + withCompletion: { (updatedValue:DJIKeyedValue?, error:Error?) in + if let isSimulatorActive = updatedValue?.boolValue { + self.isSimulatorActive = isSimulatorActive + } + }) + } + + func stopListeningOnProductState() { + let isSimulatorActiveKey = DJIFlightControllerKey(param: DJIFlightControllerParamIsSimulatorActive)! + + DJISDKManager.keyManager()?.stopListening(on: isSimulatorActiveKey, + ofListener: self) + } + + deinit { + self.stopListeningOnProductState() + } + + // Returns false if no aircraft present, true if simulator command sent + func startSimulator(at locationCoordinates:CLLocationCoordinate2D) -> Bool { + guard let aircraft = DJISDKManager.product() as? DJIAircraft else { + return false + } + + guard let simulator = aircraft.flightController?.simulator else { + return false + } + + simulator.start(withLocation: locationCoordinates, + updateFrequency: 20, + gpsSatellitesNumber: 12) { (error:Error?) in + if let e = error { + print("Start Simulator Error: \(e)") + } else { + print("Start Simulator Command Acked") + } + } + + return true + } + + func stopSimulator() -> Bool { + guard let stopSimulatorKey = DJIFlightControllerKey(param: DJIFlightControllerParamStopSimulator) else { + return false + } + + guard let keyManager = DJISDKManager.keyManager() else { + return false + } + + keyManager.performAction(for: stopSimulatorKey, + withArguments: nil, + andCompletion: { (didSucceed:Bool, value:DJIKeyedValue?, error:Error?) in + if let e = error { + print("Stop Simulator Error: \(e)") + } else { + print("Stop Simulator Command Acked") + } + }) + + return true + } +} + +// Returns "0.0.0.0" if no cached value present +func fetchCachedBridgeAppIP() -> String { + if let ip = UserDefaults.standard.value(forKey: "bridgeAppIP") as? String { + return ip + } else { + return "0.0.0.0" + } +} + +@dynamicMemberLookup +class ProductCommunicationService: NSObject, DJISDKManagerDelegate { + // Static Instance + static let shared = ProductCommunicationService() + + open weak var appDelegate = UIApplication.shared.delegate as? AppDelegate + open var connectedProduct: DJIBaseProduct! + + var registered = false + var connected = false + + var bridgeAppIP = fetchCachedBridgeAppIP() { + didSet { + UserDefaults.standard.set(bridgeAppIP, forKey: "bridgeAppIP") + } + } + var useBridge = defaultUseBridgeSetting() { + didSet { + if useBridge == false { + NSLog("Disabling bridge mode...") + DJISDKManager.disableBridgeMode() + } else { + NSLog("Enabling bridge mode with IP \(self.bridgeAppIP)...") + DJISDKManager.enableBridgeMode(withBridgeAppIP: self.bridgeAppIP) + } + } + } + + //MARK: - Start Registration + func registerWithProduct() { + guard + let path = Bundle.main.path(forResource: "Info", ofType: "plist"), + let dict = NSDictionary(contentsOfFile: path) as? Dictionary, + let appKey = dict["DJISDKAppKey"] as? String, + appKey != "PASTE_YOUR_DJI_APP_KEY_HERE" + else { + print("\n<<>>\n") + return + } + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + NSLog("Registering Product with registration ID: \(appKey)") + DJISDKManager.registerApp(with: self) + } + } + + //MARK: - Start Connecting to Product + open func connectToProduct() { + if self.useBridge { + NSLog("Connecting to Product using debug IP address: \(bridgeAppIP)...") + DJISDKManager.enableBridgeMode(withBridgeAppIP: bridgeAppIP) + } else { + NSLog("Connecting to product...") + let startedResult = DJISDKManager.startConnectionToProduct() + + if startedResult { + NSLog("Connecting to product started successfully!") + } else { + NSLog("Connecting to product failed to start!") + } + } + } + + //MARK: - DJISDKManagerDelegate + func appRegisteredWithError(_ error: Error?) { + if error == nil { + self.registered = true + postNotificationNamed(ProductCommunicationServiceStateDidChange, dispatchOntoMainQueue: true) + self.simulatorControl.startListeningOnProductState() + self.connectToProduct() + } else { + NSLog("Error Registrating App: \(String(describing: error))") + } + } + + func didUpdateDatabaseDownloadProgress(_ progress: Progress) { + print("Downloading Database Progress: \(progress.completedUnitCount) / \(progress.totalUnitCount)") + } + + func productConnected(_ product: DJIBaseProduct?) { + if product != nil { + self.connected = true + NotificationCenter.default.post(Notification(name: Notification.Name(rawValue: ProductCommunicationServiceStateDidChange))) + NSLog("Connection to new product succeeded!") + self.connectedProduct = product + } + } + + func productDisconnected() { + DJISDKManager.stopConnectionToProduct() + + self.connected = false + NotificationCenter.default.post(Notification(name: Notification.Name(rawValue: ProductCommunicationServiceStateDidChange))) + NSLog("Disconnected from product!"); + } + + //MARK: - Bridge Mode API + + //MARK: - Simulator Controls API + let simulatorControl:SimulatorControl = SimulatorControl() + + // Leverages Swift language feature described in SE-0252 + subscript(dynamicMember keyPath: KeyPath) -> Bool { + return simulatorControl[keyPath: keyPath] + } + + // Returns false if no aircraft present, true if simulator command sent + func stopSimulator() -> Bool { + return self.simulatorControl.stopSimulator() + } + + // Returns false if no aircraft present, true if simulator command sent + func startSimulator(at locationCoordinates:CLLocationCoordinate2D) -> Bool { + return self.simulatorControl.startSimulator(at: locationCoordinates) + } +} diff --git a/UXSDKBetaSample/UXSDKBetaSample/SingleWidgetViewController.swift b/UXSDKBetaSample/UXSDKBetaSample/SingleWidgetViewController.swift index 7f85f7b..ccd3d4c 100644 --- a/UXSDKBetaSample/UXSDKBetaSample/SingleWidgetViewController.swift +++ b/UXSDKBetaSample/UXSDKBetaSample/SingleWidgetViewController.swift @@ -26,6 +26,25 @@ import UIKit import DJIUXSDKBeta +extension UIColor { + static func borderColorPalette() -> [UIColor] { + return [UIColor.red, + UIColor.green, + UIColor.blue, + UIColor.orange, + UIColor.yellow, + UIColor.magenta, + UIColor.cyan, + UIColor.purple, + UIColor.brown] + } + + static func borderColor(for level:Int) -> UIColor { + let palette = self.borderColorPalette() + return palette[level % palette.count] + } +} + class SingleWidgetViewController: UIViewController { @IBOutlet var noWidgetSelectedLabel: UILabel! @@ -34,43 +53,45 @@ class SingleWidgetViewController: UIViewController { @IBOutlet var aspectRatioLabel: UILabel! @IBOutlet var currentSizeLabel: UILabel! @IBOutlet var widgetDescriptionLabel: UILabel! + var showCustomizationViewButton: UIButton? private var pinchStartHeight: CGFloat = 0.0 private var heightConstraint: NSLayoutConstraint? + private var additionalControlsView: UIView? + + private var doubleSizeControlAction: ControlAction? + private var showCustomizationViewAction: ControlAction? + + internal var shouldShowCustomizationView: Bool = false - private var _widget: DUXBetaBaseWidget? = nil + // MARK: Widget Setup / Teardown var widget: DUXBetaBaseWidget? { - set { + willSet { // Cleanup old widget from parent self.tearDownCurrentWidget() - - _widget = newValue - if let nonNilWidget = _widget { + } + + didSet { + if let nonNilWidget = self.widget { self.setupViewHierarchyFor(widget: nonNilWidget) } else { self.setupEmptyViewHierarchy() } } - get { return _widget } - } - - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - if let widget = self.widget { - currentSizeLabel.text = String(format: "[%.1f, %.1f]", widget.view.frame.size.width, widget.view.frame.size.height) - } } public func tearDownCurrentWidget() { - _widget?.removeFromParent() - _widget?.view.removeFromSuperview() + self.widget?.removeFromParent() + self.widget?.view.removeFromSuperview() } public func setupViewHierarchyFor(widget:DUXBetaBaseWidget) { self.noWidgetSelectedLabel.isHidden = true self.infoView.isHidden = false + self.additionalControlsView?.isHidden = false self.pinchToResizeLabel.isHidden = false - + self.showCustomizationViewButton?.isHidden = !self.shouldShowCustomizationView + widget.install(in: self) self.configureConstraints() self.recursivelySetBorderForView(view: widget.view, borderEnabled: true, level: 0) @@ -82,11 +103,100 @@ class SingleWidgetViewController: UIViewController { public func setupEmptyViewHierarchy() { self.noWidgetSelectedLabel.isHidden = false self.infoView.isHidden = true + self.additionalControlsView?.isHidden = true self.pinchToResizeLabel.isHidden = true self.aspectRatioLabel.text = "1.0" self.currentSizeLabel.text = "[40.0, 40.0]" } + // MARK: View Lifecycle + + override func viewDidLoad() { + super.viewDidLoad() + self.configureControlViews() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + if let widget = self.widget { + currentSizeLabel.text = String(format: "[%.1f, %.1f]", widget.view.frame.size.width, widget.view.frame.size.height) + } + self.view.bringSubviewToFront(self.infoView) + self.view.bringSubviewToFront(self.pinchToResizeLabel) + if let v = self.additionalControlsView { + self.view.bringSubviewToFront(v) + } + } + + // MARK: Construct View and Constraint Hierarchy + + func configureControlViews() { + let doubleSizeButton = UIButton(type: .system) + doubleSizeButton.translatesAutoresizingMaskIntoConstraints = false + doubleSizeButton.setTitle("Double Size", + for: .normal) + doubleSizeButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 25.0).isActive = true + self.doubleSizeControlAction = doubleSizeButton.connect(action: { + if let hc = self.heightConstraint { + hc.constant = hc.constant * 2 + self.view.setNeedsLayout() + } + }, for: .touchUpInside) + + let showCustomizationViewButton = UIButton(type: .system) + showCustomizationViewButton.translatesAutoresizingMaskIntoConstraints = false + showCustomizationViewButton.setTitle("Show Customization", + for: .normal) + showCustomizationViewButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 25.0).isActive = true + self.showCustomizationViewAction = showCustomizationViewButton.connect(action: { + self.showMapCustomizationView() + }, for: .touchUpInside) + self.showCustomizationViewButton = showCustomizationViewButton + + let subviews:[UIView] = [doubleSizeButton, showCustomizationViewButton] + let stackview = UIStackView(arrangedSubviews: subviews) + stackview.translatesAutoresizingMaskIntoConstraints = false + stackview.axis = .vertical + stackview.distribution = .equalSpacing + stackview.spacing = UIStackView.spacingUseSystem + stackview.isLayoutMarginsRelativeArrangement = true + stackview.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 5, + leading: 5, + bottom: 5, + trailing: 5) + + let additionalControlsView = UIView() + additionalControlsView.translatesAutoresizingMaskIntoConstraints = false + additionalControlsView.addSubview(stackview) + + let effect = UIBlurEffect(style: .regular) + let effectView = UIVisualEffectView(effect: effect) + effectView.translatesAutoresizingMaskIntoConstraints = false + additionalControlsView.addSubview(effectView) + additionalControlsView.sendSubviewToBack(effectView) + additionalControlsView.addConstraints([ + effectView.leadingAnchor.constraint(equalTo: additionalControlsView.leadingAnchor), + effectView.trailingAnchor.constraint(equalTo: additionalControlsView.trailingAnchor), + effectView.topAnchor.constraint(equalTo: additionalControlsView.topAnchor), + effectView.bottomAnchor.constraint(equalTo: additionalControlsView.bottomAnchor), + ]) + + stackview.leadingAnchor.constraint(equalTo: additionalControlsView.leadingAnchor).isActive = true + stackview.trailingAnchor.constraint(equalTo: additionalControlsView.trailingAnchor).isActive = true + stackview.topAnchor.constraint(equalTo: additionalControlsView.topAnchor).isActive = true + stackview.bottomAnchor.constraint(equalTo: additionalControlsView.bottomAnchor).isActive = true + + self.view.addSubview(additionalControlsView) + self.view.addConstraints([ + self.view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: additionalControlsView.topAnchor, constant: 10.0), + self.infoView.trailingAnchor.constraint(equalTo: additionalControlsView.leadingAnchor, constant: -8.0) + ]) + + self.additionalControlsView = additionalControlsView + + self.setupEmptyViewHierarchy() + } + func configureConstraints () { if let widget = self.widget { @@ -128,11 +238,34 @@ class SingleWidgetViewController: UIViewController { } } + // MARK: Widget Customization View + + func showMapCustomizationView() { + if let mapWidget = self.widget as? DUXBetaMapWidget { + let storyboard = UIStoryboard(name: "Main", bundle: Bundle(for: type(of: self))) + let mapCustomizationViewController = storyboard.instantiateViewController(withIdentifier: "CustomMapViewController") as! CustomMapViewController + mapCustomizationViewController.mapWidget = mapWidget + + self.addChild(mapCustomizationViewController) + self.view.addSubview(mapCustomizationViewController.view) + mapCustomizationViewController.didMove(toParent: self) + + mapCustomizationViewController.view.translatesAutoresizingMaskIntoConstraints = false + mapCustomizationViewController.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0).isActive = true + mapCustomizationViewController.view.topAnchor.constraint(equalTo: self.widgetDescriptionLabel.bottomAnchor, constant: 16.0).isActive = true + mapCustomizationViewController.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true + mapCustomizationViewController.view.widthAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.widthAnchor, multiplier: 1.0/3.0).isActive = true + mapCustomizationViewController.view.setNeedsLayout() + mapCustomizationViewController.view.layoutIfNeeded() + } + } + + // MARK: User Interaction Handlers + @IBAction func handlePinch(recognizer: UIPinchGestureRecognizer) { if let hc = self.heightConstraint { if (recognizer.state == .began) { self.pinchStartHeight = hc.constant - print("Pinch Zoom Height: \(hc.constant)") } hc.constant = self.pinchStartHeight * recognizer.scale self.view.setNeedsLayout() @@ -140,23 +273,15 @@ class SingleWidgetViewController: UIViewController { } @IBAction func doubleTap(recognizer: UITapGestureRecognizer) { - if let nonNilWidget = _widget { + if let nonNilWidget = self.widget { let borderEnabled = nonNilWidget.view.layer.borderWidth == 0 self.recursivelySetBorderForView(view: nonNilWidget.view, borderEnabled: borderEnabled, level: 0) } } - private let colors: [UIColor] = [UIColor.red, - UIColor.green, - UIColor.blue, - UIColor.orange, - UIColor.yellow, - UIColor.magenta, - UIColor.cyan, - UIColor.purple, - UIColor.brown] + func recursivelySetBorderForView(view: UIView, borderEnabled: Bool, level: Int) { if (borderEnabled) { - view.layer.borderColor = colors[level % colors.count].cgColor + view.layer.borderColor = UIColor.borderColor(for: level).cgColor view.layer.borderWidth = 1 } else { view.layer.borderColor = UIColor.clear.cgColor @@ -166,22 +291,11 @@ class SingleWidgetViewController: UIViewController { self.recursivelySetBorderForView(view: subView, borderEnabled: borderEnabled, level: level + 1) } } - - func closeButtonPressed(for widget: DUXBetaBaseWidget!) { - self.widget?.removeFromParent() - self.widget?.view.removeFromSuperview() - } - - func currentScreenChanged(to newScreen: DUXBetaBaseWidget) { - if let widget = self.widget { - widget.view.removeConstraints(widget.view.constraints) - } - self.configureConstraints() - } } extension SingleWidgetViewController: WidgetSelectionDelegate { - func widgetSelected(_ newWidget: DUXBetaBaseWidget?) { + func widgetSelected(_ newWidget: DUXBetaBaseWidget?, shouldShowCustomizationView:Bool) { + self.shouldShowCustomizationView = shouldShowCustomizationView self.widget = newWidget } } diff --git a/UXSDKBetaSample/UXSDKBetaSample/WidgetsListViewController.swift b/UXSDKBetaSample/UXSDKBetaSample/WidgetsListViewController.swift index bbbdeaa..1c91152 100644 --- a/UXSDKBetaSample/UXSDKBetaSample/WidgetsListViewController.swift +++ b/UXSDKBetaSample/UXSDKBetaSample/WidgetsListViewController.swift @@ -27,7 +27,7 @@ import UIKit import DJIUXSDKBeta protocol WidgetSelectionDelegate: class { - func widgetSelected(_ widget: DUXBetaBaseWidget?) + func widgetSelected(_ newWidget: DUXBetaBaseWidget?, shouldShowCustomizationView:Bool) } class WidgetsListViewController: UITableViewController { @@ -42,16 +42,46 @@ class WidgetsListViewController: UITableViewController { "Vision Widget" : "Widget to display the vision status/collision avodance status, of the aircraft. It's state depends on sensors availability, flight mode, and aircraft type.", ] - static let widgets:[DUXBetaBaseWidget] = [DUXBetaMapWidget(), - DUXBetaBatteryWidget(), - DUXBetaCompassWidget(), - DUXBetaDashboardWidget(), - DUXBetaVisionWidget()] - static let widgetTitles:[String] = ["Map Widget", - "Battery Widget", - "Compass Widget", - "Dashboard Widget", - "Vision Widget"] + static var widgetClosures: [() -> DUXBetaBaseWidget] { + let mapWidgetClosure: () -> DUXBetaBaseWidget = { + let mapWidget = DUXBetaMapWidget() + mapWidget.showFlyZoneLegend = false + return mapWidget + } + + let batteryWidgetClosure: () -> DUXBetaBaseWidget = { + return DUXBetaBatteryWidget() + } + + let compassWidgetClosure: () -> DUXBetaBaseWidget = { + return DUXBetaCompassWidget() + } + + let dashboardWidgetClosure: () -> DUXBetaBaseWidget = { + return DUXBetaDashboardWidget() + } + + let visionWidgetClosure: () -> DUXBetaBaseWidget = { + return DUXBetaVisionWidget() + } + + return [ + mapWidgetClosure, + batteryWidgetClosure, + compassWidgetClosure, + dashboardWidgetClosure, + visionWidgetClosure + ] + } + + static let widgetMetadata:[(String, Bool)] = [ + ("Map Widget", true), + ("Battery Widget", false), + ("Compass Widget", false), + ("Dashboard Widget", false), + ("Vision Widget", false) + ] + override func viewDidLoad() { super.viewDidLoad() @@ -63,10 +93,13 @@ class WidgetsListViewController: UITableViewController { // MARK: UITableViewDelegate public override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let widget = WidgetsListViewController.widgets[indexPath.row] - delegate?.widgetSelected(widget) + let widgetClosure = WidgetsListViewController.widgetClosures[indexPath.row] + let widget = widgetClosure() + let shouldShowCustomizationView = WidgetsListViewController.widgetMetadata[indexPath.row].1 + delegate?.widgetSelected(widget, shouldShowCustomizationView: shouldShowCustomizationView) if let singleWidgetViewController = delegate as? SingleWidgetViewController { - let title = WidgetsListViewController.widgetTitles[indexPath.row] + let title = WidgetsListViewController.widgetMetadata[indexPath.row].0 + singleWidgetViewController.shouldShowCustomizationView = shouldShowCustomizationView singleWidgetViewController.title = title singleWidgetViewController.widgetDescriptionLabel.text = WidgetsListViewController.widgetDescriptions[title] splitViewController?.showDetailViewController(singleWidgetViewController, sender: nil) @@ -76,12 +109,13 @@ class WidgetsListViewController: UITableViewController { // MARK: UITableViewDataSource public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return WidgetsListViewController.widgets.count + return WidgetsListViewController.widgetClosures.count } public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "WidgetsListCellIdentifier", for: indexPath) - cell.textLabel?.text = WidgetsListViewController.widgetTitles[indexPath.row] + let title = WidgetsListViewController.widgetMetadata[indexPath.row].0 + cell.textLabel?.text = title return cell }