From 4a9e7ad2a1b400f4120ccb316a38be9d366c3eb4 Mon Sep 17 00:00:00 2001 From: True Zhuanjia Date: Sat, 7 Mar 2026 10:08:17 +0800 Subject: [PATCH] iOS26 UI adaption: host view navigation bar --- VoidLink/Limelight-Info.plist | 34 ++++----- VoidLink/Utility/GenericUtils.swift | 10 +++ .../ViewControllers/MainFrameViewController.m | 70 ++++++++++++------- 3 files changed, 71 insertions(+), 43 deletions(-) diff --git a/VoidLink/Limelight-Info.plist b/VoidLink/Limelight-Info.plist index 0d58e637..98345ea4 100644 --- a/VoidLink/Limelight-Info.plist +++ b/VoidLink/Limelight-Info.plist @@ -2,8 +2,6 @@ - GCSupportsGameMode - CADisableMinimumFrameDuration CADisableMinimumFrameDurationOnPhone @@ -58,31 +56,24 @@ Bluetooth access allows Moonlight to connect to Citrix X1 mice. NSBluetoothPeripheralUsageDescription Bluetooth access allows Moonlight to connect to Citrix X1 mice. - NSMicrophoneUsageDescription - VoidLink需要麦克风访问权限。现阶段,麦克风重定向仅支持Sunshine基地版,且麦克风音频流【未经加密】。 - -VoidLink uses microphone to stream your voice to gaming PC. Microphone redirection currently works with Sunshine-Foundation (a fork of Sunshine) only, and the mic audio stream is NOT ENCRYPTED at this stage. NSBonjourServices _nvstream._tcp NSLocalNetworkUsageDescription VoidLink uses the local network to connect to your gaming PC for streaming. + NSMicrophoneUsageDescription + VoidLink需要麦克风访问权限。现阶段,麦克风重定向仅支持Sunshine基地版,且麦克风音频流【未经加密】。 + +VoidLink uses microphone to stream your voice to gaming PC. Microphone redirection currently works with Sunshine-Foundation (a fork of Sunshine) only, and the mic audio stream is NOT ENCRYPTED at this stage. NSMotionUsageDescription VoidLink uses the device Gyro and Acceleration data to emulate motion controls when streaming. UIApplicationSceneManifest + UIApplicationSupportsMultipleScenes + UISceneConfigurations - UIWindowSceneSessionRoleExternalDisplay - - - UISceneDelegateClassName - SceneDelegate - UISceneConfigurationName - External Display Configuration - - UIWindowSceneSessionRoleApplication @@ -92,9 +83,16 @@ VoidLink uses microphone to stream your voice to gaming PC. Microphone redirecti SceneDelegate + UIWindowSceneSessionRoleExternalDisplay + + + UISceneConfigurationName + External Display Configuration + UISceneDelegateClassName + SceneDelegate + + - UIApplicationSupportsMultipleScenes - UIApplicationSupportsIndirectInputEvents @@ -102,6 +100,8 @@ VoidLink uses microphone to stream your voice to gaming PC. Microphone redirecti audio + UIDesignRequiresCompatibility + UILaunchStoryboardName Launch Screen UIMainStoryboardFile diff --git a/VoidLink/Utility/GenericUtils.swift b/VoidLink/Utility/GenericUtils.swift index f40a36b2..b403a70a 100644 --- a/VoidLink/Utility/GenericUtils.swift +++ b/VoidLink/Utility/GenericUtils.swift @@ -49,4 +49,14 @@ import Foundation @objc static func isIPad() -> Bool { return UIDevice.current.userInterfaceIdiom == .pad } + + @objc static let liquidGlassEnabled: Bool = { + if #available(iOS 26.0, tvOS 26.0, *) { + let useLegacyUI = Bundle.main.object(forInfoDictionaryKey: "UIDesignRequiresCompatibility") as? Bool + return useLegacyUI != true + } else { + return false + } + }() + } diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index e719c3af..601bdc17 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -148,11 +148,13 @@ static NSMutableSet* hostList; - (void)updateTitle { if (@available(iOS 13.0, *)) { + UINavigationBarAppearance* appearance = navBarAppearanceStandard; NSDictionary* titleTextAttributes = @{ NSFontAttributeName: [UIFont systemFontOfSize:20 weight:UIFontWeightMedium], NSForegroundColorAttributeName: [ThemeManager textColor] // 可选,设置标题颜色 }; - [navBarAppearanceStandard setValue:titleTextAttributes forKey:@"titleTextAttributes"]; + appearance.titleTextAttributes = titleTextAttributes; + navBarAppearanceStandard = appearance; } if (_selectedHost != nil) { @@ -164,11 +166,14 @@ static NSMutableSet* hostList; } else { if (@available(iOS 13.0, *)) { + + UINavigationBarAppearance* appearance = navBarAppearanceStandard; NSDictionary* titleTextAttributes = @{ - NSFontAttributeName: [UIFont systemFontOfSize:22 weight:UIFontWeightMedium], + NSFontAttributeName: [UIFont systemFontOfSize:20 weight:UIFontWeightMedium], NSForegroundColorAttributeName: [ThemeManager textColor] // 可选,设置标题颜色 }; - [navBarAppearanceStandard setValue:titleTextAttributes forKey:@"titleTextAttributes"]; + appearance.titleTextAttributes = titleTextAttributes; + navBarAppearanceStandard = appearance; } /* self.navigationController.navigationBar.titleTextAttributes = @{ @@ -1386,26 +1391,35 @@ static NSMutableSet* hostList; // 创建按钮 CGFloat buttonHeight = 30; UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; - button.backgroundColor = [ThemeManager appPrimaryColor]; // #0A85FF + button.backgroundColor = GenericUtils.liquidGlassEnabled ? UIColor.whiteColor : [ThemeManager appPrimaryColor]; // #0A85FF button.layer.cornerRadius = buttonHeight/2; - button.clipsToBounds = YES; + button.clipsToBounds = !GenericUtils.liquidGlassEnabled; // 设置图标(SF Symbol) if (@available(iOS 13.0, *)) { - UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:17 weight:UIImageSymbolWeightMedium]; + UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration + configurationWithPointSize:GenericUtils.liquidGlassEnabled ? 18.7 :17 + weight:GenericUtils.liquidGlassEnabled ? UIImageSymbolWeightRegular :UIImageSymbolWeightMedium]; UIImage *image = [UIImage systemImageNamed:@"plus.circle" withConfiguration:config]; [button setImage:image forState:UIControlStateNormal]; - [button setTitle:[LocalizationHelper localizedStringForKey:@" Add Host"] forState:UIControlStateNormal]; // 注意空格用于间隔 + button.imageEdgeInsets = GenericUtils.liquidGlassEnabled ? UIEdgeInsetsMake(0, 7.6, 0.75, 0) : UIEdgeInsetsZero;; + NSString* buttonStringHead = GenericUtils.liquidGlassEnabled ? @" " : @""; + [button setTitle: [buttonStringHead stringByAppendingString: + [LocalizationHelper localizedStringForKey:@" Add Host"]] + forState:UIControlStateNormal]; // 注意空格用于间隔 } else { [button setTitle:[LocalizationHelper localizedStringForKey:@"Add Host"] forState:UIControlStateNormal]; // 注意空格用于间隔 } // [button setTitleColor:UIColor.whiteColor forState:UIControlStateNormal]; - button.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium]; + button.titleLabel.font = [UIFont systemFontOfSize:GenericUtils.liquidGlassEnabled ? 17 : 16 weight:UIFontWeightMedium]; // 文字颜色设置为 tintColor 控制 - button.tintColor = UIColor.whiteColor; + if(GenericUtils.liquidGlassEnabled) button.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0.9, 0); + button.tintColor = GenericUtils.liquidGlassEnabled ? [ThemeManager appPrimaryColor] : UIColor.whiteColor; [button setTitleColor:button.tintColor forState:UIControlStateNormal]; + // button.tintColor = UIColor.whiteColor; + // [button setTitleColor:button.tintColor forState:UIControlStateNormal]; // 设置按下时的 tintColor(变灰或淡) UIColor *highlightColor = [ThemeManager textColorGray]; @@ -1420,6 +1434,7 @@ static NSMutableSet* hostList; // 创建 UIBarButtonItem UIBarButtonItem *barItem = [[UIBarButtonItem alloc] initWithCustomView:button]; + return barItem; } @@ -1433,7 +1448,9 @@ static NSMutableSet* hostList; // 设置图标(SF Symbol) if (@available(iOS 13.0, *)) { - UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:buttonHeight*0.85 weight:UIImageSymbolWeightRegular]; + UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize: + GenericUtils.liquidGlassEnabled ? buttonHeight*0.73 : buttonHeight*0.85 + weight:UIImageSymbolWeightRegular]; UIImage *image = [UIImage systemImageNamed:@"questionmark.circle" withConfiguration:config]; [button setImage:image forState:UIControlStateNormal]; [button setTitle:@"" forState:UIControlStateNormal]; // 注意空格用于间隔 @@ -1486,19 +1503,17 @@ static NSMutableSet* hostList; - (void)setupNavBar{ if (@available(iOS 13.0, *)) { - Class appearanceClass = NSClassFromString(@"UINavigationBarAppearance"); - navBarAppearanceStandard = [[appearanceClass alloc] init]; - [navBarAppearanceStandard performSelector:@selector(configureWithOpaqueBackground)]; // 不透明 - [navBarAppearanceStandard setValue:[ThemeManager hostViewBackgroundColor] forKey:@"backgroundColor"]; // 设置你需要的背景色 - [navBarAppearanceStandard setValue:nil forKey:@"shadowColor"]; // 设置你需要的背景色 + UINavigationBarAppearance* appearance = [[UINavigationBarAppearance alloc] init]; + [appearance configureWithOpaqueBackground]; + appearance.backgroundColor = [ThemeManager hostViewBackgroundColor]; + appearance.shadowColor = nil; NSDictionary* titleTextAttributes = @{ NSForegroundColorAttributeName: [ThemeManager textColor] }; - [navBarAppearanceStandard setValue:titleTextAttributes forKey:@"titleTextAttributes"]; - [navBarAppearanceStandard setValue:[UIColor clearColor] forKey:@"shadowColor"]; - [navBarAppearanceStandard setValue:nil forKey:@"backgroundImage"]; - - //navBarAppearanceStandard.backgroundImage = nil; + appearance.titleTextAttributes = titleTextAttributes; + appearance.shadowColor = [UIColor clearColor]; + appearance.backgroundImage = nil; + navBarAppearanceStandard = appearance; } [self applyNavBarAppearance]; @@ -1516,10 +1531,10 @@ static NSMutableSet* hostList; [_settingsButton setAction:@selector(revealToggle:)]; if (@available(iOS 13.0, *)) { [_settingsButton setTitle:nil]; - UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:23 weight:UIImageSymbolWeightMedium ]; + UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:GenericUtils.liquidGlassEnabled ? 18 : 23 weight:UIImageSymbolWeightMedium ]; UIImage *image = [[UIImage systemImageNamed:@"sidebar.left" withConfiguration:config] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [_settingsButton setImage:image]; - _settingsButton.imageInsets = UIEdgeInsetsMake(10, 10, 0, 0); + _settingsButton.imageInsets = GenericUtils.liquidGlassEnabled ? UIEdgeInsetsMake(0, 0, 0, 0.55) : UIEdgeInsetsMake(10, 10, 0, 0); } else { [_settingsButton setTitle:[LocalizationHelper localizedStringForKey:@"Settings"]]; } @@ -1530,9 +1545,10 @@ static NSMutableSet* hostList; // Set the host name button action. When it's tapped, it'll show the host selection view. _upButton = [[UIBarButtonItem alloc] init]; + if (@available(iOS 13.0, *)) { [_upButton setTitle:@""]; - UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:21.5 weight:UIImageSymbolWeightMedium ]; + UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:21.5 weight:UIImageSymbolWeightMedium]; UIImage *image = [[UIImage systemImageNamed:@"tv" withConfiguration:config] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [_upButton setImage:image]; _upButton.imageInsets = UIEdgeInsetsMake(25, 20, 0, 15); @@ -1551,16 +1567,18 @@ static NSMutableSet* hostList; self.collectionView.backgroundColor = [ThemeManager hostViewBackgroundColor]; if (@available(iOS 13.0, *)) { - [navBarAppearanceStandard setValue:[ThemeManager hostViewBackgroundColor] forKey:@"backgroundColor"]; + UINavigationBarAppearance* appearance = navBarAppearanceStandard; + appearance.backgroundColor = [ThemeManager hostViewBackgroundColor]; NSDictionary* titleTextAttributes = @{ NSForegroundColorAttributeName: [ThemeManager textColor] }; - [navBarAppearanceStandard setValue:titleTextAttributes forKey:@"titleTextAttributes"]; + appearance.titleTextAttributes = titleTextAttributes; + navBarAppearanceStandard = appearance; } _settingsButton.tintColor = [ThemeManager appPrimaryColor]; _upButton.tintColor = [ThemeManager appPrimaryColor]; - ((UIButton*)_addHostButton.customView).backgroundColor = [ThemeManager appPrimaryColor]; + ((UIButton*)_addHostButton.customView).backgroundColor = GenericUtils.liquidGlassEnabled ? UIColor.clearColor : [ThemeManager appPrimaryColor]; ((UIButton*)_helpButton.customView).tintColor = [ThemeManager appPrimaryColor]; [self applyNavBarAppearance];