refactor profileSelector view

This commit is contained in:
True Zhuanjia
2026-07-29 18:01:33 +08:00
parent 4a3ca7774e
commit ef5d839f5e
19 changed files with 1917 additions and 1000 deletions
+4 -8
View File
@@ -47,7 +47,7 @@
2AC1E3832F507D80004F76C0 /* GenericUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC1E3822F507D80004F76C0 /* GenericUtils.swift */; };
2AC1EAFB2F92260F004EDA80 /* UIStackExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC1EAFA2F922602004EDA80 /* UIStackExtension.swift */; };
2AC1EAFC2F922889004EDA80 /* UIStackExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC1EAFA2F922602004EDA80 /* UIStackExtension.swift */; };
3A4A3A112F0C000100000001 /* OSCProfilesTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4A3A102F0C000100000001 /* OSCProfilesTableViewController.swift */; };
3A4A3A112F0C000100000001 /* ProfileSelectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4A3A102F0C000100000001 /* ProfileSelectorViewController.swift */; };
3A4A3A132F0C000100000003 /* LayoutOnScreenControlsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4A3A122F0C000100000002 /* LayoutOnScreenControlsViewController.swift */; };
840751B32BF49C050096B657 /* CustomTapGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 840751B22BF49C050096B657 /* CustomTapGestureRecognizer.m */; };
84203F232C5F24C00032EEED /* OnScreenWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84203F222C5F24C00032EEED /* OnScreenWidgetView.swift */; };
@@ -56,7 +56,6 @@
84351B102C29ACCB00A77989 /* OSCProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = 84351B0A2C29ACCA00A77989 /* OSCProfile.m */; };
84351B112C29ACCB00A77989 /* OSCProfilesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4E0A22FBBBBCC00112233 /* OSCProfilesManager.swift */; };
84351B2D2C29AD6100A77989 /* ToolBarContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 84351B292C29AD6000A77989 /* ToolBarContainerView.m */; };
84351B5D2C29ADBE00A77989 /* ProfileTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 84351B5B2C29ADBE00A77989 /* ProfileTableViewCell.xib */; };
843667F02D9D7173004A7D7F /* widgetTemplates.bin in Resources */ = {isa = PBXBuildFile; fileRef = 843667EF2D9D7173004A7D7F /* widgetTemplates.bin */; };
843CC7F02C1E757D00BBC475 /* NativeTouchPointer.m in Sources */ = {isa = PBXBuildFile; fileRef = 843CC7EF2C1E757D00BBC475 /* NativeTouchPointer.m */; };
843CC7F62C1E7A1700BBC475 /* NativeTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 843CC7F52C1E7A1700BBC475 /* NativeTouchHandler.m */; };
@@ -219,7 +218,7 @@
2A7114002F5A6C65001D852D /* GamepadNavigationIllustrationHud.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GamepadNavigationIllustrationHud.swift; sourceTree = "<group>"; };
2AC1E3822F507D80004F76C0 /* GenericUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericUtils.swift; sourceTree = "<group>"; };
2AC1EAFA2F922602004EDA80 /* UIStackExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackExtension.swift; sourceTree = "<group>"; };
3A4A3A102F0C000100000001 /* OSCProfilesTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSCProfilesTableViewController.swift; sourceTree = "<group>"; };
3A4A3A102F0C000100000001 /* ProfileSelectorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSelectorViewController.swift; sourceTree = "<group>"; };
3A4A3A122F0C000100000002 /* LayoutOnScreenControlsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutOnScreenControlsViewController.swift; sourceTree = "<group>"; };
693B3A9A218638CD00982F7B /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
840751B12BF49B5A0096B657 /* CustomTapGestureRecognizer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CustomTapGestureRecognizer.h; sourceTree = "<group>"; };
@@ -233,7 +232,6 @@
84351B0D2C29ACCB00A77989 /* OSCProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSCProfile.h; sourceTree = "<group>"; };
84351B292C29AD6000A77989 /* ToolBarContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ToolBarContainerView.m; sourceTree = "<group>"; };
84351B2A2C29AD6000A77989 /* ToolBarContainerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ToolBarContainerView.h; sourceTree = "<group>"; };
84351B5B2C29ADBE00A77989 /* ProfileTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ProfileTableViewCell.xib; sourceTree = "<group>"; };
843667EF2D9D7173004A7D7F /* widgetTemplates.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = widgetTemplates.bin; path = PresetConfigs/widgetTemplates.bin; sourceTree = "<group>"; };
843CC7EE2C1E757D00BBC475 /* NativeTouchPointer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeTouchPointer.h; sourceTree = "<group>"; };
843CC7EF2C1E757D00BBC475 /* NativeTouchPointer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NativeTouchPointer.m; sourceTree = "<group>"; };
@@ -522,8 +520,7 @@
84351B2E2C29AD6600A77989 /* Table */ = {
isa = PBXGroup;
children = (
84351B5B2C29ADBE00A77989 /* ProfileTableViewCell.xib */,
3A4A3A102F0C000100000001 /* OSCProfilesTableViewController.swift */,
3A4A3A102F0C000100000001 /* ProfileSelectorViewController.swift */,
);
path = Table;
sourceTree = "<group>";
@@ -1112,7 +1109,6 @@
8454AC6A2C319BDA00F0A302 /* iPhone.storyboard in Resources */,
843667F02D9D7173004A7D7F /* widgetTemplates.bin in Resources */,
84FD0B542C3134A6006B5BE5 /* Localizable.xcstrings in Resources */,
84351B5D2C29ADBE00A77989 /* ProfileTableViewCell.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1218,7 +1214,7 @@
97E20DD02E21647A000D5DCA /* MetalViewController.m in Sources */,
97E20DD12E21647A000D5DCA /* MetalView.m in Sources */,
97E20DD22E21647A000D5DCA /* MetalVideoRenderer.m in Sources */,
3A4A3A112F0C000100000001 /* OSCProfilesTableViewController.swift in Sources */,
3A4A3A112F0C000100000001 /* ProfileSelectorViewController.swift in Sources */,
98D585701C0ED0E800F6CC00 /* TemporarySettings.m in Sources */,
1829CE3F2E2154DF0074A99A /* SceneDelegate.m in Sources */,
18729EDE2E8BD68800101194 /* UUIDHelper.swift in Sources */,
+3 -76
View File
@@ -2,7 +2,6 @@
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24506" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="EVd-wq-ego">
<device id="ipad11_0rounded" orientation="portrait" layout="fullscreen" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24504"/>
<capability name="Image references" minToolsVersion="12.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -13,7 +12,7 @@
<objects>
<viewController id="wb7-af-jn8" customClass="MainFrameViewController" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" indicatorStyle="black" dataMode="prototypes" id="TZj-Lc-M9d" customClass="AppCollectionView">
<rect key="frame" x="0.0" y="0.0" width="834" height="1124"/>
<rect key="frame" x="0.0" y="0.0" width="834" height="1210"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="20" minimumInteritemSpacing="20" id="f7l-kG-hJc">
@@ -3879,10 +3878,10 @@
</objects>
<point key="canvasLocation" x="2529.4964028776981" y="957.52066115702485"/>
</scene>
<!--Profiles Table View Controller-->
<!--Profile Selector View Controller-->
<scene sceneID="W0E-wz-n2b">
<objects>
<viewController storyboardIdentifier="OSCProfilesTableViewController" id="Atv-Nj-0qv" customClass="OSCProfilesTableViewController" customModule="VoidLink" customModuleProvider="target" sceneMemberID="viewController">
<viewController storyboardIdentifier="ProfileSelectorViewController" id="Atv-Nj-0qv" customClass="ProfileSelectorViewController" customModule="VoidLink" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="osA-Uy-SE1"/>
<viewControllerLayoutGuide type="bottom" id="8ja-wQ-jbJ"/>
@@ -3890,83 +3889,11 @@
<view key="view" opaque="NO" contentMode="scaleToFill" id="PlJ-LJ-d1p">
<rect key="frame" x="0.0" y="0.0" width="1133" height="744"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<navigationBar clipsSubviews="YES" alpha="0.77500000000000002" contentMode="scaleToFill" translucent="NO" translatesAutoresizingMaskIntoConstraints="NO" id="62V-DN-FUk">
<rect key="frame" x="226.5" y="61" width="680" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="lQr-5u-g05"/>
</constraints>
<color key="barTintColor" red="0.81960784310000001" green="0.81960784310000001" blue="0.83921568629999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<textAttributes key="titleTextAttributes">
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle3"/>
<color key="textColor" systemColor="systemGray5Color"/>
</textAttributes>
<textAttributes key="largeTitleTextAttributes">
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</textAttributes>
<items>
<navigationItem id="Q2V-XF-4jq">
<leftBarButtonItems>
<barButtonItem title="ExportAll" width="70" style="plain" id="Bhn-IO-Wlt" userLabel="Export Data">
<color key="tintColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<action selector="exportDataTapped:" destination="Atv-Nj-0qv" id="pwG-sO-9he"/>
</connections>
</barButtonItem>
<barButtonItem title="Import" width="48" style="plain" id="CpY-Sb-BDc" userLabel="Import Data">
<color key="tintColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<action selector="importDataTapped:" destination="Atv-Nj-0qv" id="Uq0-Ct-LbS"/>
</connections>
</barButtonItem>
<barButtonItem title="Restore" width="45" style="plain" id="czn-mj-8d1">
<color key="tintColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<action selector="restoreTapped:" destination="Atv-Nj-0qv" id="IvQ-c3-bmW"/>
</connections>
</barButtonItem>
</leftBarButtonItems>
<rightBarButtonItems>
<barButtonItem title="Duplicate" width="80" style="plain" id="P34-hO-6cX">
<color key="tintColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<action selector="duplicateTapped:" destination="Atv-Nj-0qv" id="Ymg-8q-iIc"/>
</connections>
</barButtonItem>
<barButtonItem title="Delete" width="80" style="plain" id="egC-9E-zS4" userLabel="Delete">
<color key="tintColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<action selector="deleteTapped:" destination="Atv-Nj-0qv" id="UpR-VB-qez"/>
</connections>
</barButtonItem>
</rightBarButtonItems>
</navigationItem>
</items>
</navigationBar>
<tableView opaque="NO" clearsContextBeforeDrawing="NO" alpha="0.5" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="-1" estimatedSectionHeaderHeight="-1" sectionFooterHeight="-1" estimatedSectionFooterHeight="-1" translatesAutoresizingMaskIntoConstraints="NO" id="BA3-hd-bDp">
<rect key="frame" x="226.5" y="105" width="680" height="589"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tableView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="62V-DN-FUk" firstAttribute="bottom" secondItem="BA3-hd-bDp" secondAttribute="top" id="600-lH-rte"/>
<constraint firstItem="62V-DN-FUk" firstAttribute="width" secondItem="PlJ-LJ-d1p" secondAttribute="width" multiplier="0.6" id="OWL-Yd-KfG"/>
<constraint firstItem="BA3-hd-bDp" firstAttribute="centerX" secondItem="PlJ-LJ-d1p" secondAttribute="centerX" id="Xte-bA-pFb"/>
<constraint firstItem="BA3-hd-bDp" firstAttribute="width" secondItem="PlJ-LJ-d1p" secondAttribute="width" multiplier="0.6" id="eQf-gF-6kX"/>
<constraint firstItem="BA3-hd-bDp" firstAttribute="top" secondItem="PlJ-LJ-d1p" secondAttribute="top" constant="105" id="eje-7O-RaF"/>
<constraint firstItem="BA3-hd-bDp" firstAttribute="bottom" secondItem="PlJ-LJ-d1p" secondAttribute="bottom" constant="-50" id="j96-Mz-xiG"/>
<constraint firstItem="62V-DN-FUk" firstAttribute="centerX" secondItem="PlJ-LJ-d1p" secondAttribute="centerX" id="qmh-Pp-B9S"/>
</constraints>
</view>
<toolbarItems/>
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
<size key="freeformSize" width="1133" height="744"/>
<connections>
<outlet property="profileTableViewNavigationBar" destination="62V-DN-FUk" id="c3i-qa-4ix"/>
<outlet property="profileTableViewNavigationItem" destination="Q2V-XF-4jq" id="TmF-ZM-NKC"/>
<outlet property="tableView" destination="BA3-hd-bDp" id="Jzl-sP-ANp"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="K4u-Tz-tXU" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
+8 -75
View File
@@ -2,7 +2,6 @@
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="DL0-L5-LOv">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24504"/>
<capability name="Image references" minToolsVersion="12.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
@@ -15,7 +14,7 @@
<objects>
<viewController id="dgh-JZ-Q7z" customClass="MainFrameViewController" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="Rtu-AT-Alw" customClass="AppCollectionView">
<rect key="frame" x="0.0" y="0.0" width="414" height="746"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.3333333432674408" green="0.3333333432674408" blue="0.3333333432674408" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="15" minimumInteritemSpacing="15" id="YcZ-cR-3tK">
@@ -1507,83 +1506,17 @@
</objects>
<point key="canvasLocation" x="-96" y="1822"/>
</scene>
<!--Profiles Table View Controller-->
<!--Profile Selector View Controller-->
<scene sceneID="C7c-yg-gQK">
<objects>
<viewController storyboardIdentifier="OSCProfilesTableViewController" id="0K8-kQ-xlN" customClass="OSCProfilesTableViewController" customModule="VoidLink" customModuleProvider="target" sceneMemberID="viewController">
<viewController storyboardIdentifier="ProfileSelectorViewController" id="0K8-kQ-xlN" customClass="ProfileSelectorViewController" customModule="VoidLink" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="bTO-bs-q93">
<rect key="frame" x="0.0" y="0.0" width="852" height="393"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView opaque="NO" clearsContextBeforeDrawing="NO" alpha="0.5" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="-1" estimatedSectionHeaderHeight="-1" sectionFooterHeight="-1" estimatedSectionFooterHeight="-1" translatesAutoresizingMaskIntoConstraints="NO" id="kTH-ez-aKD">
<rect key="frame" x="80" y="156" width="692" height="217"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="sectionIndexBackgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tableView>
<navigationBar alpha="0.77500000000000002" contentMode="scaleToFill" translucent="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zsA-6X-Qc5">
<rect key="frame" x="80" y="112" width="692" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="R90-2b-UrU"/>
</constraints>
<color key="barTintColor" red="0.81960784310000001" green="0.81960784310000001" blue="0.83921568629999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<items>
<navigationItem style="editor" id="5pN-Sn-SCN">
<leftBarButtonItems>
<barButtonItem title="ExportAll" id="D9X-7p-9oc">
<color key="tintColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<action selector="exportDataTapped:" destination="0K8-kQ-xlN" id="Sam-Ct-kqC"/>
</connections>
</barButtonItem>
<barButtonItem title="Import" id="Z9M-yq-an2">
<color key="tintColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<action selector="importDataTapped:" destination="0K8-kQ-xlN" id="OaF-8j-7Wy"/>
</connections>
</barButtonItem>
<barButtonItem title="Restore" id="sUC-Ur-7eI">
<color key="tintColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<action selector="restoreTapped:" destination="0K8-kQ-xlN" id="lAJ-eO-ez7"/>
</connections>
</barButtonItem>
</leftBarButtonItems>
<rightBarButtonItems>
<barButtonItem title="Duplicate" width="80" style="plain" id="bue-hp-CWD">
<color key="tintColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<action selector="duplicateTapped:" destination="0K8-kQ-xlN" id="Gb7-bx-nxp"/>
</connections>
</barButtonItem>
<barButtonItem title="Delete" width="80" style="plain" id="ghM-zc-sr6">
<color key="tintColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<action selector="deleteTapped:" destination="0K8-kQ-xlN" id="h6N-Gl-oiW"/>
</connections>
</barButtonItem>
</rightBarButtonItems>
</navigationItem>
</items>
</navigationBar>
</subviews>
<viewLayoutGuide key="safeArea" id="tvP-w4-JSG"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="zsA-6X-Qc5" firstAttribute="leading" secondItem="tvP-w4-JSG" secondAttribute="leading" constant="80" id="0Fi-vo-M6P"/>
<constraint firstAttribute="bottom" secondItem="kTH-ez-aKD" secondAttribute="bottom" constant="20" symbolic="YES" id="1FM-gO-6PR"/>
<constraint firstItem="tvP-w4-JSG" firstAttribute="trailing" secondItem="kTH-ez-aKD" secondAttribute="trailing" constant="80" id="ArS-pG-QUR"/>
<constraint firstItem="kTH-ez-aKD" firstAttribute="leading" secondItem="tvP-w4-JSG" secondAttribute="leading" constant="80" id="Ujg-Sw-4hq"/>
<constraint firstItem="tvP-w4-JSG" firstAttribute="trailing" secondItem="zsA-6X-Qc5" secondAttribute="trailing" constant="80" id="YKK-qs-mZR"/>
<constraint firstItem="zsA-6X-Qc5" firstAttribute="top" secondItem="bTO-bs-q93" secondAttribute="topMargin" constant="16" id="gZg-eJ-rnP"/>
<constraint firstItem="kTH-ez-aKD" firstAttribute="top" secondItem="zsA-6X-Qc5" secondAttribute="bottom" id="txs-Df-ANQ"/>
</constraints>
</view>
<size key="freeformSize" width="852" height="393"/>
<connections>
<outlet property="profileTableViewNavigationBar" destination="zsA-6X-Qc5" id="13u-ks-fVC"/>
<outlet property="profileTableViewNavigationItem" destination="5pN-Sn-SCN" id="Ybc-XZ-vcP"/>
<outlet property="tableView" destination="kTH-ez-aKD" id="pqR-CC-Kfh"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Sod-EL-V75" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
@@ -3796,19 +3729,19 @@
<image name="trash.fill" catalog="system" width="117" height="128"/>
<image name="tray.full.fill" catalog="system" width="128" height="88"/>
<systemColor name="linkColor">
<color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemBrownColor">
<color red="0.67450980392156867" green="0.49803921568627452" blue="0.36862745098039218" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.63529411759999999" green="0.51764705879999995" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemGray2Color">
<color red="0.68235294117647061" green="0.68235294117647061" blue="0.69803921568627447" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.68235294120000001" green="0.68235294120000001" blue="0.69803921570000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemGray3Color">
<color red="0.7803921568627451" green="0.7803921568627451" blue="0.80000000000000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.78039215689999997" green="0.78039215689999997" blue="0.80000000000000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemTealColor">
<color red="0.0" green="0.76470588235294112" blue="0.81568627450980391" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.18823529410000001" green="0.69019607839999997" blue="0.78039215689999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>
+6 -3
View File
@@ -696,7 +696,7 @@ final class ControllerNavigator: NSObject {
if let uiNavigationDelegate = uiNavigationDelegate, !mainFrameVC.isStreaming() || mainFrameVC.settingsExpandedInStreamView || uiNavigationDelegate is ToolboxViewController {
if let uiNavigationDelegate = uiNavigationDelegate, !mainFrameVC.isStreaming() || mainFrameVC.settingsExpandedInStreamView || uiNavigationDelegate is ToolboxViewController || uiNavigationDelegate is ProfileSelectorViewController {
let verticalNavigationAxis: ControllerElement = ControllerNavigator.radialMenuButtonPosition == .right ? .leftStickY : .rightStickY
ControllerUtil.listenPrimaryControllerStickAxis(verticalNavigationAxis, threshold: 0.6) {state in
GamepadNavigationIllustrationHud.updateActionState(for: verticalNavigationAxis, isInAction: state != .orderedSame)
@@ -726,7 +726,7 @@ final class ControllerNavigator: NSObject {
}
}
if let uiNavigationDelegate = uiNavigationDelegate, !mainFrameVC.isStreaming() || uiNavigationDelegate is ToolboxViewController{
if let uiNavigationDelegate = uiNavigationDelegate, !mainFrameVC.isStreaming() || uiNavigationDelegate is ToolboxViewController || uiNavigationDelegate is ProfileSelectorViewController {
let horizontalNavigationAxis: ControllerElement = ControllerNavigator.radialMenuButtonPosition == .right ? .leftStickX : .rightStickX
ControllerUtil.listenPrimaryControllerStickAxis(horizontalNavigationAxis, threshold: 0.6) {state in
GamepadNavigationIllustrationHud.updateActionState(for: horizontalNavigationAxis, isInAction: state != .orderedSame)
@@ -750,7 +750,7 @@ final class ControllerNavigator: NSObject {
}
}
if let uiNavigationDelegate = uiNavigationDelegate, !mainFrameVC.isStreaming() || mainFrameVC.settingsExpandedInStreamView || uiNavigationDelegate is UIAlertController || uiNavigationDelegate is OSCProfilesTableViewController || uiNavigationDelegate is ToolboxViewController {
if let uiNavigationDelegate = uiNavigationDelegate, !mainFrameVC.isStreaming() || mainFrameVC.settingsExpandedInStreamView || uiNavigationDelegate is UIAlertController || uiNavigationDelegate is ProfileSelectorViewController || uiNavigationDelegate is ToolboxViewController {
let navigations = uiNavigationDelegate.getNavigationElements()
let buttonNavigations = navigations.filter({$0.control.type == .button})
// let stickNavigations = navigations.filter({$0.control.type == .stick || $0.control.type == .stickAxis})
@@ -1494,6 +1494,9 @@ extension ControllerCollectionNavigationDelegate {
}
func applyControllerNavigationHighlight(to cell: UICollectionViewCell, highlighted: Bool) {
if self is ProfileSelectorViewController {
return
}
clearControllerNavigationHighlightBorder(in: cell)
guard highlighted else { return }
let highlightedView = controllerNavigationHighlightTargetView(for: cell)
@@ -86,6 +86,10 @@ class OSCProfilesManager: NSObject {
persistEncodedProfiles(profilesEncoded)
}
func persistProfileOrder(_ orderedProfiles: NSMutableArray) {
persistProfiles(orderedProfiles)
}
private func persistEncodedProfiles(_ profilesEncoded: NSMutableArray) {
guard let data = try? NSKeyedArchiver.archivedData(withRootObject: profilesEncoded, requiringSecureCoding: true) else {
return
@@ -172,13 +176,19 @@ class OSCProfilesManager: NSObject {
func importEncodedProfiles(_ profilesEncoded: NSMutableArray) {
let localProfiles = currentProfiles.mutableCopy() as? NSMutableArray ?? NSMutableArray()
let profilesDecoded = decodeProfiles(from: profilesEncoded)
let importedIncludesDefault = (profilesDecoded.firstObject as? OSCProfile)?.name == "Default"
let isSingleProfile = profilesEncoded.count == 1
if localProfiles.count > 0 {
if importedIncludesDefault && localProfiles.count > 0 {
localProfiles.removeObject(at: 0)
} else if !importedIncludesDefault,
let localDefaultProfile = localProfiles.firstObject as? OSCProfile,
localDefaultProfile.name == "Default" {
profilesDecoded.insert(localDefaultProfile, at: 0)
localProfiles.removeObject(at: 0)
}
let profilesDecoded = decodeProfiles(from: profilesEncoded)
let localProfileNames = Set(
localProfiles.compactMap { ($0 as? OSCProfile)?.name }
)
@@ -202,13 +212,24 @@ class OSCProfilesManager: NSObject {
for case let profile as Any in localProfilesToRemove {
localProfiles.remove(profile)
}
var indexOffset = 0
var indexOffset = importedIncludesDefault ? 0: 0
for profile in localProfiles {
profilesDecoded.insert(profile, at: 1+indexOffset)
indexOffset += 1
}
if isSingleProfile, !importedIncludesDefault {
for profile in profilesDecoded {
if let profile = profile as? OSCProfile {
profile.isSelected = false
}
}
if let profile = profilesDecoded[profilesDecoded.count-1] as? OSCProfile {
profile.isSelected = true
}
}
persistEncodedProfiles(encodedProfiles(from: profilesDecoded))
}
+116 -1
View File
@@ -4592,6 +4592,29 @@
}
}
},
"All Profiles" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "全部配置"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "全部設定檔"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "全部設定檔"
}
}
}
},
"Alpha" : {
"extractionState" : "manual",
"localizations" : {
@@ -6511,6 +6534,29 @@
}
}
},
"Current Profile" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "当前配置"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "目前設定檔"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "目前設定檔"
}
}
}
},
"Del" : {
"extractionState" : "manual",
"localizations" : {
@@ -6586,6 +6632,29 @@
}
}
},
"Duplicate" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "复制"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "複製"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "複製"
}
}
}
},
"Delete Buttons Here" : {
"extractionState" : "manual",
"localizations" : {
@@ -8142,6 +8211,29 @@
}
}
},
"Export" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "导出"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "匯出"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "匯出"
}
}
}
},
"Expands setting menu during streaming" : {
"extractionState" : "manual",
"localizations" : {
@@ -14837,6 +14929,29 @@
}
}
},
"Restore" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "恢复"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "還原"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "還原"
}
}
}
},
"Restore Purchase" : {
"extractionState" : "manual",
"localizations" : {
@@ -18862,4 +18977,4 @@
}
},
"version" : "1.1"
}
}
+30 -3
View File
@@ -33,17 +33,44 @@ class ThemeManager: NSObject {
_ = setPublicUIStyle()
return _userInterfaceStyle
}
@objc class func overrideUserInterfaceStyle() -> UIUserInterfaceStyle {
return _privateUserInterfaceStyle
}
@objc class func setUserInterfaceStyle(_ style: UIUserInterfaceStyle) {
let oldUserInterfaceStyle = _userInterfaceStyle
_privateUserInterfaceStyle = style
applyUserInterfaceStyleOverride(style)
if _userInterfaceStyle == style {
_ = setPublicUIStyle()
if oldUserInterfaceStyle == _userInterfaceStyle {
return
}
_ = setPublicUIStyle()
NotificationCenter.default.post(
name: Notification.Name(ThemeDidChangeNotification),
object: nil
)
}
@objc class func systemUserInterfaceStyleDidChange(_ style: UIUserInterfaceStyle) {
guard #available(iOS 13.0, *) else { return }
if _privateUserInterfaceStyle != .unspecified {
applyUserInterfaceStyleOverride(_privateUserInterfaceStyle)
return
}
let oldUserInterfaceStyle = _userInterfaceStyle
_userInterfaceStyle = style
applyUserInterfaceStyleOverride(.unspecified)
if oldUserInterfaceStyle == _userInterfaceStyle {
return
}
NotificationCenter.default.post(
name: Notification.Name(ThemeDidChangeNotification),
object: nil
@@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)profileRefresh;
- (void)reloadOnScreenWidgetViews;
- (void)presentProfilesTableViewWithLoadingMode:(NSInteger)loadingMode;
- (void)presentProfileSelectorWithLoadingMode:(NSInteger)loadingMode;
@end
@@ -322,14 +322,14 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleProfileTablViewDismiss)
name:@"OscLayoutTableViewCloseNotification"
selector:@selector(handleProfileSelectorDismiss)
name:@"ProfileSelectorCloseNotification"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(dummytest)
name:@"OscLayoutProfileSelctedInTableView" // This is a special notification for reloading the on screen keyboard buttons. which can't be executed by _oscProfilesTableViewController.needToUpdateOscLayoutTVC code block, and has to be triggered by a notification
name:@"OscLayoutProfileSelectedNotification" // This is a special notification for reloading the on screen keyboard buttons. It has to be triggered by a notification because it cannot be handled by the profile selector dismiss block.
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
@@ -364,7 +364,7 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
selectedWidgetView = nil;
selectedControllerLayer = nil;
_oscProfilesTableViewController.layoutViewBounds = self.view.bounds;
_profileSelectorViewController.layoutViewBounds = self.view.bounds;
[OSCProfilesManager setLayoutViewBounds:self.view.bounds];
[OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager
@@ -1029,7 +1029,7 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
else{
UIAlertController * savedAlertController = [UIAlertController alertControllerWithTitle: [NSString stringWithFormat:@""] message: [LocalizationHelper localizedStringForKey:@"Profile Default can not be overwritten"] preferredStyle:UIAlertControllerStyleAlert];
[savedAlertController addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Ok"] style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self.oscProfilesTableViewController profileViewRefresh]; // execute this will reset layout in OSC tool!
[self.profileSelectorViewController profileViewRefresh]; // execute this will reset layout in OSC tool!
}]];
if(sender) [self presentViewController:savedAlertController animated:YES completion:nil];
}
@@ -1863,7 +1863,7 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone);
}
- (void)handleProfileTablViewDismiss{
- (void)handleProfileSelectorDismiss{
if(_quickSwitchEnabled){
[self clearOnScreenWidgets];
[self dismissViewControllerAnimated:NO completion:^{
@@ -1876,28 +1876,19 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
/* Basically the same method as loadTapped, without parameter*/
// Make sure whenever self view controller load the selected profile and layout its buttons.
- (void)profileRefresh{
UIStoryboard *storyboard;
BOOL isIPhone = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone);
if (isIPhone) {
storyboard = [UIStoryboard storyboardWithName:@"iPhone" bundle:nil];
}
else {
storyboard = [UIStoryboard storyboardWithName:@"iPad" bundle:nil];
}
//initialiaze _oscProfilesTableViewController
self->_oscProfilesTableViewController = [storyboard instantiateViewControllerWithIdentifier:@"OSCProfilesTableViewController"];
// initialiaze _profileSelectorViewController
self->_profileSelectorViewController = [[ProfileSelectorViewController alloc] init];
self->_profileSelectorViewController.layoutViewBounds = self.view.bounds;
//this part is just for registration, will not be immediately executed.
__weak typeof(self) weakSelf = self;
self->_oscProfilesTableViewController.needToUpdateOscLayoutTVC = ^() { // a block that will be called when the modally presented 'OSCProfilesTableViewController' VC is dismissed. By the time the 'OSCProfilesTableViewController' VC is dismissed the user would have potentially selected a different OSC profile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the profile and then hide/show and move each OSC button to their appropriate position
self->_profileSelectorViewController.needToUpdateOscLayoutTVC = ^() { // a block that will be called when the modally presented 'ProfileSelectorViewController' VC is dismissed. By the time it is dismissed the user would have potentially selected a different OSC profile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the profile and then hide/show and move each OSC button to their appropriate position
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) return;
[weakSelf reloadOnScreenWidgetViews];
strongSelf->_oscProfilesTableViewController.currentOSCButtonLayers = weakSelf.layoutOSC.OSCButtonLayerPool; //pass updated OSCLayout to OSCProfileTableView again
strongSelf->_profileSelectorViewController.currentOSCButtonLayers = weakSelf.layoutOSC.OSCButtonLayerPool; // pass updated OSCLayout to profile selector again
};
[self.oscProfilesTableViewController.tableView reloadData];
[self reloadOnScreenWidgetViews];
NSLog(@"profileRefresh %f", CACurrentMediaTime());
@@ -1906,43 +1897,34 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
// [self presentViewController:vc animated:YES completion:nil];
}
- (void) presentProfilesTableViewWithPickProfile:(bool)pickProfile{
- (void) presentProfileSelectorWithPickProfile:(bool)pickProfile{
[self hideStickIndicators];
selectedWidgetView = nil;
// if(pickProfile) [self clearOnScreenWidgets];
UIStoryboard *storyboard;
BOOL isIPhone = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone);
if (isIPhone) {
storyboard = [UIStoryboard storyboardWithName:@"iPhone" bundle:nil];
}
else {
storyboard = [UIStoryboard storyboardWithName:@"iPad" bundle:nil];
}
_oscProfilesTableViewController = [storyboard instantiateViewControllerWithIdentifier:@"OSCProfilesTableViewController"];
_oscProfilesTableViewController.layoutViewBounds = self.view.bounds;
_profileSelectorViewController = [[ProfileSelectorViewController alloc] init];
_profileSelectorViewController.layoutViewBounds = self.view.bounds;
__weak typeof(self) weakSelf = self;
_oscProfilesTableViewController.needToUpdateOscLayoutTVC = ^() { // a block that will be called when the modally presented 'OSCProfilesTableViewController' VC is dismissed. By the time the 'OSCProfilesTableViewController' VC is dismissed the user would have potentially selected a different OSC ofile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the proffile and then hide/show and move each OSC button to their appropriate position
_profileSelectorViewController.needToUpdateOscLayoutTVC = ^() { // a block that will be called when the modally presented 'ProfileSelectorViewController' VC is dismissed. By the time it is dismissed the user would have potentially selected a different OSC profile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the profile and then hide/show and move each OSC button to their appropriate position
if(!pickProfile) [weakSelf reloadOnScreenWidgetViews];
};
self.widgetPanelStack.hidden = YES;
_oscProfilesTableViewController.currentOSCButtonLayers = self.layoutOSC.OSCButtonLayerPool;
_profileSelectorViewController.currentOSCButtonLayers = self.layoutOSC.OSCButtonLayerPool;
// _oscProfilesTableViewController.modalPresentationStyle = UIModalPresentationCurrentContext;
_oscProfilesTableViewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
// _oscProfilesTableViewController.modalPresentationStyle = UIModalPresentationPageSheet;
_oscProfilesTableViewController.pickProfileEnabled = pickProfile;
[self presentViewController:_oscProfilesTableViewController animated:NO completion:nil];
// _profileSelectorViewController.modalPresentationStyle = UIModalPresentationCurrentContext;
_profileSelectorViewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
// _profileSelectorViewController.modalPresentationStyle = UIModalPresentationPageSheet;
_profileSelectorViewController.pickProfileEnabled = pickProfile;
[self presentViewController:_profileSelectorViewController animated:NO completion:nil];
}
/* Presents the view controller that lists all OSC profiles the user can choose from */
- (IBAction) loadTapped:(id)sender {
[self saveTapped:nil];
[self presentProfilesTableViewWithPickProfile:false];
[self presentProfileSelectorWithPickProfile:false];
}
@@ -2081,7 +2063,7 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
if([profilesManager getIndexOfSelectedProfile] == 0 && [self.layoutOSC.layoutChanges count] > 0){
UIAlertController * movedAlertController = [UIAlertController alertControllerWithTitle: [NSString stringWithFormat:@""] message: [LocalizationHelper localizedStringForKey:@"Layout of the Default profile can not be changed"] preferredStyle:UIAlertControllerStyleAlert];
[movedAlertController addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Ok"] style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self.oscProfilesTableViewController profileViewRefresh];
[self.profileSelectorViewController profileViewRefresh];
}]];
[self presentViewController:movedAlertController animated:YES completion:nil];
}
@@ -188,7 +188,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
@IBOutlet private weak var toolbarTopConstraintiPhone: NSLayoutConstraint!
@IBOutlet private weak var toolbarTopConstraintiPad: NSLayoutConstraint!
var oscProfilesTableViewController: OSCProfilesTableViewController?
var profileSelectorViewController: ProfileSelectorViewController?
private var profilesManager: OSCProfilesManager!
private var bulkEditEnabled: Bool = false
private var selectedWidget: OnScreenWidgetView?
@@ -286,7 +286,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(legacyOscLayerTapped(_:)), name: Notification.Name("LegacyOscCALayerSelectedNotification"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleProfileTablViewDismiss), name: Notification.Name("OscLayoutTableViewCloseNotification"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleProfileSelectorDismiss), name: Notification.Name("ProfileSelectorCloseNotification"), object: nil)
// NotificationCenter.default.addObserver(self, selector: #selector(dummytest), name: Notification.Name("GameProfileSelectedNotification"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(widgetViewTapped(_:)), name: Notification.Name("OnScreenWidgetViewSelected"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(OSCLayoutChanged), name: Notification.Name("OSCLayoutChanged"), object: nil)
@@ -358,7 +358,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
}
@objc func reloadOnScreenWidgetViews() {
if self.profileTableLoadingMode == .selectProfileFromMainFrame { return }
if self.profileSelectorLoadingMode == .selectProfileFromMainFrame { return }
OnScreenWidgetView.isTweakingHighlight = false
OnScreenWidgetView.editMode = true
@@ -369,7 +369,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
guard let oscProfile = self.profilesManager?.getSelectedProfile() else { return }
if self.profileTableLoadingMode == .selectProfileFromStreamView {
if self.profileSelectorLoadingMode == .selectProfileFromStreamView || self.profileSelectorLoadingMode == .pickProfile {
NotificationCenter.default.post(name: Notification.Name("GameProfileSelectedNotification"), object: oscProfile)
}
@@ -525,66 +525,68 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
}
@objc func profileRefresh() {
let storyboardName = UIDevice.current.userInterfaceIdiom == .phone ? "iPhone" : "iPad"
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
oscProfilesTableViewController = storyboard.instantiateViewController(withIdentifier: "OSCProfilesTableViewController") as? OSCProfilesTableViewController
oscProfilesTableViewController?.needToUpdateOscLayoutTVC = { [weak self] in
profileSelectorViewController = ProfileSelectorViewController()
profileSelectorViewController?.layoutViewBounds = view.bounds
profileSelectorViewController?.needToUpdateOscLayoutTVC = { [weak self] in
guard let self else { return }
if !loadJustTapped {
self.reloadOnScreenWidgetViews()
}
loadJustTapped = false
self.oscProfilesTableViewController?.currentOSCButtonLayers = self.layoutOSC.oscButtonLayerPool
self.profileSelectorViewController?.currentOSCButtonLayers = self.layoutOSC.oscButtonLayerPool
}
oscProfilesTableViewController?.tableView?.reloadData()
reloadOnScreenWidgetViews()
}
@objc var profileTableLoadingMode: OSCProfilesTableViewLoadingMode = .selectProfile
@objc(presentProfilesTableViewWithLoadingMode: animated:)
func presentProfilesTableView(with loadingMode: OSCProfilesTableViewLoadingMode, animated: Bool = false) {
profileTableLoadingMode = loadingMode
presentProfilesTableView(with: loadingMode, pickedProfileDataHandler: nil, animated:animated)
@objc var profileSelectorLoadingMode: ProfileSelectorLoadingMode = .selectProfile
@objc(presentProfileSelectorWithLoadingMode:)
func presentProfileSelector(with loadingModeRawValue: Int) {
guard let loadingMode = ProfileSelectorLoadingMode(rawValue: loadingModeRawValue) else {
return
}
presentProfileSelector(with: loadingMode)
}
private func presentProfilesTableView(
with loadingMode: OSCProfilesTableViewLoadingMode,
@objc func presentProfileSelector(with loadingMode: ProfileSelectorLoadingMode, animated: Bool = false) {
profileSelectorLoadingMode = loadingMode
presentProfileSelector(with: loadingMode, pickedProfileDataHandler: nil, animated:animated)
}
private func presentProfileSelector(
with loadingMode: ProfileSelectorLoadingMode,
pickedProfileDataHandler: ((OSCProfile) -> Void)? = nil,
animated: Bool = false
) {
hideStickIndicators()
if loadingMode != .pickProfileData {selectedWidget = nil}
let storyboardName = UIDevice.current.userInterfaceIdiom == .phone ? "iPhone" : "iPad"
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "OSCProfilesTableViewController") as? OSCProfilesTableViewController
controller?.layoutViewBounds = view.bounds
controller?.needToUpdateOscLayoutTVC = { [weak self] in
let controller = ProfileSelectorViewController()
controller.layoutViewBounds = view.bounds
controller.needToUpdateOscLayoutTVC = { [weak self] in
guard let self else { return }
if loadingMode == .selectProfile || loadingMode == .selectProfileFromStreamView {
if loadingMode == .selectProfile || loadingMode == .selectProfileFromStreamView || loadingMode == .pickProfile {
self.reloadOnScreenWidgetViews()
}
}
controller?.currentOSCButtonLayers = layoutOSC.oscButtonLayerPool
controller?.modalPresentationStyle = .overCurrentContext
controller?.loadingMode = loadingMode
controller?.pickedProfileDataHandler = pickedProfileDataHandler
controller.currentOSCButtonLayers = layoutOSC.oscButtonLayerPool
controller.modalPresentationStyle = .overCurrentContext
controller.loadingMode = loadingMode
controller.pickedProfileDataHandler = pickedProfileDataHandler
widgetPanelStack.isHidden = true
oscProfilesTableViewController = controller
if let controller {
present(controller, animated: animated)
}
profileSelectorViewController = controller
present(controller, animated: animated)
}
private var loadJustTapped:Bool = false
@IBAction func loadTapped(_ sender: Any?) {
// saveTapped(nil)
loadJustTapped = true
presentProfilesTableView(with: .selectProfile)
presentProfileSelector(with: .selectProfile)
}
@IBAction func importFromOtherButtonTapped(_ sender: Any?) {
// importFromOtherButton.setTitle(LocalizationHelper.localizedString(forKey: "Import"), for: .normal)
presentProfilesTableView(with: .pickProfileData) { [weak self] profile in
presentProfileSelector(with: .pickProfileData) { [weak self] profile in
if profile.name == self?.profilesManager.getSelectedProfile().name {return}
self?.loadWidgets(from: profile, to: self?.selectedWidget)
}
@@ -647,8 +649,8 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
*/
@objc private func handleReturnToForeground() {
if (self.profileTableLoadingMode != .selectProfileFromMainFrame
&& self.profileTableLoadingMode != .selectProfileFromMainFrame)
if (self.profileSelectorLoadingMode != .selectProfileFromMainFrame
&& self.profileSelectorLoadingMode != .selectProfileFromMainFrame)
{setupWidgetPanel()}
updateViewBounds()
}
@@ -657,7 +659,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
super.viewWillTransition(to: size, with: coordinator)
viewWillBeResized = true
hideStickIndicators()
if self.profileTableLoadingMode == .selectProfile {
if self.profileSelectorLoadingMode == .selectProfile {
let oldSize = view.bounds.size
let scaleX = size.width / oldSize.width
@@ -710,7 +712,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
viewWillBeResized = false
selectedWidget = nil
selectedControllerLayer = nil
oscProfilesTableViewController?.layoutViewBounds = view.bounds
profileSelectorViewController?.layoutViewBounds = view.bounds
OSCProfilesManager.setLayoutViewBounds(view.bounds)
OSCProfilesManager.setOnScreenWidgetViewsSet(onScreenWidgetViews)
// reloadOnScreenWidgetViews()
@@ -1560,7 +1562,6 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
let alert = UIAlertController(title: success ? LocalizationHelper.localizedString(forKey: "Profile updated successfully") : "", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: LocalizationHelper.localizedString(forKey: "Ok"), style: .default) { _ in
if !success {
// self.oscProfilesTableViewController?.profileViewRefresh()
}
})
present(alert, animated: true)
@@ -1628,11 +1629,11 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
}
}
@objc private func handleProfileTablViewDismiss() {
@objc private func handleProfileSelectorDismiss() {
switch profileTableLoadingMode {
switch profileSelectorLoadingMode {
case .selectProfile:
if oscProfilesTableViewController?.loadingMode != .pickProfileData {
if profileSelectorViewController?.loadingMode != .pickProfileData {
setupWidgetPanel()
}
else {
@@ -1647,17 +1648,6 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
case .pickProfileData:
widgetPanelStack.isHidden = false
}
/*
if quickSwitchEnabled {
clearOnScreenWidgets()
dismiss(animated: false)
} else {
if oscProfilesTableViewController?.loadingMode != .pickProfileData {
setupWidgetPanel()
}
else {widgetPanelStack.isHidden = false}
}*/
}
private func addInnerAnalogSticksToOuterAnalogLayers() {
@@ -1731,7 +1721,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
}
private func setupWidgetPanel() {
widgetPanelStack.isHidden = profileTableLoadingMode != .selectProfile
widgetPanelStack.isHidden = profileSelectorLoadingMode != .selectProfile
tipTitleLabel.textAlignment = .left
tipTitleLabel.contentMode = .top
tipTitleLabel.lineBreakMode = .byWordWrapping
@@ -2590,7 +2580,7 @@ final class LayoutOnScreenControlsViewController: UIViewController, OnScreenWidg
if profilesManager.getIndexOfSelectedProfile() == 0, layoutOSC.layoutChanges.count > 0 {
let alert = UIAlertController(title: "", message: LocalizationHelper.localizedString(forKey: "Layout of the Default profile can not be changed"), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: LocalizationHelper.localizedString(forKey: "Ok"), style: .default) { _ in
self.oscProfilesTableViewController?.profileViewRefresh()
self.profileSelectorViewController?.profileViewRefresh()
})
present(alert, animated: true)
}
@@ -1,662 +0,0 @@
//
// OSCProfilesTableViewController.m -> OSCProfilesTableViewController.swift
// Moonlight
//
// Created by Long Le on 11/28/22.
// Copyright © 2022 Moonlight Game Streaming Project. All rights reserved.
//
// Modified by True砖家 since 2024.6.24
// Copyright © 2024 True砖家 on Bilibili. All rights reserved.
//
import UIKit
final class ProfileTableViewCell: UITableViewCell {
@IBOutlet weak var name: UILabel!
}
@objc(FileOperation)
enum FileOperation: Int {
case importOperation = 0
case exportOperation = 1
}
@objc enum OSCProfilesTableViewLoadingMode: Int {
case selectProfile
case selectProfileFromStreamView
case selectProfileFromMainFrame
case pickProfile
case pickProfileData
}
@objcMembers
final class OSCProfilesTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIDocumentPickerDelegate, UIGestureRecognizerDelegate, ControllerUINavigationDelegate {
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var profileTableViewNavigationBar: UINavigationBar!
@IBOutlet private weak var profileTableViewNavigationItem: UINavigationItem!
var currentFileOperation: FileOperation = .importOperation
var needToUpdateOscLayoutTVC: (() -> Void)?
var loadingMode: OSCProfilesTableViewLoadingMode = .selectProfile
@nonobjc var pickedProfileDataHandler: ((OSCProfile) -> Void)?
weak var currentOSCButtonLayers: NSMutableSet?
var layoutViewBounds: CGRect = .zero
private var profilesManager: OSCProfilesManager!
private var horizontalConstraintsConfigured = false
private var profiles: NSMutableArray = []
private var selectedProfileIndex = 0
private var profilesLoadGeneration = 0
private func contentWidthMultiplier() -> CGFloat {
PublicUtils.viewIsLandscape(view) ? (PublicUtils.isIPhone ? 0.8 : 0.65) : (PublicUtils.isIPhone ? 0.83 : 0.85)
}
private func updateHorizontalLayoutConstraints() {
let horizontalConstraints = view.constraints.filter { constraint in
let firstItem = constraint.firstItem as? UIView
let secondItem = constraint.secondItem as? UIView
let involvesManagedView = firstItem == tableView ||
firstItem == profileTableViewNavigationBar ||
secondItem == tableView ||
secondItem == profileTableViewNavigationBar
let isHorizontalConstraint: Bool = {
switch (constraint.firstAttribute, constraint.secondAttribute) {
case (.leading, _), (.trailing, _), (.left, _), (.right, _), (.width, _), (.centerX, _),
(_, .leading), (_, .trailing), (_, .left), (_, .right), (_, .width), (_, .centerX):
return true
default:
return false
}
}()
return involvesManagedView && isHorizontalConstraint
}
NSLayoutConstraint.deactivate(horizontalConstraints)
[tableView, profileTableViewNavigationBar].forEach { controlledView in
controlledView?.translatesAutoresizingMaskIntoConstraints = false
}
NSLayoutConstraint.activate([
profileTableViewNavigationBar.centerXAnchor.constraint(equalTo: view.centerXAnchor),
profileTableViewNavigationBar.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: contentWidthMultiplier()),
tableView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
tableView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: contentWidthMultiplier()),
])
}
private func getCurrentOrientation() -> UIInterfaceOrientationMask {
let bounds = UIScreen.main.bounds
if bounds.width > bounds.height {
return .landscape
} else {
return [.portrait, .portraitUpsideDown]
}
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
getCurrentOrientation()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.post(name: Notification.Name("OscLayoutTableViewCloseNotification"), object: self)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if #available(iOS 13.0, *) {
ControllerNavigator.restorePreviousUINavigationDelegate(ifCurrentDelegateIs: self)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
profileTableViewNavigationBar.layer.cornerRadius = 15
profileTableViewNavigationBar.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
profileTableViewNavigationBar.layer.masksToBounds = true
tableView.layer.cornerRadius = 15
tableView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
tableView.layer.masksToBounds = true
tableView.separatorStyle = .singleLine
tableView.separatorInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
if PublicUtils.liquidGlassEnabled {
tableView.separatorColor = UIColor.white.withAlphaComponent(PublicUtils.isIPhone ? 0.28 : 0.165)
} else {
tableView.separatorColor = UIColor.white.withAlphaComponent(0.3)
}
}
override func viewDidLoad() {
super.viewDidLoad()
profilesManager = OSCProfilesManager.sharedManager(layoutViewBounds)
configureTableView()
updateHorizontalLayoutConstraints()
horizontalConstraintsConfigured = true
tableView.alpha = 1
tableView.backgroundColor = UIColor.black.withAlphaComponent(self.loadingMode == .selectProfileFromMainFrame ? (
ThemeManager.userInterfaceStyle() == .dark ? 0.83 : 0.6
) : 0.43)
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissSelf))
tap.cancelsTouchesInView = false
tap.delegate = self
view.addGestureRecognizer(tap)
if PublicUtils.liquidGlassEnabled, #available(iOS 26.0, *) {
profileTableViewNavigationItem.leftBarButtonItems?.forEach { $0.hidesSharedBackground = true }
profileTableViewNavigationItem.rightBarButtonItems?.forEach { $0.hidesSharedBackground = true }
}
reloadProfilesAsync(scrollToSelected: true)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { _ in
self.updateHorizontalLayoutConstraints()
self.view.layoutIfNeeded()
})
}
@objc private func dismissSelf() {
dismiss(animated: false)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !horizontalConstraintsConfigured {
updateHorizontalLayoutConstraints()
horizontalConstraintsConfigured = true
}
configureTableView()
if profiles.count > 0 {
scrollToSelectedProfile(animated: true)
} else {
reloadProfilesAsync(scrollToSelected: true)
}
if (loadingMode == .selectProfileFromMainFrame || loadingMode == .selectProfileFromStreamView)
&& ControllerUtil.primaryGCController != nil {
if #available(iOS 13.0, *) {
ControllerNavigator.setUINavigationDelegate(self)
}
}
}
private func configureTableView() {
tableView.tableHeaderView = UIView(frame: .zero)
tableView.delegate = self
tableView.dataSource = self
tableView.register(UINib(nibName: "ProfileTableViewCell", bundle: nil), forCellReuseIdentifier: "Cell")
}
private func reloadProfilesAsync(scrollToSelected: Bool = false, notify: Bool = false) {
profilesLoadGeneration += 1
let generation = profilesLoadGeneration
let manager = profilesManager
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard let manager else { return }
let loadedProfiles = manager.getAllProfiles()
let loadedSelectedIndex = Self.selectedProfileIndex(in: loadedProfiles)
DispatchQueue.main.async { [weak self] in
guard let self, generation == self.profilesLoadGeneration else { return }
self.profiles = loadedProfiles
self.selectedProfileIndex = loadedSelectedIndex
self.tableView.reloadData()
if scrollToSelected {
self.scrollToSelectedProfile(animated: true)
}
if notify {
self.notifyProfileViewDidRefresh()
}
}
}
}
private func scrollToSelectedProfile(animated: Bool) {
guard let selectedProfileIndex = validProfileIndex(selectedProfileIndex) else {
return
}
let indexPath = IndexPath(row: selectedProfileIndex, section: 0)
tableView.scrollToRow(at: indexPath, at: .middle, animated: animated)
}
private func moveSelectedProfile(by offset: Int) {
PublicUtils.runOnMain { [weak self] in
guard let self,
self.profiles.count > 0,
self.tableView.numberOfSections > 0,
self.tableView.numberOfRows(inSection: 0) > 0 else {
return
}
let maxIndex = min(self.profiles.count, self.tableView.numberOfRows(inSection: 0)) - 1
let currentIndex = min(max(self.selectedProfileIndex, 0), maxIndex)
let nextIndex = min(max(currentIndex + offset, 0), maxIndex)
let nextIndexPath = IndexPath(row: nextIndex, section: 0)
self.tableView.selectRow(at: nextIndexPath, animated: true, scrollPosition: .middle)
self.tableView(self.tableView, didSelectRowAt: nextIndexPath)
}
}
private func validProfileIndex(_ index: Int) -> Int? {
guard profiles.count > 0 else { return nil }
return min(max(index, 0), profiles.count - 1)
}
private func notifyProfileViewDidRefresh() {
needToUpdateOscLayoutTVC?()
if loadingMode != .selectProfileFromStreamView
&& loadingMode != .pickProfile
&& loadingMode != .pickProfileData
&& loadingMode != .selectProfile
{
guard let selectedProfileIndex = validProfileIndex(selectedProfileIndex) else { return }
NotificationCenter.default.post(name: Notification.Name("GameProfileSelectedNotification"), object: self.profiles[selectedProfileIndex])
}
}
private static func selectedProfileIndex(in profiles: NSMutableArray) -> Int {
for index in 0..<profiles.count {
guard let profile = profiles.object(at: index) as? OSCProfile else {
continue
}
if profile.isSelected {
return index
}
}
return 0
}
@IBAction func duplicateTapped(_ sender: Any?) {
let inputAlert = UIAlertController(
title: LocalizationHelper.localizedString(forKey: "Enter the name you want to save this profile as"),
message: "",
preferredStyle: .alert
)
inputAlert.addTextField { textField in
textField.placeholder = LocalizationHelper.localizedString(forKey: "name")
if #available(iOS 13.0, *) {
// textField.textColor = .label
}
textField.clearButtonMode = .whileEditing
textField.borderStyle = .none
}
inputAlert.addAction(UIAlertAction(title: LocalizationHelper.localizedString(forKey: "Cancel"), style: .default) { _ in
inputAlert.dismiss(animated: false)
})
inputAlert.addAction(UIAlertAction(title: LocalizationHelper.localizedString(forKey: "Save"), style: .default) { [weak self] _ in
guard let self else { return }
let enteredProfileName = inputAlert.textFields?.first?.text ?? ""
if enteredProfileName == "Default" {
let alert = UIAlertController(
title: "",
message: LocalizationHelper.localizedString(forKey: "Saving over the 'Default' profile is not allowed"),
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: LocalizationHelper.localizedString(forKey: "Ok"), style: .default) { _ in
alert.dismiss(animated: false) {
self.present(inputAlert, animated: true)
}
})
self.present(alert, animated: true)
} else if enteredProfileName.isEmpty {
let alert = UIAlertController(
title: "",
message: LocalizationHelper.localizedString(forKey: "Profile name cannot be blank!"),
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: LocalizationHelper.localizedString(forKey: "Ok"), style: .default) { _ in
alert.dismiss(animated: false) {
self.present(inputAlert, animated: true)
}
})
self.present(alert, animated: true)
} else if self.profilesManager.profileNameAlreadyExist(enteredProfileName) {
let alert = UIAlertController(
title: "",
message: LocalizationHelper.localizedString(forKey: "Profile name already exists"),
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: LocalizationHelper.localizedString(forKey: "Ok"), style: .default))
self.present(alert, animated: true)
} else {
self.profilesManager.duplicateSelectedProfile(withName: enteredProfileName)
let alert = UIAlertController(
title: "",
message: LocalizationHelper.localizedString(forKey: "Profile %@ duplicated from current layout", enteredProfileName),
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: LocalizationHelper.localizedString(forKey: "Ok"), style: .default) { _ in
alert.dismiss(animated: false)
self.profileViewRefresh()
})
self.present(alert, animated: true)
}
})
present(inputAlert, animated: true)
}
func profileViewRefresh() {
reloadProfilesAsync(notify: true)
}
@IBAction func deleteTapped(_ sender: Any?) {
profilesManager.deleteCurrentSelectedProfile()
profileViewRefresh()
}
@IBAction func exportDataTapped(_ sender: Any?) {
currentFileOperation = .exportOperation
let tempPath = NSTemporaryDirectory().appending("profiles.bin")
try? Data().write(to: URL(fileURLWithPath: tempPath), options: .atomic)
let picker = UIDocumentPickerViewController(url: URL(fileURLWithPath: tempPath), in: .exportToService)
picker.delegate = self
present(picker, animated: true)
}
@IBAction func importDataTapped(_ sender: Any?) {
currentFileOperation = .importOperation
let picker = UIDocumentPickerViewController(documentTypes: ["public.item"], in: .open)
picker.delegate = self
picker.allowsMultipleSelection = false
present(picker, animated: true)
}
@IBAction func restoreTapped(_ sender: Any?) {
profilesManager.importDefaultTemplates()
profileViewRefresh()
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let url = urls.first else { return }
switch currentFileOperation {
case .exportOperation:
profilesToFile(url)
case .importOperation:
fileToProfiles(url)
}
}
private func profilesToFile(_ destinationURL: URL) {
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: profilesManager.getEncodedProfiles(), requiringSecureCoding: true)
let accessed = destinationURL.startAccessingSecurityScopedResource()
defer {
if accessed {
destinationURL.stopAccessingSecurityScopedResource()
}
}
try data.write(to: destinationURL, options: .atomic)
} catch {
// NSLog("写入失败: \(error)")
}
}
private func fileToProfiles(_ sourceURL: URL) {
var restoreFailed = false
let accessed = sourceURL.startAccessingSecurityScopedResource()
if !accessed {
restoreFailed = true
}
defer {
if accessed {
sourceURL.stopAccessingSecurityScopedResource()
}
}
do {
let fileData = try Data(contentsOf: sourceURL)
// NSLog("profile file read: \(UInt32(fileData.count))")
let classes: [AnyClass] = [NSMutableData.self, NSMutableArray.self]
let profilesEncoded = try NSKeyedUnarchiver.unarchivedObject(ofClasses: classes, from: fileData) as? NSMutableArray
let restoredAlert = UIAlertController(
title: LocalizationHelper.localizedString(forKey: ""),
message: LocalizationHelper.localizedString(forKey: "Pofiles imported"),
preferredStyle: .alert
)
let failedAlert = UIAlertController(
title: LocalizationHelper.localizedString(forKey: ""),
message: LocalizationHelper.localizedString(forKey: "Failed to import profiles"),
preferredStyle: .alert
)
let okAction = UIAlertAction(title: LocalizationHelper.localizedString(forKey: "OK"), style: .default)
if restoreFailed || profilesEncoded == nil {
failedAlert.addAction(okAction)
present(failedAlert, animated: true)
} else {
profilesManager.importEncodedProfiles(profilesEncoded!)
restoredAlert.addAction(okAction)
present(restoredAlert, animated: true)
}
profileViewRefresh()
// NSLog("profile test: \(UInt32(profilesEncoded?.count ?? 0))")
} catch {
restoreFailed = true
let failedAlert = UIAlertController(
title: LocalizationHelper.localizedString(forKey: ""),
message: LocalizationHelper.localizedString(forKey: "Failed to import profiles"),
preferredStyle: .alert
)
failedAlert.addAction(UIAlertAction(title: LocalizationHelper.localizedString(forKey: "OK"), style: .default))
present(failedAlert, animated: true)
profileViewRefresh()
// NSLog("解码失败: \(error)")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
profiles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? ProfileTableViewCell,
indexPath.row < profiles.count,
let profile = profiles.object(at: indexPath.row) as? OSCProfile,
let nameLabel = cell.name else {
return UITableViewCell()
}
nameLabel.text = profile.name.localizedProfileName
nameLabel.backgroundColor = UIColor.clear
nameLabel.alpha = 1.0
nameLabel.textColor = UIColor.white.withAlphaComponent(0.9)
nameLabel.font = UIFont.systemFont(ofSize: 18.5, weight: .medium)
nameLabel.shadowColor = UIColor.black
nameLabel.shadowOffset = CGSize(width: 0.5, height: 0.5)
cell.backgroundColor = UIColor.clear
cell.contentView.backgroundColor = UIColor.clear
if loadingMode != .pickProfileData,
indexPath.row == selectedProfileIndex {
cell.accessoryType = UITableViewCell.AccessoryType.checkmark
let checkmarkLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
checkmarkLabel.text = "✓"
checkmarkLabel.font = .systemFont(ofSize: 25)
checkmarkLabel.textAlignment = .center
checkmarkLabel.textColor = .green
checkmarkLabel.layer.zPosition = 0
cell.accessoryView = checkmarkLabel
} else {
cell.accessoryType = UITableViewCell.AccessoryType.none
cell.accessoryView = nil
}
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
true
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
60.0
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
guard indexPath.row < profiles.count else { return }
if let profile = profiles.object(at: indexPath.row) as? OSCProfile, profile.name == "Default" {
let alert = UIAlertController(title: "", message: "Deleting the 'Default' profile is not allowed", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: LocalizationHelper.localizedString(forKey: "Ok"), style: .default) { _ in
alert.dismiss(animated: false)
})
present(alert, animated: true)
return
}
if editingStyle == .delete {
if let profile = profiles.object(at: indexPath.row) as? OSCProfile, profile.isSelected, indexPath.row > 0,
let previousProfile = profiles.object(at: indexPath.row - 1) as? OSCProfile {
previousProfile.isSelected = true
}
profiles.removeObject(at: indexPath.row)
let profilesEncoded = NSMutableArray()
for case let profileDecoded as OSCProfile in profiles {
if let profileEncoded = try? NSKeyedArchiver.archivedData(withRootObject: profileDecoded, requiringSecureCoding: true) {
profilesEncoded.add(profileEncoded)
}
}
if let data = try? NSKeyedArchiver.archivedData(withRootObject: profilesEncoded, requiringSecureCoding: true) {
UserDefaults.standard.set(data, forKey: "OSCProfiles")
UserDefaults.standard.synchronize()
}
reloadProfilesAsync()
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard indexPath.section == 0,
indexPath.row >= 0,
indexPath.row < profiles.count else {
return
}
if loadingMode == .pickProfileData,
let pickedProfile = profiles.object(at: indexPath.row) as? OSCProfile {
pickedProfileDataHandler?(pickedProfile)
dismiss(animated: false)
return
}
let selectedIndexPath = IndexPath(row: indexPath.row, section: 0)
let lastSelectedIndex = validProfileIndex(selectedProfileIndex)
let lastSelectedIndexPath = lastSelectedIndex.map { IndexPath(row: $0, section: 0) }
if selectedIndexPath != lastSelectedIndexPath {
if let lastSelectedIndexPath {
let previousCell = tableView.cellForRow(at: lastSelectedIndexPath)
previousCell?.accessoryType = .none
previousCell?.accessoryView = nil
}
profilesManager.setProfileToSelected(UInt32(indexPath.row))
selectedProfileIndex = indexPath.row
let selectedCell = tableView.cellForRow(at: selectedIndexPath)
selectedCell?.accessoryType = .checkmark
let checkmarkLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
checkmarkLabel.text = "✓"
checkmarkLabel.font = .systemFont(ofSize: 25)
checkmarkLabel.textAlignment = .center
checkmarkLabel.textColor = .green
checkmarkLabel.layer.zPosition = 0
selectedCell?.accessoryView = checkmarkLabel
let rowsToReload = [lastSelectedIndexPath, selectedIndexPath]
.compactMap { $0 }
.filter {
$0.section < tableView.numberOfSections &&
$0.row >= 0 &&
$0.row < tableView.numberOfRows(inSection: $0.section)
}
if !rowsToReload.isEmpty {
tableView.reloadRows(at: rowsToReload, with: .none)
}
}
profileViewRefresh()
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if (loadingMode != .selectProfile
&& loadingMode != .selectProfileFromMainFrame
&& loadingMode != .selectProfileFromStreamView
),
let touchView = touch.view,
touchView.isDescendant(of: tableView) {
return true
}
return touch.view == view
}
@available(iOS 13.0, *)
func getNavigationElements() -> [ControllerNavigationElement] {
var elements: [ControllerNavigationElement] = []
elements.append(ControllerNavigationElement(control:ControllerNavigator.radialMenuButtonPosition == .left ? .dpadUp : .y, action: "previousItem"))
elements.append(ControllerNavigationElement(control:ControllerNavigator.radialMenuButtonPosition == .left ? .dpadDown : .a, action: "nextItem"))
elements.append(ControllerNavigationElement(control:ControllerNavigator.radialMenuButtonPosition == .left ? .dpadRight : .x, action: "exit"))
return elements
}
func navigateByController(forward: Bool) {
}
func navigateByController(downward: Bool) {
}
func persistControllerNavigationHighlight() {}
func restoreControllerNavigationHighlight() {}
func restoreControllerNavigationHighlightAfterSettingsModeSwitch() {}
func uiWidgetActionForControllerNavigator(forward: Bool, from navigation: ControllerNavigationElement) {
}
func uiButtonActionForControllerNavigator(pressed: Bool, from navigation: ControllerNavigationElement) {
guard pressed else { return }
PublicUtils.runOnMain { [weak self] in
switch navigation.action {
case "previousItem":
self?.moveSelectedProfile(by: -1)
case "nextItem":
self?.moveSelectedProfile(by: 1)
case "exit":
self?.dismiss(animated: false)
default:
break
}
}
}
}
@@ -1,42 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" accessoryType="checkmark" indentationWidth="10" reuseIdentifier="Cell" id="KGk-i7-Jjw" customClass="ProfileTableViewCell" customModule="VoidLink" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="280" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Profile Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="b1d-b4-TCK">
<rect key="frame" x="16.000000000000007" y="11.666666666666664" width="97.666666666666686" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.95294117647058818" green="0.97647058823529409" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="0.1215686275" green="0.12941176469999999" blue="0.14117647059999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="b1d-b4-TCK" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="16" id="pxI-vz-oR6"/>
<constraint firstItem="b1d-b4-TCK" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="xCR-Og-ne7"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="aW0-zy-SZf"/>
<color key="backgroundColor" red="0.1215686275" green="0.12941176469999999" blue="0.14117647059999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="tintColor" red="0.6705882353" green="0.61568627450000002" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="name" destination="b1d-b4-TCK" id="CX8-IC-jlM"/>
</connections>
<point key="canvasLocation" x="108" y="39"/>
</tableViewCell>
</objects>
</document>
@@ -1288,13 +1288,13 @@ static NSMutableSet* hostList;
layoutToolVC.modalPresentationStyle = UIModalPresentationOverCurrentContext;
layoutToolVC.profileTableLoadingMode = OSCProfilesTableViewLoadingModeSelectProfileFromMainFrame;
layoutToolVC.profileSelectorLoadingMode = ProfileSelectorLoadingModeSelectProfileFromMainFrame;
layoutToolVC.toolbarStackView.hidden = true;
layoutToolVC.toolbarRootView.hidden = true;
self.gameProfileSelectorVC = layoutToolVC;
[self presentViewController:layoutToolVC animated:false completion:^{
[layoutToolVC presentProfilesTableViewWithLoadingMode:OSCProfilesTableViewLoadingModeSelectProfileFromMainFrame animated:animated];
[layoutToolVC presentProfileSelectorWith:ProfileSelectorLoadingModeSelectProfileFromMainFrame animated:animated];
}];
}
@@ -1663,7 +1663,7 @@ static NSMutableSet* hostList;
barButtonItem.tintColor = ThemeManager.appPrimaryColor;
if (@available(iOS 13.0, *)) {
UIUserInterfaceStyle style = ThemeManager.userInterfaceStyle;
UIUserInterfaceStyle style = ThemeManager.overrideUserInterfaceStyle;
UIView *customView = barButtonItem.customView;
customView.overrideUserInterfaceStyle = style;
@@ -1685,7 +1685,7 @@ static NSMutableSet* hostList;
- (void)applyThemeToNavigationControls {
if (@available(iOS 13.0, *)) {
UIUserInterfaceStyle style = ThemeManager.userInterfaceStyle;
UIUserInterfaceStyle style = ThemeManager.overrideUserInterfaceStyle;
self.overrideUserInterfaceStyle = style;
self.view.overrideUserInterfaceStyle = style;
self.navigationController.overrideUserInterfaceStyle = style;
@@ -1856,7 +1856,7 @@ static NSMutableSet* hostList;
TemporarySettings* tempSettings = [dataMan getSettings];
if(tempSettings.appTheme.intValue != UIUserInterfaceStyleUnspecified) return;
if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
[ThemeManager setUserInterfaceStyle:self.traitCollection.userInterfaceStyle];
[ThemeManager systemUserInterfaceStyleDidChange:self.traitCollection.userInterfaceStyle];
}
}
}
@@ -2806,8 +2806,8 @@ static NSMutableSet* hostList;
}];
}
else if(!self.gameProfileSelectorVC && !self.settingsViewController.layoutOnScreenControlsVC) [self openGameProfileSeletorWithAnimated:true];
else if (self.gameProfileSelectorVC) [self.gameProfileSelectorVC.oscProfilesTableViewController dismissViewControllerAnimated:true completion:^{}];
else if (self.settingsViewController.layoutOnScreenControlsVC) [self.settingsViewController.layoutOnScreenControlsVC.oscProfilesTableViewController dismissViewControllerAnimated:true completion:^{}];
else if (self.gameProfileSelectorVC) [self.gameProfileSelectorVC.profileSelectorViewController dismissViewControllerAnimated:true completion:^{}];
else if (self.settingsViewController.layoutOnScreenControlsVC) [self.settingsViewController.layoutOnScreenControlsVC.profileSelectorViewController dismissViewControllerAnimated:true completion:^{}];
break;
case RadialMenuItemHostView:
[self switchToHostView];
@@ -2833,8 +2833,8 @@ static NSMutableSet* hostList;
- (void)controllerNavigatorDidSelectGameProfiles {
dispatch_async(dispatch_get_main_queue(), ^{
if(!self.gameProfileSelectorVC && !self.settingsViewController.layoutOnScreenControlsVC) [self openGameProfileSeletorWithAnimated:true];
else if (self.gameProfileSelectorVC) [self.gameProfileSelectorVC.oscProfilesTableViewController dismissViewControllerAnimated:true completion:^{}];
else if (self.settingsViewController.layoutOnScreenControlsVC) [self.settingsViewController.layoutOnScreenControlsVC.oscProfilesTableViewController dismissViewControllerAnimated:true completion:^{}];
else if (self.gameProfileSelectorVC) [self.gameProfileSelectorVC.profileSelectorViewController dismissViewControllerAnimated:true completion:^{}];
else if (self.settingsViewController.layoutOnScreenControlsVC) [self.settingsViewController.layoutOnScreenControlsVC.profileSelectorViewController dismissViewControllerAnimated:true completion:^{}];
});
}
@@ -3226,9 +3226,9 @@ BOOL isCustomResolution(int resolutionSelected) {
// self.layoutOnScreenControlsVC.view.backgroundColor = [UIColor colorWithWhite:0.55 alpha:1.0];
self.layoutOnScreenControlsVC.view.backgroundColor = UIColor.clearColor;
self.layoutOnScreenControlsVC.modalPresentationStyle = UIModalPresentationOverCurrentContext;
self.layoutOnScreenControlsVC.profileTableLoadingMode = OSCProfilesTableViewLoadingModeSelectProfileFromMainFrame;
self.layoutOnScreenControlsVC.profileSelectorLoadingMode = ProfileSelectorLoadingModeSelectProfileFromMainFrame;
[self presentViewController:self.layoutOnScreenControlsVC animated:NO completion:^{
[self->_layoutOnScreenControlsVC presentProfilesTableViewWithLoadingMode:OSCProfilesTableViewLoadingModeSelectProfileFromMainFrame animated:animated];
[self->_layoutOnScreenControlsVC presentProfileSelectorWith:ProfileSelectorLoadingModeSelectProfileFromMainFrame animated:animated];
}];
_layoutOnScreenControlsVC.toolbarStackView.hidden = true;
_layoutOnScreenControlsVC.toolbarRootView.hidden = true;
@@ -1064,9 +1064,9 @@ static __weak StreamFrameViewController *VLSharedStreamFrameViewController = nil
_layoutOnScreenControlsVC.quickSwitchEnabled = true;
_layoutOnScreenControlsVC.toolbarStackView.hidden = true;
_layoutOnScreenControlsVC.toolbarRootView.hidden = true;
OSCProfilesTableViewLoadingMode loadingMode = pickProfile ? OSCProfilesTableViewLoadingModePickProfile : OSCProfilesTableViewLoadingModeSelectProfileFromStreamView;
ProfileSelectorLoadingMode loadingMode = pickProfile ? ProfileSelectorLoadingModePickProfile : ProfileSelectorLoadingModeSelectProfileFromStreamView;
[self presentViewController:_layoutOnScreenControlsVC animated:NO completion:^{
[self->_layoutOnScreenControlsVC presentProfilesTableViewWithLoadingMode:loadingMode animated:false];
[self->_layoutOnScreenControlsVC presentProfileSelectorWith:loadingMode animated:false];
}];
}
@@ -295,11 +295,8 @@ import UIKit
contentView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: contentWidthMultiplier()),
contentView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: PublicUtils.isIPhone ? 0.93 : 0.93),
contentView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
// contentView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
contentView.topAnchor.constraint(greaterThanOrEqualTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),
contentView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: PublicUtils.isIPhone ? 3 : -5),
toolbarView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: sideInset),
toolbarView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -sideInset),
@@ -316,6 +313,14 @@ import UIKit
collectionView.topAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.topAnchor, constant: topInset),
collectionView.bottomAnchor.constraint(equalTo: toolbarView.topAnchor, constant: -toolbarVerticalInset),
]
if PublicUtils.isIPhone {
layoutConstraints.append(contentView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 3))
}
else {
layoutConstraints.append(contentView.centerYAnchor.constraint(equalTo: view.centerYAnchor))
}
NSLayoutConstraint.activate(layoutConstraints)
setupToolbarConstraints()
+5 -5
View File
@@ -1647,7 +1647,7 @@
},
"Bhn-IO-Wlt.title" : {
"comment" : "Class = \"UIBarButtonItem\"; title = \"ExportAll\"; ObjectID = \"Bhn-IO-Wlt\";",
"extractionState" : "extracted_with_value",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -2409,7 +2409,7 @@
},
"CpY-Sb-BDc.title" : {
"comment" : "Class = \"UIBarButtonItem\"; title = \"Import\"; ObjectID = \"CpY-Sb-BDc\";",
"extractionState" : "extracted_with_value",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -2517,7 +2517,7 @@
},
"czn-mj-8d1.title" : {
"comment" : "Class = \"UIBarButtonItem\"; title = \"Restore\"; ObjectID = \"czn-mj-8d1\";",
"extractionState" : "extracted_with_value",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -2848,7 +2848,7 @@
},
"egC-9E-zS4.title" : {
"comment" : "Class = \"UIBarButtonItem\"; title = \"Delete\"; ObjectID = \"egC-9E-zS4\";",
"extractionState" : "extracted_with_value",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -5374,7 +5374,7 @@
},
"P34-hO-6cX.title" : {
"comment" : "Class = \"UIBarButtonItem\"; title = \"Duplicate\"; ObjectID = \"P34-hO-6cX\";",
"extractionState" : "extracted_with_value",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
+5 -5
View File
@@ -1713,7 +1713,7 @@
},
"bue-hp-CWD.title" : {
"comment" : "Class = \"UIBarButtonItem\"; title = \"Duplicate\"; ObjectID = \"bue-hp-CWD\";",
"extractionState" : "extracted_with_value",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -2079,7 +2079,7 @@
},
"D9X-7p-9oc.title" : {
"comment" : "Class = \"UIBarButtonItem\"; title = \"ExportAll\"; ObjectID = \"D9X-7p-9oc\";",
"extractionState" : "extracted_with_value",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -2649,7 +2649,7 @@
},
"ghM-zc-sr6.title" : {
"comment" : "Class = \"UIBarButtonItem\"; title = \"Delete\"; ObjectID = \"ghM-zc-sr6\";",
"extractionState" : "extracted_with_value",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -5601,7 +5601,7 @@
},
"sUC-Ur-7eI.title" : {
"comment" : "Class = \"UIBarButtonItem\"; title = \"Restore\"; ObjectID = \"sUC-Ur-7eI\";",
"extractionState" : "extracted_with_value",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@@ -6663,7 +6663,7 @@
},
"Z9M-yq-an2.title" : {
"comment" : "Class = \"UIBarButtonItem\"; title = \"Import\"; ObjectID = \"Z9M-yq-an2\";",
"extractionState" : "extracted_with_value",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {