From 1d1cdb625a5e8133e096b3072174160dc6e96e49 Mon Sep 17 00:00:00 2001 From: qinhui <> Date: Fri, 18 Oct 2024 18:18:16 +0800 Subject: [PATCH] SwiftUI project low version adaptation, API adaptation to iOS14 --- .../APIExample-OC.xcodeproj/project.pbxproj | 36 ++++--- .../project.pbxproj | 42 +++++++-- .../Common/AgoraExtension.swift | 12 ++- .../Common/PickerView.swift | 10 +- .../Common/ViewExtensions.swift | 94 +++++++++++++++++++ .../APIExample-SwiftUI/ContentView.swift | 42 ++++++--- .../Examples/Advanced/ARKit/ARKit.swift | 3 +- .../Advanced/AudioMixing/AudioMixing.swift | 33 +++++-- .../AudioWaveform/AudioWaveform.swift | 22 +++-- .../ContentInspect/ContentInspect.swift | 3 +- .../CreateDataStream/CreateDataStream.swift | 2 +- .../CustomAudioRender/CustomAudioRender.swift | 2 +- .../CustomPCMAudioSource.swift | 2 +- .../JoinMultiChannel/JoinMultiChannel.swift | 8 +- .../KtvCopyrightMusic/KtvCopyrightMusic.swift | 3 +- .../LiveStreaming/LiveStreaming.swift | 51 ++++++---- .../Advanced/MediaPlayer/MediaPlayer.swift | 6 +- .../CustomRender/CustomRenderExample.swift | 9 +- .../PIPCommon/PIPDisplayView.swift | 3 + .../PictureInPicture/PictureInPicture.swift | 18 +++- .../SDKRender/SDKRenderExample.swift | 9 +- .../Advanced/PrecallTest/PrecallTest.swift | 2 +- .../Advanced/RawAudioData/RawAudioData.swift | 4 +- .../Advanced/RhythmPlayer/RhythmPlayer.swift | 22 +++-- .../Advanced/SpatialAudio/SpatialAudio.swift | 8 +- .../StreamEncryption/StreamEncryption.swift | 20 ++-- .../Advanced/VoiceChanger/VoiceChanger.swift | 12 +-- .../JoinChannelAudio/JoinChannelAudio.swift | 34 +++++-- .../JoinChannelVideoRecorder.swift | 5 +- 29 files changed, 386 insertions(+), 131 deletions(-) create mode 100644 iOS/APIExample-SwiftUI/APIExample-SwiftUI/Common/ViewExtensions.swift diff --git a/iOS/APIExample-OC/APIExample-OC.xcodeproj/project.pbxproj b/iOS/APIExample-OC/APIExample-OC.xcodeproj/project.pbxproj index 67ae6dcdd..925d38fa0 100644 --- a/iOS/APIExample-OC/APIExample-OC.xcodeproj/project.pbxproj +++ b/iOS/APIExample-OC/APIExample-OC.xcodeproj/project.pbxproj @@ -1571,9 +1571,11 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = YS397FG5PA; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = YS397FG5PA; ENABLE_BITCODE = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "APIExample-OC/Info.plist"; @@ -1594,6 +1596,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "io.agora.api.examples-oc"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = wildcard; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; @@ -1613,9 +1616,11 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = YS397FG5PA; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = YS397FG5PA; ENABLE_BITCODE = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "APIExample-OC/Info.plist"; @@ -1636,6 +1641,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "io.agora.api.examples-oc"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = wildcard; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; @@ -1651,9 +1657,11 @@ baseConfigurationReference = B653B1910F3D2DB664FFCA69 /* Pods-Agora-ScreenShare-Extension-OC.debug.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = YS397FG5PA; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = YS397FG5PA; ENABLE_BITCODE = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "Agora-ScreenShare-Extension-OC/Info.plist"; @@ -1669,6 +1677,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "io.agora.api.examples-oc.Agora-ScreenShare-Extension-OC"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = wildcard; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1680,9 +1689,11 @@ baseConfigurationReference = C4F0E8C20AFE260912A60198 /* Pods-Agora-ScreenShare-Extension-OC.release.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = YS397FG5PA; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = YS397FG5PA; ENABLE_BITCODE = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "Agora-ScreenShare-Extension-OC/Info.plist"; @@ -1698,6 +1709,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "io.agora.api.examples-oc.Agora-ScreenShare-Extension-OC"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = wildcard; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1712,10 +1724,10 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = YS397FG5PA; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -1759,10 +1771,10 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = YS397FG5PA; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI.xcodeproj/project.pbxproj b/iOS/APIExample-SwiftUI/APIExample-SwiftUI.xcodeproj/project.pbxproj index 53cc9f4ad..f7fb5b894 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI.xcodeproj/project.pbxproj +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI.xcodeproj/project.pbxproj @@ -134,6 +134,7 @@ F73B016D2CBA1AE40077B7D2 /* KeyCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9306E12CB961150085EFF9 /* KeyCenter.swift */; }; F73B016E2CBA1AED0077B7D2 /* GlobalSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9306AF2CB961150085EFF9 /* GlobalSettings.swift */; }; F73B016F2CBA1AF40077B7D2 /* SampleHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9307F42CB963B30085EFF9 /* SampleHandler.swift */; }; + F73B018D2CC10E240077B7D2 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73B018C2CC10E240077B7D2 /* ViewExtensions.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -308,6 +309,7 @@ D7DAC714DCF967FD618502DC /* Pods-APIExample-SwiftUI.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-APIExample-SwiftUI.debug.xcconfig"; path = "Target Support Files/Pods-APIExample-SwiftUI/Pods-APIExample-SwiftUI.debug.xcconfig"; sourceTree = ""; }; E633FB65347D029555FE8BDB /* Pods_APIExample_SwiftUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_APIExample_SwiftUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F73B016A2CBA1AC60077B7D2 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; + F73B018C2CC10E240077B7D2 /* ViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtensions.swift; sourceTree = ""; }; F89807E42BC51A25F44D3692 /* Pods-Agora-ScreenShare-Extension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Agora-ScreenShare-Extension.debug.xcconfig"; path = "Target Support Files/Pods-Agora-ScreenShare-Extension/Pods-Agora-ScreenShare-Extension.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -421,6 +423,7 @@ 4C9306DD2CB961150085EFF9 /* View */, 4C9306DF2CB961150085EFF9 /* ARKit */, 4C9306E12CB961150085EFF9 /* KeyCenter.swift */, + F73B018C2CC10E240077B7D2 /* ViewExtensions.swift */, ); path = Common; sourceTree = ""; @@ -1152,6 +1155,7 @@ 4C9307C52CB961160085EFF9 /* APIExample_SwiftUIApp.swift in Sources */, 4C9309DB2CBA91530085EFF9 /* VoiceChangerRTC.swift in Sources */, 4C9309C72CBA91530085EFF9 /* SDKRenderExample.swift in Sources */, + F73B018D2CC10E240077B7D2 /* ViewExtensions.swift in Sources */, 4C9309AB2CBA91530085EFF9 /* RawAudioDataRTC.swift in Sources */, 4C9307752CB961150085EFF9 /* AgoraPcmSourcePush.swift in Sources */, 4C9307BD2CB961160085EFF9 /* JoinChannelVideoRecorder.swift in Sources */, @@ -1348,10 +1352,12 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"APIExample-SwiftUI/Preview Content\""; - DEVELOPMENT_TEAM = YS397FG5PA; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = YS397FG5PA; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -1362,7 +1368,7 @@ INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1370,6 +1376,8 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "io.agora.api.examples-swiftUI"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = wildcard; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "APIExample-SwiftUI/APIExample-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -1384,10 +1392,12 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"APIExample-SwiftUI/Preview Content\""; - DEVELOPMENT_TEAM = YS397FG5PA; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = YS397FG5PA; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -1398,7 +1408,7 @@ INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1406,6 +1416,8 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "io.agora.api.examples-swiftUI"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = wildcard; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "APIExample-SwiftUI/APIExample-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -1417,14 +1429,17 @@ isa = XCBuildConfiguration; baseConfigurationReference = F89807E42BC51A25F44D3692 /* Pods-Agora-ScreenShare-Extension.debug.xcconfig */; buildSettings = { - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = YS397FG5PA; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = YS397FG5PA; ENABLE_BITCODE = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "Agora-ScreenShare-Extension/Info.plist"; INFOPLIST_KEY_CFBundleDisplayName = "Agora-ScreenShare-Extension"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1433,6 +1448,8 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "io.agora.api.examples-swiftUI.Agora-ScreenShare-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = wildcard; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "Agora-ScreenShare-Extension/Agora-ScreenShare-Extension-Bridging-Header.h"; @@ -1445,14 +1462,17 @@ isa = XCBuildConfiguration; baseConfigurationReference = 2FF1D082DDF2E2198DB041E4 /* Pods-Agora-ScreenShare-Extension.release.xcconfig */; buildSettings = { - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = YS397FG5PA; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = YS397FG5PA; ENABLE_BITCODE = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "Agora-ScreenShare-Extension/Info.plist"; INFOPLIST_KEY_CFBundleDisplayName = "Agora-ScreenShare-Extension"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1461,6 +1481,8 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "io.agora.api.examples-swiftUI.Agora-ScreenShare-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = wildcard; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "Agora-ScreenShare-Extension/Agora-ScreenShare-Extension-Bridging-Header.h"; diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Common/AgoraExtension.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Common/AgoraExtension.swift index 40df8c6ae..f2119fdfe 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Common/AgoraExtension.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Common/AgoraExtension.swift @@ -75,7 +75,9 @@ extension AgoraClientRole { } } -extension AgoraAudioProfile { +extension AgoraAudioProfile: Identifiable { + public var id: Int { self.rawValue } + func description() -> String { switch self { case .default: return "Default".localized @@ -93,7 +95,7 @@ extension AgoraAudioProfile { } } -extension AgoraAudioScenario { +extension AgoraAudioScenario: Identifiable { func description() -> String { switch self { case .default: return "Default".localized @@ -103,12 +105,16 @@ extension AgoraAudioScenario { } } + public var id: Int { self.rawValue } + static func allValues() -> [AgoraAudioScenario] { return [.default, .gameStreaming] } } -extension AgoraEncryptionMode { +extension AgoraEncryptionMode: Identifiable { + public var id: Int { self.rawValue } + func description() -> String { switch self { case .AES128GCM2: return "AES128GCM" diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Common/PickerView.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Common/PickerView.swift index 265a7fe94..f1fd41218 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Common/PickerView.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Common/PickerView.swift @@ -26,8 +26,8 @@ struct PickerView: View { } .padding(.horizontal, 5) .padding(.vertical, 3) - .background(.blue) - .foregroundStyle(.white) + .adaptiveBackground(.blue) + .adaptiveForegroundStyle(.white) #if os(iOS) && swift(>=5.7) .clipShape(.rect(cornerRadius: 5)) #else @@ -40,8 +40,8 @@ struct PickerView: View { } .padding(.horizontal, 5) .padding(.vertical, 3) - .background(.blue) - .foregroundStyle(.white) + .adaptiveBackground(.blue) + .adaptiveForegroundStyle(.white) #if os(iOS) && swift(>=5.7) .clipShape(.rect(cornerRadius: 5)) #else @@ -50,7 +50,7 @@ struct PickerView: View { } .padding(.horizontal, 20) .padding(.bottom, 150) - }.background(ignoresSafeAreaEdges: .bottom) + }.adaptiveBackground(ignoresSafeAreaEdges: .bottom) } } diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Common/ViewExtensions.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Common/ViewExtensions.swift new file mode 100644 index 000000000..8a6c17296 --- /dev/null +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Common/ViewExtensions.swift @@ -0,0 +1,94 @@ +// +// Extensions.swift +// APIExample-SwiftUI +// +// Created by qinhui on 2024/10/17. +// + +import SwiftUI +extension View { + @ViewBuilder + func adaptiveNavigationBarTitleDisplayMode(dispalayMode: NavigationBarItem.TitleDisplayMode = .inline) -> some View { + if #available(iOS 14.0, *) { + self.navigationBarTitleDisplayMode(dispalayMode) + } else { + self + } + } + + @ViewBuilder + func adaptiveBackground(_ color: Color = .white, ignoresSafeAreaEdges edges: Edge.Set = .all) -> some View { + if #available(iOS 15.0, *) { + self.background(color, ignoresSafeAreaEdges: edges) + } else { + self.background(color.edgesIgnoringSafeArea(edges)) + } + } + + @ViewBuilder + func adaptiveNavigationTitle(_ title: String = "") -> some View { + if #available(iOS 14.0, *) { + self.navigationTitle(title) + .adaptiveNavigationBarTitleDisplayMode() + } else { + self.navigationBarTitle(Text(title), displayMode: .inline) + } + } + + @ViewBuilder + func adaptiveForegroundStyle(_ color: Color) -> some View { + if #available(iOS 15.0, *) { + self.foregroundStyle(color) + } else { + self.foregroundColor(color) + } + } + + @ViewBuilder + func adaptiveCancelStyleButton(title: String, action: @escaping () -> Void) -> some View { + if #available(iOS 15.0, *) { + Button(title, role: .cancel, action: action) + } else { + Button(title, action: action) + .foregroundColor(.red) + } + } + + @ViewBuilder + func adaptiveDialog( + title: String, + isPresented: Binding, + @ViewBuilder actions: @escaping () -> Actions, + actionSheetActions: [ActionSheet.Button] + ) -> some View { + if #available(iOS 15.0, *) { + self.confirmationDialog(title, isPresented: isPresented, titleVisibility: .visible, actions: actions) + } else { + self.actionSheet(isPresented: isPresented) { + ActionSheet( + title: Text(title), + buttons: actionSheetActions + ) + } + } + } + + @ViewBuilder + func adaptiveButtonStyle() -> some View { + if #available(iOS 15.0, *) { + self.buttonStyle(.borderedProminent) + } else { + self.buttonStyle(DefaultButtonStyle()) + } + } + + @ViewBuilder + func adaptiveTint(_ color: Color) -> some View { + if #available(iOS 16.0, *) { + self.tint(color) + } else { + self.foregroundColor(color) + } + } +} + diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/ContentView.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/ContentView.swift index 80d6f6b9a..c8ffe817c 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/ContentView.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/ContentView.swift @@ -101,10 +101,26 @@ struct ContentView: View { ForEach(menus) { section in Section(header: Text(section.name)) { ForEach(section.rows) { item in - NavigationLink(destination: { - item.view.navigationTitle(item.name) - }) { - Text(item.name) + if item.name == "Picture In Picture".localized { + if #available(iOS 15.0, *) { + NavigationLink(destination: { + item.view.navigationTitle(item.name) + }) { + Text(item.name) + } + } else { + Button(action: { + showAlert() + }) { + Text(item.name) + } + } + } else { + NavigationLink(destination: { + item.view.navigationTitle(item.name) + }) { + Text(item.name) + } } } } @@ -113,15 +129,15 @@ struct ContentView: View { .listStyle(GroupedListStyle()) .navigationTitle("Agora API Example") .navigationBarTitleDisplayMode(.inline) - .toolbar { -// ToolbarItem(placement: .topBarTrailing) { -// NavigationLink(destination: { -// SettingsView() -// }) { -// Text("Settings").foregroundStyle(.black) -// } -// } - } + } + } + + func showAlert() { + let alert = UIAlertController(title: "Unsupported", message: "Picture in Picture is not supported on this version of iOS.", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .default)) + + if let rootViewController = UIApplication.shared.windows.first?.rootViewController { + rootViewController.present(alert, animated: true, completion: nil) } } } diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/ARKit/ARKit.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/ARKit/ARKit.swift index 0f72410af..e8f41557a 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/ARKit/ARKit.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/ARKit/ARKit.swift @@ -102,7 +102,8 @@ struct ARKit: View { var body: some View { VStack { - sceneView.background(ignoresSafeAreaEdges: .all).alert(isPresented: $agoraKit.isSupportedAR) { + sceneView + .adaptiveBackground().alert(isPresented: $agoraKit.isSupportedAR) { let message = "This app requires world tracking, which is available only on iOS devices with the A9 processor or later.".localized return Alert(title: Text("ARKit is not available on this device.".localized), message: Text(message), dismissButton: .cancel()) }.onReceive(agoraKit.$planarDetected, perform: { _ in diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/AudioMixing/AudioMixing.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/AudioMixing/AudioMixing.swift index 3554d04fb..4be5d8d94 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/AudioMixing/AudioMixing.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/AudioMixing/AudioMixing.swift @@ -27,24 +27,41 @@ struct AudioMixingEntry: View { self.isShowAudioProfile = true } label: { Text(scenario.description()) - }.confirmationDialog("Set Audio Scenario".localized, isPresented: $isShowAudioProfile, titleVisibility: .visible) { - ForEach(AgoraAudioScenario.allValues(), id: \.self) { item in - getAudioScenarioAction(item) - } - Button("Cancel".localized, role: .cancel) {} } + .adaptiveDialog( + title: "Set Audio Scenario".localized, + isPresented: $isShowAudioProfile, + actions: { + ForEach(AgoraAudioScenario.allValues(), id: \.self) { item in + getAudioScenarioAction(item) + } + + adaptiveCancelStyleButton(title: "Cancel".localized) { } + }, + actionSheetActions: AgoraAudioScenario.allValues().map { item in + .default(Text(item.description())) { + // Handle item selection + self.scenario = item + } + } + [.cancel(Text("Cancel".localized))] + ) }.padding(EdgeInsets(top: 0, leading: 35, bottom: 15, trailing: 35)) HStack { Text("Audio Profile".localized) Spacer() Button(profile.description()) { self.isShowAudioScence = true - }.confirmationDialog("Set Audio Profile".localized, isPresented: $isShowAudioScence, titleVisibility: .visible) { + } + .adaptiveDialog(title: "Set Audio Profile".localized, isPresented: $isShowAudioScence, actions: { ForEach(AgoraAudioProfile.allValues(), id: \.self) { item in getAudioProfileAction(item) } - Button("Cancel".localized, role: .cancel) {} - } + adaptiveCancelStyleButton(title: "Cancel".localized) { } + }, actionSheetActions: AgoraAudioProfile.allValues().map { item in + .default(Text(item.description())) { + self.profile = profile + } + } + [.cancel(Text("Cancel".localized))]) }.padding(EdgeInsets(top: 0, leading: 35, bottom: 0, trailing: 35)) TextField("Enter channel name".localized, text: $channelName).textFieldStyle(.roundedBorder).padding() Button { diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/AudioWaveform/AudioWaveform.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/AudioWaveform/AudioWaveform.swift index a4914bb92..c49232bfd 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/AudioWaveform/AudioWaveform.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/AudioWaveform/AudioWaveform.swift @@ -27,24 +27,34 @@ struct AudioWaveformEntry: View { self.isShowAudioProfile = true } label: { Text(scenario.description()) - }.confirmationDialog("Set Audio Scenario".localized, isPresented: $isShowAudioProfile, titleVisibility: .visible) { + } + .adaptiveDialog(title: "Set Audio Scenario".localized, isPresented: $isShowAudioProfile, actions: { ForEach(AgoraAudioScenario.allValues(), id: \.self) { item in getAudioScenarioAction(item) } - Button("Cancel".localized, role: .cancel) {} - } + adaptiveCancelStyleButton(title: "Cancel".localized) {} + }, actionSheetActions: AgoraAudioScenario.allValues().map { item in + .default(Text(item.description())) { + self.scenario = item + } + } + [.cancel(Text("Cancel".localized))]) }.padding(EdgeInsets(top: 0, leading: 35, bottom: 15, trailing: 35)) HStack { Text("Audio Profile".localized) Spacer() Button(profile.description()) { self.isShowAudioScence = true - }.confirmationDialog("Set Audio Profile".localized, isPresented: $isShowAudioScence, titleVisibility: .visible) { + } + .adaptiveDialog(title: "Set Audio Profile".localized, isPresented: $isShowAudioScence, actions: { ForEach(AgoraAudioProfile.allValues(), id: \.self) { item in getAudioProfileAction(item) } - Button("Cancel".localized, role: .cancel) {} - } + adaptiveCancelStyleButton(title: "Cancel".localized) {} + }, actionSheetActions: AgoraAudioProfile.allValues().map { item in + .default(Text(item.description())) { + self.profile = item + } + } + [.cancel(Text("Cancel".localized))]) }.padding(EdgeInsets(top: 0, leading: 35, bottom: 0, trailing: 35)) TextField("Enter channel name".localized, text: $channelName).textFieldStyle(.roundedBorder).padding() Button { diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/ContentInspect/ContentInspect.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/ContentInspect/ContentInspect.swift index 350e94b64..95365c679 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/ContentInspect/ContentInspect.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/ContentInspect/ContentInspect.swift @@ -52,7 +52,8 @@ struct ContentInspect: View { localView ZStack(alignment: .center) { Color.black.opacity(0.5).frame(height: 55) - Text("Please obtain the pornographic identification results through the console webhook.".localized).foregroundStyle(.white) + Text("Please obtain the pornographic identification results through the console webhook.".localized) + .adaptiveForegroundStyle(.white) } } .onAppear(perform: { diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/CreateDataStream/CreateDataStream.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/CreateDataStream/CreateDataStream.swift index fdbee7adf..6ddf97b4c 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/CreateDataStream/CreateDataStream.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/CreateDataStream/CreateDataStream.swift @@ -28,7 +28,7 @@ struct CreateDataStreamEntry: View { } Spacer() } - .navigationBarTitleDisplayMode(.inline) + .adaptiveNavigationBarTitleDisplayMode() } } diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/CustomAudioRender/CustomAudioRender.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/CustomAudioRender/CustomAudioRender.swift index aa01a01f9..d5b3978a2 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/CustomAudioRender/CustomAudioRender.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/CustomAudioRender/CustomAudioRender.swift @@ -47,7 +47,7 @@ struct CustomAudioRender: View { .font(.system(size: 12)) .foregroundColor(.gray) .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(.gray.opacity(0.5)) + .adaptiveBackground(.gray.opacity(0.5)) } .frame(width: geometry.size.width / 3, height: 200) } diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/CustomPCMAudioSource/CustomPCMAudioSource.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/CustomPCMAudioSource/CustomPCMAudioSource.swift index 1dc7f5326..495acaef7 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/CustomPCMAudioSource/CustomPCMAudioSource.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/CustomPCMAudioSource/CustomPCMAudioSource.swift @@ -73,7 +73,7 @@ struct DynamicGridView: View { LazyVGrid(columns: Array(repeating: GridItem(.fixed(itemWidth)), count: columns)) { ForEach(items, id: \.self) { item in VideoView(type: item.local ? .local : .remote, audioOnly: true) - .background(.gray) + .adaptiveBackground(.gray) .frame(width: itemWidth, height: 200) .padding(4) } diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/JoinMultiChannel/JoinMultiChannel.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/JoinMultiChannel/JoinMultiChannel.swift index c02178b53..cd0b2be28 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/JoinMultiChannel/JoinMultiChannel.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/JoinMultiChannel/JoinMultiChannel.swift @@ -66,8 +66,8 @@ struct JoinMultiChannel: View { .disabled(!agoraKit.joinEx) .colorMultiply(agoraKit.joinEx ? .white : .gray) .padding(5) - .background(.blue) - .foregroundStyle(.white) + .adaptiveBackground(.blue) + .adaptiveForegroundStyle(.white) #if os(iOS) && swift(>=5.7) .clipShape(.rect(cornerRadius: 5)) #else @@ -88,8 +88,8 @@ struct JoinMultiChannel: View { .disabled(!agoraKit.joinEx) .colorMultiply(agoraKit.joinEx ? .white : .gray) .padding(5) - .background(.blue) - .foregroundStyle(.white) + .adaptiveBackground(.blue) + .adaptiveForegroundStyle(.white) #if os(iOS) && swift(>=5.7) .clipShape(.rect(cornerRadius: 5)) #else diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/KtvCopyrightMusic/KtvCopyrightMusic.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/KtvCopyrightMusic/KtvCopyrightMusic.swift index a5c207370..2c62d1903 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/KtvCopyrightMusic/KtvCopyrightMusic.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/KtvCopyrightMusic/KtvCopyrightMusic.swift @@ -15,7 +15,8 @@ struct KtvCopyrightMusic: View { if let url = URL(string: urlString) { UIApplication.shared.open(url) } - }.buttonStyle(.borderedProminent) + } + .adaptiveButtonStyle() } } } diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/LiveStreaming/LiveStreaming.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/LiveStreaming/LiveStreaming.swift index 128057f48..a8563e159 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/LiveStreaming/LiveStreaming.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/LiveStreaming/LiveStreaming.swift @@ -91,7 +91,7 @@ struct LiveStreamingEntry: View { } label: { Text("Join".localized) } - .confirmationDialog("Pick Role".localized, isPresented: $roleSheetIsShow, actions: { + .adaptiveDialog(title: "Pick Role".localized, isPresented: $roleSheetIsShow, actions: { Button("Broadcaster".localized) { self.role = .broadcaster prepareConfig() @@ -102,8 +102,23 @@ struct LiveStreamingEntry: View { prepareConfig() self.channelButtonIsActive = true } - Button("Cancel".localized, role: .cancel) {} - }) + adaptiveCancelStyleButton(title: "Cancel".localized) { + + } + }, actionSheetActions: [ + .default(Text("Broadcaster".localized)) { + self.role = .broadcaster + prepareConfig() + self.channelButtonIsActive = true + }, + .default(Text("Audience".localized)) { + self.role = .audience + prepareConfig() + self.channelButtonIsActive = true + }, + + .cancel(Text("Cancel".localized)) + ]) .disabled(channelName.isEmpty) Spacer() @@ -192,13 +207,13 @@ struct LiveStreaming: View { } else { Rectangle() .frame(width: 136, height: 182) - .foregroundStyle(.clear) + .adaptiveForegroundStyle(.clear) } //防抖 HStack { Text("anti shake".localized) - .foregroundStyle(.white) + .adaptiveForegroundStyle(.white) Picker("", selection: $selectStabilizationMode) { ForEach(AntiShakeLevel.allCases) { e in Text(e.suggestedLevel).tag(e) @@ -208,14 +223,14 @@ struct LiveStreaming: View { liveStreamRTCKit.agoraKit.setCameraStabilizationMode(newValue.value) } } - .background(.gray.opacity(0.3)) + .adaptiveBackground(.gray.opacity(0.3)) .padding(.top, 30) if liveStreamRTCKit.role == .broadcaster { //centerStage, 相机对焦 HStack { Toggle("CenterStage", isOn: $centerStage) - .foregroundStyle(.white) + .adaptiveForegroundStyle(.white) .onChange(of: centerStage) { newValue in let centerStageNotSupported = liveStreamRTCKit.agoraKit.isCameraCenterStageSupported() if newValue && !centerStageNotSupported { @@ -238,7 +253,7 @@ struct LiveStreaming: View { }) Text("Camera Selected".localized) - .foregroundStyle(.white) + .adaptiveForegroundStyle(.white) Button(selectedCamertOption) { self.cameraSheetIsShow = true } @@ -265,12 +280,12 @@ struct LiveStreaming: View { }) } .fixedSize() - .background(.gray.opacity(0.3)) + .adaptiveBackground(.gray.opacity(0.3)) //B帧,编码方式 HStack { Toggle("B Fps".localized, isOn: $bFpsState) - .foregroundStyle(.white) + .adaptiveForegroundStyle(.white) .onChange(of: bFpsState) { newValue in let encoderConfig = AgoraVideoEncoderConfiguration() let videoOptions = AgoraAdvancedVideoOptions() @@ -280,7 +295,7 @@ struct LiveStreaming: View { liveStreamRTCKit.agoraKit.setVideoEncoderConfiguration(encoderConfig) } Text("Code Type".localized) - .foregroundStyle(.white) + .adaptiveForegroundStyle(.white) Picker("", selection: $selectEncodingType) { ForEach(CodeType.allCases) { e in Text(e.suggestedType) @@ -299,12 +314,12 @@ struct LiveStreaming: View { } .fixedSize() - .background(.gray.opacity(0.3)) + .adaptiveBackground(.gray.opacity(0.3)) //水印, 垫片推流 HStack { Toggle("Water Mark".localized, isOn: $waterMarkState) - .foregroundStyle(.white) + .adaptiveForegroundStyle(.white) .onChange(of: waterMarkState) { newValue in if newValue { if let filepath = Bundle.main.path(forResource: "agora-logo", ofType: "png") { @@ -323,7 +338,7 @@ struct LiveStreaming: View { } Toggle("Gasket push flow".localized, isOn: $gasketPushFlow) - .foregroundStyle(.white) + .adaptiveForegroundStyle(.white) .onChange(of: gasketPushFlow) { newValue in let options = AgoraImageTrackOptions() let imgPath = Bundle.main.path(forResource: "agora-logo", ofType: "png") @@ -334,7 +349,7 @@ struct LiveStreaming: View { } .fixedSize() - .background(.gray.opacity(0.3)) + .adaptiveBackground(.gray.opacity(0.3)) } //截图,大小流 @@ -364,7 +379,7 @@ struct LiveStreaming: View { Text(simulcastStreamState ? "Opened State".localized : "Default State".localized) .font(.system(size: 11)) } - .foregroundStyle(.white) + .adaptiveForegroundStyle(.white) }) .onChange(of: simulcastStreamState) { newValue in simulcastStreamState = newValue @@ -372,7 +387,7 @@ struct LiveStreaming: View { } } .fixedSize() - .background(.gray.opacity(0.3)) + .adaptiveBackground(.gray.opacity(0.3)) //极速直播, 连麦 HStack { @@ -393,7 +408,7 @@ struct LiveStreaming: View { } } .fixedSize() - .background(.gray.opacity(0.3)) + .adaptiveBackground(.gray.opacity(0.3)) Spacer() } diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/MediaPlayer/MediaPlayer.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/MediaPlayer/MediaPlayer.swift index 2355ed345..07e65b4e5 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/MediaPlayer/MediaPlayer.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/MediaPlayer/MediaPlayer.swift @@ -115,14 +115,16 @@ struct MediaPlayer: View { withAnimation { isShowPlayPickerView.toggle() } - }.foregroundStyle(.gray) + } + .adaptiveForegroundStyle(.gray) Spacer() Text("publish Track:".localized).padding(.trailing, 5) Button(agoraKit.codecName) { withAnimation { isShowPublishPickerView.toggle() } - }.foregroundStyle(.gray) + } + .adaptiveForegroundStyle(.gray) }.padding(20) } Spacer() diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/CustomRender/CustomRenderExample.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/CustomRender/CustomRenderExample.swift index e2d4e0a33..29f2aa4ba 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/CustomRender/CustomRenderExample.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/CustomRender/CustomRenderExample.swift @@ -22,6 +22,7 @@ struct CustomRenderMockContainerView: View { } } +@available(iOS 15.0, *) struct CustomRenderExample: View { @State var configs: [String: Any] @@ -77,13 +78,17 @@ struct CustomRenderExample: View { customRenderViewModel.cleanRtc() } .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color.orange) + .adaptiveBackground(Color.orange) } } struct CustomRenderExamplePreviews: PreviewProvider { static var previews: some View { - CustomRenderExample(configs: [:]) + if #available(iOS 15.0, *) { + CustomRenderExample(configs: [:]) + } else { + // Fallback on earlier versions + } } } diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/PIPCommon/PIPDisplayView.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/PIPCommon/PIPDisplayView.swift index 36b92b4fe..a0782f96b 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/PIPCommon/PIPDisplayView.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/PIPCommon/PIPDisplayView.swift @@ -31,6 +31,7 @@ protocol PIPController { @class PIPDisplayView,SwiftUI Picture-in-picture view wrapper class @abstract The picture-in-picture view is a UIKit-related API that allows you to bridge SwiftUI's controls to the PIP view (view with UIKit layout, class). */ +@available(iOS 15.0, *) struct PIPDisplayView: UIViewRepresentable { ///PIP controller, used to control picture-in-picture @ObservedObject var viewModel: PIPViewModel @@ -46,6 +47,7 @@ struct PIPDisplayView: UIViewRepresentable { @class PIPViewModel,Picture-in-picture view control class @abstract Provide the view that needs to be displayed in picture to PIPViewModel to quickly realize the picture-in-picture function (picture-in-picture window open or close, window size switch) */ +@available(iOS 15.0, *) class PIPViewModel:NSObject, ObservableObject, PIPController { var backgroundView: PIPBackgroundView private var videoCallController: AVPictureInPictureVideoCallViewController? @@ -92,6 +94,7 @@ class PIPViewModel:NSObject, ObservableObject, PIPController { @class (inner class) PIPViewModel extension to handle draw-in-picture agent callbacks @abstract Add or delete views through draw-in-picture proxy callbacks */ +@available(iOS 15.0, *) extension PIPViewModel: AVPictureInPictureControllerDelegate { func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { guard let vc = pictureInPictureController.contentSource?.activeVideoCallContentViewController, let pipSourceView = backgroundView.pipSourceView else { return } diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/PictureInPicture.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/PictureInPicture.swift index de2bbe21d..a34388bb1 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/PictureInPicture.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/PictureInPicture.swift @@ -55,12 +55,20 @@ struct PictureInPictureJoinEntry: View { }.disabled(channelName.isEmpty) Spacer() if pipType == .sdkRender { - NavigationLink(destination: sdkRenderView().navigationTitle(channelName).navigationBarTitleDisplayMode(.inline), isActive: $isActive) { - EmptyView() + if #available(iOS 15.0, *) { + NavigationLink(destination: sdkRenderView().navigationTitle(channelName).navigationBarTitleDisplayMode(.inline), isActive: $isActive) { + EmptyView() + } + } else { + // Fallback on earlier versions } } else { - NavigationLink(destination: customRenderView().navigationTitle(channelName).navigationBarTitleDisplayMode(.inline), isActive: $isActive) { - EmptyView() + if #available(iOS 15.0, *) { + NavigationLink(destination: customRenderView().navigationTitle(channelName).navigationBarTitleDisplayMode(.inline), isActive: $isActive) { + EmptyView() + } + } else { + // Fallback on earlier versions } } @@ -69,10 +77,12 @@ struct PictureInPictureJoinEntry: View { .navigationBarTitleDisplayMode(.inline) } + @available(iOS 15.0, *) private func customRenderView() -> CustomRenderExample { return CustomRenderExample(configs: configs) } + @available(iOS 15.0, *) private func sdkRenderView() -> SDKRenderExample{ return SDKRenderExample(configs: configs) } diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/SDKRender/SDKRenderExample.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/SDKRender/SDKRenderExample.swift index 8e103f2ca..2e35b343e 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/SDKRender/SDKRenderExample.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PictureInPicture/SDKRender/SDKRenderExample.swift @@ -21,6 +21,7 @@ struct SDKRenderMockContainerView: View { } } +@available(iOS 15.0, *) struct SDKRenderExample: View { @State var configs: [String: Any] @@ -76,12 +77,16 @@ struct SDKRenderExample: View { sdkRenderViewModel.cleanRtc() } .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color.orange) + .adaptiveBackground(Color.orange) } } struct SDKRenderExamplePreviews: PreviewProvider { static var previews: some View { - SDKRenderExample(configs: [:]) + if #available(iOS 15.0, *) { + SDKRenderExample(configs: [:]) + } else { + // Fallback on earlier versions + } } } diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PrecallTest/PrecallTest.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PrecallTest/PrecallTest.swift index e1538101e..c072fbfea 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PrecallTest/PrecallTest.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/PrecallTest/PrecallTest.swift @@ -62,7 +62,7 @@ struct PrecallTest: View { } .frame(width: geo.size.width, height: geo.size.height) - .background(.black.opacity(0.3)) + .adaptiveBackground(.black.opacity(0.3)) .opacity(agoraKit.isEchoTestEnd ? 0.0 : 1.0) }.onAppear(perform: { agoraKit.setupRTC(configs: configs, diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/RawAudioData/RawAudioData.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/RawAudioData/RawAudioData.swift index e6b7cfb3a..94fbf13e8 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/RawAudioData/RawAudioData.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/RawAudioData/RawAudioData.swift @@ -23,12 +23,12 @@ struct RawAudioDataEntry: View { Text("Join".localized) }.disabled(channelName.isEmpty) Spacer() - NavigationLink(destination: RawAudioData(configs: configs).navigationTitle(channelName).navigationBarTitleDisplayMode(.inline), isActive: $isActive) { + NavigationLink(destination: RawAudioData(configs: configs).adaptiveNavigationTitle(channelName), isActive: $isActive) { EmptyView() } Spacer() } - .navigationBarTitleDisplayMode(.inline) + .adaptiveNavigationTitle() } } diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/RhythmPlayer/RhythmPlayer.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/RhythmPlayer/RhythmPlayer.swift index 5666741da..8570b694f 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/RhythmPlayer/RhythmPlayer.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/RhythmPlayer/RhythmPlayer.swift @@ -27,24 +27,34 @@ struct RhythmPlayerEntry: View { self.isShowAudioProfile = true } label: { Text(scenario.description()) - }.confirmationDialog("Set Audio Scenario".localized, isPresented: $isShowAudioProfile, titleVisibility: .visible) { + } + .adaptiveDialog(title: "Set Audio Scenario".localized, isPresented: $isShowAudioProfile, actions: { ForEach(AgoraAudioScenario.allValues(), id: \.self) { item in getAudioScenarioAction(item) } - Button("Cancel".localized, role: .cancel) {} - } + adaptiveCancelStyleButton(title: "Cancel".localized) {} + }, actionSheetActions: AgoraAudioScenario.allValues().map { item in + .default(Text(item.description())) { + self.scenario = item + } + } + [.cancel(Text("Cancel".localized))]) }.padding(EdgeInsets(top: 0, leading: 35, bottom: 15, trailing: 35)) HStack { Text("Audio Profile".localized) Spacer() Button(profile.description()) { self.isShowAudioScence = true - }.confirmationDialog("Set Audio Profile".localized, isPresented: $isShowAudioScence, titleVisibility: .visible) { + } + .adaptiveDialog(title: "Set Audio Profile".localized, isPresented: $isShowAudioScence, actions: { ForEach(AgoraAudioProfile.allValues(), id: \.self) { item in getAudioProfileAction(item) } - Button("Cancel".localized, role: .cancel) {} - } + adaptiveCancelStyleButton(title: "Cancel".localized) {} + }, actionSheetActions: AgoraAudioProfile.allValues().map { item in + .default(Text(item.description())) { + self.profile = item + } + } + [.cancel(Text("Cancel".localized))]) }.padding(EdgeInsets(top: 0, leading: 35, bottom: 0, trailing: 35)) TextField("Enter channel name".localized, text: $channelName).textFieldStyle(.roundedBorder).padding() Button { diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/SpatialAudio/SpatialAudio.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/SpatialAudio/SpatialAudio.swift index b2e1f90b4..1a00342e4 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/SpatialAudio/SpatialAudio.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/SpatialAudio/SpatialAudio.swift @@ -23,12 +23,12 @@ struct SpatialAudioEntry: View { Text("Join".localized) }.disabled(channelName.isEmpty) Spacer() - NavigationLink(destination: SpatialAudio(configs: configs).navigationTitle(channelName).navigationBarTitleDisplayMode(.inline), isActive: $isActive) { + NavigationLink(destination: SpatialAudio(configs: configs).adaptiveNavigationTitle(channelName), isActive: $isActive) { EmptyView() } Spacer() } - .navigationBarTitleDisplayMode(.inline) + .adaptiveNavigationBarTitleDisplayMode() } } @@ -169,7 +169,7 @@ struct SpatialAudio: View { Text("\(agoraKit.remoteUser1)") } .opacity(agoraKit.remoteUser1 == 0 ? 0.0 : 1.0) - .background(.clear) + .adaptiveBackground(.clear) .onAppear { let frame = g.frame(in: .named("OuterV")) agoraKit.remoteUser2Frame = frame @@ -299,7 +299,7 @@ struct ActionSheetView: View { } }.padding(.horizontal, 15) }.frame(height: 200) - .background(Color.white) + .adaptiveBackground(Color.white) .offset(x: 0, y: isShow ? 200 : 0) } } diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/StreamEncryption/StreamEncryption.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/StreamEncryption/StreamEncryption.swift index bed8dd4a1..7d047faca 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/StreamEncryption/StreamEncryption.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/StreamEncryption/StreamEncryption.swift @@ -24,18 +24,26 @@ struct StreamEncryptionEntry: View { Spacer() Button(useCustom ? "Custom" : mode.description()) { isShowActionSheet.toggle() - }.confirmationDialog("Set Encryption Mode".localized, isPresented: $isShowActionSheet, titleVisibility: .visible) { + } + .adaptiveDialog(title: "Set Encryption Mode".localized, isPresented: $isShowActionSheet, actions: { ForEach(AgoraEncryptionMode.allValues(), id: \.self) { item in Button(item.description()) { - mode = item - useCustom = false + self.mode = item + self.useCustom = false } } Button("Custom") { - useCustom = true + self.useCustom = true } - Button("Cancel".localized, role: .cancel) {} - } + adaptiveCancelStyleButton(title: "Cancel".localized) { } + }, actionSheetActions: AgoraEncryptionMode.allValues().map { item in + .default(Text(item.description())) { + self.mode = item + self.useCustom = false + } + } + [.default(Text("Custom")) { + self.useCustom = true + }] + [.cancel(Text("Cancel".localized))]) Spacer() }.padding(.leading, 15) TextField("Enter encryption secret".localized, text: $encryptSecretField).textFieldStyle(.roundedBorder) diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/VoiceChanger/VoiceChanger.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/VoiceChanger/VoiceChanger.swift index 9c69e16ac..79d310a14 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/VoiceChanger/VoiceChanger.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/VoiceChanger/VoiceChanger.swift @@ -277,23 +277,23 @@ struct VoiceChanger: View { HStack { Text(selectedPitchCorrection == .off ? "N/A" : "Tonic Mode".localized) - .foregroundStyle(tonicModeEnable ? .black : .gray) + .adaptiveForegroundStyle(tonicModeEnable ? .black : .gray) Slider(value: $tonicModeValue, in: tonicModeValueSliderRange) { isEditing in if !isEditing { voiceChangerRTC.agoraKit.setAudioEffectParameters(currentAudioEffects, param1: Int32(tonicModeValue), param2: Int32(tonicPitchValue)) } } - .tint(tonicModeEnable ? .blue : .gray) + .adaptiveTint(tonicModeEnable ? .blue : .gray) .disabled(!tonicModeEnable) Text(selectedPitchCorrection == .off ? "N/A" : "Tonic Pitch".localized) - .foregroundStyle(tonicModeEnable ? .black : .gray) + .adaptiveForegroundStyle(tonicModeEnable ? .black : .gray) Slider(value: $tonicPitchValue, in: tonicPitchValueSliderRange) { isEditing in if !isEditing { voiceChangerRTC.agoraKit.setAudioEffectParameters(currentAudioEffects, param1: Int32(tonicModeValue), param2: Int32(tonicPitchValue)) } } - .tint(tonicModeEnable ? .blue : .gray) + .adaptiveTint(tonicModeEnable ? .blue : .gray) .disabled(!tonicPitchEnable) } @@ -406,9 +406,9 @@ struct VoiceChanger: View { VStack { HStack { localView - .background(.gray) + .adaptiveBackground(.gray) remoteView - .background(.gray) + .adaptiveBackground(.gray) } .frame(maxHeight: 200) .padding(.bottom, 50) diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Basic/JoinChannelAudio/JoinChannelAudio.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Basic/JoinChannelAudio/JoinChannelAudio.swift index 74e24438a..b3ed1635c 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Basic/JoinChannelAudio/JoinChannelAudio.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Basic/JoinChannelAudio/JoinChannelAudio.swift @@ -27,24 +27,34 @@ struct JoinChannelAudioEntry: View { self.isShowAudioProfile = true } label: { Text(scenario.description()) - }.confirmationDialog("Set Audio Scenario".localized, isPresented: $isShowAudioProfile, titleVisibility: .visible) { + } + .adaptiveDialog(title: "Set Audio Scenario".localized, isPresented: $isShowAudioProfile, actions: { ForEach(AgoraAudioScenario.allValues(), id: \.self) { item in getAudioScenarioAction(item) } - Button("Cancel".localized, role: .cancel) {} - } + adaptiveCancelStyleButton(title: "Cancel".localized) {} + }, actionSheetActions: AgoraAudioScenario.allValues().map { item in + .default(Text(item.description())) { + self.scenario = item + } + } + [.cancel(Text("Cancel".localized))]) }.padding(EdgeInsets(top: 0, leading: 35, bottom: 15, trailing: 35)) HStack { Text("Audio Profile".localized) Spacer() Button(profile.description()) { self.isShowAudioScence = true - }.confirmationDialog("Set Audio Profile".localized, isPresented: $isShowAudioScence, titleVisibility: .visible) { + } + .adaptiveDialog(title: "Set Audio Profile".localized, isPresented: $isShowAudioScence, actions: { ForEach(AgoraAudioProfile.allValues(), id: \.self) { item in getAudioProfileAction(item) } - Button("Cancel".localized, role: .cancel) {} - } + adaptiveCancelStyleButton(title: "Cancel".localized) {} + }, actionSheetActions: AgoraAudioScenario.allValues().map { item in + .default(Text(item.description())) { + self.profile = profile + } + } + [.cancel(Text("Cancel".localized))]) }.padding(EdgeInsets(top: 0, leading: 35, bottom: 0, trailing: 35)) TextField("Enter channel name".localized, text: $channelName).textFieldStyle(.roundedBorder).padding() Button { @@ -112,12 +122,18 @@ struct JoinChannelAudio: View { Spacer() Button(scenario.description()) { isShowAudioProfile = true - }.confirmationDialog("Set Audio Scenario".localized, isPresented: $isShowAudioProfile, titleVisibility: .visible) { + } + .adaptiveDialog(title: "Set Audio Scenario".localized, isPresented: $isShowAudioProfile, actions: { ForEach(AgoraAudioScenario.allValues(), id: \.self) { item in getAudioScenarioAction(item) } - Button("Cancel".localized, role: .cancel) {} - }.padding(.trailing, 30) + adaptiveCancelStyleButton(title: "Cancel".localized) {} + }, actionSheetActions: AgoraAudioScenario.allValues().map { item in + .default(Text(item.description())) { + self.scenario = scenario + self.agoraKit.setAudioScenario(scenario: scenario) + } + } + [.cancel(Text("Cancel".localized))]) }.padding(EdgeInsets(top: 0, leading: 15, bottom: 10, trailing: 15)) HStack { Text("RecordingVolume".localized) diff --git a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Basic/JoinChannelVideo(Recorder)/JoinChannelVideoRecorder.swift b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Basic/JoinChannelVideo(Recorder)/JoinChannelVideoRecorder.swift index c3df9a488..de9bff8be 100644 --- a/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Basic/JoinChannelVideo(Recorder)/JoinChannelVideoRecorder.swift +++ b/iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Basic/JoinChannelVideo(Recorder)/JoinChannelVideoRecorder.swift @@ -64,7 +64,8 @@ struct JoinChannelVideoRecorder: View { agoraKit.onTapLocalRecordButton(isStart: isLocalStart) }.overlay( RoundedRectangle(cornerRadius: 5).stroke(Color.red, lineWidth: 2).padding(-5) - ).foregroundStyle(.red) + ) + .adaptiveForegroundStyle(.red) .padding(.bottom, 20) .padding(.trailing, 20) } @@ -76,7 +77,7 @@ struct JoinChannelVideoRecorder: View { agoraKit.onTapRemoteRecordButton(isStart: isRemoteStart) }.overlay( RoundedRectangle(cornerRadius: 5).stroke(Color.red, lineWidth: 2).padding(-5) - ).foregroundStyle(.red) + ).adaptiveForegroundStyle(.red) .padding(.bottom, 20) .padding(.trailing, 20) .opacity(agoraKit.isRemoteJoind ? 1.0 : 0)