From ecacb1afa7edd7aa0de07b647521b090fd8238bd Mon Sep 17 00:00:00 2001 From: True Zhuanjia Date: Wed, 22 Apr 2026 23:53:19 +0800 Subject: [PATCH 1/3] lower iOS compatibility fixes --- .../Contents.json | 21 +++++++++++++++ .../gamecontroller.circle.svg | 12 +++++++++ .../Contents.json | 21 +++++++++++++++ .../gamecontroller.circle.tag.svg | 12 +++++++++ VoidLink/Input/OnScreenWidgetView.swift | 13 +++++++--- VoidLink/Localization/Localizable.xcstrings | 2 +- VoidLink/Utility/GenericUtils.swift | 5 ++++ .../ViewControllers/MainFrameViewController.m | 26 ++++++++++++++----- .../ViewControllers/SettingsViewController.m | 14 +++++++--- 9 files changed, 111 insertions(+), 15 deletions(-) create mode 100644 VoidLink/Images.xcassets/gamecontroller.circle.regular.imageset/Contents.json create mode 100644 VoidLink/Images.xcassets/gamecontroller.circle.regular.imageset/gamecontroller.circle.svg create mode 100644 VoidLink/Images.xcassets/gamecontroller.circle.tag.imageset/Contents.json create mode 100644 VoidLink/Images.xcassets/gamecontroller.circle.tag.imageset/gamecontroller.circle.tag.svg diff --git a/VoidLink/Images.xcassets/gamecontroller.circle.regular.imageset/Contents.json b/VoidLink/Images.xcassets/gamecontroller.circle.regular.imageset/Contents.json new file mode 100644 index 00000000..bb128f53 --- /dev/null +++ b/VoidLink/Images.xcassets/gamecontroller.circle.regular.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "gamecontroller.circle.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/VoidLink/Images.xcassets/gamecontroller.circle.regular.imageset/gamecontroller.circle.svg b/VoidLink/Images.xcassets/gamecontroller.circle.regular.imageset/gamecontroller.circle.svg new file mode 100644 index 00000000..71c89fea --- /dev/null +++ b/VoidLink/Images.xcassets/gamecontroller.circle.regular.imageset/gamecontroller.circle.svg @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/VoidLink/Images.xcassets/gamecontroller.circle.tag.imageset/Contents.json b/VoidLink/Images.xcassets/gamecontroller.circle.tag.imageset/Contents.json new file mode 100644 index 00000000..d9d22101 --- /dev/null +++ b/VoidLink/Images.xcassets/gamecontroller.circle.tag.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "gamecontroller.circle.tag.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/VoidLink/Images.xcassets/gamecontroller.circle.tag.imageset/gamecontroller.circle.tag.svg b/VoidLink/Images.xcassets/gamecontroller.circle.tag.imageset/gamecontroller.circle.tag.svg new file mode 100644 index 00000000..f3971993 --- /dev/null +++ b/VoidLink/Images.xcassets/gamecontroller.circle.tag.imageset/gamecontroller.circle.tag.svg @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index 65334486..0b70e06b 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -824,6 +824,9 @@ import ObjectiveC.runtime private func setSquareWidgetCornerRadius(){ let shortSideLen = min(self.layer.bounds.size.width, self.layer.bounds.size.height) self.layer.cornerRadius = shortSideLen/2 < 16 ? shortSideLen/3.2 : 16 + if #available(iOS 13.0, *) { + self.layer.cornerCurve = .continuous + } } func containsNonLatin(_ text: String) -> Bool { @@ -866,6 +869,7 @@ import ObjectiveC.runtime ) label.attributedText = attr label.textAlignment = .center + label.baselineAdjustment = .alignCenters } private func setupView() { @@ -900,7 +904,6 @@ import ObjectiveC.runtime self.translatesAutoresizingMaskIntoConstraints = true // this is mandatory to prevent unexpected key view location change - self.setSquareWidgetCornerRadius() self.layer.borderWidth = self.borderWidth self.tweakAlpha(tweakBorderAlpha: false, tweakLabelAlpha: false) @@ -927,7 +930,7 @@ import ObjectiveC.runtime label.isHidden = self.widgetLabel.uppercased() == self.touchPadString // allow touchPad label to be display if it's different from touchPad cmdString } } - + if !self.functionalButtonString.isEmpty{ self.layer.borderWidth = self.borderWidth } @@ -935,11 +938,10 @@ import ObjectiveC.runtime if self.shape == "round" { //setup round buttons self.layer.cornerRadius = self.frame.width/2 - // self.layer.borderWidth = self.borderWidth label.minimumScaleFactor = 0.15 // Adjust the scale factor for oscButtons } if self.shape == "square" || self.shape == "largeSquare" { - //just do nothing here + self.setSquareWidgetCornerRadius() } @@ -1809,6 +1811,9 @@ import ObjectiveC.runtime buttonDownVisualEffectLayer.borderColor = standardHighlightColor.cgColor buttonDownVisualEffectLayer.frame = self.bounds.insetBy(dx: -buttonDownVisualEffectLayer.borderWidth, dy: -buttonDownVisualEffectLayer.borderWidth) // Adjust the inset as needed buttonDownVisualEffectLayer.cornerRadius = self.layer.cornerRadius + buttonDownVisualEffectLayer.borderWidth + if self.shape == "square" {if #available(iOS 13.0, *) { + buttonDownVisualEffectLayer.cornerCurve = .continuous + }} buttonDownVisualEffectLayer.backgroundColor = UIColor.clear.cgColor; buttonDownVisualEffectLayer.fillColor = UIColor.clear.cgColor; diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index e8125a33..d7c9f075 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -5465,7 +5465,7 @@ }, "zh-Hant" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "- 螢幕控制項佈局(包含虛擬手把、虛擬按鍵等),以及設定選單中標記 [手把圖示] 的選項(如體感控制參數),皆保存在遊戲配置中。\n\n- 串流過程中,由螢幕 %@ 邊緣向內滑動 %@ 螢幕寬度,即可在工具箱選單中開啟[切換遊戲配置]" } }, diff --git a/VoidLink/Utility/GenericUtils.swift b/VoidLink/Utility/GenericUtils.swift index 01241d51..ee84bb9f 100644 --- a/VoidLink/Utility/GenericUtils.swift +++ b/VoidLink/Utility/GenericUtils.swift @@ -248,4 +248,9 @@ import Foundation @objc static var screenHeight: CGFloat { return UIScreen.main.bounds.height } + + @objc static var iOS18Available: Bool { + if #available(iOS 18.0, *) {return true} + else {return false} + } } diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index 2757cbbe..c09cf510 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -1613,16 +1613,28 @@ static NSMutableSet* hostList; [_profilesButton setAction:@selector(profilesButtonTapped)]; if (@available(iOS 13.0, *)) { [_profilesButton setTitle:nil]; - UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:GenericUtils.liquidGlassEnabled ? 20.5 : 23 weight:UIImageSymbolWeightRegular ]; - UIImage *image = [[UIImage systemImageNamed:@"gamecontroller.circle" withConfiguration:config] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - [_profilesButton setImage:image]; - _profilesButton.imageInsets = GenericUtils.liquidGlassEnabled ? UIEdgeInsetsMake(0, 0, 0, 0.55) : UIEdgeInsetsMake(10, 10, 0, 0); - if(GenericUtils.liquidGlassEnabled){ - // if(@available(iOS 26.0, *)) _settingsButton.hidesSharedBackground = YES; + + UIImageSymbolConfiguration *config; + UIImage *image; + if(GenericUtils.iOS18Available){ + config = [UIImageSymbolConfiguration configurationWithPointSize:GenericUtils.liquidGlassEnabled ? 20.5 : 23 weight:UIImageSymbolWeightRegular ]; + image = [[UIImage systemImageNamed: @"gamecontroller.circle" withConfiguration:config] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + [_profilesButton setImage:image]; + _profilesButton.imageInsets = GenericUtils.liquidGlassEnabled ? UIEdgeInsetsMake(0, 0, 0, 0.55) : UIEdgeInsetsMake(10, 10, 0, 0); + if(GenericUtils.liquidGlassEnabled){ + _profilesButton.tintColor = ThemeManager.appPrimaryColor; + } + } + else{ + image = [[UIImage imageNamed: @"gamecontroller.circle.regular"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + [_profilesButton setImage:image]; + _profilesButton.imageInsets = UIEdgeInsetsMake(-1.5, 0, -1.5, 0); _profilesButton.tintColor = ThemeManager.appPrimaryColor; } + + } else { - [_profilesButton setTitle:[LocalizationHelper localizedStringForKey:@"Settings"]]; + [_profilesButton setTitle:[LocalizationHelper localizedStringForKey:@"Game Profile"]]; } if (@available(iOS 26.0, *)) { diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index 37fb98fa..b5bc10a0 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -1507,15 +1507,23 @@ BOOL isCustomResolution(int resolutionSelected) { button = [UIButton buttonWithType:UIButtonTypeSystem]; if (@available(iOS 13.0, *)) { if(stack.isGameProfileSetting){ - UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:14.5 weight:UIImageSymbolWeightRegular]; - [button setImage:[UIImage systemImageNamed:@"gamecontroller.circle" withConfiguration:config] forState:UIControlStateNormal]; + UIImage* image; + if(GenericUtils.iOS18Available){ + UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:14.5 weight:UIImageSymbolWeightRegular]; + image = [UIImage systemImageNamed:@"gamecontroller.circle" withConfiguration:config]; + } + else{ + image = [[UIImage imageNamed: @"gamecontroller.circle.tag"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + } + + [button setImage:image forState:UIControlStateNormal]; } else{ UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:13.5 weight:UIImageSymbolWeightMedium]; [button setImage:[UIImage systemImageNamed:@"info.circle" withConfiguration:config] forState:UIControlStateNormal]; } } else { - [button setTitle:@"info" forState:UIControlStateNormal]; + [button setTitle:stack.isGameProfileSetting ? @"game" : @"info" forState:UIControlStateNormal]; button.titleLabel.font = [UIFont systemFontOfSize:13 weight:UIFontWeightBold]; button.titleLabel.accessibilityIdentifier = @"infoButton"; } From 4dc96ba189b3f4a57988d5777ba6b2a3a90d699f Mon Sep 17 00:00:00 2001 From: True Zhuanjia Date: Thu, 23 Apr 2026 15:14:33 +0800 Subject: [PATCH 2/3] widget alpha tweak fix --- VoidLink/Input/OnScreenWidgetView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index 0b70e06b..eba6f87b 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -694,12 +694,12 @@ import ObjectiveC.runtime private func tweakAlpha(tweakBorderAlpha:Bool, tweakLabelAlpha:Bool = true){ // setup default border from self.backgroundAlpha - let realBackgroundAlpha = abs(self.backgroundAlpha) - 0.18 // offset to be consistent with legacy onScreen controller layer opacity + let realBackgroundAlpha = max(abs(self.backgroundAlpha) - 0.18, 0) // offset to be consistent with legacy onScreen controller layer opacity self.backgroundColor = UIColor(white:self.backgroundAlpha>0 ? 0.1 : 0.9, alpha: realBackgroundAlpha) // offset to be consistent with legacy onScreen controller layer opacity if tweakBorderAlpha { - borderAlpha = realBackgroundAlpha * 1.01 - defaultBorderColor = UIColor(white: self.backgroundAlpha>0 ? 0.1 : 0.9, alpha: abs(borderAlpha)).cgColor + borderAlpha = realBackgroundAlpha * 1.005 * (self.backgroundAlpha>0 ? 1 : -1) + defaultBorderColor = UIColor(white: borderAlpha>0 ? 0.1 : 0.9, alpha: abs(borderAlpha)).cgColor self.layer.borderColor = defaultBorderColor } From 5ee443337cbede765c66d39ed7e2c837a25400d3 Mon Sep 17 00:00:00 2001 From: True Zhuanjia Date: Thu, 23 Apr 2026 17:09:53 +0800 Subject: [PATCH 3/3] autoDock and gesture interaction fixes --- VoidLink/Input/OnScreenWidgetView.swift | 5 +++++ .../ViewControllers/StreamFrameViewController.m | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index eba6f87b..84bf585a 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -19,6 +19,7 @@ import ObjectiveC.runtime @objc public static var mapping: [Int16:OnScreenWidgetView] = [:] @objc public static var isRestoring: Bool = false @objc public static var hasRestoredFromAutoDock: Bool = false + @objc public static var autoDockRestoreInitByViewResize: Bool = false @objc public static func set(widget:OnScreenWidgetView, for key:Int16) { mapping[key] = widget } @@ -3384,6 +3385,10 @@ import ObjectiveC.runtime completion: {_ in self.autoDockIsDocked = false self.restartAutoDockCountdown() + if OnScreenWidgetView.autoDockRestoreInitByViewResize { + OnScreenWidgetView.autoDockRestoreInitByViewResize = false + OnScreenWidgetView.hasRestoredFromAutoDock = false + } } ) } diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index a88793e9..87145785 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -551,6 +551,11 @@ name:UIKeyboardWillHideNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(keyboardDidHide) + name:UIKeyboardDidHideNotification + object:nil]; + [safeTimer start]; #endif } @@ -767,6 +772,11 @@ [_streamView keyboardWillHide]; } +- (void)keyboardDidHide{ + _streamView.bounds = _deviceWindow.bounds; + _streamView.frame = _deviceWindow.frame; +} + - (void)handleAbnormalKeyboards:(NSNotification *)notification{ [_streamView handleAbnormalKeyboards:notification]; } @@ -1757,7 +1767,10 @@ } dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)); dispatch_after(delayTime, dispatch_get_main_queue(), ^{ - if(widget.autoDockEnabled) [widget restoreFromAutoDockWithAnimated:true]; + if(widget.autoDockEnabled){ + OnScreenWidgetView.autoDockRestoreInitByViewResize = true; + [widget restoreFromAutoDockWithAnimated:true]; + } }); [coordinator animateAlongsideTransition:^(id _Nonnull context) {