diff --git a/.gitignore b/.gitignore index 634eb150..bca4bde6 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ Build DerivedData *.xccheckout -.github \ No newline at end of file +.github +README.md \ No newline at end of file diff --git a/README.md b/README.md index 6580a883..5f1ba5ed 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,42 @@ -# Moonlight iOS/tvOS +# VoidLink 已上架App Store. +# VoidLink is available on App Store +### [https://apps.apple.com/app/voidlink/id6747717070](https://apps.apple.com/cn/app/voidlink/id6747717070) -[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/kwv8vpwr457lqn25/branch/master?svg=true)](https://ci.appveyor.com/project/cgutman/moonlight-ios/branch/master) +
-[Moonlight for iOS/tvOS](https://moonlight-stream.org) is an open source client for [Sunshine](https://github.com/LizardByte/Sunshine) and NVIDIA GameStream. Moonlight for iOS/tvOS allows you to stream your full collection of games and apps from your powerful desktop computer to your iOS device or Apple TV. +# 代码 Coding +- 代码提交在`Integration`分支。 +- For latest coding commits, go to branch `Integration`. -Moonlight also has a [PC client](https://github.com/moonlight-stream/moonlight-qt) and [Android client](https://github.com/moonlight-stream/moonlight-android). +
-Check out [the Moonlight wiki](https://github.com/moonlight-stream/moonlight-docs/wiki) for more detailed project information, setup guide, or troubleshooting steps. +# 关于VoidLink. About VoidLink +- 本项目最初基于开源项目 [moonlight-iOS] fork 而来。在此基础上,True砖家(True Zhuanjia)@ Bilibili 及其他社区开发者对项目进行了大量重构、重新设计与功能扩展,包括全新的用户界面和显著增强的功能特性。
我们对 moonlight-iOS 开发者的开创性工作表示衷心感谢。

+- VoidLink was originally forked from the open-source project [moonlight-iOS], but has since been extensively reworked, redesigned, and expanded by True砖家 (True Zhuanjia) @ Bilibili and other community developers. These contributions include a completely new user interface and significant enhancements to the application's functionality.
We gratefully acknowledge the foundational work of the moonlight-iOS developers. -[![Moonlight for iOS and tvOS](https://moonlight-stream.org/images/App_Store_Badge_135x40.svg)](https://apps.apple.com/us/app/moonlight-game-streaming/id1000551566) +
-## Building -* Install Xcode from the [App Store page](https://apps.apple.com/us/app/xcode/id497799835) -* Run `git clone --recursive https://github.com/moonlight-stream/moonlight-ios.git` - * If you've already clone the repo without `--recursive`, run `git submodule update --init --recursive` -* Open Moonlight.xcodeproj in Xcode -* To run on a real device, you will need to locally modify the signing options: - * Click on "Moonlight" at the top of the left sidebar - * Click on the "Signing & Capabilities" tab - * Under "Targets", select "Moonlight" (for iOS/iPadOS) or "Moonlight TV" (for tvOS) - * In the "Team" dropdown, select your name. If your name doesn't appear, you may need to sign into Xcode with your Apple account. - * Change the "Bundle Identifier" to something different. You can add your name or some random letters to make it unique. - * Now you can select your Apple device in the top bar as a target and click the Play button to run. +# App Store 分发一次性收费声明. +# Statement on One-Time App Store Distribution Fee. +- VoidLink 的 App Store 安装费用用于覆盖通过 Apple 框架进行安全可靠应用分发的成本。同时也支持项目维护者进行持续的开发、维护,以及公众用户访问并下载官方签名版本。感谢您对项目的支持,帮助我们持续改进和优化。

+- The App Store fee for VoidLink covers the cost of secure and trusted distribution using Apple's infrastructure. It supports ongoing development, maintenance, and access to the official signed build. Thank you for supporting the project and helping sustain ongoing improvements. + +
+ +# 开发者B站号. Developer on Bilibili + +如果你在用Bilibili, 请关注`True砖家`,了解该fork的最新消息。
+If you are on Bilibili, subscribe `True砖家` to get the latest news of this fork:
+https://b23.tv/A0F9v7n + +
+ +# 贡献者 Contributors +[@TrueZhuangJia](https://github.com/TrueZhuangJia)
+[@All contributors from moonlight-iOS](https://github.com/moonlight-stream/moonlight-ios/graphs/contributors)
+[@stefanilijev97](https://github.com/stefanilijev97/stefanilijev97)
+[@Acaki](https://github.com/Acaki)
+[@seastwood](https://github.com/seastwood)
+[@Danos0100](https://github.com/Danos0100)
+[@xzzpig](https://github.com/xzzpig)
+[@King0fSpace](https://github.com/King0fSpace)
diff --git a/VoidLink/HostCardView.m b/VoidLink/HostCardView.m index ff4852e2..692b7b07 100644 --- a/VoidLink/HostCardView.m +++ b/VoidLink/HostCardView.m @@ -521,6 +521,9 @@ static const float REFRESH_CYCLE = 2.0f; _hostNameLabel.text = host.name; backgroundLayer.hidden = !(host.state == StateOnline && host.pairState == PairStatePaired); + BOOL hostPaired = host.pairState == PairStatePaired; + bool hasValidMac = host.mac != nil && ![host.mac isEqualToString:@"00:00:00:00:00:00"]; + switch (host.state) { case StateOnline: [_hostSpinner stopAnimating]; @@ -576,8 +579,6 @@ static const float REFRESH_CYCLE = 2.0f; self.statusIcon.hidden = YES; } - bool hostPaired = host.pairState == PairStatePaired; - _hostIconView.tintColor = [ThemeManager lowProfileGray]; lockIconView.tintColor = [ThemeManager lowProfileGray]; lockIconView.hidden = hostPaired; @@ -589,14 +590,14 @@ static const float REFRESH_CYCLE = 2.0f; if (@available(iOS 13.0, *)) { UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:buttonHeight/3.45 weight:UIImageSymbolWeightBold]; UIImage *templateImage = [UIImage systemImageNamed:@"power" withConfiguration:config]; - UIImage *coloredImage = [templateImage imageWithTintColor:hostPaired ? defaultBlue : [ThemeManager textColorGray] renderingMode:UIImageRenderingModeAlwaysOriginal]; + UIImage *coloredImage = [templateImage imageWithTintColor:hasValidMac ? defaultBlue : [ThemeManager textColorGray] renderingMode:UIImageRenderingModeAlwaysOriginal]; [self.wakeupButton setImage:coloredImage forState:UIControlStateNormal]; } else { // Fallback on earlier versions } - self.wakeupButton.backgroundColor = hostPaired ? [ThemeManager textTintColorWithAlpha] : [[ThemeManager textColorGray] colorWithAlphaComponent:0.2]; - [self.wakeupButton setTitleColor: hostPaired ? defaultBlue : [ThemeManager textColorGray] forState:UIControlStateNormal]; + self.wakeupButton.backgroundColor = hasValidMac ? [ThemeManager textTintColorWithAlpha] : [[ThemeManager textColorGray] colorWithAlphaComponent:0.2]; + [self.wakeupButton setTitleColor: hasValidMac ? defaultBlue : [ThemeManager textColorGray] forState:UIControlStateNormal]; break; case StateUnknown: @@ -620,17 +621,16 @@ static const float REFRESH_CYCLE = 2.0f; _pairButton.hidden = YES; _wakeupButton.hidden = NO; - [_wakeupButton setTitleColor:[ThemeManager textColorGray] forState:UIControlStateNormal]; // theme if (@available(iOS 13.0, *)) { UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:buttonHeight/3.45 weight:UIImageSymbolWeightBold]; UIImage *templateImage = [UIImage systemImageNamed:@"power" withConfiguration:config]; - UIImage *coloredImage = [templateImage imageWithTintColor:[ThemeManager textColorGray] renderingMode:UIImageRenderingModeAlwaysOriginal]; + UIImage *coloredImage = [templateImage imageWithTintColor:hasValidMac ? defaultBlue : [ThemeManager textColorGray] renderingMode:UIImageRenderingModeAlwaysOriginal]; [self.wakeupButton setImage:coloredImage forState:UIControlStateNormal]; } else { // Fallback on earlier versions } - self.wakeupButton.backgroundColor = [[ThemeManager textColorGray] colorWithAlphaComponent:0.2]; - [self.wakeupButton setTitleColor:[ThemeManager textColorGray] forState:UIControlStateNormal]; + self.wakeupButton.backgroundColor = hasValidMac ? [ThemeManager textTintColorWithAlpha] : [[ThemeManager textColorGray] colorWithAlphaComponent:0.2]; + [self.wakeupButton setTitleColor: hasValidMac ? defaultBlue : [ThemeManager textColorGray] forState:UIControlStateNormal]; break; default:break; } diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index 1a2b2f5b..30fa6670 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -1503,25 +1503,25 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/uscwmv3vz8g30rmk?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/uscwmv3vz8g30rmk" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3" } } } @@ -1751,25 +1751,25 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bu3hki4pezy93g6r?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bu3hki4pezy93g6r" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1" } } } @@ -2068,25 +2068,25 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/uscwmv3vz8g30rmk?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/uscwmv3vz8g30rmk" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3" } } } @@ -2500,25 +2500,25 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zdf5l29s3vohs6iq?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zdf5l29s3vohs6iq" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01" } } } @@ -3131,22 +3131,28 @@ "onScreenWidgetStackDoc" : { "extractionState" : "manual", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/dcke52amz8ay3g37" + } + }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747" } } } @@ -3353,24 +3359,24 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "- Enable auto picture-in-picture by switching on Settings -> Multitasking & Gestures -> Start PiP Automatically.\n\n- PiP can be manually started in toolbox menu.\n\n- PiP connection timeout is also defined by “Background Session” settings in “Others” section\n\n-PiP is available for iOS15+" + "value" : "- Enable auto picture-in-picture by switching on Settings -> Multitasking & Gestures -> Start PiP Automatically.\n\n- PiP can be manually started in toolbox menu.\n\n- PiP connection timeout is also defined by “Background Session” settings in “Others” section\n\n- PiP is available for iOS15+" } }, "zh-Hans" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "- App切后台时自动开启画中画, 需要打开 iOS 设置-> 多任务与手势 -> 自动开启画中画。\n\n- 画中画可在工具箱菜单中手动开启。\n\n- 画中画时长由 “其他 -> 后台会话” 设置项控制\n\n- 仅 iOS15+ 支持画中画。" } }, "zh-Hant" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "- App 切到背景時會自動啟用子母畫面,需開啟 iOS 「設定」→「多工與手勢」→「自動啟用子母畫面」。\n\n- 子母畫面也可透過工具箱選單手動啟用。\n\n- 子母畫面顯示時長由「其他」→「背景執行」設定項目控制。\n\n- 子母畫面功能自 iOS 15 起支援。" } }, "zh-HK" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "- App 切到背景時會自動啟用子母畫面,需開啟 iOS 「設定」→「多工與手勢」→「自動啟用子母畫面」。\n\n- 子母畫面也可透過工具箱選單手動啟用。\n\n- 子母畫面顯示時長由「其他」→「背景執行」設定項目控制。\n\n- 子母畫面功能自 iOS 15 起支援。" } } @@ -3399,6 +3405,35 @@ } } }, + "pointerVelocityDividerStackDoc" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ct0xcq77u6pgyvxy" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/qt0gthxxnr94b3sz" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/qt0gthxxnr94b3sz" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/qt0gthxxnr94b3sz" + } + } + } + }, "pointerVelocityDividerStackTip" : { "extractionState" : "manual", "localizations" : { @@ -3428,6 +3463,35 @@ } } }, + "pointerVelocityFactorStackDoc" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ct0xcq77u6pgyvxy" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/qt0gthxxnr94b3sz" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/qt0gthxxnr94b3sz" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/qt0gthxxnr94b3sz" + } + } + } + }, "pointerVelocityFactorStackTip" : { "extractionState" : "manual", "localizations" : { @@ -4309,25 +4373,25 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/rgc9if4xrf352555?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/rgc9if4xrf352555" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f" } } } @@ -4413,25 +4477,25 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/dhmg3h27ls8mwt4a?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/dhmg3h27ls8mwt4a" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp" } } } diff --git a/VoidLink/ViewControllers/CustomEdgeSlideGestureRecognizer.m b/VoidLink/ViewControllers/CustomEdgeSlideGestureRecognizer.m index 858c0486..af0e2a39 100644 --- a/VoidLink/ViewControllers/CustomEdgeSlideGestureRecognizer.m +++ b/VoidLink/ViewControllers/CustomEdgeSlideGestureRecognizer.m @@ -14,11 +14,11 @@ @implementation CustomEdgeSlideGestureRecognizer UITouch* capturedUITouch; CGFloat startPointX; -static CGFloat screenWidthInPoints; +//static CGFloat screenWidthInPoints; - (instancetype)initWithTarget:(nullable id)target action:(nullable SEL)action { self = [super initWithTarget:target action:action]; - screenWidthInPoints = CGRectGetWidth([UIApplication.sharedApplication.windows.firstObject.screen bounds]); // Get the screen's bounds (in points) +// screenWidthInPoints = CGRectGetWidth([UIApplication.sharedApplication.windows.firstObject.screen bounds]); // Get the screen's bounds (in points) _immediateTriggering = false; _EDGE_TOLERANCE = 10.0f; return self; @@ -29,6 +29,7 @@ static CGFloat screenWidthInPoints; UITouch *touch = [touches anyObject]; capturedUITouch = touch; startPointX = [capturedUITouch locationInView:self.view].x; + CGFloat streamFrameViewWidthInPoints = self.view.frame.size.width; if(_immediateTriggering){ if(self.edges & UIRectEdgeLeft){ @@ -38,7 +39,7 @@ static CGFloat screenWidthInPoints; // NSLog(@"startPointX %f , normalizedGestureDeltaX %f", startPointX, normalizedGestureDistance); } if(self.edges & UIRectEdgeRight){ - if(startPointX > screenWidthInPoints - _EDGE_TOLERANCE){ + if(startPointX > streamFrameViewWidthInPoints - _EDGE_TOLERANCE){ self.state = UIGestureRecognizerStateEnded; } // NSLog(@"startPointX %f , normalizedGestureDeltaX %f", startPointX, normalizedGestureDistance); @@ -52,6 +53,7 @@ static CGFloat screenWidthInPoints; if([touches containsObject:capturedUITouch]){ CGFloat _endPointX = [capturedUITouch locationInView:self.view].x; + CGFloat screenWidthInPoints = self.view.frame.size.width; CGFloat normalizedGestureDistance = fabs(_endPointX - startPointX)/screenWidthInPoints; if(self.edges & UIRectEdgeLeft){ diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index 8b847391..25a389ad 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -255,6 +255,10 @@ selector: @selector(handleReturnToForeground) name: UIApplicationDidBecomeActiveNotification object: nil]; + [[NSNotificationCenter defaultCenter] addObserver: self + selector: @selector(handleEnterBackground) + name: UIApplicationWillResignActiveNotification + object: nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange) // handle orientation change since i made portrait mode available name:UIDeviceOrientationDidChangeNotification @@ -267,24 +271,7 @@ #pragma mark - Class Helper Functions -- (void)handleReturnToForeground { - [OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager -} - -- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator{ - viewWillBeResized = true; - [self clearStickIndicator]; - if(!_quickSwitchEnabled) [self saveTapped:nil]; -} - -- (void)deviceOrientationDidChange{ - [self performSelector:@selector(handleOrientationChangeForOnScreenWidgets) withObject:self afterDelay:0.05]; -} - -- (void)handleOrientationChangeForOnScreenWidgets{ - if(!viewWillBeResized) return; - [self setupWidgetPanel]; - +- (void)updateViewBounds{ viewWillBeResized = false; selectedWidgetView = nil; selectedControllerLayer = nil; @@ -295,6 +282,33 @@ [self reloadLegacyOnScreenControls]; } +- (void)handleEnterBackground{ + [self saveTapped:nil]; +} + +- (void)handleReturnToForeground { + // [OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager + [self setupWidgetPanel]; + [self updateViewBounds]; +} + +- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator{ + if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive) return; + viewWillBeResized = true; + [self clearStickIndicator]; + if(!_quickSwitchEnabled) [self saveTapped:nil]; +} + +- (void)deviceOrientationDidChange{ + [self performSelector:@selector(handleOrientationChangeForOnScreenWidgets) withObject:self afterDelay:0.0]; +} + +- (void)handleOrientationChangeForOnScreenWidgets{ + if(!viewWillBeResized) return; + [self setupWidgetPanel]; + [self updateViewBounds]; +} + /* fades the 'Undo Button' in or out depending on whether the user has any OSC layout changes to undo */ - (void) OSCLayoutChanged { if ([self.layoutOSC.layoutChanges count] > 0) { diff --git a/VoidLink/ViewControllers/MainFrameViewController.h b/VoidLink/ViewControllers/MainFrameViewController.h index 8d978cd8..1f11da5a 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.h +++ b/VoidLink/ViewControllers/MainFrameViewController.h @@ -34,5 +34,6 @@ -(void)reloadStreamConfig; -(bool)isIPhonePortrait; #endif +- (void)fillResolutionTable:(CGSize*)resolutionTable externalDisplayMode:(NSInteger)externalDisplayMode; @end diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index 461af22b..c6866950 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -409,19 +409,22 @@ static NSMutableSet* hostList; - (void)wakeupButtonTappedForHost:(TemporaryHost *)host{ _selectedHost = host; - if (host.state == StateOffline && host.pairState == PairStatePaired) { - UIAlertController* wolAlert = [UIAlertController alertControllerWithTitle:[LocalizationHelper localizedStringForKey:@"Wake-On-LAN"] message:@"" preferredStyle:UIAlertControllerStyleAlert]; - [wolAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Ok"] style:UIAlertActionStyleDefault handler:nil]]; - if (host.mac == nil || [host.mac isEqualToString:@"00:00:00:00:00:00"]) { - wolAlert.message = [LocalizationHelper localizedStringForKey: @"Host MAC unknown, unable to send WOL Packet"]; - } else { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [WakeOnLanManager wakeHost:host]; - }); - wolAlert.message = [LocalizationHelper localizedStringForKey:@"Successfully sent wake-up request. It may take a few moments for the PC to wake. If it never wakes up, ensure it's properly configured for Wake-on-LAN."]; - } - [[self activeViewController] presentViewController:wolAlert animated:YES completion:nil]; + bool hasValidMac = host.mac != nil && ![host.mac isEqualToString:@"00:00:00:00:00:00"]; + + //if (hasValidMac) { + UIAlertController* wolAlert = [UIAlertController alertControllerWithTitle:[LocalizationHelper localizedStringForKey:@"Wake-On-LAN"] message:@"" preferredStyle:UIAlertControllerStyleAlert]; + [wolAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Ok"] style:UIAlertActionStyleDefault handler:nil]]; + + if (!hasValidMac) { + wolAlert.message = [LocalizationHelper localizedStringForKey: @"Host MAC unknown, unable to send WOL Packet"]; + } else { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [WakeOnLanManager wakeHost:host]; + }); + wolAlert.message = [LocalizationHelper localizedStringForKey:@"Successfully sent wake-up request. It may take a few moments for the PC to wake. If it never wakes up, ensure it's properly configured for Wake-on-LAN."]; } + [[self activeViewController] presentViewController:wolAlert animated:YES completion:nil]; + //} } - (void)pairButtonTappedForHost:(TemporaryHost *)host{ @@ -1598,50 +1601,56 @@ static NSMutableSet* hostList; // }); //} --(void) updateResolutionAccordingly { - DataManager* dataMan = [[DataManager alloc] init]; - Settings *currentSettings = [dataMan retrieveSettings]; - UIWindow *window = UIApplication.sharedApplication.windows.firstObject; +-(void) fillResolutionTable:(CGSize*)resolutionTable externalDisplayMode:(NSInteger)externalDisplayMode{ + UIWindow *window = self.view.window; + NSLog(@" window %@", window); + CGFloat screenScale = window.screen.scale; - CGFloat appWindowWidth = CGRectGetWidth(window.frame) * screenScale; - CGFloat appWindowHeight = CGRectGetHeight(window.frame) * screenScale; - CGFloat screenWidthInPoints = CGRectGetWidth([[UIScreen mainScreen] bounds]); - CGFloat screenHeightInPoints = CGRectGetHeight([[UIScreen mainScreen] bounds]); - - if(currentSettings.externalDisplayMode.intValue == 1 && UIScreen.screens.count > 1){ + CGFloat safeAreaWidth = (window.frame.size.width - window.safeAreaInsets.left - window.safeAreaInsets.right) * screenScale; + CGFloat appWindowWidth = window.frame.size.width * screenScale; + CGFloat appWindowHeight = window.frame.size.height * screenScale; + + if(externalDisplayMode == 1 && UIScreen.screens.count > 1){ CGRect bounds = [UIScreen.screens.lastObject bounds]; screenScale = [UIScreen.screens.lastObject scale]; appWindowWidth = bounds.size.width * screenScale; appWindowHeight = bounds.size.height * screenScale; } - - bool needSwap = false; - if([self isFullScreenRequired]){ // if force fullscreen is enabled in app bundle, we use screen bounds to tell if a swap between width & height is needed - needSwap = (currentSettings.width.floatValue - currentSettings.height.floatValue) * (screenWidthInPoints - screenHeightInPoints) < 0; //update the current resolution accordingly - NSLog(@"need to swap width & height (non-app window mode): %d", needSwap); - if(needSwap){ - CGFloat tmpLength = currentSettings.width.floatValue; - currentSettings.width = @(currentSettings.height.floatValue); - currentSettings.height = @(tmpLength); - } + bool needSwapWidthAndHeight = appWindowWidth < appWindowHeight; + + resolutionTable[0] = CGSizeMake(1280, 720); + resolutionTable[1] = CGSizeMake(1920, 1080); + resolutionTable[2] = CGSizeMake(3840, 2160); + + for(uint8_t i=0;i<6;i++){ + CGFloat longSideLen = resolutionTable[i].height > resolutionTable[i].width ? resolutionTable[i].height : resolutionTable[i].width; + CGFloat shortSideLen = resolutionTable[i].height < resolutionTable[i].width ? resolutionTable[i].height : resolutionTable[i].width; + if(needSwapWidthAndHeight) resolutionTable[i] = CGSizeMake(shortSideLen, longSideLen); + else resolutionTable[i] = CGSizeMake(longSideLen, shortSideLen); } - else{// if force fullscreen is disabled in app bundle, we use appWindowSize to directly update or to get if we need a swap - if(currentSettings.resolutionSelected.intValue == 5){ // app window res, previous fullscreen, update resolution directly - currentSettings.width = @(appWindowWidth); - currentSettings.height = @(appWindowHeight); - NSLog(@"Directly Update app window resolution: %f, %f", appWindowWidth, appWindowHeight); - } - else if(currentSettings.resolutionSelected.intValue){ - needSwap = (currentSettings.width.floatValue - currentSettings.height.floatValue) * (appWindowWidth - appWindowHeight) < 0; - if(needSwap){ - CGFloat tmpLength = currentSettings.width.floatValue; - currentSettings.width = @(currentSettings.height.floatValue); - currentSettings.height = @(tmpLength); - NSLog(@"Swap resolution width & height"); - } - } + + // add app window resolution and not swap width and height + resolutionTable[3] = CGSizeMake(safeAreaWidth, appWindowHeight); + resolutionTable[4] = CGSizeMake(appWindowWidth, appWindowHeight); +} + +-(void) updateResolutionAccordingly { + DataManager* dataMan = [[DataManager alloc] init]; + Settings *currentSettings = [dataMan retrieveSettings]; + + CGSize tempResolutionTable[6] = {0}; + tempResolutionTable[5] = CGSizeMake(currentSettings.width.intValue, currentSettings.height.intValue); + [self fillResolutionTable:tempResolutionTable externalDisplayMode:currentSettings.externalDisplayMode.intValue]; + + int selectedIndex = currentSettings.resolutionSelected.intValue; + if (selectedIndex >= 0 && selectedIndex < 6) { + CGSize selectedSize = tempResolutionTable[selectedIndex]; + currentSettings.width = @(selectedSize.width); + currentSettings.height = @(selectedSize.height); + NSLog(@"Updated resolution to: %@ x %@", currentSettings.width, currentSettings.height); } + [dataMan saveData]; } diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index 73ad1e32..e66ad118 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -21,6 +21,9 @@ #import "LocalizationHelper.h" @implementation SettingsViewController { + NSLayoutConstraint *parentStackLeadingConstraint; + NSLayoutConstraint *parentStackWidthConstraint; + NSInteger _bitrate; NSInteger _lastSelectedResolutionIndex; bool justEnteredSettingsView; @@ -232,39 +235,15 @@ CGSize resolutionTable[RESOLUTION_TABLE_SIZE]; dispatch_after(delayTime, dispatch_get_main_queue(), ^{// Code to execute after the delay // [self updateResolutionAccordingly]; }); - } // Adjust the subviews for the safe area on the iPhone X. - (void)viewSafeAreaInsetsDidChange { [super viewSafeAreaInsetsDidChange]; - - //if (@available(iOS 11.0, *)) { - if (false) { // cancel this for portrait mode - for (UIView* view in self.view.subviews) { - // HACK: The official safe area is much too large for our purposes - // so we'll just use the presence of any safe area to indicate we should - // pad by 20. - if (self.view.safeAreaInsets.left >= 20 || self.view.safeAreaInsets.right >= 20) { - view.frame = CGRectMake(view.frame.origin.x + 20, view.frame.origin.y, view.frame.size.width, view.frame.size.height); - } - } - } } -BOOL isCustomResolution(CGSize res) { - if (res.width == 0 && res.height == 0) { - return NO; - } - - for (int i = 0; i < RESOLUTION_TABLE_CUSTOM_INDEX; i++) { - NSLog(@"customRes: %f, %f, isrl: %f, %f", res.width, res.height, resolutionTable[i].width, resolutionTable[i].height); - if ((res.width == resolutionTable[i].width && res.height == resolutionTable[i].height) || (res.height == resolutionTable[i].width && res.width == resolutionTable[i].height)) { - return NO; - } - } - - return YES; +BOOL isCustomResolution(int resolutionSelected) { + return resolutionSelected == RESOLUTION_TABLE_CUSTOM_INDEX; } + (bool)isLandscapeNow { @@ -288,32 +267,10 @@ BOOL isCustomResolution(CGSize res) { - (void)updateResolutionTable{ if(self.mainFrameViewController.settingsExpandedInStreamView) return; - - UIWindow *window = self.view.window; - NSLog(@" window %@", window); - CGFloat screenScale = window.screen.scale; - CGFloat safeAreaWidth = (window.frame.size.width - window.safeAreaInsets.left - window.safeAreaInsets.right) * screenScale; - CGFloat appWindowWidth = window.frame.size.width * screenScale; - CGFloat appWindowHeight = window.frame.size.height * screenScale; - if([self isAirPlayEnabled] && UIScreen.screens.count > 1){ - CGRect bounds = [UIScreen.screens.lastObject bounds]; - screenScale = [UIScreen.screens.lastObject scale]; - appWindowWidth = bounds.size.width * screenScale; - appWindowHeight = bounds.size.height * screenScale; - } - - bool needSwapWidthAndHeight = appWindowWidth < appWindowHeight; - - for(uint8_t i=0;i<6;i++){ - CGFloat longSideLen = resolutionTable[i].height > resolutionTable[i].width ? resolutionTable[i].height : resolutionTable[i].width; - CGFloat shortSideLen = resolutionTable[i].height < resolutionTable[i].width ? resolutionTable[i].height : resolutionTable[i].width; - if(needSwapWidthAndHeight) resolutionTable[i] = CGSizeMake(shortSideLen, longSideLen); - else resolutionTable[i] = CGSizeMake(longSideLen, shortSideLen); - } - - resolutionTable[3] = CGSizeMake(safeAreaWidth, appWindowHeight); - resolutionTable[4] = CGSizeMake(appWindowWidth, appWindowHeight); + NSInteger externalDisplayMode = [self.externalDisplayModeSelector selectedSegmentIndex]; + // 调用主界面方法统一填充 resolutionTable + [self.mainFrameViewController fillResolutionTable:resolutionTable externalDisplayMode:externalDisplayMode]; [self updateResolutionDisplayLabel]; } @@ -332,6 +289,11 @@ BOOL isCustomResolution(CGSize res) { - (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:NO]; + [self updateParentStackHorizontalConstraints]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(deviceOrientationDidChange:) // handle orientation change since i made portrait mode available + name:UIDeviceOrientationDidChangeNotification + object:nil]; } - (void)viewDidAppear:(BOOL)animated{ @@ -342,17 +304,22 @@ BOOL isCustomResolution(CGSize res) { DataManager* dataMan = [[DataManager alloc] init]; TemporarySettings *currentSettings = [dataMan getSettings]; - CGSize currentResolution = CGSizeMake(currentSettings.width.intValue, currentSettings.height.intValue); - [self.customResolutionSwitch setOn: isCustomResolution(currentResolution)]; + //CGSize currentResolution = CGSizeMake(currentSettings.width.intValue, currentSettings.height.intValue); + [self.customResolutionSwitch setOn: isCustomResolution(currentSettings.resolutionSelected.intValue)]; [self.resolutionSelector setEnabled:!self.customResolutionSwitch.isOn]; } +- (void)viewWillDisappear:(BOOL)animated{ + [super viewWillDisappear:animated]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} - (void)viewDidDisappear:(BOOL)animated{ [super viewDidDisappear:NO]; [[NSNotificationCenter defaultCenter] postNotificationName:@"SettingsViewClosedNotification" object:self]; // notify other view that settings view just closed } + - (SettingsMenuMode)getSettingsMenuMode{ return currentSettingsMenuMode; } @@ -392,14 +359,57 @@ BOOL isCustomResolution(CGSize res) { _parentStack.spacing = 0; _parentStack.translatesAutoresizingMaskIntoConstraints = NO; [self.scrollView addSubview:_parentStack]; + [NSLayoutConstraint activateConstraints:@[ [_parentStack.topAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.topAnchor constant: currentSettingsMenuMode == AllSettings ? [self getStandardNavBarHeight] : [self getStandardNavBarHeight]+10], [_parentStack.bottomAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.bottomAnchor constant:-20], - [_parentStack.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor constant: 0], //mark: settingMenuLayout - [_parentStack.widthAnchor constraintEqualToAnchor:self.view.widthAnchor constant:-20] // section width adjusted here //mark: settingMenuLayout ]]; + + [self updateParentStackHorizontalConstraints]; } +-(void)deviceOrientationDidChange:(NSNotification *)notification { + [self updateParentStackHorizontalConstraints]; +} + +- (void)updateParentStackHorizontalConstraints{ + if(![self isIPhone]){ + [NSLayoutConstraint activateConstraints:@[ + [_parentStack.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor constant: 0], //mark: settingMenuLayout + [_parentStack.widthAnchor constraintEqualToAnchor:self.view.widthAnchor constant:-20] // section width adjusted here + ]]; + return; + } + + UIWindow *keyWindow = [UIApplication sharedApplication].windows.firstObject; + + if (@available(iOS 13.0, *)) { + if(parentStackWidthConstraint && parentStackLeadingConstraint) [NSLayoutConstraint deactivateConstraints:@[parentStackLeadingConstraint, parentStackWidthConstraint]]; + UIInterfaceOrientation currentOrientation = keyWindow.windowScene.interfaceOrientation; + switch (currentOrientation) { + case UIInterfaceOrientationLandscapeRight: + parentStackLeadingConstraint = [_parentStack.leadingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor constant:0]; + parentStackWidthConstraint = [_parentStack.widthAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.widthAnchor constant:-10]; + break; + default: + parentStackLeadingConstraint = [_parentStack.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:10]; + parentStackWidthConstraint = [_parentStack.widthAnchor constraintEqualToAnchor:self.view.widthAnchor constant:-20]; + break; + } + [NSLayoutConstraint activateConstraints:@[parentStackLeadingConstraint, parentStackWidthConstraint]]; + } else { + // Fallback on earlier versions + } + + + double delayInSeconds = 0.05; + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); + dispatch_after(popTime, dispatch_get_main_queue(), ^{ + [self hideOverlappedDynamicLabels]; + }); +} + + - (UIButton* )findInfoButtonFromStack:(UIStackView* )stack{ UIButton* button; for(UIView *view in stack.subviews){ @@ -497,7 +507,6 @@ BOOL isCustomResolution(CGSize res) { [label.heightAnchor constraintEqualToAnchor:stack.arrangedSubviews[0].heightAnchor], [label.widthAnchor constraintEqualToConstant:150], ]]; - } } @@ -510,8 +519,6 @@ BOOL isCustomResolution(CGSize res) { } - (void)layoutSections{ - - MenuSectionView *videoSection = [[MenuSectionView alloc] init]; videoSection.delegate = self; videoSection.sectionTitle = [LocalizationHelper localizedStringForKey:@"Video"]; @@ -721,8 +728,6 @@ BOOL isCustomResolution(CGSize res) { } - - - (NSInteger)parentStackIndexForLocation:(CGPoint)location { for (NSInteger i = _parentStack.arrangedSubviews.count-1; i >=0; i--) { UIView *subview = _parentStack.arrangedSubviews[i]; @@ -978,10 +983,12 @@ BOOL isCustomResolution(CGSize res) { if([sender.superview.accessibilityIdentifier isEqualToString: @"pointerVelocityDividerStack"]){ tipText = [LocalizationHelper localizedStringForKey:@"pointerVelocityDividerStackTip"]; showOnlineDocAction = true; + onlineDocLink =[LocalizationHelper localizedStringForKey:@"pointerVelocityDividerStackDoc"]; } if([sender.superview.accessibilityIdentifier isEqualToString: @"pointerVelocityFactorStack"]){ tipText = [LocalizationHelper localizedStringForKey:@"pointerVelocityFactorStackTip"]; showOnlineDocAction = true; + onlineDocLink = [LocalizationHelper localizedStringForKey:@"pointerVelocityFactorStackDoc"]; } if([sender.superview.accessibilityIdentifier isEqualToString: @"hdrStack"]){ tipText = [LocalizationHelper localizedStringForKey:@"hdrStackTip"]; @@ -1035,10 +1042,10 @@ BOOL isCustomResolution(CGSize res) { } - UIAlertController *tipsAlertController = [UIAlertController alertControllerWithTitle: [LocalizationHelper localizedStringForKey:@"Tips"] message: [LocalizationHelper localizedStringForKey:@"%@", tipText] preferredStyle:UIAlertControllerStyleAlert]; + UIAlertController *tipsAlertController = [UIAlertController alertControllerWithTitle: [LocalizationHelper localizedStringForKey:@"Tips"] message:tipText preferredStyle:UIAlertControllerStyleAlert]; - + /* NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; paragraphStyle.alignment = NSTextAlignmentLeft; @@ -1052,7 +1059,7 @@ BOOL isCustomResolution(CGSize res) { // 使用 KVC 设置 attributedMessage(注意审核风险) [tipsAlertController setValue:attributedMessage forKey:@"attributedMessage"]; - + */ UIAlertAction *readInstruction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Online Documentation"] style:UIAlertActionStyleDefault @@ -1298,12 +1305,8 @@ BOOL isCustomResolution(CGSize res) { [self.resolutionSelector removeSegmentAtIndex:0 animated:NO]; // remove 360p [self.resolutionSelector removeSegmentAtIndex:5 animated:NO]; // remove custom segment - resolutionTable[0] = CGSizeMake(1280, 720); - resolutionTable[1] = CGSizeMake(1920, 1080); - resolutionTable[2] = CGSizeMake(3840, 2160); - resolutionTable[3] = CGSizeMake(safeAreaWidth, fullScreenHeight); - resolutionTable[4] = CGSizeMake(fullScreenWidth, fullScreenHeight); resolutionTable[5] = CGSizeMake([currentSettings.width integerValue], [currentSettings.height integerValue]); // custom initial value + [self updateResolutionTable]; NSInteger framerate; @@ -2189,6 +2192,9 @@ BOOL isCustomResolution(CGSize res) { BOOL unlockDisplayOrientation = [self.unlockDisplayOrientationSelector selectedSegmentIndex] == 1; BOOL enableGraphs = self.enableGraphsSwitch.isOn; NSInteger resolutionSelected = [self.resolutionSelector selectedSegmentIndex]; + if (self.customResolutionSwitch.isOn) { + resolutionSelected = RESOLUTION_TABLE_CUSTOM_INDEX; + } NSInteger externalDisplayMode = [self.externalDisplayModeSelector selectedSegmentIndex]; NSInteger localMousePointerMode = [self.localMousePointerModeSelector selectedSegmentIndex]; NSInteger backgroundSessionTimer = self.backgroundSessionTimerSlider.value == self.backgroundSessionTimerSlider.maximumValue ? (uint32_t) INT16_MAX : (uint32_t)self.backgroundSessionTimerSlider.value;