Merge remote-tracking branch 'upstream/Integration' into feat/frame_pacing_metal

This commit is contained in:
Acaki committed 2025-08-08 16:34:34 +08:00
commit a9c4417434
9 files changed
+297 -182

No files matched your search

+1
View File
@@ -6,3 +6,4 @@ Build
DerivedData DerivedData
*.xccheckout *.xccheckout
.github .github
README.md
+36 -18
View File
@@ -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) <br>
[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). <br>
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 及其他社区开发者对项目进行了大量重构、重新设计与功能扩展,包括全新的用户界面和显著增强的功能特性。<br>我们对 moonlight-iOS 开发者的开创性工作表示衷心感谢。 <br><br>
- 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.<br>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) <br>
## Building # App Store 分发一次性收费声明.
* Install Xcode from the [App Store page](https://apps.apple.com/us/app/xcode/id497799835) # Statement on One-Time App Store Distribution Fee.
* Run `git clone --recursive https://github.com/moonlight-stream/moonlight-ios.git` - VoidLink 的 App Store 安装费用用于覆盖通过 Apple 框架进行安全可靠应用分发的成本。同时也支持项目维护者进行持续的开发、维护,以及公众用户访问并下载官方签名版本。感谢您对项目的支持,帮助我们持续改进和优化。<br><br>
* If you've already clone the repo without `--recursive`, run `git submodule update --init --recursive` - 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.
* Open Moonlight.xcodeproj in Xcode
* To run on a real device, you will need to locally modify the signing options: <br>
* Click on "Moonlight" at the top of the left sidebar
* Click on the "Signing & Capabilities" tab # 开发者B站号. Developer on Bilibili
* 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. 如果你在用Bilibili, 请关注`True砖家`,了解该fork的最新消息。 <br>
* Change the "Bundle Identifier" to something different. You can add your name or some random letters to make it unique. If you are on Bilibili, subscribe `True砖家` to get the latest news of this fork: <br>
* Now you can select your Apple device in the top bar as a target and click the Play button to run. https://b23.tv/A0F9v7n
<br>
# 贡献者 Contributors
[@TrueZhuangJia](https://github.com/TrueZhuangJia) <br>
[@All contributors from moonlight-iOS](https://github.com/moonlight-stream/moonlight-ios/graphs/contributors) <br>
[@stefanilijev97](https://github.com/stefanilijev97/stefanilijev97) <br>
[@Acaki](https://github.com/Acaki) <br>
[@seastwood](https://github.com/seastwood) <br>
[@Danos0100](https://github.com/Danos0100) <br>
[@xzzpig](https://github.com/xzzpig) <br>
[@King0fSpace](https://github.com/King0fSpace) <br>
+9 -9
View File
@@ -521,6 +521,9 @@ static const float REFRESH_CYCLE = 2.0f;
_hostNameLabel.text = host.name; _hostNameLabel.text = host.name;
backgroundLayer.hidden = !(host.state == StateOnline && host.pairState == PairStatePaired); 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) { switch (host.state) {
case StateOnline: case StateOnline:
[_hostSpinner stopAnimating]; [_hostSpinner stopAnimating];
@@ -576,8 +579,6 @@ static const float REFRESH_CYCLE = 2.0f;
self.statusIcon.hidden = YES; self.statusIcon.hidden = YES;
} }
bool hostPaired = host.pairState == PairStatePaired;
_hostIconView.tintColor = [ThemeManager lowProfileGray]; _hostIconView.tintColor = [ThemeManager lowProfileGray];
lockIconView.tintColor = [ThemeManager lowProfileGray]; lockIconView.tintColor = [ThemeManager lowProfileGray];
lockIconView.hidden = hostPaired; lockIconView.hidden = hostPaired;
@@ -589,14 +590,14 @@ static const float REFRESH_CYCLE = 2.0f;
if (@available(iOS 13.0, *)) { if (@available(iOS 13.0, *)) {
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:buttonHeight/3.45 weight:UIImageSymbolWeightBold]; UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:buttonHeight/3.45 weight:UIImageSymbolWeightBold];
UIImage *templateImage = [UIImage systemImageNamed:@"power" withConfiguration:config]; 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]; [self.wakeupButton setImage:coloredImage forState:UIControlStateNormal];
} else { } else {
// Fallback on earlier versions // Fallback on earlier versions
} }
self.wakeupButton.backgroundColor = hostPaired ? [ThemeManager textTintColorWithAlpha] : [[ThemeManager textColorGray] colorWithAlphaComponent:0.2]; self.wakeupButton.backgroundColor = hasValidMac ? [ThemeManager textTintColorWithAlpha] : [[ThemeManager textColorGray] colorWithAlphaComponent:0.2];
[self.wakeupButton setTitleColor: hostPaired ? defaultBlue : [ThemeManager textColorGray] forState:UIControlStateNormal]; [self.wakeupButton setTitleColor: hasValidMac ? defaultBlue : [ThemeManager textColorGray] forState:UIControlStateNormal];
break; break;
case StateUnknown: case StateUnknown:
@@ -620,17 +621,16 @@ static const float REFRESH_CYCLE = 2.0f;
_pairButton.hidden = YES; _pairButton.hidden = YES;
_wakeupButton.hidden = NO; _wakeupButton.hidden = NO;
[_wakeupButton setTitleColor:[ThemeManager textColorGray] forState:UIControlStateNormal]; // theme
if (@available(iOS 13.0, *)) { if (@available(iOS 13.0, *)) {
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:buttonHeight/3.45 weight:UIImageSymbolWeightBold]; UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:buttonHeight/3.45 weight:UIImageSymbolWeightBold];
UIImage *templateImage = [UIImage systemImageNamed:@"power" withConfiguration:config]; 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]; [self.wakeupButton setImage:coloredImage forState:UIControlStateNormal];
} else { } else {
// Fallback on earlier versions // Fallback on earlier versions
} }
self.wakeupButton.backgroundColor = [[ThemeManager textColorGray] colorWithAlphaComponent:0.2]; self.wakeupButton.backgroundColor = hasValidMac ? [ThemeManager textTintColorWithAlpha] : [[ThemeManager textColorGray] colorWithAlphaComponent:0.2];
[self.wakeupButton setTitleColor:[ThemeManager textColorGray] forState:UIControlStateNormal]; [self.wakeupButton setTitleColor: hasValidMac ? defaultBlue : [ThemeManager textColorGray] forState:UIControlStateNormal];
break; break;
default:break; default:break;
} }
+94 -30
View File
@@ -1503,25 +1503,25 @@
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-Hans" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-Hant" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-HK" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "en" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-Hans" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-Hant" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-HK" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "en" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-Hans" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-Hant" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-HK" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "en" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-Hans" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-Hant" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-HK" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "onScreenWidgetStackDoc" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/dcke52amz8ay3g37"
}
},
"zh-Hans" : { "zh-Hans" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-Hant" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-HK" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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"
} }
} }
} }
@@ -3358,19 +3364,19 @@
}, },
"zh-Hans" : { "zh-Hans" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "state" : "needs_review",
"value" : "- App切后台时自动开启画中画, 需要打开 iOS 设置-> 多任务与手势 -> 自动开启画中画。\n\n- 画中画可在工具箱菜单中手动开启。\n\n- 画中画时长由 “其他 -> 后台会话” 设置项控制\n\n- 仅 iOS15+ 支持画中画。" "value" : "- App切后台时自动开启画中画, 需要打开 iOS 设置-> 多任务与手势 -> 自动开启画中画。\n\n- 画中画可在工具箱菜单中手动开启。\n\n- 画中画时长由 “其他 -> 后台会话” 设置项控制\n\n- 仅 iOS15+ 支持画中画。"
} }
}, },
"zh-Hant" : { "zh-Hant" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "state" : "needs_review",
"value" : "- App 切到背景時會自動啟用子母畫面,需開啟 iOS 「設定」→「多工與手勢」→「自動啟用子母畫面」。\n\n- 子母畫面也可透過工具箱選單手動啟用。\n\n- 子母畫面顯示時長由「其他」→「背景執行」設定項目控制。\n\n- 子母畫面功能自 iOS 15 起支援。" "value" : "- App 切到背景時會自動啟用子母畫面,需開啟 iOS 「設定」→「多工與手勢」→「自動啟用子母畫面」。\n\n- 子母畫面也可透過工具箱選單手動啟用。\n\n- 子母畫面顯示時長由「其他」→「背景執行」設定項目控制。\n\n- 子母畫面功能自 iOS 15 起支援。"
} }
}, },
"zh-HK" : { "zh-HK" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "state" : "needs_review",
"value" : "- App 切到背景時會自動啟用子母畫面,需開啟 iOS 「設定」→「多工與手勢」→「自動啟用子母畫面」。\n\n- 子母畫面也可透過工具箱選單手動啟用。\n\n- 子母畫面顯示時長由「其他」→「背景執行」設定項目控制。\n\n- 子母畫面功能自 iOS 15 起支援。" "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" : { "pointerVelocityDividerStackTip" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "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" : { "pointerVelocityFactorStackTip" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@@ -4309,25 +4373,25 @@
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-Hans" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-Hant" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-HK" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "en" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-Hans" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-Hant" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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" : { "zh-HK" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "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"
} }
} }
} }
@@ -14,11 +14,11 @@
@implementation CustomEdgeSlideGestureRecognizer @implementation CustomEdgeSlideGestureRecognizer
UITouch* capturedUITouch; UITouch* capturedUITouch;
CGFloat startPointX; CGFloat startPointX;
static CGFloat screenWidthInPoints; //static CGFloat screenWidthInPoints;
- (instancetype)initWithTarget:(nullable id)target action:(nullable SEL)action { - (instancetype)initWithTarget:(nullable id)target action:(nullable SEL)action {
self = [super initWithTarget:target action: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; _immediateTriggering = false;
_EDGE_TOLERANCE = 10.0f; _EDGE_TOLERANCE = 10.0f;
return self; return self;
@@ -29,6 +29,7 @@ static CGFloat screenWidthInPoints;
UITouch *touch = [touches anyObject]; UITouch *touch = [touches anyObject];
capturedUITouch = touch; capturedUITouch = touch;
startPointX = [capturedUITouch locationInView:self.view].x; startPointX = [capturedUITouch locationInView:self.view].x;
CGFloat streamFrameViewWidthInPoints = self.view.frame.size.width;
if(_immediateTriggering){ if(_immediateTriggering){
if(self.edges & UIRectEdgeLeft){ if(self.edges & UIRectEdgeLeft){
@@ -38,7 +39,7 @@ static CGFloat screenWidthInPoints;
// NSLog(@"startPointX %f , normalizedGestureDeltaX %f", startPointX, normalizedGestureDistance); // NSLog(@"startPointX %f , normalizedGestureDeltaX %f", startPointX, normalizedGestureDistance);
} }
if(self.edges & UIRectEdgeRight){ if(self.edges & UIRectEdgeRight){
if(startPointX > screenWidthInPoints - _EDGE_TOLERANCE){ if(startPointX > streamFrameViewWidthInPoints - _EDGE_TOLERANCE){
self.state = UIGestureRecognizerStateEnded; self.state = UIGestureRecognizerStateEnded;
} }
// NSLog(@"startPointX %f , normalizedGestureDeltaX %f", startPointX, normalizedGestureDistance); // NSLog(@"startPointX %f , normalizedGestureDeltaX %f", startPointX, normalizedGestureDistance);
@@ -52,6 +53,7 @@ static CGFloat screenWidthInPoints;
if([touches containsObject:capturedUITouch]){ if([touches containsObject:capturedUITouch]){
CGFloat _endPointX = [capturedUITouch locationInView:self.view].x; CGFloat _endPointX = [capturedUITouch locationInView:self.view].x;
CGFloat screenWidthInPoints = self.view.frame.size.width;
CGFloat normalizedGestureDistance = fabs(_endPointX - startPointX)/screenWidthInPoints; CGFloat normalizedGestureDistance = fabs(_endPointX - startPointX)/screenWidthInPoints;
if(self.edges & UIRectEdgeLeft){ if(self.edges & UIRectEdgeLeft){
@@ -255,6 +255,10 @@
selector: @selector(handleReturnToForeground) selector: @selector(handleReturnToForeground)
name: UIApplicationDidBecomeActiveNotification name: UIApplicationDidBecomeActiveNotification
object: nil]; object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(handleEnterBackground)
name: UIApplicationWillResignActiveNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver:self [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deviceOrientationDidChange) // handle orientation change since i made portrait mode available selector:@selector(deviceOrientationDidChange) // handle orientation change since i made portrait mode available
name:UIDeviceOrientationDidChangeNotification name:UIDeviceOrientationDidChangeNotification
@@ -267,24 +271,7 @@
#pragma mark - Class Helper Functions #pragma mark - Class Helper Functions
- (void)handleReturnToForeground { - (void)updateViewBounds{
[OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)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];
viewWillBeResized = false; viewWillBeResized = false;
selectedWidgetView = nil; selectedWidgetView = nil;
selectedControllerLayer = nil; selectedControllerLayer = nil;
@@ -295,6 +282,33 @@
[self reloadLegacyOnScreenControls]; [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<UIViewControllerTransitionCoordinator>)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 */ /* fades the 'Undo Button' in or out depending on whether the user has any OSC layout changes to undo */
- (void) OSCLayoutChanged { - (void) OSCLayoutChanged {
if ([self.layoutOSC.layoutChanges count] > 0) { if ([self.layoutOSC.layoutChanges count] > 0) {
@@ -34,5 +34,6 @@
-(void)reloadStreamConfig; -(void)reloadStreamConfig;
-(bool)isIPhonePortrait; -(bool)isIPhonePortrait;
#endif #endif
- (void)fillResolutionTable:(CGSize*)resolutionTable externalDisplayMode:(NSInteger)externalDisplayMode;
@end @end
@@ -409,10 +409,13 @@ static NSMutableSet* hostList;
- (void)wakeupButtonTappedForHost:(TemporaryHost *)host{ - (void)wakeupButtonTappedForHost:(TemporaryHost *)host{
_selectedHost = host; _selectedHost = host;
if (host.state == StateOffline && host.pairState == PairStatePaired) { 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]; UIAlertController* wolAlert = [UIAlertController alertControllerWithTitle:[LocalizationHelper localizedStringForKey:@"Wake-On-LAN"] message:@"" preferredStyle:UIAlertControllerStyleAlert];
[wolAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Ok"] style:UIAlertActionStyleDefault handler:nil]]; [wolAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Ok"] style:UIAlertActionStyleDefault handler:nil]];
if (host.mac == nil || [host.mac isEqualToString:@"00:00:00:00:00:00"]) {
if (!hasValidMac) {
wolAlert.message = [LocalizationHelper localizedStringForKey: @"Host MAC unknown, unable to send WOL Packet"]; wolAlert.message = [LocalizationHelper localizedStringForKey: @"Host MAC unknown, unable to send WOL Packet"];
} else { } else {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@@ -421,7 +424,7 @@ static NSMutableSet* hostList;
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."]; 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]; [[self activeViewController] presentViewController:wolAlert animated:YES completion:nil];
} //}
} }
- (void)pairButtonTappedForHost:(TemporaryHost *)host{ - (void)pairButtonTappedForHost:(TemporaryHost *)host{
@@ -1598,50 +1601,56 @@ static NSMutableSet* hostList;
// }); // });
//} //}
-(void) updateResolutionAccordingly { -(void) fillResolutionTable:(CGSize*)resolutionTable externalDisplayMode:(NSInteger)externalDisplayMode{
DataManager* dataMan = [[DataManager alloc] init]; UIWindow *window = self.view.window;
Settings *currentSettings = [dataMan retrieveSettings]; NSLog(@" window %@", window);
UIWindow *window = UIApplication.sharedApplication.windows.firstObject;
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 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(externalDisplayMode == 1 && UIScreen.screens.count > 1){
CGRect bounds = [UIScreen.screens.lastObject bounds]; CGRect bounds = [UIScreen.screens.lastObject bounds];
screenScale = [UIScreen.screens.lastObject scale]; screenScale = [UIScreen.screens.lastObject scale];
appWindowWidth = bounds.size.width * screenScale; appWindowWidth = bounds.size.width * screenScale;
appWindowHeight = bounds.size.height * screenScale; appWindowHeight = bounds.size.height * screenScale;
} }
bool needSwap = false; bool needSwapWidthAndHeight = appWindowWidth < appWindowHeight;
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 resolutionTable[0] = CGSizeMake(1280, 720);
needSwap = (currentSettings.width.floatValue - currentSettings.height.floatValue) * (screenWidthInPoints - screenHeightInPoints) < 0; //update the current resolution accordingly resolutionTable[1] = CGSizeMake(1920, 1080);
NSLog(@"need to swap width & height (non-app window mode): %d", needSwap); resolutionTable[2] = CGSizeMake(3840, 2160);
if(needSwap){
CGFloat tmpLength = currentSettings.width.floatValue; for(uint8_t i=0;i<6;i++){
currentSettings.width = @(currentSettings.height.floatValue); CGFloat longSideLen = resolutionTable[i].height > resolutionTable[i].width ? resolutionTable[i].height : resolutionTable[i].width;
currentSettings.height = @(tmpLength); 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]; [dataMan saveData];
} }
@@ -21,6 +21,9 @@
#import "LocalizationHelper.h" #import "LocalizationHelper.h"
@implementation SettingsViewController { @implementation SettingsViewController {
NSLayoutConstraint *parentStackLeadingConstraint;
NSLayoutConstraint *parentStackWidthConstraint;
NSInteger _bitrate; NSInteger _bitrate;
NSInteger _lastSelectedResolutionIndex; NSInteger _lastSelectedResolutionIndex;
bool justEnteredSettingsView; bool justEnteredSettingsView;
@@ -232,39 +235,15 @@ CGSize resolutionTable[RESOLUTION_TABLE_SIZE];
dispatch_after(delayTime, dispatch_get_main_queue(), ^{// Code to execute after the delay dispatch_after(delayTime, dispatch_get_main_queue(), ^{// Code to execute after the delay
// [self updateResolutionAccordingly]; // [self updateResolutionAccordingly];
}); });
} }
// Adjust the subviews for the safe area on the iPhone X. // Adjust the subviews for the safe area on the iPhone X.
- (void)viewSafeAreaInsetsDidChange { - (void)viewSafeAreaInsetsDidChange {
[super 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) { BOOL isCustomResolution(int resolutionSelected) {
if (res.width == 0 && res.height == 0) { return resolutionSelected == RESOLUTION_TABLE_CUSTOM_INDEX;
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)isLandscapeNow { + (bool)isLandscapeNow {
@@ -289,31 +268,9 @@ BOOL isCustomResolution(CGSize res) {
- (void)updateResolutionTable{ - (void)updateResolutionTable{
if(self.mainFrameViewController.settingsExpandedInStreamView) return; if(self.mainFrameViewController.settingsExpandedInStreamView) return;
UIWindow *window = self.view.window; NSInteger externalDisplayMode = [self.externalDisplayModeSelector selectedSegmentIndex];
NSLog(@" window %@", window); // 调用主界面方法统一填充 resolutionTable
[self.mainFrameViewController fillResolutionTable:resolutionTable externalDisplayMode:externalDisplayMode];
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);
[self updateResolutionDisplayLabel]; [self updateResolutionDisplayLabel];
} }
@@ -332,6 +289,11 @@ BOOL isCustomResolution(CGSize res) {
- (void)viewWillAppear:(BOOL)animated{ - (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:NO]; [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{ - (void)viewDidAppear:(BOOL)animated{
@@ -342,17 +304,22 @@ BOOL isCustomResolution(CGSize res) {
DataManager* dataMan = [[DataManager alloc] init]; DataManager* dataMan = [[DataManager alloc] init];
TemporarySettings *currentSettings = [dataMan getSettings]; TemporarySettings *currentSettings = [dataMan getSettings];
CGSize currentResolution = CGSizeMake(currentSettings.width.intValue, currentSettings.height.intValue); //CGSize currentResolution = CGSizeMake(currentSettings.width.intValue, currentSettings.height.intValue);
[self.customResolutionSwitch setOn: isCustomResolution(currentResolution)]; [self.customResolutionSwitch setOn: isCustomResolution(currentSettings.resolutionSelected.intValue)];
[self.resolutionSelector setEnabled:!self.customResolutionSwitch.isOn]; [self.resolutionSelector setEnabled:!self.customResolutionSwitch.isOn];
} }
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)viewDidDisappear:(BOOL)animated{ - (void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:NO]; [super viewDidDisappear:NO];
[[NSNotificationCenter defaultCenter] postNotificationName:@"SettingsViewClosedNotification" object:self]; // notify other view that settings view just closed [[NSNotificationCenter defaultCenter] postNotificationName:@"SettingsViewClosedNotification" object:self]; // notify other view that settings view just closed
} }
- (SettingsMenuMode)getSettingsMenuMode{ - (SettingsMenuMode)getSettingsMenuMode{
return currentSettingsMenuMode; return currentSettingsMenuMode;
} }
@@ -392,14 +359,57 @@ BOOL isCustomResolution(CGSize res) {
_parentStack.spacing = 0; _parentStack.spacing = 0;
_parentStack.translatesAutoresizingMaskIntoConstraints = NO; _parentStack.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addSubview:_parentStack]; [self.scrollView addSubview:_parentStack];
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[_parentStack.topAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.topAnchor constant: currentSettingsMenuMode == AllSettings ? [self getStandardNavBarHeight] : [self getStandardNavBarHeight]+10], [_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.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* )findInfoButtonFromStack:(UIStackView* )stack{
UIButton* button; UIButton* button;
for(UIView *view in stack.subviews){ for(UIView *view in stack.subviews){
@@ -497,7 +507,6 @@ BOOL isCustomResolution(CGSize res) {
[label.heightAnchor constraintEqualToAnchor:stack.arrangedSubviews[0].heightAnchor], [label.heightAnchor constraintEqualToAnchor:stack.arrangedSubviews[0].heightAnchor],
[label.widthAnchor constraintEqualToConstant:150], [label.widthAnchor constraintEqualToConstant:150],
]]; ]];
} }
} }
@@ -510,8 +519,6 @@ BOOL isCustomResolution(CGSize res) {
} }
- (void)layoutSections{ - (void)layoutSections{
MenuSectionView *videoSection = [[MenuSectionView alloc] init]; MenuSectionView *videoSection = [[MenuSectionView alloc] init];
videoSection.delegate = self; videoSection.delegate = self;
videoSection.sectionTitle = [LocalizationHelper localizedStringForKey:@"Video"]; videoSection.sectionTitle = [LocalizationHelper localizedStringForKey:@"Video"];
@@ -721,8 +728,6 @@ BOOL isCustomResolution(CGSize res) {
} }
- (NSInteger)parentStackIndexForLocation:(CGPoint)location { - (NSInteger)parentStackIndexForLocation:(CGPoint)location {
for (NSInteger i = _parentStack.arrangedSubviews.count-1; i >=0; i--) { for (NSInteger i = _parentStack.arrangedSubviews.count-1; i >=0; i--) {
UIView *subview = _parentStack.arrangedSubviews[i]; UIView *subview = _parentStack.arrangedSubviews[i];
@@ -978,10 +983,12 @@ BOOL isCustomResolution(CGSize res) {
if([sender.superview.accessibilityIdentifier isEqualToString: @"pointerVelocityDividerStack"]){ if([sender.superview.accessibilityIdentifier isEqualToString: @"pointerVelocityDividerStack"]){
tipText = [LocalizationHelper localizedStringForKey:@"pointerVelocityDividerStackTip"]; tipText = [LocalizationHelper localizedStringForKey:@"pointerVelocityDividerStackTip"];
showOnlineDocAction = true; showOnlineDocAction = true;
onlineDocLink =[LocalizationHelper localizedStringForKey:@"pointerVelocityDividerStackDoc"];
} }
if([sender.superview.accessibilityIdentifier isEqualToString: @"pointerVelocityFactorStack"]){ if([sender.superview.accessibilityIdentifier isEqualToString: @"pointerVelocityFactorStack"]){
tipText = [LocalizationHelper localizedStringForKey:@"pointerVelocityFactorStackTip"]; tipText = [LocalizationHelper localizedStringForKey:@"pointerVelocityFactorStackTip"];
showOnlineDocAction = true; showOnlineDocAction = true;
onlineDocLink = [LocalizationHelper localizedStringForKey:@"pointerVelocityFactorStackDoc"];
} }
if([sender.superview.accessibilityIdentifier isEqualToString: @"hdrStack"]){ if([sender.superview.accessibilityIdentifier isEqualToString: @"hdrStack"]){
tipText = [LocalizationHelper localizedStringForKey:@"hdrStackTip"]; 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]; NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.alignment = NSTextAlignmentLeft; paragraphStyle.alignment = NSTextAlignmentLeft;
@@ -1052,7 +1059,7 @@ BOOL isCustomResolution(CGSize res) {
// 使用 KVC 设置 attributedMessage(注意审核风险) // 使用 KVC 设置 attributedMessage(注意审核风险)
[tipsAlertController setValue:attributedMessage forKey:@"attributedMessage"]; [tipsAlertController setValue:attributedMessage forKey:@"attributedMessage"];
*/
UIAlertAction *readInstruction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Online Documentation"] UIAlertAction *readInstruction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Online Documentation"]
style:UIAlertActionStyleDefault style:UIAlertActionStyleDefault
@@ -1298,12 +1305,8 @@ BOOL isCustomResolution(CGSize res) {
[self.resolutionSelector removeSegmentAtIndex:0 animated:NO]; // remove 360p [self.resolutionSelector removeSegmentAtIndex:0 animated:NO]; // remove 360p
[self.resolutionSelector removeSegmentAtIndex:5 animated:NO]; // remove custom segment [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 resolutionTable[5] = CGSizeMake([currentSettings.width integerValue], [currentSettings.height integerValue]); // custom initial value
[self updateResolutionTable];
NSInteger framerate; NSInteger framerate;
@@ -2189,6 +2192,9 @@ BOOL isCustomResolution(CGSize res) {
BOOL unlockDisplayOrientation = [self.unlockDisplayOrientationSelector selectedSegmentIndex] == 1; BOOL unlockDisplayOrientation = [self.unlockDisplayOrientationSelector selectedSegmentIndex] == 1;
BOOL enableGraphs = self.enableGraphsSwitch.isOn; BOOL enableGraphs = self.enableGraphsSwitch.isOn;
NSInteger resolutionSelected = [self.resolutionSelector selectedSegmentIndex]; NSInteger resolutionSelected = [self.resolutionSelector selectedSegmentIndex];
if (self.customResolutionSwitch.isOn) {
resolutionSelected = RESOLUTION_TABLE_CUSTOM_INDEX;
}
NSInteger externalDisplayMode = [self.externalDisplayModeSelector selectedSegmentIndex]; NSInteger externalDisplayMode = [self.externalDisplayModeSelector selectedSegmentIndex];
NSInteger localMousePointerMode = [self.localMousePointerModeSelector selectedSegmentIndex]; NSInteger localMousePointerMode = [self.localMousePointerModeSelector selectedSegmentIndex];
NSInteger backgroundSessionTimer = self.backgroundSessionTimerSlider.value == self.backgroundSessionTimerSlider.maximumValue ? (uint32_t) INT16_MAX : (uint32_t)self.backgroundSessionTimerSlider.value; NSInteger backgroundSessionTimer = self.backgroundSessionTimerSlider.value == self.backgroundSessionTimerSlider.maximumValue ? (uint32_t) INT16_MAX : (uint32_t)self.backgroundSessionTimerSlider.value;