├── .gitignore
├── LICENSE
├── README.md
├── Symbals.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ ├── IDEWorkspaceChecks.plist
│ └── swiftpm
│ └── Package.resolved
├── Symbals
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── icon-1024.png
│ │ ├── icon-120.png
│ │ ├── icon-121.png
│ │ ├── icon-152.png
│ │ ├── icon-167.png
│ │ ├── icon-180.png
│ │ ├── icon-20.png
│ │ ├── icon-30.png
│ │ ├── icon-40.png
│ │ ├── icon-41.png
│ │ ├── icon-42.png
│ │ ├── icon-58.png
│ │ ├── icon-59.png
│ │ ├── icon-60.png
│ │ ├── icon-76.png
│ │ ├── icon-80.png
│ │ ├── icon-81.png
│ │ └── icon-87.png
│ ├── Contents.json
│ ├── Other Apps
│ │ ├── Contents.json
│ │ ├── HomeCam.imageset
│ │ │ ├── Contents.json
│ │ │ ├── RoundedIcon-2.png
│ │ │ ├── RoundedIcon-3.png
│ │ │ └── RoundedIcon-4.png
│ │ ├── HomePass.imageset
│ │ │ ├── Contents.json
│ │ │ ├── RoundedIcon-6.png
│ │ │ └── RoundedIcon-8.png
│ │ ├── HomeRun.imageset
│ │ │ ├── Contents.json
│ │ │ ├── RoundedIcon-12.png
│ │ │ └── RoundedIcon-13.png
│ │ └── HomeScan.imageset
│ │ │ ├── Contents.json
│ │ │ ├── RoundedIcon-10.png
│ │ │ └── RoundedIcon-9.png
│ └── primary.colorset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Configuration
│ ├── Base.xcconfig
│ ├── Local.templates
│ │ ├── BundleId.xcconfig
│ │ ├── DevTeam.xcconfig
│ │ └── README
│ ├── Local.xcconfig
│ └── Symbals.xcconfig
├── Exporters
│ ├── Exporter.swift
│ ├── PDFExporter.swift
│ ├── PNGExporter.swift
│ ├── SVGExporter.swift
│ ├── ShortcutIconExporter.swift
│ └── SquaredPNGExporter.swift
├── Extensions
│ ├── Bundle+Version.swift
│ ├── UIImage-SymbolScale+Display.swift
│ ├── UIView+Additions.swift
│ └── UIimage-SymboiWeight+Display.swift
├── Info.plist
├── Models
│ ├── Symbol.swift
│ └── Symbols.swift
├── SceneDelegate.swift
├── Symbals.entitlements
├── View Controllers
│ ├── BaseCollectionViewController.swift
│ ├── DetailViewController.swift
│ ├── PopoverPushController.swift
│ ├── ScaleTableViewController.swift
│ ├── SettingsTableViewController.swift
│ ├── SplitViewController.swift
│ ├── SymbolsCollectionViewController.swift
│ ├── WeightAndScaleViewController.swift
│ └── WeightTableViewController.swift
└── Views
│ ├── DetailCell.swift
│ ├── ReusableViews.swift
│ └── SymbolCell.swift
├── app-icon.png
└── bootstrap.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | build/
3 | *.pbxuser
4 | !default.pbxuser
5 | *.mode1v3
6 | !default.mode1v3
7 | *.mode2v3
8 | !default.mode2v3
9 | *.perspectivev3
10 | !default.perspectivev3
11 | xcuserdata
12 | *.xccheckout
13 | *.moved-aside
14 | DerivedData
15 | *.hmap
16 | *.ipa
17 | *.xcuserstate
18 | *.xcscmblueprint
19 |
20 | Symbals/Configuration/Local/
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2020 Aaron Pearce https://aaronpearce.com/
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | SF Viewer for iOS
7 | ===============
8 |
9 | SF Viewer is the best way to view, compare and export SF Symbols on your iOS device.
10 |
11 | Features:
12 | - View all SF Symbols
13 | - Change weight and scale to see how each icon is displayed in a certain setting.
14 | - View additional metadata for a symbol, easily copy it's unicode encoding.
15 | - Export to SVG, PDF, PNG, Squared PNG.
16 | - You can also export icons explicitly for use with Shortcuts on your Home Screen.
17 |
18 |
19 | Follow me on Twitter at [@aaron_pearce](https://twitter.com/aaron_pearce).
20 |
21 | Getting involved
22 | ----------------
23 |
24 | Please feel free to participate in this open source project. I'd love to see Pull Requests, Bug Reports, ideas and any other positive contributions from the community!
25 |
26 | Building the code
27 | -----------------
28 |
29 | 1. Clone the repository:
30 | ```shell
31 | git clone https://github.com/aaronpearce/SF-Viewer.git
32 | ```
33 | 2. Pull in the project dependencies:
34 | ```shell
35 | cd Symbals
36 | sh ./bootstrap.sh
37 | ```
38 | 3. Open `Symbals.xcworkspace` in Xcode.
39 | 4. Find a source for the following fonts and drop them into the Fonts directory in Xcode:
40 | ```SF-Pro-Text-Black.otf
41 | SF-Pro-Text-Bold.otf
42 | SF-Pro-Text-Heavy.otf
43 | SF-Pro-Text-Light.otf
44 | SF-Pro-Text-Medium.otf
45 | SF-Pro-Text-Regular.otf
46 | SF-Pro-Text-Semibold.otf
47 | SF-Pro-Text-Thin.otf
48 | SF-Pro-Text-Ultralight.otf
49 | ```
50 | 5. Build the `Symbals` scheme in Xcode.
51 |
52 | ## Code Signing
53 |
54 | If *bootstrap.sh* fails to correctly offer your Apple Team ID, please follow this guide to manually add it.
55 |
56 | 1. After running the *bootstrap.sh* script in the setup instructions navigate to:
57 |
`Symbals/Configuration/Local/DevTeam.xcconfig`
58 | 1. Add your *Apple Team ID* in this file:
59 |
`LOCAL_DEVELOPMENT_TEAM = KL8N8XSYF4`
60 |
61 | >Team IDs look identical to provisioning profile UUIDs, so make sure this is the correct one.
62 |
63 | The entire `Local` directory is included in the `.gitignore`, so these changes are not tracked by source control. This allows code signing without making tracked changes. Updating this file will only sign the `Symbals` target for local builds.
64 |
65 | ### Finding Team IDs
66 |
67 | The easiest known way to find your team ID is to log into your [Apple Developer](https://developer.apple.com) account. After logging in, the team ID is currently shown at the end of the URL:
68 |
`https://developer.apple.com/account/`
69 |
70 | Use this string literal in the above, `DevTeam.xcconfig` file to code sign
71 |
72 | ## Thanks
73 |
74 | Thanks to everyone for their support in development and throughout the initial releases and then the review that failed and a particular thanks to [@kylehickinson](https://github.com/kylehickinson) for the suggestion to use Brave's `.xcconfig` based setup for local development signing. Credit to [@jhreis](https://github.com/jhreis) for the initial implementation that I based this upon.
75 |
76 | Thanks to [@davedelong](https://github.com/davedelog) for his [sfsymbols](https://github.com/davedelong/sfsymbols) project which helped with the exporter code within SF Viewer.
77 |
78 | ## Open Source & Copying
79 |
80 | SF Viewer is licensed under MIT so that you can use any code in your own apps, if you choose.
81 |
82 | However, **please do not ship this app** under your own account. Paid or free. Not that Apple will accept it.
83 |
--------------------------------------------------------------------------------
/Symbals.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 52;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | CD11DE1623617BE500C93776 /* SplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD11DE1523617BE500C93776 /* SplitViewController.swift */; };
11 | CD19DA6923BC1CB20009B352 /* Symbals.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = CD19DA5F23BC1CB20009B352 /* Symbals.xcconfig */; };
12 | CD38E31D2355A9760093B49D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD38E31C2355A9760093B49D /* AppDelegate.swift */; };
13 | CD38E31F2355A9760093B49D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD38E31E2355A9760093B49D /* SceneDelegate.swift */; };
14 | CD38E3232355A9760093B49D /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD38E3222355A9760093B49D /* DetailViewController.swift */; };
15 | CD38E3262355A9760093B49D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CD38E3242355A9760093B49D /* Main.storyboard */; };
16 | CD38E3282355A9770093B49D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CD38E3272355A9770093B49D /* Assets.xcassets */; };
17 | CD38E32B2355A9770093B49D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CD38E3292355A9770093B49D /* LaunchScreen.storyboard */; };
18 | CD38E3352355A9840093B49D /* Symbols.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD38E3332355A9840093B49D /* Symbols.swift */; };
19 | CD38E3372355A99F0093B49D /* SymbolsCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD38E3362355A99F0093B49D /* SymbolsCollectionViewController.swift */; };
20 | CD38E3392355A9D50093B49D /* UIView+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD38E3382355A9D50093B49D /* UIView+Additions.swift */; };
21 | CD38E33B2355A9DE0093B49D /* Bundle+Version.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD38E33A2355A9DE0093B49D /* Bundle+Version.swift */; };
22 | CD38E33D2355AA110093B49D /* ReusableViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD38E33C2355AA110093B49D /* ReusableViews.swift */; };
23 | CD38E33F2355AA1C0093B49D /* BaseCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD38E33E2355AA1C0093B49D /* BaseCollectionViewController.swift */; };
24 | CD54201D235850D200D0F58E /* SettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD54201C235850D200D0F58E /* SettingsTableViewController.swift */; };
25 | CD7C548B235AE87400EFBA62 /* SymbolCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7C548A235AE87400EFBA62 /* SymbolCell.swift */; };
26 | CD7C548D235AEB5200EFBA62 /* Symbol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7C548C235AEB5200EFBA62 /* Symbol.swift */; };
27 | CD7C5492235AEC1300EFBA62 /* DetailCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7C5491235AEC1300EFBA62 /* DetailCell.swift */; };
28 | CD8F8D94235EE70500D2EE81 /* SquaredPNGExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8F8D93235EE70500D2EE81 /* SquaredPNGExporter.swift */; };
29 | CD8F8D96235EECA200D2EE81 /* ShortcutIconExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8F8D95235EECA200D2EE81 /* ShortcutIconExporter.swift */; };
30 | CDAC67312356B7A1005E1183 /* CSV in Frameworks */ = {isa = PBXBuildFile; productRef = CDAC67302356B7A1005E1183 /* CSV */; };
31 | CDAC67332356D1AD005E1183 /* Exporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDAC67322356D1AD005E1183 /* Exporter.swift */; };
32 | CDAC67362356D288005E1183 /* SVGExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDAC67352356D288005E1183 /* SVGExporter.swift */; };
33 | CDAC67382356D495005E1183 /* PNGExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDAC67372356D495005E1183 /* PNGExporter.swift */; };
34 | CDAC673A2356D5E6005E1183 /* PDFExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDAC67392356D5E6005E1183 /* PDFExporter.swift */; };
35 | CDC75935235D5ED00096EE95 /* WeightTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC75934235D5ED00096EE95 /* WeightTableViewController.swift */; };
36 | CDC75937235D765A0096EE95 /* PopoverPushController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC75936235D765A0096EE95 /* PopoverPushController.swift */; };
37 | CDC75939235D797A0096EE95 /* UIimage-SymboiWeight+Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC75938235D797A0096EE95 /* UIimage-SymboiWeight+Display.swift */; };
38 | CDC7593B235D798E0096EE95 /* UIImage-SymbolScale+Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC7593A235D798E0096EE95 /* UIImage-SymbolScale+Display.swift */; };
39 | CDC7593D235D7A540096EE95 /* ScaleTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC7593C235D7A540096EE95 /* ScaleTableViewController.swift */; };
40 | CDE7945E235D35030075CA0F /* WeightAndScaleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE7945D235D35030075CA0F /* WeightAndScaleViewController.swift */; };
41 | /* End PBXBuildFile section */
42 |
43 | /* Begin PBXFileReference section */
44 | CD11DE1523617BE500C93776 /* SplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitViewController.swift; sourceTree = ""; };
45 | CD19DA5F23BC1CB20009B352 /* Symbals.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Symbals.xcconfig; sourceTree = ""; };
46 | CD38E3192355A9760093B49D /* SF Viewer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SF Viewer.app"; sourceTree = BUILT_PRODUCTS_DIR; };
47 | CD38E31C2355A9760093B49D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
48 | CD38E31E2355A9760093B49D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
49 | CD38E3222355A9760093B49D /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; };
50 | CD38E3252355A9760093B49D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
51 | CD38E3272355A9770093B49D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
52 | CD38E32A2355A9770093B49D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
53 | CD38E32C2355A9770093B49D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
54 | CD38E3332355A9840093B49D /* Symbols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Symbols.swift; sourceTree = ""; };
55 | CD38E3362355A99F0093B49D /* SymbolsCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolsCollectionViewController.swift; sourceTree = ""; };
56 | CD38E3382355A9D50093B49D /* UIView+Additions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Additions.swift"; sourceTree = ""; };
57 | CD38E33A2355A9DE0093B49D /* Bundle+Version.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bundle+Version.swift"; sourceTree = ""; };
58 | CD38E33C2355AA110093B49D /* ReusableViews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableViews.swift; sourceTree = ""; };
59 | CD38E33E2355AA1C0093B49D /* BaseCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseCollectionViewController.swift; sourceTree = ""; };
60 | CD54201C235850D200D0F58E /* SettingsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTableViewController.swift; sourceTree = ""; };
61 | CD7C548A235AE87400EFBA62 /* SymbolCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolCell.swift; sourceTree = ""; };
62 | CD7C548C235AEB5200EFBA62 /* Symbol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Symbol.swift; sourceTree = ""; };
63 | CD7C5491235AEC1300EFBA62 /* DetailCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailCell.swift; sourceTree = ""; };
64 | CD8F8D93235EE70500D2EE81 /* SquaredPNGExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SquaredPNGExporter.swift; sourceTree = ""; };
65 | CD8F8D95235EECA200D2EE81 /* ShortcutIconExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutIconExporter.swift; sourceTree = ""; };
66 | CDAC67322356D1AD005E1183 /* Exporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Exporter.swift; sourceTree = ""; };
67 | CDAC67352356D288005E1183 /* SVGExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SVGExporter.swift; sourceTree = ""; };
68 | CDAC67372356D495005E1183 /* PNGExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNGExporter.swift; sourceTree = ""; };
69 | CDAC67392356D5E6005E1183 /* PDFExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PDFExporter.swift; sourceTree = ""; };
70 | CDAC673C2356EA54005E1183 /* Symbals.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Symbals.entitlements; sourceTree = ""; };
71 | CDC75934235D5ED00096EE95 /* WeightTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeightTableViewController.swift; sourceTree = ""; };
72 | CDC75936235D765A0096EE95 /* PopoverPushController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverPushController.swift; sourceTree = ""; };
73 | CDC75938235D797A0096EE95 /* UIimage-SymboiWeight+Display.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIimage-SymboiWeight+Display.swift"; sourceTree = ""; };
74 | CDC7593A235D798E0096EE95 /* UIImage-SymbolScale+Display.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage-SymbolScale+Display.swift"; sourceTree = ""; };
75 | CDC7593C235D7A540096EE95 /* ScaleTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaleTableViewController.swift; sourceTree = ""; };
76 | CDE7945D235D35030075CA0F /* WeightAndScaleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeightAndScaleViewController.swift; sourceTree = ""; };
77 | /* End PBXFileReference section */
78 |
79 | /* Begin PBXFrameworksBuildPhase section */
80 | CD38E3162355A9760093B49D /* Frameworks */ = {
81 | isa = PBXFrameworksBuildPhase;
82 | buildActionMask = 2147483647;
83 | files = (
84 | CDAC67312356B7A1005E1183 /* CSV in Frameworks */,
85 | );
86 | runOnlyForDeploymentPostprocessing = 0;
87 | };
88 | /* End PBXFrameworksBuildPhase section */
89 |
90 | /* Begin PBXGroup section */
91 | CD19DA3823BC1AEA0009B352 /* Fonts */ = {
92 | isa = PBXGroup;
93 | children = (
94 | );
95 | path = Fonts;
96 | sourceTree = "";
97 | };
98 | CD19DA5E23BC1CB20009B352 /* Configuration */ = {
99 | isa = PBXGroup;
100 | children = (
101 | CD19DA5F23BC1CB20009B352 /* Symbals.xcconfig */,
102 | );
103 | path = Configuration;
104 | sourceTree = "";
105 | };
106 | CD38E3102355A9760093B49D = {
107 | isa = PBXGroup;
108 | children = (
109 | CD38E31B2355A9760093B49D /* Symbals */,
110 | CD38E31A2355A9760093B49D /* Products */,
111 | );
112 | sourceTree = "";
113 | };
114 | CD38E31A2355A9760093B49D /* Products */ = {
115 | isa = PBXGroup;
116 | children = (
117 | CD38E3192355A9760093B49D /* SF Viewer.app */,
118 | );
119 | name = Products;
120 | sourceTree = "";
121 | };
122 | CD38E31B2355A9760093B49D /* Symbals */ = {
123 | isa = PBXGroup;
124 | children = (
125 | CD19DA5E23BC1CB20009B352 /* Configuration */,
126 | CD19DA3823BC1AEA0009B352 /* Fonts */,
127 | CDAC673C2356EA54005E1183 /* Symbals.entitlements */,
128 | CD7C548E235AEBE800EFBA62 /* View Controllers */,
129 | CD7C548F235AEBF300EFBA62 /* Models */,
130 | CD7C5490235AEBFD00EFBA62 /* Views */,
131 | CDAC673B2356EA36005E1183 /* Extensions */,
132 | CDAC67342356D273005E1183 /* Exporters */,
133 | CD38E31C2355A9760093B49D /* AppDelegate.swift */,
134 | CD38E31E2355A9760093B49D /* SceneDelegate.swift */,
135 | CD38E3242355A9760093B49D /* Main.storyboard */,
136 | CD38E3272355A9770093B49D /* Assets.xcassets */,
137 | CD38E3292355A9770093B49D /* LaunchScreen.storyboard */,
138 | CD38E32C2355A9770093B49D /* Info.plist */,
139 | );
140 | path = Symbals;
141 | sourceTree = "";
142 | };
143 | CD7C548E235AEBE800EFBA62 /* View Controllers */ = {
144 | isa = PBXGroup;
145 | children = (
146 | CD38E33E2355AA1C0093B49D /* BaseCollectionViewController.swift */,
147 | CD38E3362355A99F0093B49D /* SymbolsCollectionViewController.swift */,
148 | CD38E3222355A9760093B49D /* DetailViewController.swift */,
149 | CDE7945D235D35030075CA0F /* WeightAndScaleViewController.swift */,
150 | CDC75934235D5ED00096EE95 /* WeightTableViewController.swift */,
151 | CDC7593C235D7A540096EE95 /* ScaleTableViewController.swift */,
152 | CDC75936235D765A0096EE95 /* PopoverPushController.swift */,
153 | CD54201C235850D200D0F58E /* SettingsTableViewController.swift */,
154 | CD11DE1523617BE500C93776 /* SplitViewController.swift */,
155 | );
156 | path = "View Controllers";
157 | sourceTree = "";
158 | };
159 | CD7C548F235AEBF300EFBA62 /* Models */ = {
160 | isa = PBXGroup;
161 | children = (
162 | CD38E3332355A9840093B49D /* Symbols.swift */,
163 | CD7C548C235AEB5200EFBA62 /* Symbol.swift */,
164 | );
165 | path = Models;
166 | sourceTree = "";
167 | };
168 | CD7C5490235AEBFD00EFBA62 /* Views */ = {
169 | isa = PBXGroup;
170 | children = (
171 | CD7C548A235AE87400EFBA62 /* SymbolCell.swift */,
172 | CD38E33C2355AA110093B49D /* ReusableViews.swift */,
173 | CD7C5491235AEC1300EFBA62 /* DetailCell.swift */,
174 | );
175 | path = Views;
176 | sourceTree = "";
177 | };
178 | CDAC67342356D273005E1183 /* Exporters */ = {
179 | isa = PBXGroup;
180 | children = (
181 | CDAC67322356D1AD005E1183 /* Exporter.swift */,
182 | CDAC67352356D288005E1183 /* SVGExporter.swift */,
183 | CDAC67372356D495005E1183 /* PNGExporter.swift */,
184 | CDAC67392356D5E6005E1183 /* PDFExporter.swift */,
185 | CD8F8D93235EE70500D2EE81 /* SquaredPNGExporter.swift */,
186 | CD8F8D95235EECA200D2EE81 /* ShortcutIconExporter.swift */,
187 | );
188 | path = Exporters;
189 | sourceTree = "";
190 | };
191 | CDAC673B2356EA36005E1183 /* Extensions */ = {
192 | isa = PBXGroup;
193 | children = (
194 | CD38E3382355A9D50093B49D /* UIView+Additions.swift */,
195 | CD38E33A2355A9DE0093B49D /* Bundle+Version.swift */,
196 | CDC75938235D797A0096EE95 /* UIimage-SymboiWeight+Display.swift */,
197 | CDC7593A235D798E0096EE95 /* UIImage-SymbolScale+Display.swift */,
198 | );
199 | path = Extensions;
200 | sourceTree = "";
201 | };
202 | /* End PBXGroup section */
203 |
204 | /* Begin PBXNativeTarget section */
205 | CD38E3182355A9760093B49D /* Symbals */ = {
206 | isa = PBXNativeTarget;
207 | buildConfigurationList = CD38E32F2355A9770093B49D /* Build configuration list for PBXNativeTarget "Symbals" */;
208 | buildPhases = (
209 | CD38E3152355A9760093B49D /* Sources */,
210 | CD38E3162355A9760093B49D /* Frameworks */,
211 | CD38E3172355A9760093B49D /* Resources */,
212 | );
213 | buildRules = (
214 | );
215 | dependencies = (
216 | );
217 | name = Symbals;
218 | packageProductDependencies = (
219 | CDAC67302356B7A1005E1183 /* CSV */,
220 | );
221 | productName = Symbals;
222 | productReference = CD38E3192355A9760093B49D /* SF Viewer.app */;
223 | productType = "com.apple.product-type.application";
224 | };
225 | /* End PBXNativeTarget section */
226 |
227 | /* Begin PBXProject section */
228 | CD38E3112355A9760093B49D /* Project object */ = {
229 | isa = PBXProject;
230 | attributes = {
231 | LastSwiftUpdateCheck = 1120;
232 | LastUpgradeCheck = 1120;
233 | ORGANIZATIONNAME = Sunya;
234 | TargetAttributes = {
235 | CD38E3182355A9760093B49D = {
236 | CreatedOnToolsVersion = 11.2;
237 | };
238 | };
239 | };
240 | buildConfigurationList = CD38E3142355A9760093B49D /* Build configuration list for PBXProject "Symbals" */;
241 | compatibilityVersion = "Xcode 9.3";
242 | developmentRegion = en;
243 | hasScannedForEncodings = 0;
244 | knownRegions = (
245 | en,
246 | Base,
247 | );
248 | mainGroup = CD38E3102355A9760093B49D;
249 | packageReferences = (
250 | CDAC672F2356B7A1005E1183 /* XCRemoteSwiftPackageReference "CSV.swift" */,
251 | );
252 | productRefGroup = CD38E31A2355A9760093B49D /* Products */;
253 | projectDirPath = "";
254 | projectRoot = "";
255 | targets = (
256 | CD38E3182355A9760093B49D /* Symbals */,
257 | );
258 | };
259 | /* End PBXProject section */
260 |
261 | /* Begin PBXResourcesBuildPhase section */
262 | CD38E3172355A9760093B49D /* Resources */ = {
263 | isa = PBXResourcesBuildPhase;
264 | buildActionMask = 2147483647;
265 | files = (
266 | CD38E32B2355A9770093B49D /* LaunchScreen.storyboard in Resources */,
267 | CD38E3282355A9770093B49D /* Assets.xcassets in Resources */,
268 | CD19DA6923BC1CB20009B352 /* Symbals.xcconfig in Resources */,
269 | CD38E3262355A9760093B49D /* Main.storyboard in Resources */,
270 | );
271 | runOnlyForDeploymentPostprocessing = 0;
272 | };
273 | /* End PBXResourcesBuildPhase section */
274 |
275 | /* Begin PBXSourcesBuildPhase section */
276 | CD38E3152355A9760093B49D /* Sources */ = {
277 | isa = PBXSourcesBuildPhase;
278 | buildActionMask = 2147483647;
279 | files = (
280 | CDC7593D235D7A540096EE95 /* ScaleTableViewController.swift in Sources */,
281 | CD38E3372355A99F0093B49D /* SymbolsCollectionViewController.swift in Sources */,
282 | CDC75935235D5ED00096EE95 /* WeightTableViewController.swift in Sources */,
283 | CD8F8D94235EE70500D2EE81 /* SquaredPNGExporter.swift in Sources */,
284 | CDE7945E235D35030075CA0F /* WeightAndScaleViewController.swift in Sources */,
285 | CD7C548B235AE87400EFBA62 /* SymbolCell.swift in Sources */,
286 | CD7C5492235AEC1300EFBA62 /* DetailCell.swift in Sources */,
287 | CDC75939235D797A0096EE95 /* UIimage-SymboiWeight+Display.swift in Sources */,
288 | CD38E31D2355A9760093B49D /* AppDelegate.swift in Sources */,
289 | CD7C548D235AEB5200EFBA62 /* Symbol.swift in Sources */,
290 | CDAC67332356D1AD005E1183 /* Exporter.swift in Sources */,
291 | CD38E33B2355A9DE0093B49D /* Bundle+Version.swift in Sources */,
292 | CDAC67362356D288005E1183 /* SVGExporter.swift in Sources */,
293 | CD11DE1623617BE500C93776 /* SplitViewController.swift in Sources */,
294 | CD38E3352355A9840093B49D /* Symbols.swift in Sources */,
295 | CDC75937235D765A0096EE95 /* PopoverPushController.swift in Sources */,
296 | CD54201D235850D200D0F58E /* SettingsTableViewController.swift in Sources */,
297 | CDAC673A2356D5E6005E1183 /* PDFExporter.swift in Sources */,
298 | CD38E33D2355AA110093B49D /* ReusableViews.swift in Sources */,
299 | CD38E31F2355A9760093B49D /* SceneDelegate.swift in Sources */,
300 | CD38E3392355A9D50093B49D /* UIView+Additions.swift in Sources */,
301 | CD38E3232355A9760093B49D /* DetailViewController.swift in Sources */,
302 | CDC7593B235D798E0096EE95 /* UIImage-SymbolScale+Display.swift in Sources */,
303 | CD8F8D96235EECA200D2EE81 /* ShortcutIconExporter.swift in Sources */,
304 | CDAC67382356D495005E1183 /* PNGExporter.swift in Sources */,
305 | CD38E33F2355AA1C0093B49D /* BaseCollectionViewController.swift in Sources */,
306 | );
307 | runOnlyForDeploymentPostprocessing = 0;
308 | };
309 | /* End PBXSourcesBuildPhase section */
310 |
311 | /* Begin PBXVariantGroup section */
312 | CD38E3242355A9760093B49D /* Main.storyboard */ = {
313 | isa = PBXVariantGroup;
314 | children = (
315 | CD38E3252355A9760093B49D /* Base */,
316 | );
317 | name = Main.storyboard;
318 | sourceTree = "";
319 | };
320 | CD38E3292355A9770093B49D /* LaunchScreen.storyboard */ = {
321 | isa = PBXVariantGroup;
322 | children = (
323 | CD38E32A2355A9770093B49D /* Base */,
324 | );
325 | name = LaunchScreen.storyboard;
326 | sourceTree = "";
327 | };
328 | /* End PBXVariantGroup section */
329 |
330 | /* Begin XCBuildConfiguration section */
331 | CD38E32D2355A9770093B49D /* Debug */ = {
332 | isa = XCBuildConfiguration;
333 | baseConfigurationReference = CD19DA5F23BC1CB20009B352 /* Symbals.xcconfig */;
334 | buildSettings = {
335 | ALWAYS_SEARCH_USER_PATHS = NO;
336 | CLANG_ANALYZER_NONNULL = YES;
337 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
338 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
339 | CLANG_CXX_LIBRARY = "libc++";
340 | CLANG_ENABLE_MODULES = YES;
341 | CLANG_ENABLE_OBJC_ARC = YES;
342 | CLANG_ENABLE_OBJC_WEAK = YES;
343 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
344 | CLANG_WARN_BOOL_CONVERSION = YES;
345 | CLANG_WARN_COMMA = YES;
346 | CLANG_WARN_CONSTANT_CONVERSION = YES;
347 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
348 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
349 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
350 | CLANG_WARN_EMPTY_BODY = YES;
351 | CLANG_WARN_ENUM_CONVERSION = YES;
352 | CLANG_WARN_INFINITE_RECURSION = YES;
353 | CLANG_WARN_INT_CONVERSION = YES;
354 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
355 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
356 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
357 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
358 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
359 | CLANG_WARN_STRICT_PROTOTYPES = YES;
360 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
361 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
362 | CLANG_WARN_UNREACHABLE_CODE = YES;
363 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
364 | COPY_PHASE_STRIP = NO;
365 | DEBUG_INFORMATION_FORMAT = dwarf;
366 | ENABLE_STRICT_OBJC_MSGSEND = YES;
367 | ENABLE_TESTABILITY = YES;
368 | GCC_C_LANGUAGE_STANDARD = gnu11;
369 | GCC_DYNAMIC_NO_PIC = NO;
370 | GCC_NO_COMMON_BLOCKS = YES;
371 | GCC_OPTIMIZATION_LEVEL = 0;
372 | GCC_PREPROCESSOR_DEFINITIONS = (
373 | "DEBUG=1",
374 | "$(inherited)",
375 | );
376 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
377 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
378 | GCC_WARN_UNDECLARED_SELECTOR = YES;
379 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
380 | GCC_WARN_UNUSED_FUNCTION = YES;
381 | GCC_WARN_UNUSED_VARIABLE = YES;
382 | IPHONEOS_DEPLOYMENT_TARGET = 13.2;
383 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
384 | MTL_FAST_MATH = YES;
385 | ONLY_ACTIVE_ARCH = YES;
386 | SDKROOT = iphoneos;
387 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
388 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
389 | };
390 | name = Debug;
391 | };
392 | CD38E32E2355A9770093B49D /* Release */ = {
393 | isa = XCBuildConfiguration;
394 | baseConfigurationReference = CD19DA5F23BC1CB20009B352 /* Symbals.xcconfig */;
395 | buildSettings = {
396 | ALWAYS_SEARCH_USER_PATHS = NO;
397 | CLANG_ANALYZER_NONNULL = YES;
398 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
399 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
400 | CLANG_CXX_LIBRARY = "libc++";
401 | CLANG_ENABLE_MODULES = YES;
402 | CLANG_ENABLE_OBJC_ARC = YES;
403 | CLANG_ENABLE_OBJC_WEAK = YES;
404 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
405 | CLANG_WARN_BOOL_CONVERSION = YES;
406 | CLANG_WARN_COMMA = YES;
407 | CLANG_WARN_CONSTANT_CONVERSION = YES;
408 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
409 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
410 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
411 | CLANG_WARN_EMPTY_BODY = YES;
412 | CLANG_WARN_ENUM_CONVERSION = YES;
413 | CLANG_WARN_INFINITE_RECURSION = YES;
414 | CLANG_WARN_INT_CONVERSION = YES;
415 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
416 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
417 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
418 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
419 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
420 | CLANG_WARN_STRICT_PROTOTYPES = YES;
421 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
422 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
423 | CLANG_WARN_UNREACHABLE_CODE = YES;
424 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
425 | COPY_PHASE_STRIP = NO;
426 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
427 | ENABLE_NS_ASSERTIONS = NO;
428 | ENABLE_STRICT_OBJC_MSGSEND = YES;
429 | GCC_C_LANGUAGE_STANDARD = gnu11;
430 | GCC_NO_COMMON_BLOCKS = YES;
431 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
432 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
433 | GCC_WARN_UNDECLARED_SELECTOR = YES;
434 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
435 | GCC_WARN_UNUSED_FUNCTION = YES;
436 | GCC_WARN_UNUSED_VARIABLE = YES;
437 | IPHONEOS_DEPLOYMENT_TARGET = 13.2;
438 | MTL_ENABLE_DEBUG_INFO = NO;
439 | MTL_FAST_MATH = YES;
440 | SDKROOT = iphoneos;
441 | SWIFT_COMPILATION_MODE = wholemodule;
442 | SWIFT_OPTIMIZATION_LEVEL = "-O";
443 | VALIDATE_PRODUCT = YES;
444 | };
445 | name = Release;
446 | };
447 | CD38E3302355A9770093B49D /* Debug */ = {
448 | isa = XCBuildConfiguration;
449 | buildSettings = {
450 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
451 | CODE_SIGN_ENTITLEMENTS = Symbals/Symbals.entitlements;
452 | CODE_SIGN_IDENTITY = "Apple Development";
453 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
454 | CODE_SIGN_STYLE = Automatic;
455 | CURRENT_PROJECT_VERSION = 15;
456 | DEVELOPMENT_TEAM = "$(LOCAL_DEVELOPMENT_TEAM)";
457 | INFOPLIST_FILE = Symbals/Info.plist;
458 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
459 | LD_RUNPATH_SEARCH_PATHS = (
460 | "$(inherited)",
461 | "@executable_path/Frameworks",
462 | );
463 | MARKETING_VERSION = 1.0.4;
464 | PRODUCT_BUNDLE_IDENTIFIER = "$(SYMBALS_BUNDLE_ID)";
465 | PRODUCT_NAME = "SF Viewer";
466 | PROVISIONING_PROFILE_SPECIFIER = "";
467 | "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
468 | SUPPORTS_MACCATALYST = NO;
469 | SWIFT_VERSION = 5.0;
470 | TARGETED_DEVICE_FAMILY = "1,2";
471 | };
472 | name = Debug;
473 | };
474 | CD38E3312355A9770093B49D /* Release */ = {
475 | isa = XCBuildConfiguration;
476 | buildSettings = {
477 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
478 | CODE_SIGN_ENTITLEMENTS = Symbals/Symbals.entitlements;
479 | CODE_SIGN_IDENTITY = "Apple Development";
480 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
481 | CODE_SIGN_STYLE = Automatic;
482 | CURRENT_PROJECT_VERSION = 15;
483 | DEVELOPMENT_TEAM = "$(LOCAL_DEVELOPMENT_TEAM)";
484 | INFOPLIST_FILE = Symbals/Info.plist;
485 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
486 | LD_RUNPATH_SEARCH_PATHS = (
487 | "$(inherited)",
488 | "@executable_path/Frameworks",
489 | );
490 | MARKETING_VERSION = 1.0.4;
491 | PRODUCT_BUNDLE_IDENTIFIER = "$(SYMBALS_BUNDLE_ID)";
492 | PRODUCT_NAME = "SF Viewer";
493 | PROVISIONING_PROFILE_SPECIFIER = "";
494 | "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
495 | SUPPORTS_MACCATALYST = NO;
496 | SWIFT_VERSION = 5.0;
497 | TARGETED_DEVICE_FAMILY = "1,2";
498 | };
499 | name = Release;
500 | };
501 | /* End XCBuildConfiguration section */
502 |
503 | /* Begin XCConfigurationList section */
504 | CD38E3142355A9760093B49D /* Build configuration list for PBXProject "Symbals" */ = {
505 | isa = XCConfigurationList;
506 | buildConfigurations = (
507 | CD38E32D2355A9770093B49D /* Debug */,
508 | CD38E32E2355A9770093B49D /* Release */,
509 | );
510 | defaultConfigurationIsVisible = 0;
511 | defaultConfigurationName = Release;
512 | };
513 | CD38E32F2355A9770093B49D /* Build configuration list for PBXNativeTarget "Symbals" */ = {
514 | isa = XCConfigurationList;
515 | buildConfigurations = (
516 | CD38E3302355A9770093B49D /* Debug */,
517 | CD38E3312355A9770093B49D /* Release */,
518 | );
519 | defaultConfigurationIsVisible = 0;
520 | defaultConfigurationName = Release;
521 | };
522 | /* End XCConfigurationList section */
523 |
524 | /* Begin XCRemoteSwiftPackageReference section */
525 | CDAC672F2356B7A1005E1183 /* XCRemoteSwiftPackageReference "CSV.swift" */ = {
526 | isa = XCRemoteSwiftPackageReference;
527 | repositoryURL = "https://github.com/yaslab/CSV.swift.git";
528 | requirement = {
529 | branch = master;
530 | kind = branch;
531 | };
532 | };
533 | /* End XCRemoteSwiftPackageReference section */
534 |
535 | /* Begin XCSwiftPackageProductDependency section */
536 | CDAC67302356B7A1005E1183 /* CSV */ = {
537 | isa = XCSwiftPackageProductDependency;
538 | package = CDAC672F2356B7A1005E1183 /* XCRemoteSwiftPackageReference "CSV.swift" */;
539 | productName = CSV;
540 | };
541 | /* End XCSwiftPackageProductDependency section */
542 | };
543 | rootObject = CD38E3112355A9760093B49D /* Project object */;
544 | }
545 |
--------------------------------------------------------------------------------
/Symbals.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Symbals.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Symbals.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "CSV.swift",
6 | "repositoryURL": "https://github.com/yaslab/CSV.swift.git",
7 | "state": {
8 | "branch": "master",
9 | "revision": "e8e82ac6fbc2ac4198e9916baf24892d3c8471ef",
10 | "version": null
11 | }
12 | }
13 | ]
14 | },
15 | "version": 1
16 | }
17 |
--------------------------------------------------------------------------------
/Symbals/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 15/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CommonCrypto
11 |
12 | @UIApplicationMain
13 | class AppDelegate: UIResponder, UIApplicationDelegate {
14 |
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 |
20 | return true
21 | }
22 |
23 | // MARK: UISceneSession Lifecycle
24 |
25 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
26 | // Called when a new scene session is being created.
27 | // Use this method to select a configuration to create the new scene with.
28 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
29 | }
30 |
31 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
32 | // Called when the user discards a scene session.
33 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
34 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "icon-40.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "icon-60.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "icon-58.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "icon-87.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "40x40",
29 | "idiom" : "iphone",
30 | "filename" : "icon-80.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "icon-120.png",
37 | "scale" : "3x"
38 | },
39 | {
40 | "size" : "60x60",
41 | "idiom" : "iphone",
42 | "filename" : "icon-121.png",
43 | "scale" : "2x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "icon-180.png",
49 | "scale" : "3x"
50 | },
51 | {
52 | "size" : "20x20",
53 | "idiom" : "ipad",
54 | "filename" : "icon-20.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "icon-41.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "29x29",
65 | "idiom" : "ipad",
66 | "filename" : "icon-30.png",
67 | "scale" : "1x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "icon-59.png",
73 | "scale" : "2x"
74 | },
75 | {
76 | "size" : "40x40",
77 | "idiom" : "ipad",
78 | "filename" : "icon-42.png",
79 | "scale" : "1x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "icon-81.png",
85 | "scale" : "2x"
86 | },
87 | {
88 | "size" : "76x76",
89 | "idiom" : "ipad",
90 | "filename" : "icon-76.png",
91 | "scale" : "1x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "icon-152.png",
97 | "scale" : "2x"
98 | },
99 | {
100 | "size" : "83.5x83.5",
101 | "idiom" : "ipad",
102 | "filename" : "icon-167.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "1024x1024",
107 | "idiom" : "ios-marketing",
108 | "filename" : "icon-1024.png",
109 | "scale" : "1x"
110 | }
111 | ],
112 | "info" : {
113 | "version" : 1,
114 | "author" : "xcode"
115 | }
116 | }
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-1024.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-120.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-121.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-121.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-152.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-167.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-180.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-20.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-30.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-30.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-40.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-41.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-41.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-42.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-42.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-58.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-59.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-59.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-60.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-76.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-80.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-81.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-81.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/AppIcon.appiconset/icon-87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/AppIcon.appiconset/icon-87.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Other Apps/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Other Apps/HomeCam.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "RoundedIcon-2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "RoundedIcon-3.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "RoundedIcon-4.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Other Apps/HomeCam.imageset/RoundedIcon-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/Other Apps/HomeCam.imageset/RoundedIcon-2.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Other Apps/HomeCam.imageset/RoundedIcon-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/Other Apps/HomeCam.imageset/RoundedIcon-3.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Other Apps/HomeCam.imageset/RoundedIcon-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/Other Apps/HomeCam.imageset/RoundedIcon-4.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Other Apps/HomePass.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "RoundedIcon-6.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "RoundedIcon-8.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Other Apps/HomePass.imageset/RoundedIcon-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/Other Apps/HomePass.imageset/RoundedIcon-6.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Other Apps/HomePass.imageset/RoundedIcon-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/Other Apps/HomePass.imageset/RoundedIcon-8.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Other Apps/HomeRun.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "RoundedIcon-12.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "RoundedIcon-13.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Other Apps/HomeRun.imageset/RoundedIcon-12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/Other Apps/HomeRun.imageset/RoundedIcon-12.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Other Apps/HomeRun.imageset/RoundedIcon-13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/Other Apps/HomeRun.imageset/RoundedIcon-13.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Other Apps/HomeScan.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "RoundedIcon-9.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "RoundedIcon-10.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Other Apps/HomeScan.imageset/RoundedIcon-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/Other Apps/HomeScan.imageset/RoundedIcon-10.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/Other Apps/HomeScan.imageset/RoundedIcon-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/Symbals/Assets.xcassets/Other Apps/HomeScan.imageset/RoundedIcon-9.png
--------------------------------------------------------------------------------
/Symbals/Assets.xcassets/primary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0x35",
13 | "alpha" : "1.000",
14 | "blue" : "0xE9",
15 | "green" : "0xA0"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/Symbals/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Symbals/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
173 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
253 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
280 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
--------------------------------------------------------------------------------
/Symbals/Configuration/Base.xcconfig:
--------------------------------------------------------------------------------
1 | // This Source Code Form is subject to the terms of the Mozilla Public
2 | // License, v. 2.0. If a copy of the MPL was not distributed with this
3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | // This is for _Global_ swift flags and will be applied to _all_ targets.
6 | // Configuration inheritence only works through Xcode defined levels, not configuration files.
7 | // So need to use unique name and utilize in subclasses
8 | // (e.g. can't use $(inherited) to 'pull' these changes, into sub-configurations)
9 |
10 | //OTHER_SWIFT_FLAGS_BASE=-DMOZ_TARGET_$(TARGET_NAME:upper) -DNO_SYNC
11 |
12 | BASE_BUNDLE_ID = com.aaronpearce.DevSwitch
13 | DEVELOPMENT_TEAM = KL8N8XSYF4
14 |
15 | // Additional Info //
16 | // Can specify target specific flags via:
17 | // `OTHER_SWIFT_FLAGS_=-DCUSTOM_FLAG`
18 | // e.g. `OTHER_SWIFT_FLAGS_CLIENT=-DCUSTOM_FLAG`
19 | //
20 | // Usage: `OTHER_SWIFT_FLAGS_BASE=$(OTHER_SWIFT_FLAGS_$(TARGET_NAME:upper))`
21 |
22 | // For ObjC import filtering (e.g. ObjC SDK vai bridge header), both Swift_Flags and GCC will need the flag information
23 | // Most likely, this will look like `-DFOO` and `FOO=1`
24 |
--------------------------------------------------------------------------------
/Symbals/Configuration/Local.templates/BundleId.xcconfig:
--------------------------------------------------------------------------------
1 |
2 | // App currently uses automatic code signing, so as long as a bundle id is added that is unique enough
3 | // Xcode should easily create the necessary certificates for app signing.
4 |
5 | // This may need to be adjusted if `USER` is not unique.
6 | LOCAL_BUNDLE_ID = Symbals.$(USER).local.id
7 |
--------------------------------------------------------------------------------
/Symbals/Configuration/Local.templates/DevTeam.xcconfig:
--------------------------------------------------------------------------------
1 |
2 | // Add personal development team
3 | LOCAL_DEVELOPMENT_TEAM = // Set for code signing (e.g. running on devices)
--------------------------------------------------------------------------------
/Symbals/Configuration/Local.templates/README:
--------------------------------------------------------------------------------
1 | Files in this directory should NOT be edited. They are tracked, and used to populate the
2 | parallel `Local` directory
3 |
4 | Files in the `Local` directory _should_ be edited, as they are not tracked but used for
5 | configuring the project dynamically.
--------------------------------------------------------------------------------
/Symbals/Configuration/Local.xcconfig:
--------------------------------------------------------------------------------
1 | // Imports local configurations to be utilized for local builds.
2 | // These are only compiled into the Local target
3 |
4 | #include "Local/BundleId.xcconfig"
5 | #include "Local/DevTeam.xcconfig"
6 |
7 | // Set in Local/BundleId.xcconfig
8 | SYMBALS_BUNDLE_ID = $(LOCAL_BUNDLE_ID)
9 |
10 | // Set in Local/DevTeam.xcconfig
11 | DEVELOPMENT_TEAM = $(LOCAL_DEVELOPMENT_TEAM)
12 |
--------------------------------------------------------------------------------
/Symbals/Configuration/Symbals.xcconfig:
--------------------------------------------------------------------------------
1 | // This Source Code Form is subject to the terms of the Mozilla Public
2 | // License, v. 2.0. If a copy of the MPL was not distributed with this
3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | #include "Base.xcconfig"
6 | #include "Local.xcconfig"
7 |
8 | ENABLE_TESTABILITY = YES
9 |
10 | GCC_PREPROCESSOR_DEFINITIONS= DEBUG=1
11 |
--------------------------------------------------------------------------------
/Symbals/Exporters/Exporter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Exporter.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 16/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | enum ExportFormat: String, CaseIterable {
12 |
13 | case svg = "SVG"
14 | // case iosSwift = "ios-swift"
15 | // case iosObjC = "ios-objc"
16 | // case macosSwift = "macos-swift"
17 | // case macosObjC = "macos-objc"
18 | case shortcut = "Shortcut"
19 | case squaredPng = "Squared PNG"
20 | case png = "PNG"
21 | case pdf = "PDF"
22 | // case iconset = "iconset"
23 | // case iconsetPDF = "iconset-pdf"
24 |
25 | var exporter: Exporter {
26 | switch self {
27 | case .svg: return SVGExporter()
28 | // case .iosSwift: return iOSSwiftExporter()
29 | // case .iosObjC: return iOSObjCExporter()
30 | // case .macosSwift: return macOSSwiftExporter()
31 | // case .macosObjC: return macOSObjCExporter()
32 | case .shortcut: return ShortcutIconExporter()
33 | case .squaredPng: return SquaredPNGExporter()
34 | case .png: return PNGExporter()
35 | case .pdf: return PDFExporter()
36 | // case .iconset: return IconsetExporter()
37 | // case .iconsetPDF: return PDFAssetCatalog()
38 | }
39 | }
40 | }
41 |
42 | protocol Exporter {
43 | func export(symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont, to folder: URL) throws -> URL
44 | // func exportGlyphs(in font: Font, matching pattern: String, to folder: URL) throws
45 | // func exportGlyph(_ symbol: Symbol, in font: CTFont, to folder: URL) throws
46 | func data(for symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont) -> Data
47 | }
48 |
--------------------------------------------------------------------------------
/Symbals/Exporters/PDFExporter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PDFExporter.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 16/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct PDFExporter: Exporter {
12 |
13 | func export(symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont, to folder: URL) throws -> URL {
14 | let name = "\(symbol.shortName).pdf"
15 | let file = folder.appendingPathComponent(name)
16 | let symbolData = data(for: symbol, weight: weight, scale: scale, in: font)
17 | try symbolData.write(to: file)
18 | return file
19 | }
20 |
21 | func data(for symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont) -> Data {
22 | let destination = NSMutableData()
23 | guard let dataConsumer = CGDataConsumer(data: destination as CFMutableData) else { return Data() }
24 |
25 | guard let pathTuple = try? symbol.generateCGPath(for: weight, scale: scale) else { return Data() }
26 |
27 | var box = pathTuple.boundingBox
28 | guard let pdf = CGContext(consumer: dataConsumer, mediaBox: &box, nil) else { return Data() }
29 |
30 | let pageInfo = [
31 | kCGPDFContextMediaBox: Data(bytes: &box, count: MemoryLayout.size) as CFData
32 | ]
33 | pdf.beginPDFPage(pageInfo as CFDictionary)
34 | pdf.setShouldAntialias(true)
35 | pdf.addPath(pathTuple.path)
36 |
37 | pdf.setFillColor(UIColor.black.cgColor)
38 | pdf.fillPath()
39 | pdf.endPDFPage()
40 | pdf.closePDF()
41 |
42 | return destination as Data
43 | }
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/Symbals/Exporters/PNGExporter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PNGExporter.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 16/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct PNGExporter: Exporter {
12 |
13 | func export(symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont, to folder: URL) throws -> URL {
14 | let name = "\(symbol.shortName).png"
15 | let file = folder.appendingPathComponent(name)
16 | let symbolData = data(for: symbol, weight: weight, scale: scale, in: font)
17 | try symbolData.write(to: file)
18 | return file
19 | }
20 |
21 | func data(for symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont, viewScale: CGFloat) -> Data {
22 | guard let pathTuple = try? symbol.generateCGPath(for: weight, scale: scale) else { return Data() }
23 | let size = pathTuple.boundingBox.size
24 |
25 | let format = UIGraphicsImageRendererFormat()
26 | format.scale = viewScale
27 | format.opaque = false
28 | let renderer = UIGraphicsImageRenderer(size: size, format: format)
29 |
30 | let imageData = renderer.pngData { (context) in
31 | context.cgContext.translateBy(x: 0.0, y: size.height)
32 | context.cgContext.scaleBy(x: 1.0, y: -1.0)
33 | context.cgContext.setShouldAntialias(true)
34 | context.cgContext.addPath(pathTuple.path)
35 | context.cgContext.setFillColor(UIColor.black.cgColor)
36 | context.cgContext.fillPath()
37 | }
38 |
39 | return imageData
40 | }
41 |
42 | func data(for symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont) -> Data {
43 | return data(for: symbol, weight: weight, scale: scale, in: font, viewScale: UIScreen.main.scale)
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/Symbals/Exporters/SVGExporter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SVGExporter.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 16/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct SVGExporter: Exporter {
12 |
13 | private func format(_ point: CGPoint) -> String {
14 | return "\(point.x),\(point.y)"
15 | }
16 |
17 | func export(symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont, to folder: URL) throws -> URL {
18 | let name = "\(symbol.shortName).svg"
19 | let file = folder.appendingPathComponent(name)
20 | let symbolData = data(for: symbol, weight: weight, scale: scale, in: font)
21 | try symbolData.write(to: file)
22 | return file
23 | }
24 |
25 | func data(for symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont) -> Data {
26 | var lines = Array()
27 | if let restriction = symbol.protectedSymbolNotes {
28 | lines.append("")
31 | }
32 |
33 | guard let pathTuple = try? symbol.generateCGPath(for: weight, scale: scale) else { return Data() }
34 |
35 | let direction = " direction='ltr'" // symbol.allowsMirroring ? "" : "
36 | lines.append("")
52 |
53 | let svg = lines.joined(separator: "\n")
54 |
55 | return Data(svg.utf8)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Symbals/Exporters/ShortcutIconExporter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShortcutIconExporter.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 22/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct ShortcutIconExporter: Exporter {
12 |
13 | func export(symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont, to folder: URL) throws -> URL {
14 | let name = "\(symbol.shortName).png"
15 | let file = folder.appendingPathComponent(name)
16 | let symbolData = data(for: symbol, weight: weight, scale: scale, in: font)
17 | try symbolData.write(to: file)
18 | return file
19 | }
20 |
21 |
22 | func data(for symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont, viewScale: CGFloat) -> Data {
23 | guard let pathTuple = try? symbol.generateCGPath(for: weight, scale: scale) else { return Data() }
24 | var size = pathTuple.boundingBox.size
25 |
26 | var adjustX: CGFloat = 0
27 | var adjustY: CGFloat = 0
28 | if size.height > size.width {
29 | size.width = size.height
30 | adjustX = (pathTuple.boundingBox.size.height - pathTuple.boundingBox.size.width) / 2
31 | } else if size.width > size.height {
32 | size.height = size.width
33 | adjustY = (pathTuple.boundingBox.size.width - pathTuple.boundingBox.size.height) / 2
34 | }
35 |
36 | let format = UIGraphicsImageRendererFormat()
37 | format.scale = viewScale
38 | format.opaque = false
39 | let drawingSize = CGSize(width: size.width + 16, height: size.height + 16)
40 | adjustX += 8
41 | adjustY += 8
42 | let renderer = UIGraphicsImageRenderer(size: drawingSize, format: format)
43 |
44 | let imageData = renderer.pngData { (context) in
45 | context.cgContext.translateBy(x: 0.0, y: drawingSize.height)
46 | context.cgContext.scaleBy(x: 1.0, y: -1.0)
47 | context.cgContext.setShouldAntialias(true)
48 | context.cgContext.translateBy(x: adjustX, y: adjustY)
49 | context.cgContext.addPath(pathTuple.path)
50 |
51 | context.cgContext.setFillColor(UIColor.white.cgColor)
52 | context.cgContext.fillPath()
53 | }
54 |
55 | return imageData
56 | }
57 |
58 | func data(for symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont) -> Data {
59 | return data(for: symbol, weight: weight, scale: scale, in: font, viewScale: UIScreen.main.scale)
60 | }
61 |
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/Symbals/Exporters/SquaredPNGExporter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SquaredPNGExporter.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 22/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct SquaredPNGExporter: Exporter {
12 |
13 | func export(symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont, to folder: URL) throws -> URL {
14 | let name = "\(symbol.shortName).png"
15 | let file = folder.appendingPathComponent(name)
16 | let symbolData = data(for: symbol, weight: weight, scale: scale, in: font)
17 | try symbolData.write(to: file)
18 | return file
19 | }
20 |
21 |
22 | func data(for symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont, viewScale: CGFloat) -> Data {
23 | guard let pathTuple = try? symbol.generateCGPath(for: weight, scale: scale) else { return Data() }
24 | var size = pathTuple.boundingBox.size
25 |
26 | var adjustX: CGFloat = 0
27 | var adjustY: CGFloat = 0
28 | if size.height > size.width {
29 | size.width = size.height
30 | adjustX = (pathTuple.boundingBox.size.height - pathTuple.boundingBox.size.width) / 2
31 | } else if size.width > size.height {
32 | size.height = size.width
33 | adjustY = (pathTuple.boundingBox.size.width - pathTuple.boundingBox.size.height) / 2
34 | }
35 |
36 | let format = UIGraphicsImageRendererFormat()
37 | format.scale = viewScale
38 | format.opaque = false
39 | let renderer = UIGraphicsImageRenderer(size: size, format: format)
40 |
41 | let imageData = renderer.pngData { (context) in
42 | context.cgContext.translateBy(x: 0.0, y: size.height)
43 | context.cgContext.scaleBy(x: 1.0, y: -1.0)
44 | context.cgContext.setShouldAntialias(true)
45 | context.cgContext.translateBy(x: adjustX, y: adjustY)
46 | context.cgContext.addPath(pathTuple.path)
47 |
48 | context.cgContext.setFillColor(UIColor.white.cgColor)
49 | context.cgContext.fillPath()
50 | }
51 |
52 | return imageData
53 | }
54 |
55 | func data(for symbol: Symbol, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale, in font: CTFont) -> Data {
56 | return data(for: symbol, weight: weight, scale: scale, in: font, viewScale: UIScreen.main.scale)
57 | }
58 |
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/Symbals/Extensions/Bundle+Version.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Bundle+Version.swift
3 | // DevSwitch
4 | //
5 | // Created by Aaron Pearce on 26/10/17.
6 | // Copyright © 2019 Aaron Pearce. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Bundle {
12 | var releaseVersionNumber: String? {
13 | return infoDictionary?["CFBundleShortVersionString"] as? String
14 | }
15 |
16 | var buildVersionNumber: String? {
17 | return infoDictionary?["CFBundleVersion"] as? String
18 | }
19 |
20 | var appName: String {
21 | return infoDictionary?["CFBundleName"] as? String ?? "Unknown"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Symbals/Extensions/UIImage-SymbolScale+Display.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage-SymbolScale+Display.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 21/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIImage.SymbolScale {
12 | static var allCases: [UIImage.SymbolScale] {
13 | return [.small, .medium, .large]
14 | }
15 |
16 | var displayString: String {
17 | switch self {
18 | case .default:
19 | return "Default"
20 | case .small:
21 | return "Small"
22 | case .medium:
23 | return "Medium"
24 | case .large:
25 | return "Large"
26 | default:
27 | return "Unspecified"
28 | }
29 | }
30 |
31 | var fileString: String {
32 | switch self {
33 | case .default:
34 | return "small"
35 | case .small:
36 | return "small"
37 | case .medium:
38 | return "medium"
39 | case .large:
40 | return "large"
41 | default:
42 | return "small"
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Symbals/Extensions/UIView+Additions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+Additions.swift
3 | // DevSwitch
4 | //
5 | // Created by Aaron Pearce on 20/02/17.
6 | // Copyright © 2019 Aaron Pearce. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIView {
12 |
13 | public func addSubviews(_ subviews: [UIView]) {
14 | for subview in subviews {
15 | addSubview(subview)
16 | }
17 | }
18 |
19 | public func removeSubviews() {
20 | for subview in subviews {
21 | subview.removeFromSuperview()
22 | }
23 | }
24 |
25 | public func usingAutoLayout() -> Self {
26 | translatesAutoresizingMaskIntoConstraints = false
27 | return self
28 | }
29 |
30 | public func constraintsToFit(view: UIView, insets: UIEdgeInsets = UIEdgeInsets.zero) -> [NSLayoutConstraint] {
31 |
32 | return [
33 | topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: insets.top),
34 | leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: insets.left),
35 | bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -insets.bottom),
36 | trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -insets.right),
37 | ]
38 | }
39 |
40 | /**
41 | Removes all constrains for this view
42 | */
43 | public func removeAllConstraints() {
44 | var list = [NSLayoutConstraint]()
45 | if let constraints = superview?.constraints {
46 | for c in constraints {
47 | if c.firstItem as? UIView == self || c.secondItem as? UIView == self {
48 | list.append(c)
49 | }
50 | }
51 | }
52 |
53 | superview?.removeConstraints(list)
54 | removeConstraints(constraints)
55 | }
56 |
57 | public var recursiveSubviews: [UIView] {
58 | var subviews = self.subviews.compactMap({$0})
59 | subviews.forEach { subviews.append(contentsOf: $0.recursiveSubviews) }
60 | return subviews
61 | }
62 | }
63 |
64 | extension UIEdgeInsets {
65 | static func inset(by inset: CGFloat) -> UIEdgeInsets {
66 | return UIEdgeInsets(top: inset, left: inset, bottom: inset, right: inset)
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Symbals/Extensions/UIimage-SymboiWeight+Display.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIimage-SymboiWeight+Display.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 21/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIImage.SymbolWeight {
12 |
13 | static var allCases: [UIImage.SymbolWeight] {
14 | return [.ultraLight, .thin, .light, .regular, .medium, .semibold, .bold, .heavy, .black]
15 | }
16 |
17 | var displayString: String {
18 | switch self {
19 | case .unspecified:
20 | return "Unspecified"
21 | case .ultraLight:
22 | return "Ultralight"
23 | case .thin:
24 | return "Thin"
25 | case .light:
26 | return "Light"
27 | case .regular:
28 | return "Regular"
29 | case .medium:
30 | return "Medium"
31 | case .semibold:
32 | return "Semibold"
33 | case .bold:
34 | return "Bold"
35 | case .heavy:
36 | return "Heavy"
37 | case .black:
38 | return "Black"
39 | @unknown default:
40 | return "Unspecified"
41 | }
42 | }
43 |
44 | var fontName: String {
45 | return "SF-Pro-Text-\(displayString)"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Symbals/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(SYMBALS_BUNDLE_ID)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | ITSAppUsesNonExemptEncryption
22 |
23 | LSRequiresIPhoneOS
24 |
25 | NSPhotoLibraryAddUsageDescription
26 | $(PRODUCT_NAME) needs to access your Photo Library to save images.
27 | UIAppFonts
28 |
29 | SF-Pro-Text-Ultralight.otf
30 | SF-Pro-Text-Thin.otf
31 | SF-Pro-Text-Light.otf
32 | SF-Pro-Text-Regular.otf
33 | SF-Pro-Text-Medium.otf
34 | SF-Pro-Text-Semibold.otf
35 | SF-Pro-Text-Bold.otf
36 | SF-Pro-Text-Heavy.otf
37 | SF-Pro-Text-Black.otf
38 |
39 | UIApplicationSceneManifest
40 |
41 | UIApplicationSupportsMultipleScenes
42 |
43 | UISceneConfigurations
44 |
45 | UIWindowSceneSessionRoleApplication
46 |
47 |
48 | UISceneConfigurationName
49 | Default Configuration
50 | UISceneDelegateClassName
51 | $(PRODUCT_MODULE_NAME).SceneDelegate
52 | UISceneStoryboardFile
53 | Main
54 |
55 |
56 |
57 |
58 | UILaunchStoryboardName
59 | LaunchScreen
60 | UIMainStoryboardFile
61 | Main
62 | UIRequiredDeviceCapabilities
63 |
64 | armv7
65 |
66 | UIStatusBarTintParameters
67 |
68 | UINavigationBar
69 |
70 | Style
71 | UIBarStyleDefault
72 | Translucent
73 |
74 |
75 |
76 | UISupportedInterfaceOrientations
77 |
78 | UIInterfaceOrientationPortrait
79 | UIInterfaceOrientationLandscapeLeft
80 | UIInterfaceOrientationLandscapeRight
81 |
82 | UISupportedInterfaceOrientations~ipad
83 |
84 | UIInterfaceOrientationPortrait
85 | UIInterfaceOrientationPortraitUpsideDown
86 | UIInterfaceOrientationLandscapeLeft
87 | UIInterfaceOrientationLandscapeRight
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/Symbals/Models/Symbol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // mbol.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 19/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /*
12 | {
13 | "Additional Search Metadata": "",
14 | "Categories": "indicies",
15 | "Glyph Order": 1966,
16 | "NEW PUAs": 100600,
17 | "Non-modifiable": "FALSE",
18 | "Protected Symbol Notes": "",
19 | "RTL extension": "",
20 | "semantic.name.1": "",
21 | "semantic.name.2": "",
22 | "semantic.name.3": "",
23 | "short.name": "50.square.fill",
24 | "unicodes": "NO"
25 | }
26 | */
27 | struct Symbol: Codable {
28 |
29 | enum Element {
30 | case move(CGPoint)
31 | case line(CGPoint)
32 | case quadCurve(CGPoint, CGPoint)
33 | case curve(CGPoint, CGPoint, CGPoint)
34 | case close
35 | }
36 |
37 | let additionalSearchMetadata: String?
38 | let categories: [String]?
39 | let glyphOrder: Int
40 | let newPUAs: String
41 | let nonModifiable: Bool
42 | let protectedSymbolNotes: String?
43 | let rtlExtension: String?
44 | let semanticName1: String?
45 | let semanticName2: String?
46 | let semanticName3: String?
47 | let shortName: String
48 | let unicodes: String?
49 |
50 | enum CodingKeys: String, CodingKey {
51 | case additionalSearchMetadata = "Additional Search Metadata"
52 | case categories = "Categories"
53 | case glyphOrder = "Glyph Order"
54 | case newPUAs = "NEW PUAs"
55 | case nonModifiable = "Non-modifiable"
56 | case protectedSymbolNotes = "Protected Symbol Notes"
57 | case rtlExtension = "RTL extension"
58 | case semanticName1 = "semantic.name.1"
59 | case semanticName2 = "semantic.name.2"
60 | case semanticName3 = "semantic.name.3"
61 | case shortName = "short.name"
62 | case unicodes
63 | }
64 |
65 | init(from decoder: Decoder) throws {
66 | func decodeIntoStringIfInt(_ values: KeyedDecodingContainer, key: Symbol.CodingKeys) -> String {
67 | if let value = try? values.decode(Int.self, forKey: key) {
68 | return String(value)
69 | } else {
70 | return try! values.decode(String.self, forKey: key)
71 | }
72 | }
73 |
74 | let values = try decoder.container(keyedBy: CodingKeys.self)
75 | shortName = try values.decode(String.self, forKey: .shortName)
76 | newPUAs = decodeIntoStringIfInt(values, key: .newPUAs)
77 | categories = (try? values.decode(String.self, forKey: .categories))?.components(separatedBy: ", ")
78 | additionalSearchMetadata = try? values.decode(String.self, forKey: .additionalSearchMetadata)
79 | protectedSymbolNotes = try? values.decode(String.self, forKey: .protectedSymbolNotes)
80 | rtlExtension = try? values.decode(String.self, forKey: .rtlExtension)
81 | semanticName1 = try? values.decode(String.self, forKey: .semanticName1)
82 | semanticName2 = try? values.decode(String.self, forKey: .semanticName2)
83 | semanticName3 = try? values.decode(String.self, forKey: .semanticName3)
84 | unicodes = try? values.decode(String.self, forKey: .unicodes)
85 |
86 | if let value = try? values.decode(String.self, forKey: .glyphOrder) {
87 | glyphOrder = Int(value) ?? 0
88 | } else {
89 | glyphOrder = try values.decode(Int.self, forKey: .glyphOrder)
90 | }
91 |
92 | if let value = try? values.decode(String.self, forKey: .nonModifiable) {
93 | if value == "TRUE" {
94 | nonModifiable = true
95 | } else {
96 | nonModifiable = false
97 | }
98 | } else {
99 | nonModifiable = try values.decode(Bool.self, forKey: .nonModifiable)
100 | }
101 | }
102 |
103 | func generateCGPath(for weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale) throws -> (path: CGPath, boundingBox: CGRect, originOffset: CGPoint) {
104 | let name = "uni\(newPUAs).\(scale.fileString)"
105 | guard let font = Symbols.getFont(for: weight) else { throw SymbolsError("Font not found") }
106 | var glyph = CTFontGetGlyphWithName(font, name as CFString)
107 |
108 | var box = CGRect.zero
109 | CTFontGetBoundingRectsForGlyphs(font, .default, &glyph, &box, 1)
110 | let paddedBox: CGRect
111 | let paddedBoxMultiplier: CGFloat = box.origin.y > 0 ? 2 : -2
112 | paddedBox = CGRect(x: 0, y: 0, width: box.width + (2 * box.origin.x), height: box.height + (paddedBoxMultiplier * box.origin.y))
113 |
114 | let boundingBox = paddedBox
115 | let originOffset = box.origin
116 |
117 | let path = CTFontCreatePathForGlyph(font, glyph, nil)
118 |
119 | let copy: CGPath?
120 | if box.origin.y > 0 {
121 | copy = path
122 | } else {
123 | var transform = CGAffineTransform(translationX: 0, y: -2 * originOffset.y)
124 | copy = path?.copy(using: &transform)
125 | }
126 | let cgPath = copy ?? CGPath(rect: CGRect(origin: .zero, size: paddedBox.size), transform: nil)
127 |
128 | return (cgPath, boundingBox, originOffset)
129 | }
130 |
131 | func enumerateElements(enumerator: (CGPoint?, Element) -> Void, for cgPath: CGPath) {
132 |
133 | var currentPoint: CGPoint?
134 | cgPath.applyWithBlock { elementRef in
135 | let pathElement = elementRef.pointee
136 |
137 | let points = [
138 | pathElement.points[0],
139 | pathElement.points[1],
140 | pathElement.points[2]
141 | ]
142 |
143 | let element: Element
144 | let newCurrentPoint: CGPoint?
145 |
146 | switch pathElement.type {
147 | case .moveToPoint:
148 | element = .move(points[0])
149 | newCurrentPoint = points[0]
150 |
151 | case .addLineToPoint:
152 | element = .line(points[0])
153 | newCurrentPoint = points[0]
154 |
155 | case .addQuadCurveToPoint:
156 | element = .quadCurve(points[0], points[1])
157 | newCurrentPoint = points[0]
158 |
159 | case .addCurveToPoint:
160 | element = .curve(points[0], points[1], points[2])
161 | newCurrentPoint = points[0]
162 |
163 | case .closeSubpath:
164 | element = .close
165 | newCurrentPoint = nil
166 |
167 | @unknown default: return
168 | }
169 |
170 | enumerator(currentPoint, element)
171 | currentPoint = newCurrentPoint
172 | }
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/Symbals/Models/Symbols.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Symbol.swift
3 | // MetaSymbols
4 | //
5 | // Created by Aaron Pearce on 15/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CommonCrypto
11 | import CSV
12 |
13 | struct SymbolsError: Error {
14 | let message: String
15 |
16 | init(_ message: String) {
17 | self.message = message
18 | }
19 |
20 | public var localizedDescription: String {
21 | return message
22 | }
23 | }
24 |
25 | class Symbols {
26 |
27 | init(_ callback: () -> ()) {
28 | readCSV(callback)
29 | }
30 |
31 | var allSymbols = [Symbol]()
32 | var filteredSymbols = [Symbol]()
33 |
34 | var symbolsByCategory: [String: [Symbol]] {
35 | var symbolsByCategory = [String: [Symbol]]()
36 |
37 | for symbol in filteredSymbols {
38 | func insertInto(category: String) {
39 | if symbolsByCategory.keys.contains(category) {
40 | symbolsByCategory[category]?.append(symbol)
41 | } else {
42 | symbolsByCategory[category] = [symbol]
43 | }
44 | }
45 |
46 | if let categories = symbol.categories {
47 | for category in categories {
48 | insertInto(category: category)
49 | }
50 | } else {
51 | insertInto(category: "non-categorized")
52 | }
53 |
54 | }
55 |
56 | return symbolsByCategory
57 | }
58 |
59 | var symbolKeys: [String] {
60 | var keys = symbolsByCategory.keys.sorted()
61 | if let appleIndex = keys.firstIndex(of: "apple") {
62 | keys.remove(at: appleIndex)
63 | keys.append("apple")
64 | }
65 | return keys
66 | }
67 |
68 | func symbol(for indexPath: IndexPath) -> Symbol? {
69 | let key = symbolKeys[indexPath.section]
70 | let symbols = symbolsByCategory[key]?.sorted(by: { (s1, s2) -> Bool in
71 | s1.glyphOrder < s2.glyphOrder
72 | })
73 |
74 | return symbols?[indexPath.row]
75 | }
76 |
77 | static func getFont(for weight: UIImage.SymbolWeight = .regular) -> CTFont? {
78 | guard let fontURL = Bundle.main.url(forResource: weight.fontName, withExtension: "otf") else {
79 | print("No font file found")
80 | return nil
81 | }
82 |
83 | guard let provider = CGDataProvider(url: fontURL as CFURL) else { return nil }
84 | guard let cgFont = CGFont(provider) else { return nil }
85 |
86 | let attributes = [
87 | kCTFontTraitsAttribute: [
88 | kCTFontWeightTrait: UIFont.Weight.regular.rawValue
89 | ]
90 | ]
91 | let ctAttributes = CTFontDescriptorCreateWithAttributes(attributes as CFDictionary)
92 | let font = CTFontCreateWithGraphicsFont(cgFont, 44.0, nil, ctAttributes)
93 |
94 | return font
95 | }
96 |
97 | func readCSV(_ callback: () -> ()) {
98 | guard let font = Symbols.getFont() else { return }
99 |
100 | guard let data = CTFontCopyDecodedSYMPData(font) else {
101 | print("No SYMP data found.")
102 | return
103 | }
104 | guard let csv = String(data: data, encoding: .utf8) else {
105 | print("Failed to stringify")
106 | return
107 | }
108 |
109 | var records = [Symbol]()
110 | do {
111 | let reader = try CSVReader(string: csv, hasHeaderRow: true)
112 | let decoder = CSVRowDecoder()
113 | while reader.next() != nil {
114 | let row = try decoder.decode(Symbol.self, from: reader)
115 | records.append(row)
116 | }
117 | } catch {
118 | print(error)
119 | }
120 |
121 | self.allSymbols = records
122 | self.filteredSymbols = allSymbols
123 | callback()
124 | }
125 |
126 | func filter(for searchText: String?) {
127 | if let searchText = searchText?.lowercased(), searchText != "" {
128 | filteredSymbols = allSymbols.filter {
129 | $0.additionalSearchMetadata?.contains(searchText) ?? false ||
130 | $0.shortName.contains(searchText) ||
131 | $0.semanticName1?.contains(searchText) ?? false ||
132 | $0.semanticName2?.contains(searchText) ?? false ||
133 | $0.semanticName3?.contains(searchText) ?? false ||
134 | $0.categories?.contains(where: { $0.range(of: searchText, options: .caseInsensitive) != nil }) ?? false
135 | }
136 | } else {
137 | filteredSymbols = allSymbols
138 | }
139 | }
140 | }
141 |
142 |
143 | private func CTFontCopyDecodedSYMPData(_ font: CTFont) -> Data? {
144 | func fourCharCode(_ string: String) -> FourCharCode {
145 | return string.utf16.reduce(0, {$0 << 8 + FourCharCode($1)})
146 | }
147 |
148 | let tag = fourCharCode("symp")
149 | guard let data = CTFontCopyTable(font, tag, []) else { return nil }
150 | guard let base64String = String(data: data as Data, encoding: .utf8) else { return nil }
151 | guard let encoded = NSData(base64Encoded: base64String) else { return nil }
152 | guard let decoded = NSMutableData(length: encoded.length) else { return nil }
153 |
154 | var key: Array = [0xB8, 0x85, 0xF6, 0x9E, 0x39, 0x8C, 0xBA, 0x72, 0x40, 0xDB, 0x49, 0x6B, 0xE8, 0xC6, 0x14, 0x88, 0x54, 0x9F, 0x1F, 0x88, 0x5D, 0x47, 0x6B, 0x2E, 0x2C, 0xC1, 0x14, 0xF1, 0x3B, 0x17, 0x21, 0x20]
155 | var iv: Array = [0xEF, 0xB0, 0xD1, 0x2E, 0xFA, 0xC5, 0x91, 0x14, 0xC3, 0xE5, 0xB9, 0x12, 0x70, 0xF0, 0xC0, 0x46]
156 |
157 | var bytesWritten = 0
158 | let result = CCCrypt(CCOperation(kCCDecrypt),
159 | CCAlgorithm(kCCAlgorithmAES),
160 | CCOptions(kCCOptionPKCS7Padding),
161 | &key, key.count,
162 | &iv,
163 | encoded.bytes, encoded.length,
164 | decoded.mutableBytes, decoded.length,
165 | &bytesWritten)
166 |
167 | guard result == kCCSuccess else { return nil }
168 | decoded.length = bytesWritten
169 | return decoded as Data
170 | }
171 |
172 | internal func CSVFields(_ line: String) -> Array {
173 | var fields = Array()
174 |
175 | var insideQuote = false
176 | var fieldStart = line.startIndex
177 | var currentIndex = fieldStart
178 | while currentIndex < line.endIndex {
179 | let character = line[currentIndex]
180 |
181 | if insideQuote == false {
182 | if character == "," {
183 | let subString = line[fieldStart ..< currentIndex]
184 | fields.append(String(subString))
185 | fieldStart = line.index(after: currentIndex)
186 | } else if character == "\"" {
187 | insideQuote = true
188 | }
189 | } else {
190 | if character == "\"" { insideQuote = false }
191 | }
192 |
193 | if currentIndex >= line.endIndex { break }
194 | currentIndex = line.index(after: currentIndex)
195 | }
196 |
197 | let lastField = line[fieldStart ..< line.endIndex]
198 | fields.append(String(lastField))
199 |
200 | return fields
201 | }
202 |
--------------------------------------------------------------------------------
/Symbals/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 15/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate, UISplitViewControllerDelegate {
12 |
13 | var window: UIWindow?
14 |
15 |
16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
17 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
18 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
19 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
20 | guard let window = window else { return }
21 | guard let splitViewController = window.rootViewController as? UISplitViewController else { return }
22 | guard let navigationController = splitViewController.viewControllers.last as? UINavigationController else { return }
23 | navigationController.topViewController?.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
24 | navigationController.topViewController?.navigationItem.leftItemsSupplementBackButton = true
25 | splitViewController.delegate = self
26 | window.tintColor = UIColor(named: "primary")
27 | }
28 |
29 | func sceneDidDisconnect(_ scene: UIScene) {
30 | // Called as the scene is being released by the system.
31 | // This occurs shortly after the scene enters the background, or when its session is discarded.
32 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
33 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
34 | }
35 |
36 | func sceneDidBecomeActive(_ scene: UIScene) {
37 | // Called when the scene has moved from an inactive state to an active state.
38 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
39 | }
40 |
41 | func sceneWillResignActive(_ scene: UIScene) {
42 | // Called when the scene will move from an active state to an inactive state.
43 | // This may occur due to temporary interruptions (ex. an incoming phone call).
44 | }
45 |
46 | func sceneWillEnterForeground(_ scene: UIScene) {
47 | // Called as the scene transitions from the background to the foreground.
48 | // Use this method to undo the changes made on entering the background.
49 | }
50 |
51 | func sceneDidEnterBackground(_ scene: UIScene) {
52 | // Called as the scene transitions from the foreground to the background.
53 | // Use this method to save data, release shared resources, and store enough scene-specific state information
54 | // to restore the scene back to its current state.
55 | }
56 |
57 | // MARK: - Split view
58 |
59 | func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
60 | guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }
61 | guard let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController else { return false }
62 | if topAsDetailController.symbol == nil {
63 | // Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
64 | return true
65 | }
66 | return false
67 | }
68 |
69 | }
70 |
71 |
--------------------------------------------------------------------------------
/Symbals/Symbals.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.application-groups
8 |
9 | com.apple.security.network.client
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Symbals/View Controllers/BaseCollectionViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseCollectionViewController.swift
3 | // HomeCam2
4 | //
5 | // Created by Aaron Pearce on 14/06/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class BaseCollectionViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
12 | let padding: CGFloat = 16
13 |
14 | lazy var collectionView: UICollectionView = {
15 | var collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout).usingAutoLayout()
16 | collectionView.delegate = self
17 | collectionView.dataSource = self
18 | #if os(iOS)
19 | collectionView.backgroundColor = .systemBackground
20 | #endif
21 | return collectionView
22 | }()
23 |
24 | lazy var layout: UICollectionViewFlowLayout = {
25 | let layout = UICollectionViewFlowLayout()
26 | layout.sectionInsetReference = .fromSafeArea
27 | layout.sectionInset = UIEdgeInsets(top: padding, left: padding, bottom: padding, right: padding)
28 | layout.minimumLineSpacing = padding
29 | layout.minimumInteritemSpacing = padding
30 | return layout
31 | }()
32 |
33 | var collectionViewDisplayWidth: CGFloat {
34 | return collectionView.bounds.width - layout.sectionInset.left - layout.sectionInset.right
35 | }
36 |
37 | override func viewDidLoad() {
38 | super.viewDidLoad()
39 |
40 | registerCells()
41 |
42 | view.addSubview(collectionView)
43 | NSLayoutConstraint.activate(collectionView.constraintsToFit(view: view))
44 |
45 | #if os(iOS)
46 | view.backgroundColor = .systemBackground
47 | navigationController?.navigationBar.prefersLargeTitles = true
48 | #else
49 |
50 | #endif
51 | }
52 |
53 | func registerCells() {
54 | fatalError("Subclass should implement this method to register its cells")
55 | }
56 |
57 | func numberOfSections(in collectionView: UICollectionView) -> Int {
58 | fatalError("Subclass should implement this method")
59 | }
60 |
61 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
62 | fatalError("Subclass should implement this method")
63 | }
64 |
65 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
66 | fatalError("Subclass should implement this method")
67 | }
68 |
69 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
70 | let height: CGFloat = 64
71 | return CGSize(width: collectionViewDisplayWidth, height: height)
72 | }
73 |
74 | override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
75 | super.viewWillTransition(to: size, with: coordinator)
76 | collectionView.collectionViewLayout.invalidateLayout()
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Symbals/View Controllers/DetailViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailViewController.swift
3 | // MetaSymbols
4 | //
5 | // Created by Aaron Pearce on 15/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class DetailViewController: UIViewController {
12 |
13 | @IBOutlet weak var iconView: UIImageView!
14 | @IBOutlet weak var tableView: UITableView!
15 |
16 | var delegate: WeightAndScaleViewControllerDelegate?
17 |
18 | var currentWeight: UIImage.SymbolWeight = .regular {
19 | didSet {
20 | configureView()
21 | }
22 | }
23 |
24 | var currentScale: UIImage.SymbolScale = .default {
25 | didSet {
26 | configureView()
27 | }
28 | }
29 |
30 | func configureView() {
31 | // Update the user interface for the detail item.
32 | if let symbol = symbol {
33 | self.loadViewIfNeeded()
34 | iconView.image = UIImage(systemName: symbol.shortName)
35 | iconView.preferredSymbolConfiguration = UIImage.SymbolConfiguration(pointSize: 144, weight: currentWeight, scale: currentScale)
36 | title = symbol.shortName
37 | }
38 | }
39 |
40 | override func viewDidLoad() {
41 | super.viewDidLoad()
42 | // Do any additional setup after loading the view.
43 | configureView()
44 | navigationController?.navigationBar.prefersLargeTitles = true
45 | }
46 |
47 | var symbol: Symbol? {
48 | didSet {
49 | configureView()
50 | }
51 | }
52 |
53 | @IBAction func showWeightPicker(_ sender: UIBarButtonItem) {
54 | guard let weightScale = UIStoryboard(name: "Main", bundle: nil)
55 | .instantiateViewController(identifier: "WeightScale") as? WeightAndScaleViewController else {
56 | return
57 | }
58 |
59 | weightScale.currentScale = currentScale
60 | weightScale.currentWeight = currentWeight
61 | weightScale.delegate = self
62 |
63 | let popoverPush = PopoverPushController(rootViewController: weightScale)
64 | popoverPush.modalPresentationStyle = .popover
65 | popoverPush.popoverPresentationController?.delegate = self
66 | popoverPush.popoverPresentationController?.barButtonItem = sender
67 | popoverPush.popoverPresentationController?.sourceRect = view.bounds
68 | popoverPush.popoverPresentationController?.permittedArrowDirections = [.up]
69 | present(popoverPush, animated: true)
70 | }
71 |
72 | @IBAction func export(_ sender: UIBarButtonItem) {
73 | guard let symbol = symbol, let documents = FileManager.default.urls(
74 | for: .documentDirectory,
75 | in: .userDomainMask
76 | ).first else { return }
77 |
78 | let alert = UIAlertController(title: "Choose Export Format", message: nil, preferredStyle: .actionSheet)
79 | alert.popoverPresentationController?.barButtonItem = sender
80 |
81 | for format in ExportFormat.allCases {
82 | let action = UIAlertAction(title: format.rawValue, style: .default) { (action) in
83 | do {
84 | let fullURL = try format.exporter.export(symbol: symbol, weight: self.currentWeight, scale: self.currentScale, in: Symbols.getFont()!, to: documents)
85 | let activity = UIActivityViewController(
86 | activityItems: [fullURL],
87 | applicationActivities: nil
88 | )
89 | activity.excludedActivityTypes = [
90 | .saveToCameraRoll
91 | ]
92 | activity.popoverPresentationController?.barButtonItem = sender
93 | // 3
94 | self.present(activity, animated: true, completion: nil)
95 | } catch {
96 | print(error)
97 | }
98 | }
99 | alert.addAction(action)
100 | }
101 |
102 | alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
103 |
104 | present(alert, animated: true, completion: nil)
105 | }
106 |
107 | }
108 |
109 | extension DetailViewController: WeightAndScaleViewControllerDelegate {
110 | func didUpdate(scale: UIImage.SymbolScale) {
111 | delegate?.didUpdate(scale: scale)
112 | self.currentScale = scale
113 | }
114 |
115 | func didUpdate(weight: UIImage.SymbolWeight) {
116 | delegate?.didUpdate(weight: weight)
117 | self.currentWeight = weight
118 | }
119 | }
120 |
121 |
122 | extension DetailViewController: UIPopoverPresentationControllerDelegate {
123 | func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
124 | return .none
125 | }
126 | }
127 |
128 | extension DetailViewController: UITableViewDelegate, UITableViewDataSource {
129 |
130 | func numberOfSections(in tableView: UITableView) -> Int {
131 | return 1
132 | }
133 |
134 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
135 | return 7
136 | }
137 |
138 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
139 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! DetailCell
140 |
141 | guard let symbol = symbol else { return cell }
142 | switch (indexPath.section, indexPath.row) {
143 | case (0, 0):
144 | cell.textLabel?.text = "Name"
145 | cell.detailTextLabel?.text = symbol.shortName
146 | case (0, 1):
147 | cell.textLabel?.text = "Private Unicode"
148 | cell.detailTextLabel?.text = symbol.newPUAs
149 | case (0, 2):
150 | cell.textLabel?.text = "Unicode"
151 | if let unicode = symbol.unicodes, unicode != "XXXXX", unicode != "NO" {
152 | cell.detailTextLabel?.text = unicode
153 | } else {
154 | cell.detailTextLabel?.text = "N/A"
155 | }
156 | case (0, 3):
157 | cell.textLabel?.text = "Categories"
158 | cell.detailTextLabel?.text = "\(symbol.categories?.joined(separator: ", ").capitalized ?? "N/A")"
159 | case (0, 4):
160 | cell.textLabel?.text = "Additional Keywords"
161 | cell.detailTextLabel?.text = symbol.additionalSearchMetadata ?? "N/A"
162 | case (0, 5):
163 | cell.textLabel?.text = "Modifiable"
164 | cell.detailTextLabel?.text = symbol.nonModifiable ? "No" : "Yes"
165 | case (0, 6):
166 | cell.textLabel?.text = "Notes"
167 | cell.detailTextLabel?.text = symbol.protectedSymbolNotes ?? "None"
168 | default:
169 | break
170 | }
171 |
172 | return cell
173 | }
174 |
175 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
176 |
177 | }
178 |
179 | func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
180 | switch (indexPath.row) {
181 | case 5, 6:
182 | return nil
183 | default:
184 | return UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { suggestedActions in
185 | return self.makeContextMenu(for: indexPath)
186 | })
187 | }
188 | }
189 |
190 | func makeContextMenu(for indexPath: IndexPath) -> UIMenu {
191 |
192 | let copyAction = UIAction(title: "Copy", image: UIImage(systemName: "doc.on.doc.fill")) { [weak self] _ in
193 | guard let self = self else { return }
194 | let cell = self.tableView.cellForRow(at: indexPath)
195 | let pasteboard = UIPasteboard.general
196 | pasteboard.string = cell?.detailTextLabel?.text
197 | }
198 |
199 | // Create and return a UIMenu with the share action
200 | return UIMenu(title: "", children: [copyAction])
201 | }
202 |
203 | func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
204 | animator.preferredCommitStyle = .dismiss
205 | }
206 |
207 | }
208 |
--------------------------------------------------------------------------------
/Symbals/View Controllers/PopoverPushController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PopoverPushController.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 21/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | final class PopoverPushController: UIViewController {
12 | private let wrappedNavigationController: UINavigationController
13 |
14 | init(rootViewController: UIViewController) {
15 | self.wrappedNavigationController = UINavigationController(rootViewController: rootViewController)
16 | super.init(nibName: nil, bundle: nil)
17 | }
18 |
19 | required init?(coder aDecoder: NSCoder) {
20 | fatalError("init(coder:) has not been implemented")
21 | }
22 |
23 | override func viewDidLoad() {
24 | super.viewDidLoad()
25 | wrappedNavigationController.willMove(toParent: self)
26 | self.addChild(wrappedNavigationController)
27 | self.view.addSubview(wrappedNavigationController.view)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Symbals/View Controllers/ScaleTableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScaleTableViewController.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 21/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ScaleTableViewController: UITableViewController {
12 |
13 | var currentScale: UIImage.SymbolScale = .default {
14 | didSet {
15 | delegate?.didUpdate(scale: currentScale)
16 | }
17 | }
18 |
19 | var delegate: WeightAndScaleViewControllerDelegate?
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 | tableView.tableFooterView = UIView()
24 | }
25 |
26 | override func viewWillAppear(_ animated: Bool) {
27 | super.viewWillAppear(animated)
28 | navigationController?.setNavigationBarHidden(false, animated: true)
29 | setContentSize()
30 | }
31 |
32 | func setContentSize() {
33 | let size = CGSize(width: 250, height: tableView.contentSize.height + 44)
34 | preferredContentSize = size
35 | popoverPresentationController?
36 | .presentedViewController
37 | .preferredContentSize = size
38 | }
39 |
40 | override func numberOfSections(in tableView: UITableView) -> Int {
41 | return 1
42 | }
43 |
44 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
45 | return UIImage.SymbolScale.allCases.count
46 | }
47 |
48 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
49 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
50 | let scale = UIImage.SymbolScale.allCases[indexPath.row]
51 | cell.textLabel?.text = scale.displayString
52 |
53 | cell.accessoryType = (scale == currentScale) ? .checkmark : .none
54 |
55 | return cell
56 | }
57 |
58 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
59 | currentScale = UIImage.SymbolScale.allCases[indexPath.row]
60 | tableView.cellForRow(at: indexPath)
61 | for cell in tableView.visibleCells {
62 | if cell == tableView.cellForRow(at: indexPath) {
63 | cell.accessoryType = .checkmark
64 | } else {
65 | cell.accessoryType = .none
66 | }
67 | }
68 |
69 | tableView.deselectRow(at: indexPath, animated: true)
70 | }
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/Symbals/View Controllers/SettingsTableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsTableViewController.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 17/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SafariServices
11 |
12 | class SettingsTableViewController: UITableViewController {
13 | let appIdentifier = ""
14 | override func viewDidLoad() {
15 | super.viewDidLoad()
16 |
17 | // Uncomment the following line to preserve selection between presentations
18 | self.clearsSelectionOnViewWillAppear = false
19 | }
20 |
21 | @IBAction func close() {
22 | dismiss(animated: true)
23 | }
24 |
25 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
26 | switch (indexPath.section, indexPath.row) {
27 | case (0, 0):
28 | showAttribution()
29 | default:
30 | break
31 | }
32 |
33 | tableView.deselectRow(at: indexPath, animated: true)
34 | }
35 |
36 | func showAttribution() {
37 | guard let url = URL(string: "https://github.com/davedelong/sfsymbols") else { return }
38 | let safariViewController = SFSafariViewController(url: url)
39 | self.present(safariViewController, animated: true)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Symbals/View Controllers/SplitViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SplitViewController.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 24/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SplitViewController: UISplitViewController {
12 | override func viewDidLoad() {
13 | super.viewDidLoad()
14 | self.preferredDisplayMode = .allVisible
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Symbals/View Controllers/SymbolsCollectionViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SymbolsCollectionViewController.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 15/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SymbolsCollectionViewController: BaseCollectionViewController {
12 | lazy var searchController: UISearchController = {
13 | let searchController = UISearchController(searchResultsController: nil)
14 | searchController.delegate = self
15 | searchController.searchResultsUpdater = self
16 | searchController.obscuresBackgroundDuringPresentation = false
17 | searchController.searchBar.placeholder = "Search"
18 | // searchController.searchBar.scopeButtonTitles = ["All".localized, "Home".localized, "Room".localized]
19 | return searchController
20 | }()
21 |
22 | var currentWeight: UIImage.SymbolWeight = .regular {
23 | didSet {
24 | updateWeightOrScale()
25 | }
26 | }
27 |
28 | var currentScale: UIImage.SymbolScale = .medium {
29 | didSet {
30 | updateWeightOrScale()
31 | }
32 | }
33 |
34 | var initialDetailLoaded = false
35 | lazy var symbols: Symbols = {
36 | return Symbols {
37 | DispatchQueue.main.async {
38 |
39 | self.collectionView.reloadData()
40 |
41 | if !self.initialDetailLoaded {
42 | if UIDevice.current.userInterfaceIdiom == .pad {
43 | let initialIndexPath = IndexPath(row: 0, section: 0)
44 | self.collectionView.selectItem(at: initialIndexPath, animated: true, scrollPosition: .top)
45 | if let symbol = self.symbols.symbol(for: initialIndexPath) {
46 | self.performSegue(withIdentifier: "showDetail", sender: symbol)
47 | }
48 | }
49 | self.initialDetailLoaded = true
50 |
51 | }
52 | }
53 | }
54 | }()
55 |
56 | var detailViewController: DetailViewController? = nil
57 |
58 | override func viewDidLoad() {
59 | super.viewDidLoad()
60 |
61 | if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
62 | layout.sectionInset = UIEdgeInsets(top: padding, left: padding, bottom: padding * 2, right: padding)
63 | }
64 |
65 | collectionView.contentInset = UIEdgeInsets(top: 16, left: 0, bottom: 0, right: 0)
66 |
67 | if let split = splitViewController {
68 | let controllers = split.viewControllers
69 | detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
70 | }
71 |
72 | definesPresentationContext = true
73 | navigationItem.searchController = searchController
74 |
75 | /*
76 | let segmented = UISegmentedControl(items: [UIImage(systemName: "square.grid.3x2.fill"), UIImage(systemName: "text.justify")])
77 | segmented.setTitleTextAttributes([.foregroundColor: UIColor(named: "primary")!], for: .normal)
78 | segmented.setTitleTextAttributes([.foregroundColor: UIColor(named: "primary")!], for: .selected)
79 | segmented.selectedSegmentIndex = 0
80 | navigationItem.leftBarButtonItem = UIBarButtonItem(customView: segmented)
81 | */
82 |
83 | let notificationCenter = NotificationCenter.default
84 | notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillHideNotification, object: nil)
85 | notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
86 | }
87 |
88 | func updateWeightOrScale() {
89 | collectionView.reloadData()
90 | }
91 |
92 | @IBAction func showWeightPicker(_ sender: UIBarButtonItem) {
93 | guard let weightScale = UIStoryboard(name: "Main", bundle: nil)
94 | .instantiateViewController(identifier: "WeightScale") as? WeightAndScaleViewController else {
95 | return
96 | }
97 |
98 | weightScale.currentScale = currentScale
99 | weightScale.currentWeight = currentWeight
100 | weightScale.delegate = self
101 |
102 | let popoverPush = PopoverPushController(rootViewController: weightScale)
103 | popoverPush.modalPresentationStyle = .popover
104 | popoverPush.popoverPresentationController?.delegate = self
105 | popoverPush.popoverPresentationController?.barButtonItem = sender
106 | popoverPush.popoverPresentationController?.sourceRect = view.bounds
107 | popoverPush.popoverPresentationController?.permittedArrowDirections = [.up]
108 | present(popoverPush, animated: true)
109 | }
110 |
111 | @objc func adjustForKeyboard(notification: Notification) {
112 | guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
113 |
114 | let keyboardScreenEndFrame = keyboardValue.cgRectValue
115 | let keyboardViewEndFrame = view.convert(keyboardScreenEndFrame, from: view.window)
116 |
117 | if notification.name == UIResponder.keyboardWillHideNotification {
118 | collectionView.contentInset = UIEdgeInsets(top: 16, left: 0, bottom: 0, right: 0)
119 | } else {
120 | collectionView.contentInset = UIEdgeInsets(top: 16, left: 0, bottom: keyboardViewEndFrame.height - view.safeAreaInsets.bottom, right: 0)
121 | }
122 |
123 | collectionView.scrollIndicatorInsets = collectionView.contentInset
124 | }
125 |
126 | override func registerCells() {
127 | collectionView.register(BaseHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "Header")
128 | collectionView.register(SymbolCell.self, forCellWithReuseIdentifier: "SymbolCell")
129 | }
130 |
131 | override func numberOfSections(in collectionView: UICollectionView) -> Int {
132 | return symbols.symbolKeys.count
133 | }
134 |
135 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
136 | let key = symbols.symbolKeys[section]
137 | return symbols.symbolsByCategory[key]?.count ?? 0
138 | }
139 |
140 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
141 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SymbolCell", for: indexPath) as! SymbolCell
142 |
143 | if let symbol = symbols.symbol(for: indexPath) {
144 | cell.imageView.image = UIImage(systemName: symbol.shortName)
145 | cell.imageView.preferredSymbolConfiguration = UIImage.SymbolConfiguration(pointSize: 48, weight: currentWeight, scale: currentScale)
146 | cell.label.text = symbol.shortName.replacingOccurrences(of: ".", with: ".\u{200B}")
147 | }
148 |
149 | return cell
150 | }
151 |
152 | override func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
153 | let width = (collectionViewDisplayWidth - (padding * 2)) / 3 - 1
154 |
155 | return CGSize(width: width, height: 120)
156 | }
157 |
158 | func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
159 | if kind == UICollectionView.elementKindSectionHeader {
160 | let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "Header", for: indexPath) as! BaseHeaderView
161 | header.titleLabel.text = symbols.symbolKeys[indexPath.section].capitalized
162 | return header
163 | }
164 |
165 | return UICollectionReusableView()
166 | }
167 |
168 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
169 | return CGSize(width: 0, height: 24)
170 | }
171 |
172 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
173 | if segue.identifier == "showDetail" {
174 | if let symbol = sender as? Symbol {
175 | let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
176 | controller.symbol = symbol
177 | controller.currentScale = currentScale
178 | controller.currentWeight = currentWeight
179 | controller.delegate = self
180 | controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
181 | controller.navigationItem.leftItemsSupplementBackButton = true
182 | detailViewController = controller
183 | }
184 | }
185 | }
186 |
187 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
188 | if let symbol = symbols.symbol(for: indexPath) {
189 | performSegue(withIdentifier: "showDetail", sender: symbol)
190 | }
191 | }
192 | }
193 |
194 |
195 | extension SymbolsCollectionViewController: UISearchControllerDelegate, UISearchResultsUpdating {
196 | func updateSearchResults(for searchController: UISearchController) {
197 | filterContentForSearchText(searchController.searchBar.text, scope: searchController.searchBar.selectedScopeButtonIndex)
198 | }
199 |
200 | func searchBarIsEmpty() -> Bool {
201 | // Returns true if the text is empty or nil
202 | return searchController.searchBar.text?.isEmpty ?? true
203 | }
204 |
205 | func filterContentForSearchText(_ searchText: String?, scope: Int = 0) {
206 | symbols.filter(for: searchText)
207 | collectionView.reloadData()
208 | }
209 |
210 | func isFiltering() -> Bool {
211 | return searchController.isActive && !searchBarIsEmpty()
212 | }
213 | }
214 |
215 | extension UIImage {
216 |
217 | func maskWithColor(color: UIColor) -> UIImage? {
218 | let maskImage = cgImage!
219 |
220 | let width = size.width
221 | let height = size.height
222 | let bounds = CGRect(x: 0, y: 0, width: width, height: height)
223 |
224 | let colorSpace = CGColorSpaceCreateDeviceRGB()
225 | let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
226 | let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)!
227 |
228 | context.clip(to: bounds, mask: maskImage)
229 | context.setFillColor(color.cgColor)
230 | context.fill(bounds)
231 |
232 | if let cgImage = context.makeImage() {
233 | let coloredImage = UIImage(cgImage: cgImage)
234 | return coloredImage
235 | } else {
236 | return nil
237 | }
238 | }
239 |
240 | }
241 |
242 | extension SymbolsCollectionViewController: WeightAndScaleViewControllerDelegate {
243 | func didUpdate(scale: UIImage.SymbolScale) {
244 | self.currentScale = scale
245 | }
246 |
247 | func didUpdate(weight: UIImage.SymbolWeight) {
248 | self.currentWeight = weight
249 | }
250 | }
251 |
252 |
253 | extension SymbolsCollectionViewController: UIPopoverPresentationControllerDelegate {
254 | func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
255 | return .none
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/Symbals/View Controllers/WeightAndScaleViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WeightAndScaleViewController.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 21/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol WeightAndScaleViewControllerDelegate {
12 | func didUpdate(weight: UIImage.SymbolWeight)
13 | func didUpdate(scale: UIImage.SymbolScale)
14 | }
15 |
16 | class WeightAndScaleViewController: UITableViewController {
17 |
18 | var currentScale: UIImage.SymbolScale = .default {
19 | didSet {
20 | self.loadViewIfNeeded()
21 | scaleLabel.text = currentScale.displayString
22 | }
23 | }
24 |
25 | var currentWeight: UIImage.SymbolWeight = .regular {
26 | didSet {
27 | self.loadViewIfNeeded()
28 | weightLabel.text = currentWeight.displayString
29 | }
30 | }
31 |
32 | var delegate: WeightAndScaleViewControllerDelegate?
33 |
34 | @IBOutlet weak var scaleLabel: UILabel!
35 | @IBOutlet weak var weightLabel: UILabel!
36 |
37 | override func viewDidLoad() {
38 | super.viewDidLoad()
39 | navigationController?.setNavigationBarHidden(true, animated: false)
40 | }
41 |
42 | override func viewWillAppear(_ animated: Bool) {
43 | super.viewWillAppear(animated)
44 | navigationController?.setNavigationBarHidden(true, animated: true)
45 | setContentSize()
46 | }
47 |
48 | func setContentSize() {
49 | let size = CGSize(width: 250, height: 88)
50 | preferredContentSize = size
51 | popoverPresentationController?
52 | .presentedViewController
53 | .preferredContentSize = size
54 | }
55 |
56 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
57 | if let weightVC = segue.destination as? WeightTableViewController {
58 | weightVC.delegate = self
59 | weightVC.currentWeight = currentWeight
60 | } else if let scaleVC = segue.destination as? ScaleTableViewController {
61 | scaleVC.delegate = self
62 | scaleVC.currentScale = currentScale
63 | }
64 | }
65 | }
66 |
67 | extension WeightAndScaleViewController: WeightAndScaleViewControllerDelegate {
68 | func didUpdate(scale: UIImage.SymbolScale) {
69 | delegate?.didUpdate(scale: scale)
70 | self.currentScale = scale
71 | }
72 |
73 | func didUpdate(weight: UIImage.SymbolWeight) {
74 | delegate?.didUpdate(weight: weight)
75 | self.currentWeight = weight
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Symbals/View Controllers/WeightTableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WeightTableViewController.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 21/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class WeightTableViewController: UITableViewController {
12 |
13 | var currentWeight: UIImage.SymbolWeight = .regular {
14 | didSet {
15 | delegate?.didUpdate(weight: currentWeight)
16 | }
17 | }
18 | var delegate: WeightAndScaleViewControllerDelegate?
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 | tableView.tableFooterView = UIView()
23 | }
24 |
25 | override func viewWillAppear(_ animated: Bool) {
26 | super.viewWillAppear(animated)
27 | navigationController?.setNavigationBarHidden(false, animated: true)
28 | setContentSize()
29 | }
30 |
31 | func setContentSize() {
32 | let size = CGSize(width: 250, height: tableView.contentSize.height + 44)
33 | preferredContentSize = size
34 | popoverPresentationController?
35 | .presentedViewController
36 | .preferredContentSize = size
37 | }
38 |
39 | override func numberOfSections(in tableView: UITableView) -> Int {
40 | return 1
41 | }
42 |
43 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
44 | return UIImage.SymbolWeight.allCases.count
45 | }
46 |
47 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
48 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
49 | let weight = UIImage.SymbolWeight.allCases[indexPath.row]
50 | cell.textLabel?.text = weight.displayString
51 | cell.textLabel?.font = UIFont.systemFont(ofSize: 17, weight: weight.fontWeight())
52 |
53 | cell.accessoryType = (weight == currentWeight) ? .checkmark : .none
54 |
55 | return cell
56 | }
57 |
58 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
59 | currentWeight = UIImage.SymbolWeight.allCases[indexPath.row]
60 | tableView.cellForRow(at: indexPath)
61 | for cell in tableView.visibleCells {
62 | if cell == tableView.cellForRow(at: indexPath) {
63 | cell.accessoryType = .checkmark
64 | } else {
65 | cell.accessoryType = .none
66 | }
67 | }
68 |
69 | tableView.deselectRow(at: indexPath, animated: true)
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Symbals/Views/DetailCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailCell.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 19/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class DetailCell: UITableViewCell {
12 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
13 | super.init(style: .value1, reuseIdentifier: reuseIdentifier)
14 | }
15 |
16 | required init?(coder: NSCoder) {
17 | super.init(coder: coder)
18 | }
19 |
20 | override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
21 | self.layoutIfNeeded()
22 | var size = super.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: horizontalFittingPriority, verticalFittingPriority: verticalFittingPriority)
23 | if let textLabel = self.textLabel, let detailTextLabel = self.detailTextLabel {
24 | let detailHeight = detailTextLabel.frame.size.height
25 | if detailTextLabel.frame.origin.x > textLabel.frame.origin.x { // style = Value1 or Value2
26 | let textHeight = textLabel.frame.size.height
27 | if (detailHeight > textHeight) {
28 | size.height += detailHeight - textHeight
29 | }
30 | } else { // style = Subtitle, so always add subtitle height
31 | size.height += detailHeight
32 | }
33 | }
34 | return size
35 | }
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/Symbals/Views/ReusableViews.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseHeaderView.swift
3 | // HomeRun
4 | //
5 | // Created by Aaron Pearce on 12/10/18.
6 | // Copyright © 2018 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class BaseHeaderView: UICollectionReusableView {
12 |
13 | lazy var titleLabel: UILabel = {
14 | let titleLabel = UILabel().usingAutoLayout()
15 | titleLabel.font = UIFont.boldSystemFont(ofSize: 20)
16 | titleLabel.textColor = .label
17 | return titleLabel
18 | }()
19 |
20 | let contentView = UIView().usingAutoLayout()
21 |
22 | override init(frame: CGRect) {
23 | super.init(frame: frame)
24 | initialize()
25 | }
26 |
27 | required init?(coder aDecoder: NSCoder) {
28 | fatalError("init(coder:) has not been implemented")
29 | }
30 |
31 | private func initialize() {
32 | addSubview(contentView)
33 | contentView.addSubview(titleLabel)
34 | NSLayoutConstraint.activate(contentView.constraintsToFit(view: self, insets: UIEdgeInsets(top: 0, left: 24, bottom: 0, right: 24)))
35 | NSLayoutConstraint.activate([
36 | titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
37 | titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
38 | titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
39 | ])
40 | }
41 | }
42 |
43 | class BaseFooterView: UICollectionReusableView {
44 |
45 | lazy var titleLabel: UILabel = {
46 | let titleLabel = UILabel().usingAutoLayout()
47 | titleLabel.font = UIFont.systemFont(ofSize: 13)
48 | titleLabel.textColor = UIColor.gray
49 | titleLabel.textAlignment = .center
50 | titleLabel.numberOfLines = 0
51 | return titleLabel
52 | }()
53 |
54 | let contentView = UIView().usingAutoLayout()
55 |
56 | override init(frame: CGRect) {
57 | super.init(frame: frame)
58 | initialize()
59 | }
60 |
61 | required init?(coder aDecoder: NSCoder) {
62 | fatalError("init(coder:) has not been implemented")
63 | }
64 |
65 | private func initialize() {
66 | addSubview(contentView)
67 | contentView.addSubview(titleLabel)
68 | NSLayoutConstraint.activate(contentView.constraintsToFit(view: self, insets: UIEdgeInsets(top: 0, left: 32, bottom: 0, right: 32)))
69 | NSLayoutConstraint.activate([
70 | titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
71 | titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
72 | titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor)
73 | ])
74 | }
75 | }
76 |
77 |
--------------------------------------------------------------------------------
/Symbals/Views/SymbolCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SymbolCell.swift
3 | // Symbals
4 | //
5 | // Created by Aaron Pearce on 19/10/19.
6 | // Copyright © 2019 Sunya. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SymbolCell: UICollectionViewCell {
12 | let imageView: UIImageView = {
13 | let imageView = UIImageView().usingAutoLayout()
14 | imageView.preferredSymbolConfiguration = .init(pointSize: 48)
15 | imageView.contentMode = .center
16 | return imageView
17 | }()
18 |
19 | lazy var imageContainerView: UIView = {
20 | let containerView = UIView().usingAutoLayout()
21 | containerView.backgroundColor = .secondarySystemBackground
22 | containerView.layer.cornerRadius = 8
23 | containerView.layer.cornerCurve = .continuous
24 | containerView.layer.masksToBounds = true
25 |
26 | containerView.addSubview(imageView)
27 |
28 | NSLayoutConstraint.activate(imageView.constraintsToFit(view: containerView))
29 |
30 | return containerView
31 | }()
32 |
33 | let label: UILabel = {
34 | let label = UILabel().usingAutoLayout()
35 | label.font = UIFont.systemFont(ofSize: 13)
36 | label.textAlignment = .center
37 | label.numberOfLines = 2
38 | label.lineBreakMode = .byTruncatingTail
39 | return label
40 | }()
41 |
42 | override init(frame: CGRect) {
43 | super.init(frame: frame)
44 | initialize()
45 | }
46 |
47 | required init?(coder: NSCoder) {
48 | fatalError("init(coder:) has not been implemented")
49 | }
50 |
51 | func initialize() {
52 | contentView.addSubviews([imageContainerView, label])
53 |
54 | NSLayoutConstraint.activate([
55 | imageContainerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0),
56 | imageContainerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
57 | imageContainerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
58 | imageContainerView.heightAnchor.constraint(equalToConstant: 80),
59 |
60 | label.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 8),
61 | label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8),
62 | label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8),
63 | label.bottomAnchor.constraint(lessThanOrEqualTo: contentView.bottomAnchor, constant: 0)
64 | ])
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpearce/SF-Viewer/0636db39cb2bb127db8f698c7d5a3871494159bf/app-icon.png
--------------------------------------------------------------------------------
/bootstrap.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Thanks to Kyle Hickinson (@kylehickinson) for the tip.
4 | # This is based upon the setup that Brave and Firefox implement to have a local developer setup.
5 |
6 | # Sets up local configurations from the tracked .template files
7 |
8 | # Checking the `Local` Directory
9 | CONFIG_PATH="Symbals/Configuration"
10 | if [ ! -d "$CONFIG_PATH/Local/" ]; then
11 | echo "Creating 'Local' directory"
12 |
13 | (cd $CONFIG_PATH && mkdir Local)
14 | fi
15 |
16 | # Copying over any necessary files into `Local`
17 | for CONFIG_FILE_NAME in BundleId DevTeam
18 | do
19 | CONFIG_FILE=$CONFIG_FILE_NAME.xcconfig
20 | (cd $CONFIG_PATH \
21 | && cp -n Local.templates/$CONFIG_FILE Local/$CONFIG_FILE \
22 | )
23 | done
24 |
25 | echo "Choose your developer team that you wish to sign SF Viewer with:"
26 | IFS=$'\n'
27 | developerids=($(security find-identity -v -p codesigning | awk '!/CSSMERR_TP_CERT_REVOKE|Mac/' | awk -F \" '{if ($2) print $2}'))
28 |
29 | select opt in "${developerids[@]}" "Quit"
30 | do
31 | if [[ "$opt" == "Quit" ]]; then
32 | echo "Bye!"
33 | break;
34 | fi
35 |
36 | # complain if an invalid option was chosen
37 | if [[ "$opt" == "" ]]
38 | then
39 | echo "'$REPLY' is not a valid option"
40 | continue
41 | fi
42 |
43 | # now we can use the selected option
44 | identifier=`echo $opt | awk -F '[\(\)]' '{print $2}'`
45 | sed -i '' -e "s:LOCAL_DEVELOPMENT_TEAM = \/:LOCAL_DEVELOPMENT_TEAM = $identifier \/:g" "$CONFIG_PATH/Local/DevTeam.xcconfig"
46 | echo "You selected '$opt' as your developer team, we've written this to '$CONFIG_PATH/Local/DevTeam.xcconfig'"
47 | break
48 | done
--------------------------------------------------------------------------------