Merge branch 'feature/custom_artwork' into develop
This commit is contained in:
commit
a288f564b6
@ -70,6 +70,12 @@
|
||||
BF5E7F461B9A652600AE44F8 /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF5E7F451B9A652600AE44F8 /* Settings.storyboard */; };
|
||||
BF6866171DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6866161DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift */; };
|
||||
BF696B801D9B2B02009639E0 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF696B7F1D9B2B02009639E0 /* Theme.swift */; };
|
||||
BF6BF3131EB7E47F008E83CD /* ImportOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BF3121EB7E47F008E83CD /* ImportOption.swift */; };
|
||||
BF6BF3181EB82111008E83CD /* iTunesImportOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BF3171EB82111008E83CD /* iTunesImportOption.swift */; };
|
||||
BF6BF31A1EB82146008E83CD /* ClipboardImportOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BF3191EB82146008E83CD /* ClipboardImportOption.swift */; };
|
||||
BF6BF31C1EB821A0008E83CD /* GamesDatabaseImportOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BF31B1EB821A0008E83CD /* GamesDatabaseImportOption.swift */; };
|
||||
BF6BF3211EB82362008E83CD /* GamesDatabase.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF6BF31F1EB82362008E83CD /* GamesDatabase.storyboard */; };
|
||||
BF6BF3271EB87EB8008E83CD /* PhotoLibraryImportOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BF3261EB87EB8008E83CD /* PhotoLibraryImportOption.swift */; };
|
||||
BF70798C1B6B464B0019077C /* ZipZap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF70798B1B6B464B0019077C /* ZipZap.framework */; };
|
||||
BF70798D1B6B464B0019077C /* ZipZap.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF70798B1B6B464B0019077C /* ZipZap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF797A2C1C2D339F00F1A000 /* UILabel+FontSize.swift */; };
|
||||
@ -174,7 +180,7 @@
|
||||
BF5942841E09BC8B0051894B /* _GameCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = _GameCollection.swift; path = Database/Model/Machine/_GameCollection.swift; sourceTree = "<group>"; };
|
||||
BF5942851E09BC8B0051894B /* _SaveState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = _SaveState.swift; path = Database/Model/Machine/_SaveState.swift; sourceTree = "<group>"; };
|
||||
BF59428B1E09BC930051894B /* ControllerSkinConfigurations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ControllerSkinConfigurations.h; path = Database/Model/Misc/ControllerSkinConfigurations.h; sourceTree = "<group>"; };
|
||||
BF59428D1E09BCFB0051894B /* ImportController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImportController.swift; path = Components/Importing/ImportController.swift; sourceTree = "<group>"; };
|
||||
BF59428D1E09BCFB0051894B /* ImportController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImportController.swift; path = Importing/ImportController.swift; sourceTree = "<group>"; };
|
||||
BF59428F1E09BD1A0051894B /* NSFetchedResultsController+Conveniences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSFetchedResultsController+Conveniences.h"; sourceTree = "<group>"; };
|
||||
BF5942901E09BD1A0051894B /* NSFetchedResultsController+Conveniences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSFetchedResultsController+Conveniences.m"; sourceTree = "<group>"; };
|
||||
BF5942911E09BD1A0051894B /* NSManagedObject+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+Conveniences.swift"; sourceTree = "<group>"; };
|
||||
@ -185,6 +191,12 @@
|
||||
BF6866161DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControllerSkin+Configuring.swift"; sourceTree = "<group>"; };
|
||||
BF696B7F1D9B2B02009639E0 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Theme.swift; path = Theming/Theme.swift; sourceTree = "<group>"; };
|
||||
BF6BB2451BB73FE800CCF94A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
BF6BF3121EB7E47F008E83CD /* ImportOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImportOption.swift; path = Importing/ImportOption.swift; sourceTree = "<group>"; };
|
||||
BF6BF3171EB82111008E83CD /* iTunesImportOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = iTunesImportOption.swift; path = "Importing/Import Options/iTunesImportOption.swift"; sourceTree = "<group>"; };
|
||||
BF6BF3191EB82146008E83CD /* ClipboardImportOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ClipboardImportOption.swift; path = "Importing/Import Options/ClipboardImportOption.swift"; sourceTree = "<group>"; };
|
||||
BF6BF31B1EB821A0008E83CD /* GamesDatabaseImportOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GamesDatabaseImportOption.swift; path = "Importing/Import Options/GamesDatabaseImportOption.swift"; sourceTree = "<group>"; };
|
||||
BF6BF3201EB82362008E83CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/GamesDatabase.storyboard; sourceTree = "<group>"; };
|
||||
BF6BF3261EB87EB8008E83CD /* PhotoLibraryImportOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PhotoLibraryImportOption.swift; path = "Importing/Import Options/PhotoLibraryImportOption.swift"; sourceTree = "<group>"; };
|
||||
BF70798B1B6B464B0019077C /* ZipZap.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ZipZap.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BF797A2C1C2D339F00F1A000 /* UILabel+FontSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UILabel+FontSize.swift"; sourceTree = "<group>"; };
|
||||
BF7AE8041C2E858400B1B5BC /* PauseMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PauseMenuViewController.swift; path = "Pause Menu/PauseMenuViewController.swift"; sourceTree = "<group>"; };
|
||||
@ -203,7 +215,7 @@
|
||||
BFE4269D1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SaveStatesStoryboardSegue.swift; path = Segues/SaveStatesStoryboardSegue.swift; sourceTree = "<group>"; };
|
||||
BFEC732C1AAECC4A00650035 /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFF0742B1E9DC17500ACDF4A /* GBCDeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = GBCDeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFF93A9F1E0FB036005EC865 /* InputStreamOutputWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InputStreamOutputWriter.swift; path = Components/Importing/InputStreamOutputWriter.swift; sourceTree = "<group>"; };
|
||||
BFF93A9F1E0FB036005EC865 /* InputStreamOutputWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InputStreamOutputWriter.swift; path = Database/InputStreamOutputWriter.swift; sourceTree = "<group>"; };
|
||||
BFFA4C081E8A24D600D87934 /* GameMetadataTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GameMetadataTableViewCell.swift; path = Database/OpenVGDB/GameMetadataTableViewCell.swift; sourceTree = "<group>"; };
|
||||
BFFA71D71AAC406100EE9DD1 /* Delta.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Delta.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFFA71DB1AAC406100EE9DD1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
@ -304,7 +316,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF5942581E09BB810051894B /* Action.swift */,
|
||||
BF59428C1E09BCE50051894B /* Importing */,
|
||||
BF5942671E09BBB70051894B /* Collection View */,
|
||||
BF5942601E09BBA80051894B /* Loading */,
|
||||
);
|
||||
@ -333,6 +344,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF59426D1E09BC5D0051894B /* DatabaseManager.swift */,
|
||||
BFF93A9F1E0FB036005EC865 /* InputStreamOutputWriter.swift */,
|
||||
BF5942711E09BC690051894B /* Model */,
|
||||
BF95E2751E49763D0030E7AD /* OpenVGDB */,
|
||||
);
|
||||
@ -386,7 +398,8 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF59428D1E09BCFB0051894B /* ImportController.swift */,
|
||||
BFF93A9F1E0FB036005EC865 /* InputStreamOutputWriter.swift */,
|
||||
BF6BF3121EB7E47F008E83CD /* ImportOption.swift */,
|
||||
BF6BF3161EB820F4008E83CD /* Import Options */,
|
||||
);
|
||||
name = Importing;
|
||||
sourceTree = "<group>";
|
||||
@ -399,6 +412,17 @@
|
||||
name = Theming;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BF6BF3161EB820F4008E83CD /* Import Options */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF6BF3171EB82111008E83CD /* iTunesImportOption.swift */,
|
||||
BF6BF3191EB82146008E83CD /* ClipboardImportOption.swift */,
|
||||
BF6BF3261EB87EB8008E83CD /* PhotoLibraryImportOption.swift */,
|
||||
BF6BF31B1EB821A0008E83CD /* GamesDatabaseImportOption.swift */,
|
||||
);
|
||||
name = "Import Options";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BF7AE7FA1C2E851F00B1B5BC /* Pause Menu */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -434,6 +458,7 @@
|
||||
BF95E2751E49763D0030E7AD /* OpenVGDB */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF6BF31F1EB82362008E83CD /* GamesDatabase.storyboard */,
|
||||
BF59426E1E09BC5D0051894B /* GamesDatabase.swift */,
|
||||
BF95E2761E4977BF0030E7AD /* GameMetadata.swift */,
|
||||
BF95E2781E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift */,
|
||||
@ -522,6 +547,7 @@
|
||||
BF7AE7FA1C2E851F00B1B5BC /* Pause Menu */,
|
||||
BFAA1FEB1B8AA4E800495943 /* Settings */,
|
||||
BF59426C1E09BC450051894B /* Database */,
|
||||
BF59428C1E09BCE50051894B /* Importing */,
|
||||
BF930FFB1EB6D6EC00E8DBA0 /* Systems */,
|
||||
BF5942571E09BB5D0051894B /* Components */,
|
||||
BF696B7E1D9B2AE6009639E0 /* Theming */,
|
||||
@ -657,6 +683,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BFFA71E21AAC406100EE9DD1 /* Main.storyboard in Resources */,
|
||||
BF6BF3211EB82362008E83CD /* GamesDatabase.storyboard in Resources */,
|
||||
BF3540001C5DA3C500C1184C /* PausePresentationControllerContentView.xib in Resources */,
|
||||
BF5E7F461B9A652600AE44F8 /* Settings.storyboard in Resources */,
|
||||
BF02D5DA1DDEBB3000A5E131 /* openvgdb.sqlite in Resources */,
|
||||
@ -750,6 +777,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BF59426A1E09BBD00051894B /* GridCollectionViewCell.swift in Sources */,
|
||||
BF6BF3181EB82111008E83CD /* iTunesImportOption.swift in Sources */,
|
||||
BF59427C1E09BC830051894B /* Cheat.swift in Sources */,
|
||||
BFBAB2E31EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift in Sources */,
|
||||
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */,
|
||||
@ -761,6 +789,7 @@
|
||||
BF59428E1E09BCFB0051894B /* ImportController.swift in Sources */,
|
||||
BF930FFD1EB6D6FF00E8DBA0 /* System.swift in Sources */,
|
||||
BF13A7581D5D2FD9000BB055 /* EmulatorCore+Cheats.swift in Sources */,
|
||||
BF6BF3131EB7E47F008E83CD /* ImportOption.swift in Sources */,
|
||||
BF31878B1D489AAA00BD020D /* CheatValidator.swift in Sources */,
|
||||
BFFC46201D59823500AF2CC6 /* InitialGamesStoryboardSegue.swift in Sources */,
|
||||
BF5942731E09BC700051894B /* Model.xcdatamodel in Sources */,
|
||||
@ -775,6 +804,7 @@
|
||||
BF5942891E09BC8B0051894B /* _GameCollection.swift in Sources */,
|
||||
BF34FA111CF1899D006624C7 /* CheatTextView.swift in Sources */,
|
||||
BF1DAD5D1D9F576000E752A7 /* SystemControllerSkinsViewController.swift in Sources */,
|
||||
BF6BF31C1EB821A0008E83CD /* GamesDatabaseImportOption.swift in Sources */,
|
||||
BFFA4C091E8A24D600D87934 /* GameMetadataTableViewCell.swift in Sources */,
|
||||
BFFC46231D5984A000AF2CC6 /* LaunchViewController.swift in Sources */,
|
||||
BF5942701E09BC5D0051894B /* GamesDatabase.swift in Sources */,
|
||||
@ -806,11 +836,13 @@
|
||||
BF5942861E09BC8B0051894B /* _Cheat.swift in Sources */,
|
||||
BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */,
|
||||
BF5942931E09BD1A0051894B /* NSFetchedResultsController+Conveniences.m in Sources */,
|
||||
BF6BF3271EB87EB8008E83CD /* PhotoLibraryImportOption.swift in Sources */,
|
||||
BF5942661E09BBB10051894B /* LoadImageURLOperation.swift in Sources */,
|
||||
BF353FF21C5D7FB000C1184C /* PauseViewController.swift in Sources */,
|
||||
BF59425C1E09BB810051894B /* Action.swift in Sources */,
|
||||
BF696B801D9B2B02009639E0 /* Theme.swift in Sources */,
|
||||
BF7AE8081C2E858400B1B5BC /* PauseMenuViewController.swift in Sources */,
|
||||
BF6BF31A1EB82146008E83CD /* ClipboardImportOption.swift in Sources */,
|
||||
BFF93AA01E0FB036005EC865 /* InputStreamOutputWriter.swift in Sources */,
|
||||
BF59427F1E09BC830051894B /* GameCollection.swift in Sources */,
|
||||
);
|
||||
@ -827,6 +859,14 @@
|
||||
name = PauseMenu.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BF6BF31F1EB82362008E83CD /* GamesDatabase.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
BF6BF3201EB82362008E83CD /* Base */,
|
||||
);
|
||||
name = GamesDatabase.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFFA71E01AAC406100EE9DD1 /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
|
||||
107
Delta/Base.lproj/GamesDatabase.storyboard
Normal file
107
Delta/Base.lproj/GamesDatabase.storyboard
Normal file
@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="6bq-zy-UZU">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
|
||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Games Database-->
|
||||
<scene sceneID="S7I-gw-igt">
|
||||
<objects>
|
||||
<tableViewController id="SB6-jW-dhZ" customClass="GamesDatabaseBrowserViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="97" sectionHeaderHeight="28" sectionFooterHeight="28" id="bJf-Sa-ZOX">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="4cJ-4B-Kgt" customClass="GameMetadataTableViewCell" customModule="Delta" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="28" width="375" height="97"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="4cJ-4B-Kgt" id="7ze-s0-mpI">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="96.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DSH-Hk-snb" userLabel="Selected Background View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="97.5"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="BoxArt" translatesAutoresizingMaskIntoConstraints="NO" id="tNY-2F-llo">
|
||||
<rect key="frame" x="15" y="8" width="80" height="80"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="tNY-2F-llo" secondAttribute="height" multiplier="1:1" id="f4E-bV-L96"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Super Mario World" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DND-Fv-FyB">
|
||||
<rect key="frame" x="110" y="38.5" width="250" height="20.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="DND-Fv-FyB" firstAttribute="leading" secondItem="tNY-2F-llo" secondAttribute="trailing" constant="15" id="71e-t3-7Av"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="DND-Fv-FyB" secondAttribute="bottom" constant="1" id="9No-RE-0xx"/>
|
||||
<constraint firstItem="DND-Fv-FyB" firstAttribute="top" relation="greaterThanOrEqual" secondItem="7ze-s0-mpI" secondAttribute="top" constant="1" id="F9q-6H-sqC"/>
|
||||
<constraint firstAttribute="trailing" secondItem="DND-Fv-FyB" secondAttribute="trailing" constant="15" id="KFv-7n-LrD"/>
|
||||
<constraint firstItem="DND-Fv-FyB" firstAttribute="centerY" secondItem="7ze-s0-mpI" secondAttribute="centerY" id="YBX-t4-jkR"/>
|
||||
<constraint firstItem="tNY-2F-llo" firstAttribute="top" secondItem="7ze-s0-mpI" secondAttribute="top" constant="8" id="bYX-gA-QvB"/>
|
||||
<constraint firstAttribute="bottom" secondItem="tNY-2F-llo" secondAttribute="bottom" constant="8" id="fxr-wr-I6X"/>
|
||||
<constraint firstItem="tNY-2F-llo" firstAttribute="leading" secondItem="7ze-s0-mpI" secondAttribute="leading" constant="15" id="hX2-Gr-Bnz"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="artworkImageView" destination="tNY-2F-llo" id="GqY-jv-rso"/>
|
||||
<outlet property="artworkImageViewLeadingConstraint" destination="hX2-Gr-Bnz" id="be8-dr-c8K"/>
|
||||
<outlet property="artworkImageViewTrailingConstraint" destination="71e-t3-7Av" id="y62-KO-y1r"/>
|
||||
<outlet property="nameLabel" destination="DND-Fv-FyB" id="LhN-cA-8Hy"/>
|
||||
<outlet property="selectedBackgroundView" destination="DSH-Hk-snb" id="hLY-4k-VxU"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="SB6-jW-dhZ" id="2aq-ZA-84E"/>
|
||||
<outlet property="delegate" destination="SB6-jW-dhZ" id="WgY-cp-m7K"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Games Database" id="rwF-kd-avR">
|
||||
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="BnB-5n-Rff">
|
||||
<connections>
|
||||
<segue destination="mUU-ug-yNs" kind="unwind" unwindAction="unwindFromGamesDatabaseBrowserWith:" id="zdg-Az-WwQ"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="f3a-hX-Qnu" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<exit id="mUU-ug-yNs" userLabel="Exit" sceneMemberID="exit"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2652" y="1001.649175412294"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="REv-V5-eEz">
|
||||
<objects>
|
||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="6bq-zy-UZU" sceneMemberID="viewController">
|
||||
<toolbarItems/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="black" id="uzY-vR-coL">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<nil name="viewControllers"/>
|
||||
<connections>
|
||||
<segue destination="SB6-jW-dhZ" kind="relationship" relationship="rootViewController" id="b0w-Fq-hrk"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Hr9-N6-XXA" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1854" y="1002"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="BoxArt" width="100" height="100"/>
|
||||
</resources>
|
||||
</document>
|
||||
@ -1,12 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16C68" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="SPq-Bk-fQl">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="SPq-Bk-fQl">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
@ -49,86 +48,11 @@
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<segue destination="6bq-zy-UZU" kind="presentation" identifier="gamesDatabaseBrowser" id="7TT-mP-bjt"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="JYx-xE-nis" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1036" y="1002"/>
|
||||
</scene>
|
||||
<!--Games Database-->
|
||||
<scene sceneID="S7I-gw-igt">
|
||||
<objects>
|
||||
<tableViewController id="SB6-jW-dhZ" customClass="GamesDatabaseBrowserViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="97" sectionHeaderHeight="28" sectionFooterHeight="28" id="bJf-Sa-ZOX">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="4cJ-4B-Kgt" customClass="GameMetadataTableViewCell" customModule="Delta" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="28" width="375" height="97"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="4cJ-4B-Kgt" id="7ze-s0-mpI">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="96"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DSH-Hk-snb" userLabel="Selected Background View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="97"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="BoxArt" translatesAutoresizingMaskIntoConstraints="NO" id="tNY-2F-llo">
|
||||
<rect key="frame" x="15" y="8" width="80" height="80"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="tNY-2F-llo" secondAttribute="height" multiplier="1:1" id="f4E-bV-L96"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Super Mario World" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DND-Fv-FyB">
|
||||
<rect key="frame" x="110" y="38.5" width="250" height="20.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="DND-Fv-FyB" firstAttribute="leading" secondItem="tNY-2F-llo" secondAttribute="trailing" constant="15" id="71e-t3-7Av"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="DND-Fv-FyB" secondAttribute="bottom" constant="1" id="9No-RE-0xx"/>
|
||||
<constraint firstItem="DND-Fv-FyB" firstAttribute="top" relation="greaterThanOrEqual" secondItem="7ze-s0-mpI" secondAttribute="top" constant="1" id="F9q-6H-sqC"/>
|
||||
<constraint firstAttribute="trailing" secondItem="DND-Fv-FyB" secondAttribute="trailing" constant="15" id="KFv-7n-LrD"/>
|
||||
<constraint firstItem="DND-Fv-FyB" firstAttribute="centerY" secondItem="7ze-s0-mpI" secondAttribute="centerY" id="YBX-t4-jkR"/>
|
||||
<constraint firstItem="tNY-2F-llo" firstAttribute="top" secondItem="7ze-s0-mpI" secondAttribute="top" constant="8" id="bYX-gA-QvB"/>
|
||||
<constraint firstAttribute="bottom" secondItem="tNY-2F-llo" secondAttribute="bottom" constant="8" id="fxr-wr-I6X"/>
|
||||
<constraint firstItem="tNY-2F-llo" firstAttribute="leading" secondItem="7ze-s0-mpI" secondAttribute="leading" constant="15" id="hX2-Gr-Bnz"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="artworkImageView" destination="tNY-2F-llo" id="GqY-jv-rso"/>
|
||||
<outlet property="artworkImageViewLeadingConstraint" destination="hX2-Gr-Bnz" id="be8-dr-c8K"/>
|
||||
<outlet property="artworkImageViewTrailingConstraint" destination="71e-t3-7Av" id="y62-KO-y1r"/>
|
||||
<outlet property="nameLabel" destination="DND-Fv-FyB" id="LhN-cA-8Hy"/>
|
||||
<outlet property="selectedBackgroundView" destination="DSH-Hk-snb" id="hLY-4k-VxU"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="SB6-jW-dhZ" id="2aq-ZA-84E"/>
|
||||
<outlet property="delegate" destination="SB6-jW-dhZ" id="WgY-cp-m7K"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Games Database" id="rwF-kd-avR">
|
||||
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="BnB-5n-Rff">
|
||||
<connections>
|
||||
<segue destination="mUU-ug-yNs" kind="unwind" unwindAction="unwindFromGamesDatabaseBrowserWith:" id="zdg-Az-WwQ"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="f3a-hX-Qnu" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<exit id="mUU-ug-yNs" userLabel="Exit" sceneMemberID="exit"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2652" y="1001.649175412294"/>
|
||||
</scene>
|
||||
<!--Game Collection View Controller-->
|
||||
<scene sceneID="qNA-NP-TiF">
|
||||
<objects>
|
||||
@ -161,7 +85,6 @@
|
||||
<connections>
|
||||
<segue destination="X2o-q6-XD5" kind="unwind" identifier="unwindFromGames" unwindAction="unwindFromGamesViewControllerWith:" id="k8C-Xn-maU"/>
|
||||
<segue destination="MPk-bF-nkj" kind="presentation" identifier="saveStates" customClass="SaveStatesStoryboardSegue" customModule="Delta" customModuleProvider="target" id="1Xp-2J-0cq"/>
|
||||
<segue destination="6bq-zy-UZU" kind="presentation" identifier="gamesDatabaseBrowser" id="mzX-Bb-MaX"/>
|
||||
</connections>
|
||||
</collectionViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="bW1-t8-idm" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
@ -314,31 +237,11 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="3409" y="1716"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="REv-V5-eEz">
|
||||
<objects>
|
||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="6bq-zy-UZU" sceneMemberID="viewController">
|
||||
<toolbarItems/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" misplaced="YES" barStyle="black" id="uzY-vR-coL">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<nil name="viewControllers"/>
|
||||
<connections>
|
||||
<segue destination="SB6-jW-dhZ" kind="relationship" relationship="rootViewController" id="b0w-Fq-hrk"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Hr9-N6-XXA" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1854" y="1002"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="BoxArt" width="100" height="100"/>
|
||||
<image name="Settings_Button" width="22" height="22"/>
|
||||
</resources>
|
||||
<inferredMetricsTieBreakers>
|
||||
<segue reference="mzX-Bb-MaX"/>
|
||||
<segue reference="Tey-6Z-UHp"/>
|
||||
</inferredMetricsTieBreakers>
|
||||
</document>
|
||||
|
||||
@ -1,184 +0,0 @@
|
||||
//
|
||||
// ImportController.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 10/10/15.
|
||||
// Copyright © 2015 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import ObjectiveC
|
||||
|
||||
import DeltaCore
|
||||
|
||||
import MobileCoreServices
|
||||
|
||||
protocol ImportControllerDelegate
|
||||
{
|
||||
func importController(_ importController: ImportController, didImport games: Set<Game>, with errors: Set<DatabaseManager.ImportError>)
|
||||
func importController(_ importController: ImportController, didImport controllerSkins: Set<ControllerSkin>, with errors: Set<DatabaseManager.ImportError>)
|
||||
|
||||
/** Optional **/
|
||||
func importControllerDidCancel(_ importController: ImportController)
|
||||
}
|
||||
|
||||
extension ImportControllerDelegate
|
||||
{
|
||||
func importControllerDidCancel(_ importController: ImportController)
|
||||
{
|
||||
// Empty Implementation
|
||||
}
|
||||
}
|
||||
|
||||
class ImportController: NSObject
|
||||
{
|
||||
var delegate: ImportControllerDelegate?
|
||||
|
||||
fileprivate weak var presentingViewController: UIViewController?
|
||||
|
||||
fileprivate func presentImportController(from presentingViewController: UIViewController, animated: Bool, completion: ((Void) -> Void)?)
|
||||
{
|
||||
self.presentingViewController = presentingViewController
|
||||
|
||||
var documentTypes = System.supportedSystems.map { $0.gameType.rawValue }
|
||||
documentTypes.append(kUTTypeDeltaControllerSkin as String)
|
||||
documentTypes.append(kUTTypeZipArchive as String)
|
||||
|
||||
// Add GBA4iOS's exported UTIs in case user has GBA4iOS installed (which may override Delta's UTI declarations)
|
||||
documentTypes.append("com.rileytestut.gba")
|
||||
documentTypes.append("com.rileytestut.gbc")
|
||||
documentTypes.append("com.rileytestut.gb")
|
||||
|
||||
#if os(iOS)
|
||||
let documentMenuController = UIDocumentMenuViewController(documentTypes: documentTypes, in: .import)
|
||||
documentMenuController.delegate = self
|
||||
documentMenuController.addOption(withTitle: NSLocalizedString("iTunes", comment: ""), image: nil, order: .first) { self.importFromiTunes(nil) }
|
||||
self.presentingViewController?.present(documentMenuController, animated: true, completion: nil)
|
||||
#else
|
||||
self.importFromiTunes(completion)
|
||||
#endif
|
||||
}
|
||||
|
||||
private func importFromiTunes(_ completion: ((Void) -> Void)?)
|
||||
{
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Import from iTunes?", comment: ""), message: NSLocalizedString("Delta will import the games and controller skins copied over via iTunes.", comment: ""), preferredStyle: .alert)
|
||||
|
||||
let importAction = UIAlertAction(title: NSLocalizedString("Import", comment: ""), style: .default) { action in
|
||||
|
||||
let documentsDirectoryURL = DatabaseManager.defaultDirectoryURL().deletingLastPathComponent()
|
||||
|
||||
do
|
||||
{
|
||||
let contents = try FileManager.default.contentsOfDirectory(at: documentsDirectoryURL, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
|
||||
|
||||
DatabaseManager.shared.performBackgroundTask { (context) in
|
||||
let controllerSkinURLs = contents.filter { $0.pathExtension.lowercased() == "deltaskin" }
|
||||
self.importControllerSkins(at: Set(controllerSkinURLs))
|
||||
|
||||
let gameURLs = contents.filter { GameType(fileExtension: $0.pathExtension) != nil || $0.pathExtension.lowercased() == "zip" }
|
||||
self.importGames(at: Set(gameURLs))
|
||||
}
|
||||
|
||||
}
|
||||
catch let error as NSError
|
||||
{
|
||||
print(error)
|
||||
}
|
||||
|
||||
self.presentingViewController?.importController = nil
|
||||
|
||||
}
|
||||
alertController.addAction(importAction)
|
||||
|
||||
let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) { action in
|
||||
self.delegate?.importControllerDidCancel(self)
|
||||
self.presentingViewController?.importController = nil
|
||||
}
|
||||
alertController.addAction(cancelAction)
|
||||
|
||||
self.presentingViewController?.present(alertController, animated: true, completion: completion)
|
||||
}
|
||||
|
||||
fileprivate func importGames(at urls: Set<URL>)
|
||||
{
|
||||
DatabaseManager.shared.importGames(at: urls) { (games, errors) in
|
||||
self.delegate?.importController(self, didImport: games, with: errors)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func importControllerSkins(at urls: Set<URL>)
|
||||
{
|
||||
DatabaseManager.shared.importControllerSkins(at: urls) { (controllerSkins, errors) in
|
||||
self.delegate?.importController(self, didImport: controllerSkins, with: errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
|
||||
extension ImportController: UIDocumentMenuDelegate
|
||||
{
|
||||
func documentMenu(_ documentMenu: UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController)
|
||||
{
|
||||
documentPicker.delegate = self
|
||||
self.presentingViewController?.present(documentPicker, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func documentMenuWasCancelled(_ documentMenu: UIDocumentMenuViewController)
|
||||
{
|
||||
self.delegate?.importControllerDidCancel(self)
|
||||
|
||||
self.presentingViewController?.importController = nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ImportController: UIDocumentPickerDelegate
|
||||
{
|
||||
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL)
|
||||
{
|
||||
if url.pathExtension.lowercased() == "deltaskin"
|
||||
{
|
||||
self.importControllerSkins(at: [url])
|
||||
}
|
||||
else
|
||||
{
|
||||
self.importGames(at: [url])
|
||||
}
|
||||
|
||||
self.presentingViewController?.importController = nil
|
||||
}
|
||||
|
||||
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController)
|
||||
{
|
||||
self.delegate?.importControllerDidCancel(self)
|
||||
|
||||
self.presentingViewController?.importController = nil
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private var ImportControllerKey: UInt8 = 0
|
||||
|
||||
extension UIViewController
|
||||
{
|
||||
fileprivate(set) var importController: ImportController?
|
||||
{
|
||||
set
|
||||
{
|
||||
objc_setAssociatedObject(self, &ImportControllerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
get
|
||||
{
|
||||
return objc_getAssociatedObject(self, &ImportControllerKey) as? ImportController
|
||||
}
|
||||
}
|
||||
|
||||
func present(_ importController: ImportController, animated: Bool, completion: ((Void) -> Void)?)
|
||||
{
|
||||
self.importController = importController
|
||||
|
||||
importController.presentImportController(from: self, animated: animated, completion: completion)
|
||||
}
|
||||
}
|
||||
@ -74,14 +74,12 @@ class LoadImageURLOperation: RSTLoadOperation<UIImage, NSURL>
|
||||
|
||||
private func loadLocalImage(completion: @escaping (UIImage?, Error?) -> Void)
|
||||
{
|
||||
let options: NSDictionary = [kCGImageSourceShouldCache as NSString: true]
|
||||
|
||||
guard let imageSource = CGImageSourceCreateWithURL(self.url as CFURL, options) else {
|
||||
guard let imageSource = CGImageSourceCreateWithURL(self.url as CFURL, nil) else {
|
||||
completion(nil, .doesNotExist)
|
||||
return
|
||||
}
|
||||
|
||||
guard let quartzImage = CGImageSourceCreateImageAtIndex(imageSource, 0, options) else {
|
||||
guard let quartzImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) else {
|
||||
completion(nil, .invalid)
|
||||
return
|
||||
}
|
||||
|
||||
@ -53,7 +53,6 @@ extension DatabaseManager
|
||||
case (.saveFailed, _): return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -494,6 +493,14 @@ extension DatabaseManager
|
||||
|
||||
return gameTypeDirectoryURL
|
||||
}
|
||||
|
||||
class func artworkURL(for game: Game) -> URL
|
||||
{
|
||||
let gameURL = game.fileURL
|
||||
|
||||
let artworkURL = gameURL.deletingPathExtension().appendingPathExtension("jpg")
|
||||
return artworkURL
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Private -
|
||||
|
||||
@ -22,6 +22,37 @@ public class Game: _Game, GameProtocol
|
||||
|
||||
return fileURL
|
||||
}
|
||||
|
||||
public override var artworkURL: URL? {
|
||||
get {
|
||||
self.willAccessValue(forKey: #keyPath(Game.artworkURL))
|
||||
var artworkURL = self.primitiveValue(forKey: #keyPath(Game.artworkURL)) as? URL
|
||||
self.didAccessValue(forKey: #keyPath(Game.artworkURL))
|
||||
|
||||
if let unwrappedArtworkURL = artworkURL, unwrappedArtworkURL.isFileURL
|
||||
{
|
||||
// Recreate the stored URL relative to current sandbox location.
|
||||
artworkURL = URL(fileURLWithPath: unwrappedArtworkURL.relativePath, relativeTo: DatabaseManager.gamesDirectoryURL)
|
||||
}
|
||||
|
||||
return artworkURL
|
||||
}
|
||||
set {
|
||||
self.willChangeValue(forKey: #keyPath(Game.artworkURL))
|
||||
|
||||
var artworkURL = newValue
|
||||
|
||||
if let newValue = newValue, newValue.isFileURL
|
||||
{
|
||||
// Store a relative URL, since the sandbox location changes.
|
||||
artworkURL = URL(fileURLWithPath: newValue.lastPathComponent, relativeTo: DatabaseManager.gamesDirectoryURL)
|
||||
}
|
||||
|
||||
self.setPrimitiveValue(artworkURL, forKey: #keyPath(Game.artworkURL))
|
||||
|
||||
self.didChangeValue(forKey: #keyPath(Game.artworkURL))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Game
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import MobileCoreServices
|
||||
|
||||
import DeltaCore
|
||||
|
||||
@ -47,10 +48,14 @@ class GameCollectionViewController: UICollectionViewController
|
||||
fileprivate let dataSource = RSTFetchedResultsCollectionViewDataSource<Game>(fetchedResultsController: NSFetchedResultsController())
|
||||
fileprivate let prototypeCell = GridCollectionViewCell()
|
||||
|
||||
fileprivate let imageOperationQueue = RSTOperationQueue()
|
||||
fileprivate let imageCache = NSCache<NSURL, UIImage>()
|
||||
|
||||
fileprivate var _performing3DTouchTransition = false
|
||||
fileprivate weak var _destination3DTouchTransitionViewController: UIViewController?
|
||||
|
||||
fileprivate var _renameAction: UIAlertAction?
|
||||
fileprivate var _changingArtworkGame: Game?
|
||||
}
|
||||
|
||||
//MARK: - UIViewController -
|
||||
@ -122,25 +127,7 @@ extension GameCollectionViewController
|
||||
saveStatesViewController.game = game
|
||||
saveStatesViewController.mode = .loading
|
||||
saveStatesViewController.theme = self.theme
|
||||
|
||||
case "gamesDatabaseBrowser":
|
||||
let game = sender as! Game
|
||||
|
||||
let gamesDatabaseBrowserViewController = (segue.destination as! UINavigationController).topViewController as! GamesDatabaseBrowserViewController
|
||||
gamesDatabaseBrowserViewController.selectionHandler = { (metadata) in
|
||||
|
||||
DatabaseManager.shared.performBackgroundTask({ (context) in
|
||||
let temporaryGame = context.object(with: game.objectID) as! Game
|
||||
temporaryGame.artworkURL = metadata.artworkURL
|
||||
context.saveWithErrorLogging()
|
||||
|
||||
DispatchQueue.main.async {
|
||||
gamesDatabaseBrowserViewController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
case "unwindFromGames":
|
||||
let destinationViewController = segue.destination as! GameViewController
|
||||
let cell = sender as! UICollectionViewCell
|
||||
@ -222,28 +209,28 @@ private extension GameCollectionViewController
|
||||
cell.isImageViewVibrancyEnabled = true
|
||||
}
|
||||
|
||||
cell.imageView.image = #imageLiteral(resourceName: "BoxArt")
|
||||
|
||||
cell.maximumImageSize = CGSize(width: 90, height: 90)
|
||||
cell.textLabel.text = game.name
|
||||
cell.textLabel.textColor = UIColor.gray
|
||||
|
||||
if let artworkURL = game.artworkURL, !ignoreImageOperations
|
||||
{
|
||||
cell.imageView.sd_setImage(with: artworkURL, placeholderImage: #imageLiteral(resourceName: "BoxArt"), options: .continueInBackground) { (image, error, type, url) in
|
||||
let imageOperation = LoadImageURLOperation(url: artworkURL)
|
||||
imageOperation.resultsCache = self.imageCache
|
||||
imageOperation.resultHandler = { (image, error) in
|
||||
|
||||
if let error = error
|
||||
if let image = image
|
||||
{
|
||||
print(error)
|
||||
}
|
||||
|
||||
if image != nil
|
||||
{
|
||||
cell.isImageViewVibrancyEnabled = false
|
||||
DispatchQueue.main.async {
|
||||
cell.imageView.image = image
|
||||
cell.isImageViewVibrancyEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.imageView.image = #imageLiteral(resourceName: "BoxArt")
|
||||
|
||||
self.imageOperationQueue.addOperation(imageOperation, forKey: indexPath as NSCopying)
|
||||
}
|
||||
}
|
||||
|
||||
@ -356,7 +343,16 @@ private extension GameCollectionViewController
|
||||
|
||||
func changeArtwork(for game: Game)
|
||||
{
|
||||
self.performSegue(withIdentifier: "gamesDatabaseBrowser", sender: game)
|
||||
self._changingArtworkGame = game
|
||||
|
||||
let clipboardImportOption = ClipboardImportOption()
|
||||
let photoLibraryImportOption = PhotoLibraryImportOption(presentingViewController: self)
|
||||
let gamesDatabaseImportOption = GamesDatabaseImportOption(presentingViewController: self)
|
||||
|
||||
let importController = ImportController(documentTypes: [kUTTypeImage as String])
|
||||
importController.delegate = self
|
||||
importController.importOptions = [clipboardImportOption, photoLibraryImportOption, gamesDatabaseImportOption]
|
||||
self.present(importController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func share(_ game: Game)
|
||||
@ -493,6 +489,94 @@ extension GameCollectionViewController: SaveStatesViewControllerDelegate
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - ImportControllerDelegate -
|
||||
/// ImportControllerDelegate
|
||||
extension GameCollectionViewController: ImportControllerDelegate
|
||||
{
|
||||
func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>)
|
||||
{
|
||||
guard let game = self._changingArtworkGame else { return }
|
||||
|
||||
var imageURL: URL?
|
||||
|
||||
if let url = urls.first
|
||||
{
|
||||
if url.isFileURL
|
||||
{
|
||||
do
|
||||
{
|
||||
let imageData = try Data(contentsOf: url)
|
||||
|
||||
if let image = UIImage(data: imageData)
|
||||
{
|
||||
let resizedImage = image.resizing(toFit: CGSize(width: 300, height: 300))
|
||||
|
||||
if let resizedData = UIImageJPEGRepresentation(resizedImage, 0.85)
|
||||
{
|
||||
let destinationURL = DatabaseManager.artworkURL(for: game)
|
||||
try resizedData.write(to: destinationURL, options: .atomic)
|
||||
|
||||
imageURL = destinationURL
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
imageURL = url
|
||||
}
|
||||
}
|
||||
|
||||
if let imageURL = imageURL
|
||||
{
|
||||
if let previousArtworkURL = game.artworkURL as NSURL?
|
||||
{
|
||||
// Remove previous artwork from cache.
|
||||
self.imageCache.removeObject(forKey: previousArtworkURL)
|
||||
}
|
||||
|
||||
DatabaseManager.shared.performBackgroundTask { (context) in
|
||||
let temporaryGame = context.object(with: game.objectID) as! Game
|
||||
temporaryGame.artworkURL = imageURL
|
||||
context.saveWithErrorLogging()
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.presentedViewController?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
func presentAlertController()
|
||||
{
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Unable to Change Artwork", comment: ""), message: NSLocalizedString("The image might be corrupted or in an unsupported format.", comment: ""), preferredStyle: .alert)
|
||||
alertController.addAction(UIAlertAction(title: RSTSystemLocalizedString("OK"), style: .cancel, handler: nil))
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
if let presentedViewController = self.presentedViewController
|
||||
{
|
||||
presentedViewController.dismiss(animated: true) {
|
||||
presentAlertController()
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
presentAlertController()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func importControllerDidCancel(_ importController: ImportController)
|
||||
{
|
||||
self.presentedViewController?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UICollectionViewDelegate -
|
||||
/// UICollectionViewDelegate
|
||||
extension GameCollectionViewController
|
||||
@ -545,8 +629,8 @@ extension GameCollectionViewController
|
||||
|
||||
override func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
|
||||
{
|
||||
let cell = cell as! GridCollectionViewCell
|
||||
cell.imageView.sd_cancelCurrentImageLoad()
|
||||
let operation = self.imageOperationQueue[indexPath as NSCopying]
|
||||
operation?.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
import UIKit
|
||||
import CoreData
|
||||
import MobileCoreServices
|
||||
|
||||
import DeltaCore
|
||||
|
||||
@ -266,37 +267,50 @@ extension GamesViewController: ImportControllerDelegate
|
||||
{
|
||||
@IBAction fileprivate func importFiles()
|
||||
{
|
||||
let importController = ImportController()
|
||||
var documentTypes = Set(System.supportedSystems.map { $0.gameType.rawValue })
|
||||
documentTypes.insert(kUTTypeZipArchive as String)
|
||||
|
||||
// Add GBA4iOS's exported UTIs in case user has GBA4iOS installed (which may override Delta's UTI declarations)
|
||||
documentTypes.insert("com.rileytestut.gba")
|
||||
documentTypes.insert("com.rileytestut.gbc")
|
||||
documentTypes.insert("com.rileytestut.gb")
|
||||
|
||||
let itunesImportOption = iTunesImportOption(presentingViewController: self)
|
||||
|
||||
let importController = ImportController(documentTypes: documentTypes)
|
||||
importController.delegate = self
|
||||
importController.importOptions = [itunesImportOption]
|
||||
self.present(importController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
//MARK: - ImportControllerDelegate
|
||||
@nonobjc func importController(_ importController: ImportController, didImport games: Set<Game>, with errors: Set<DatabaseManager.ImportError>)
|
||||
func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>)
|
||||
{
|
||||
if errors.count > 0
|
||||
{
|
||||
let alertController = UIAlertController.alertController(for: .games, with: errors)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
let gameURLs = urls.filter { $0.pathExtension.lowercased() != "deltaskin" }
|
||||
DatabaseManager.shared.importGames(at: Set(gameURLs)) { (games, errors) in
|
||||
if errors.count > 0
|
||||
{
|
||||
let alertController = UIAlertController.alertController(for: .games, with: errors)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
if games.count > 0
|
||||
{
|
||||
print("Imported Games:", games.map { $0.name })
|
||||
}
|
||||
}
|
||||
|
||||
if games.count > 0
|
||||
{
|
||||
print("Imported Games:", games.map { $0.name })
|
||||
}
|
||||
}
|
||||
|
||||
@nonobjc func importController(_ importController: ImportController, didImport controllerSkins: Set<ControllerSkin>, with errors: Set<DatabaseManager.ImportError>)
|
||||
{
|
||||
if errors.count > 0
|
||||
{
|
||||
let alertController = UIAlertController.alertController(for: .controllerSkins, with: errors)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
if controllerSkins.count > 0
|
||||
{
|
||||
print("Imported Controller Skins:", controllerSkins.map { $0.name })
|
||||
let controllerSkinURLs = urls.filter { $0.pathExtension.lowercased() == "deltaskin" }
|
||||
DatabaseManager.shared.importControllerSkins(at: Set(controllerSkinURLs)) { (controllerSkins, errors) in
|
||||
if errors.count > 0
|
||||
{
|
||||
let alertController = UIAlertController.alertController(for: .controllerSkins, with: errors)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
if controllerSkins.count > 0
|
||||
{
|
||||
print("Imported Controller Skins:", controllerSkins.map { $0.name })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
39
Delta/Importing/Import Options/ClipboardImportOption.swift
Normal file
39
Delta/Importing/Import Options/ClipboardImportOption.swift
Normal file
@ -0,0 +1,39 @@
|
||||
//
|
||||
// ClipboardImportOption.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 5/1/17.
|
||||
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import MobileCoreServices
|
||||
|
||||
import Roxas
|
||||
|
||||
struct ClipboardImportOption: ImportOption
|
||||
{
|
||||
let title = NSLocalizedString("Clipboard", comment: "")
|
||||
let image: UIImage? = nil
|
||||
|
||||
func `import`(withCompletionHandler completionHandler: @escaping (Set<URL>?) -> Void)
|
||||
{
|
||||
guard UIPasteboard.general.hasImages else { return completionHandler([]) }
|
||||
|
||||
guard let data = UIPasteboard.general.data(forPasteboardType: kUTTypeImage as String) else { return completionHandler([]) }
|
||||
|
||||
do
|
||||
{
|
||||
let temporaryURL = FileManager.uniqueTemporaryURL()
|
||||
try data.write(to: temporaryURL, options: .atomic)
|
||||
|
||||
completionHandler([temporaryURL])
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
|
||||
completionHandler([])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
//
|
||||
// GamesDatabaseImportOption.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 5/1/17.
|
||||
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
struct GamesDatabaseImportOption: ImportOption
|
||||
{
|
||||
let title = NSLocalizedString("Games Database", comment: "")
|
||||
let image: UIImage? = nil
|
||||
|
||||
private let presentingViewController: UIViewController
|
||||
|
||||
init(presentingViewController: UIViewController)
|
||||
{
|
||||
self.presentingViewController = presentingViewController
|
||||
}
|
||||
|
||||
func `import`(withCompletionHandler completionHandler: @escaping (Set<URL>?) -> Void)
|
||||
{
|
||||
let storyboard = UIStoryboard(name: "GamesDatabase", bundle: nil)
|
||||
let navigationController = (storyboard.instantiateInitialViewController() as! UINavigationController)
|
||||
|
||||
let gamesDatabaseBrowserViewController = navigationController.topViewController as! GamesDatabaseBrowserViewController
|
||||
gamesDatabaseBrowserViewController.selectionHandler = { (metadata) in
|
||||
if let artworkURL = metadata.artworkURL
|
||||
{
|
||||
completionHandler([artworkURL])
|
||||
}
|
||||
else
|
||||
{
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
|
||||
self.presentingViewController.present(navigationController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
//
|
||||
// PhotoLibraryImportOption.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 5/2/17.
|
||||
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import MobileCoreServices
|
||||
|
||||
class PhotoLibraryImportOption: NSObject, ImportOption
|
||||
{
|
||||
let title = NSLocalizedString("Photo Library", comment: "")
|
||||
let image: UIImage? = nil
|
||||
|
||||
private let presentingViewController: UIViewController
|
||||
fileprivate var completionHandler: ((Set<URL>?) -> Void)?
|
||||
|
||||
init(presentingViewController: UIViewController)
|
||||
{
|
||||
self.presentingViewController = presentingViewController
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
func `import`(withCompletionHandler completionHandler: @escaping (Set<URL>?) -> Void)
|
||||
{
|
||||
self.completionHandler = completionHandler
|
||||
|
||||
let imagePickerController = UIImagePickerController()
|
||||
imagePickerController.delegate = self
|
||||
imagePickerController.sourceType = .photoLibrary
|
||||
imagePickerController.mediaTypes = [kUTTypeImage as String]
|
||||
self.presentingViewController.present(imagePickerController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension PhotoLibraryImportOption: UIImagePickerControllerDelegate, UINavigationControllerDelegate
|
||||
{
|
||||
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
|
||||
{
|
||||
guard let image = info[UIImagePickerControllerOriginalImage] as? UIImage, let data = UIImageJPEGRepresentation(image, 0.85) else {
|
||||
self.completionHandler?([])
|
||||
return
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
let temporaryURL = FileManager.uniqueTemporaryURL()
|
||||
try data.write(to: temporaryURL, options: .atomic)
|
||||
|
||||
self.completionHandler?([temporaryURL])
|
||||
}
|
||||
catch
|
||||
{
|
||||
self.completionHandler?([])
|
||||
}
|
||||
}
|
||||
}
|
||||
73
Delta/Importing/Import Options/iTunesImportOption.swift
Normal file
73
Delta/Importing/Import Options/iTunesImportOption.swift
Normal file
@ -0,0 +1,73 @@
|
||||
//
|
||||
// iTunesImportOption.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 5/1/17.
|
||||
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import DeltaCore
|
||||
|
||||
struct iTunesImportOption: ImportOption
|
||||
{
|
||||
let title = NSLocalizedString("iTunes", comment: "")
|
||||
let image: UIImage? = nil
|
||||
|
||||
private let presentingViewController: UIViewController
|
||||
|
||||
init(presentingViewController: UIViewController)
|
||||
{
|
||||
self.presentingViewController = presentingViewController
|
||||
}
|
||||
|
||||
func `import`(withCompletionHandler completionHandler: @escaping (Set<URL>?) -> Void)
|
||||
{
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Import from iTunes?", comment: ""), message: NSLocalizedString("Delta will import the games and controller skins copied over via iTunes.", comment: ""), preferredStyle: .alert)
|
||||
|
||||
let importAction = UIAlertAction(title: NSLocalizedString("Import", comment: ""), style: .default) { action in
|
||||
|
||||
var importedURLs = Set<URL>()
|
||||
|
||||
let documentsDirectoryURL = DatabaseManager.defaultDirectoryURL().deletingLastPathComponent()
|
||||
|
||||
do
|
||||
{
|
||||
let contents = try FileManager.default.contentsOfDirectory(at: documentsDirectoryURL, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
|
||||
|
||||
let itemURLs = contents.filter { GameType(fileExtension: $0.pathExtension) != nil || $0.pathExtension.lowercased() == "zip" || $0.pathExtension.lowercased() == "deltaskin" }
|
||||
|
||||
for url in itemURLs
|
||||
{
|
||||
let destinationURL = FileManager.uniqueTemporaryURL().appendingPathExtension(url.pathExtension)
|
||||
|
||||
do
|
||||
{
|
||||
try FileManager.default.moveItem(at: url, to: destinationURL)
|
||||
importedURLs.insert(destinationURL)
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Error importing file at URL", url, error)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
}
|
||||
|
||||
completionHandler(importedURLs)
|
||||
}
|
||||
alertController.addAction(importAction)
|
||||
|
||||
let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) { action in
|
||||
completionHandler(nil)
|
||||
}
|
||||
alertController.addAction(cancelAction)
|
||||
|
||||
self.presentingViewController.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
135
Delta/Importing/ImportController.swift
Normal file
135
Delta/Importing/ImportController.swift
Normal file
@ -0,0 +1,135 @@
|
||||
//
|
||||
// ImportController.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 10/10/15.
|
||||
// Copyright © 2015 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import ObjectiveC
|
||||
|
||||
import DeltaCore
|
||||
|
||||
import MobileCoreServices
|
||||
|
||||
protocol ImportControllerDelegate
|
||||
{
|
||||
func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>)
|
||||
|
||||
/** Optional **/
|
||||
func importControllerDidCancel(_ importController: ImportController)
|
||||
}
|
||||
|
||||
extension ImportControllerDelegate
|
||||
{
|
||||
func importControllerDidCancel(_ importController: ImportController)
|
||||
{
|
||||
// Empty Implementation
|
||||
}
|
||||
}
|
||||
|
||||
class ImportController: NSObject
|
||||
{
|
||||
let documentTypes: Set<String>
|
||||
|
||||
var delegate: ImportControllerDelegate?
|
||||
var importOptions: [ImportOption]?
|
||||
|
||||
init(documentTypes: Set<String>)
|
||||
{
|
||||
self.documentTypes = documentTypes
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
fileprivate weak var presentingViewController: UIViewController?
|
||||
|
||||
fileprivate func presentImportController(from presentingViewController: UIViewController, animated: Bool, completionHandler: ((Void) -> Void)?)
|
||||
{
|
||||
self.presentingViewController = presentingViewController
|
||||
|
||||
let documentMenuController = UIDocumentMenuViewController(documentTypes: Array(self.documentTypes), in: .import)
|
||||
documentMenuController.delegate = self
|
||||
|
||||
if let reversedImportOptions = self.importOptions?.reversed()
|
||||
{
|
||||
for importOption in reversedImportOptions
|
||||
{
|
||||
documentMenuController.add(importOption, order: UIDocumentMenuOrder.first) { (urls) in
|
||||
if let urls = urls
|
||||
{
|
||||
self.delegate?.importController(self, didImportItemsAt: urls)
|
||||
}
|
||||
else
|
||||
{
|
||||
self.delegate?.importControllerDidCancel(self)
|
||||
}
|
||||
|
||||
self.presentingViewController?.importController = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.presentingViewController?.present(documentMenuController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension ImportController: UIDocumentMenuDelegate
|
||||
{
|
||||
func documentMenu(_ documentMenu: UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController)
|
||||
{
|
||||
documentPicker.delegate = self
|
||||
self.presentingViewController?.present(documentPicker, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func documentMenuWasCancelled(_ documentMenu: UIDocumentMenuViewController)
|
||||
{
|
||||
self.delegate?.importControllerDidCancel(self)
|
||||
|
||||
self.presentingViewController?.importController = nil
|
||||
}
|
||||
}
|
||||
|
||||
extension ImportController: UIDocumentPickerDelegate
|
||||
{
|
||||
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL)
|
||||
{
|
||||
self.delegate?.importController(self, didImportItemsAt: [url])
|
||||
|
||||
self.presentingViewController?.importController = nil
|
||||
}
|
||||
|
||||
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController)
|
||||
{
|
||||
self.delegate?.importControllerDidCancel(self)
|
||||
|
||||
self.presentingViewController?.importController = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var ImportControllerKey: UInt8 = 0
|
||||
|
||||
extension UIViewController
|
||||
{
|
||||
fileprivate(set) var importController: ImportController?
|
||||
{
|
||||
set
|
||||
{
|
||||
objc_setAssociatedObject(self, &ImportControllerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
get
|
||||
{
|
||||
return objc_getAssociatedObject(self, &ImportControllerKey) as? ImportController
|
||||
}
|
||||
}
|
||||
|
||||
func present(_ importController: ImportController, animated: Bool, completion: ((Void) -> Void)?)
|
||||
{
|
||||
self.importController = importController
|
||||
|
||||
importController.presentImportController(from: self, animated: animated, completionHandler: completion)
|
||||
}
|
||||
}
|
||||
29
Delta/Importing/ImportOption.swift
Normal file
29
Delta/Importing/ImportOption.swift
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// ImportOption.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 5/1/17.
|
||||
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import DeltaCore
|
||||
|
||||
extension UIDocumentMenuViewController
|
||||
{
|
||||
func add(_ importOption: ImportOption, order: UIDocumentMenuOrder, completionHandler: @escaping (Set<URL>?) -> Void)
|
||||
{
|
||||
self.addOption(withTitle: importOption.title, image: importOption.image, order: order) {
|
||||
importOption.import(withCompletionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protocol ImportOption
|
||||
{
|
||||
var title: String { get }
|
||||
var image: UIImage? { get }
|
||||
|
||||
func `import`(withCompletionHandler completionHandler: @escaping (Set<URL>?) -> Void)
|
||||
}
|
||||
@ -126,6 +126,8 @@
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Press "OK" to allow Delta to use images from your Photo Library as game artwork.</string>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
||||
@ -16,7 +16,7 @@ extension GameType
|
||||
{
|
||||
init?(fileExtension: String)
|
||||
{
|
||||
switch fileExtension
|
||||
switch fileExtension.lowercased()
|
||||
{
|
||||
case "smc", "sfc", "fig": self = .snes
|
||||
case "gba": self = .gba
|
||||
|
||||
Loading…
Reference in New Issue
Block a user