├── icon.png
├── icon.psd
├── README.md
├── flipflap
├── Assets.xcassets
│ ├── Contents.json
│ └── AppIcon.appiconset
│ │ ├── icon.png
│ │ ├── icon-40.png
│ │ ├── icon-72.png
│ │ ├── icon-76.png
│ │ ├── icon@2x.png
│ │ ├── icon-40@2x.png
│ │ ├── icon-40@3x.png
│ │ ├── icon-60@2x.png
│ │ ├── icon-60@3x.png
│ │ ├── icon-72@2x.png
│ │ ├── icon-76@2x.png
│ │ ├── icon-small.png
│ │ ├── icon-83.5@2x.png
│ │ ├── icon-small-50.png
│ │ ├── icon-small@2x.png
│ │ ├── icon-small@3x.png
│ │ ├── ios-marketing.png
│ │ ├── icon-small-50@2x.png
│ │ ├── notification-icon@2x.png
│ │ ├── notification-icon@3x.png
│ │ ├── notification-icon~ipad.png
│ │ ├── notification-icon~ipad@2x.png
│ │ └── Contents.json
├── flipflap.xcdatamodeld
│ ├── .xccurrentversion
│ └── flipflap.xcdatamodel
│ │ └── contents
├── UIViewController.swift
├── Base.lproj
│ └── LaunchScreen.storyboard
├── Info.plist
├── AppDelegate.swift
├── SceneDelegate.swift
├── FontViewController.swift
├── ViewController.swift
└── SettingsViewController.swift
└── flipflap.xcodeproj
├── project.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── xcuserdata
└── twodayslate.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
└── project.pbxproj
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/icon.png
--------------------------------------------------------------------------------
/icon.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/icon.psd
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Split Flap
2 |
3 | Special thanks to https://github.com/yannickl/Splitflap
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-40.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-72.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-76.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon@2x.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-small.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-small-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-small-50.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-small@2x.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-small@3x.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/ios-marketing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/ios-marketing.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/icon-small-50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/icon-small-50@2x.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/notification-icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/notification-icon@2x.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/notification-icon@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/notification-icon@3x.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad.png
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twodayslate/Split-Flap/master/flipflap/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad@2x.png
--------------------------------------------------------------------------------
/flipflap.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/flipflap/flipflap.xcdatamodeld/.xccurrentversion:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | _XCCurrentVersionName
6 | flipflap.xcdatamodel
7 |
8 |
9 |
--------------------------------------------------------------------------------
/flipflap.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/flipflap/flipflap.xcdatamodeld/flipflap.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/flipflap.xcodeproj/xcuserdata/twodayslate.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | flipflap.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/flipflap/UIViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | extension UIViewController {
4 | /**
5 | - seealso: https://stackoverflow.com/a/54932223/193772
6 | */
7 | public func addActionSheetForiPad(actionSheet: UIViewController, sourceView _view: UIView? = nil, sourceRect _rect: CGRect? = nil, permittedArrowDirections _arrowDirections: UIPopoverArrowDirection? = nil) {
8 | let useView = (_view ?? self.view) as UIView
9 | let useRect = (_rect ?? CGRect(x: useView.bounds.midX, y: useView.bounds.midY, width: 0, height: 0)) as CGRect
10 | let useArrows = (_arrowDirections ?? []) as UIPopoverArrowDirection
11 |
12 | actionSheet.popoverPresentationController?.sourceView = useView
13 | actionSheet.popoverPresentationController?.sourceRect = useRect
14 | actionSheet.popoverPresentationController?.permittedArrowDirections = useArrows
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/flipflap/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 |
--------------------------------------------------------------------------------
/flipflap/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | Split Flap
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 | UISceneStoryboardFile
37 | LaunchScreen
38 |
39 |
40 |
41 |
42 | UILaunchStoryboardName
43 | LaunchScreen
44 | UIRequiredDeviceCapabilities
45 |
46 | armv7
47 |
48 | UIStatusBarHidden
49 |
50 | UISupportedInterfaceOrientations
51 |
52 | UIInterfaceOrientationPortrait
53 | UIInterfaceOrientationLandscapeLeft
54 | UIInterfaceOrientationLandscapeRight
55 | UIInterfaceOrientationPortraitUpsideDown
56 |
57 | UISupportedInterfaceOrientations~ipad
58 |
59 | UIInterfaceOrientationPortrait
60 | UIInterfaceOrientationPortraitUpsideDown
61 | UIInterfaceOrientationLandscapeLeft
62 | UIInterfaceOrientationLandscapeRight
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/flipflap/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "icon-40.png",
5 | "scale" : "1x",
6 | "idiom" : "ipad",
7 | "size" : "40x40"
8 | },
9 | {
10 | "size" : "40x40",
11 | "scale" : "2x",
12 | "idiom" : "ipad",
13 | "filename" : "icon-40@2x.png"
14 | },
15 | {
16 | "size" : "60x60",
17 | "filename" : "icon-60@2x.png",
18 | "idiom" : "iphone",
19 | "scale" : "2x"
20 | },
21 | {
22 | "idiom" : "ipad",
23 | "size" : "72x72",
24 | "scale" : "1x",
25 | "filename" : "icon-72.png"
26 | },
27 | {
28 | "size" : "72x72",
29 | "idiom" : "ipad",
30 | "scale" : "2x",
31 | "filename" : "icon-72@2x.png"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "scale" : "1x",
36 | "size" : "76x76",
37 | "filename" : "icon-76.png"
38 | },
39 | {
40 | "filename" : "icon-76@2x.png",
41 | "idiom" : "ipad",
42 | "size" : "76x76",
43 | "scale" : "2x"
44 | },
45 | {
46 | "idiom" : "ipad",
47 | "filename" : "icon-small-50.png",
48 | "scale" : "1x",
49 | "size" : "50x50"
50 | },
51 | {
52 | "scale" : "2x",
53 | "idiom" : "ipad",
54 | "filename" : "icon-small-50@2x.png",
55 | "size" : "50x50"
56 | },
57 | {
58 | "idiom" : "iphone",
59 | "filename" : "icon-small.png",
60 | "scale" : "1x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "size" : "29x29",
65 | "filename" : "icon-small@2x.png",
66 | "idiom" : "iphone",
67 | "scale" : "2x"
68 | },
69 | {
70 | "scale" : "1x",
71 | "size" : "57x57",
72 | "idiom" : "iphone",
73 | "filename" : "icon.png"
74 | },
75 | {
76 | "idiom" : "iphone",
77 | "filename" : "icon@2x.png",
78 | "scale" : "2x",
79 | "size" : "57x57"
80 | },
81 | {
82 | "size" : "29x29",
83 | "filename" : "icon-small@3x.png",
84 | "scale" : "3x",
85 | "idiom" : "iphone"
86 | },
87 | {
88 | "scale" : "3x",
89 | "idiom" : "iphone",
90 | "filename" : "icon-40@3x.png",
91 | "size" : "40x40"
92 | },
93 | {
94 | "scale" : "3x",
95 | "filename" : "icon-60@3x.png",
96 | "idiom" : "iphone",
97 | "size" : "60x60"
98 | },
99 | {
100 | "idiom" : "iphone",
101 | "filename" : "icon-40@2x.png",
102 | "size" : "40x40",
103 | "scale" : "2x"
104 | },
105 | {
106 | "scale" : "1x",
107 | "filename" : "icon-small.png",
108 | "size" : "29x29",
109 | "idiom" : "ipad"
110 | },
111 | {
112 | "filename" : "icon-small@2x.png",
113 | "idiom" : "ipad",
114 | "size" : "29x29",
115 | "scale" : "2x"
116 | },
117 | {
118 | "size" : "83.5x83.5",
119 | "filename" : "icon-83.5@2x.png",
120 | "idiom" : "ipad",
121 | "scale" : "2x"
122 | },
123 | {
124 | "size" : "20x20",
125 | "filename" : "notification-icon@2x.png",
126 | "idiom" : "iphone",
127 | "scale" : "2x"
128 | },
129 | {
130 | "scale" : "3x",
131 | "size" : "20x20",
132 | "filename" : "notification-icon@3x.png",
133 | "idiom" : "iphone"
134 | },
135 | {
136 | "scale" : "1x",
137 | "size" : "20x20",
138 | "idiom" : "ipad",
139 | "filename" : "notification-icon~ipad.png"
140 | },
141 | {
142 | "size" : "20x20",
143 | "idiom" : "ipad",
144 | "filename" : "notification-icon~ipad@2x.png",
145 | "scale" : "2x"
146 | },
147 | {
148 | "size" : "1024x1024",
149 | "scale" : "1x",
150 | "idiom" : "ios-marketing",
151 | "filename" : "ios-marketing.png"
152 | }
153 | ],
154 | "info" : {
155 | "author" : "xcode",
156 | "version" : 1
157 | }
158 | }
--------------------------------------------------------------------------------
/flipflap/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // flipflap
4 | //
5 | // Created by Zachary Gorak on 2/7/20.
6 | // Copyright © 2020 twodayslate. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreData
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 | return true
20 | }
21 |
22 | // MARK: UISceneSession Lifecycle
23 |
24 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
25 | // Called when a new scene session is being created.
26 | // Use this method to select a configuration to create the new scene with.
27 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
28 | }
29 |
30 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
31 | // Called when the user discards a scene session.
32 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
33 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
34 | }
35 |
36 | // MARK: - Core Data stack
37 |
38 | lazy var persistentContainer: NSPersistentCloudKitContainer = {
39 | /*
40 | The persistent container for the application. This implementation
41 | creates and returns a container, having loaded the store for the
42 | application to it. This property is optional since there are legitimate
43 | error conditions that could cause the creation of the store to fail.
44 | */
45 | let container = NSPersistentCloudKitContainer(name: "flipflap")
46 | container.loadPersistentStores(completionHandler: { (storeDescription, error) in
47 | if let error = error as NSError? {
48 | // Replace this implementation with code to handle the error appropriately.
49 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
50 |
51 | /*
52 | Typical reasons for an error here include:
53 | * The parent directory does not exist, cannot be created, or disallows writing.
54 | * The persistent store is not accessible, due to permissions or data protection when the device is locked.
55 | * The device is out of space.
56 | * The store could not be migrated to the current model version.
57 | Check the error message to determine what the actual problem was.
58 | */
59 | fatalError("Unresolved error \(error), \(error.userInfo)")
60 | }
61 | })
62 | return container
63 | }()
64 |
65 | // MARK: - Core Data Saving support
66 |
67 | func saveContext () {
68 | let context = persistentContainer.viewContext
69 | if context.hasChanges {
70 | do {
71 | try context.save()
72 | } catch {
73 | // Replace this implementation with code to handle the error appropriately.
74 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
75 | let nserror = error as NSError
76 | fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
77 | }
78 | }
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/flipflap/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // flipflap
4 | //
5 | // Created by Zachary Gorak on 2/7/20.
6 | // Copyright © 2020 twodayslate. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
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 _ = (scene as? UIWindowScene) else {
21 | print("returning")
22 | return
23 | }
24 |
25 | if let windowScene = scene as? UIWindowScene {
26 | print("setting controller")
27 | let window = UIWindow(windowScene: windowScene)
28 | window.rootViewController = ViewController()
29 | self.window = window
30 |
31 | self.window?.backgroundColor = UIColor.black
32 |
33 | if let data = UserDefaults.standard.object(forKey: "tint") as? Data, let color = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) {
34 |
35 | self.window?.tintColor = color
36 | }
37 |
38 | UserDefaults.standard.addObserver(self, forKeyPath: "theme", options: .new, context: nil)
39 | self.setTheme()
40 |
41 | window.makeKeyAndVisible()
42 | }
43 |
44 |
45 |
46 | // don't fall asleep
47 | UIApplication.shared.isIdleTimerDisabled = true
48 | }
49 |
50 | deinit {
51 | UserDefaults.standard.removeObserver(self, forKeyPath: "theme")
52 | }
53 |
54 | func setTheme() {
55 | DispatchQueue.main.async {
56 | switch UserDefaults.standard.integer(forKey: "theme") {
57 | case 1:
58 | self.window?.overrideUserInterfaceStyle = .light
59 | case 2:
60 | self.window?.overrideUserInterfaceStyle = .dark
61 | default:
62 | self.window?.overrideUserInterfaceStyle = .unspecified
63 | }
64 | }
65 | }
66 |
67 | override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
68 | if keyPath == "theme" {
69 | self.setTheme()
70 | }
71 | }
72 |
73 | func sceneDidDisconnect(_ scene: UIScene) {
74 | // Called as the scene is being released by the system.
75 | // This occurs shortly after the scene enters the background, or when its session is discarded.
76 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
77 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
78 | }
79 |
80 | func sceneDidBecomeActive(_ scene: UIScene) {
81 | // Called when the scene has moved from an inactive state to an active state.
82 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
83 | }
84 |
85 | func sceneWillResignActive(_ scene: UIScene) {
86 | // Called when the scene will move from an active state to an inactive state.
87 | // This may occur due to temporary interruptions (ex. an incoming phone call).
88 | }
89 |
90 | func sceneWillEnterForeground(_ scene: UIScene) {
91 | // Called as the scene transitions from the background to the foreground.
92 | // Use this method to undo the changes made on entering the background.
93 | }
94 |
95 | func sceneDidEnterBackground(_ scene: UIScene) {
96 | // Called as the scene transitions from the foreground to the background.
97 | // Use this method to save data, release shared resources, and store enough scene-specific state information
98 | // to restore the scene back to its current state.
99 |
100 | // Save changes in the application's managed object context when the application transitions to the background.
101 | (UIApplication.shared.delegate as? AppDelegate)?.saveContext()
102 | }
103 |
104 |
105 | }
106 |
107 |
--------------------------------------------------------------------------------
/flipflap/FontViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FontViewController.swift
3 | // flipflap
4 | //
5 | // Created by Zachary Gorak on 2/12/20.
6 | // Copyright © 2020 twodayslate. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | protocol FontControllerDelegate {
13 | var currentlySelectedFont: String? { get }
14 | func didChangeFont(name: String)
15 | }
16 |
17 | class FontNavigationController: UINavigationController, FontControllerDelegate {
18 | var fontDelegate: FontControllerDelegate? = nil
19 | func didChangeFont(name: String) {
20 | self.fontDelegate?.didChangeFont(name: name)
21 | }
22 |
23 | var currentlySelectedFont: String? = nil {
24 | didSet {
25 | self.settings.currentlySelectedFont = self.currentlySelectedFont
26 | self.settings.tableView.reloadData()
27 | }
28 | }
29 |
30 | convenience init() {
31 | self.init(nibName: nil, bundle: nil)
32 | }
33 |
34 | let settings = FontTableViewController(style: .plain)
35 |
36 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
37 | super.init(nibName: nil, bundle: nil)
38 |
39 | self.settings.fontDelegate = self
40 | self.viewControllers = [settings]
41 |
42 | self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .close, target: self, action: #selector(close(_:)))
43 | }
44 |
45 | override func viewDidLoad() {
46 | super.viewDidLoad()
47 | self.view.backgroundColor = .systemBackground
48 | }
49 |
50 | @objc func close(_ sender: Any?) {
51 | self.dismiss(animated: true, completion: {})
52 | }
53 |
54 | required init?(coder aDecoder: NSCoder) {
55 | fatalError("init(coder:) has not been implemented")
56 | }
57 | }
58 |
59 | class FontTableViewController: UITableViewController, FontControllerDelegate {
60 | var fontDelegate: FontControllerDelegate? = nil
61 | func didChangeFont(name: String) {
62 | self.fontDelegate?.didChangeFont(name: name)
63 | }
64 |
65 | var currentlySelectedFont: String? {
66 | didSet {
67 | self.tableView.reloadData()
68 | if let name = self.currentlySelectedFont {
69 | self.didChangeFont(name: name)
70 | }
71 |
72 | }
73 | }
74 |
75 | let familyNames = UIFont.familyNames.sorted()
76 |
77 | override func viewDidLoad() {
78 | super.viewDidLoad()
79 | self.title = "Settings"
80 | self.view.backgroundColor = .systemBackground
81 | self.tableView.backgroundColor = .systemGroupedBackground
82 |
83 | self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .close, target: self.navigationController, action: #selector(FontNavigationController.close(_:)))
84 |
85 | self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Reset", style: .done, target: self, action: #selector(reset(_:)))
86 | }
87 |
88 | @objc func reset(_ sender: Any?) {
89 | let font = UIFont(name: "Courier", size: 45.0)
90 | guard let data = try? NSKeyedArchiver.archivedData(withRootObject: font, requiringSecureCoding: false) else {
91 | return
92 | }
93 | UserDefaults.standard.set(data, forKey: "font")
94 |
95 | self.currentlySelectedFont = font?.familyName
96 | }
97 |
98 | override func numberOfSections(in tableView: UITableView) -> Int {
99 | return 1
100 | }
101 |
102 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
103 | return self.familyNames.count
104 | }
105 |
106 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
107 | let cell = UITableViewCell(style: .default, reuseIdentifier: "font")
108 | let fontName = self.familyNames[indexPath.row]
109 | cell.textLabel?.text = fontName
110 | cell.textLabel?.font = UIFont(name: fontName, size: cell.textLabel?.font.pointSize ?? 14.0)
111 |
112 | if self.currentlySelectedFont == cell.textLabel?.font.familyName {
113 | cell.accessoryType = .checkmark
114 | } else {
115 | cell.accessoryType = .none
116 | }
117 | return cell
118 | }
119 |
120 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
121 | let fontName = self.familyNames[indexPath.row]
122 |
123 | let font = UIFont(name: fontName, size: 45.0)
124 | guard let data = try? NSKeyedArchiver.archivedData(withRootObject: font, requiringSecureCoding: false) else {
125 | return
126 | }
127 | UserDefaults.standard.set(data, forKey: "font")
128 |
129 | tableView.deselectRow(at: indexPath, animated: true)
130 |
131 | self.currentlySelectedFont = font?.familyName
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/flipflap/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // flipflap
4 | //
5 | // Created by Zachary Gorak on 2/7/20.
6 | // Copyright © 2020 twodayslate. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Splitflap
11 |
12 | extension Date {
13 | func convertToLocaleDate(template: String) -> String {
14 | let dateFormatter = DateFormatter()
15 |
16 | let calender = Calendar.current
17 |
18 | dateFormatter.timeZone = calender.timeZone
19 | dateFormatter.locale = calender.locale
20 | dateFormatter.setLocalizedDateFormatFromTemplate(template)
21 |
22 | return dateFormatter.string(from: self)
23 | }
24 | }
25 |
26 | extension Calendar {
27 | var is24Hour: Bool {
28 | guard let locale = self.locale else {
29 | return false
30 | }
31 | guard let c = DateFormatter.dateFormat(fromTemplate: "j", options: 0, locale: locale) else {
32 | return false
33 | }
34 | // "h a" when not 24 hours
35 | return !c.contains("a")
36 | }
37 | }
38 |
39 | class ViewController: UIViewController, SplitflapDataSource, SplitflapDelegate {
40 |
41 | var flaps = Splitflap()
42 | private var previousFlapText: String?
43 | var flapText = "Hello" {
44 | willSet {
45 | self.previousFlapText = self.flapText
46 | }
47 | didSet {
48 | guard let previousFlapText = self.previousFlapText else {
49 | return
50 | }
51 | if previousFlapText.count != self.flapText.count {
52 | self.flaps.reload()
53 | }
54 | }
55 | }
56 | let settingsButton = UIButton(type: .system)
57 | var timer = Timer()
58 | var shouldSetTime = true
59 |
60 | override func viewDidLoad() {
61 | super.viewDidLoad()
62 | // Do any additional setup after loading the view.
63 |
64 | self.view.backgroundColor = .systemBackground
65 | if let data = UserDefaults.standard.object(forKey: "background_color") as? Data, let color = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) {
66 |
67 | self.view.backgroundColor = color
68 | }
69 |
70 | self.flaps.delegate = self
71 | self.flaps.datasource = self
72 | self.flaps.translatesAutoresizingMaskIntoConstraints = false
73 |
74 | let innerView = UIView()
75 | innerView.translatesAutoresizingMaskIntoConstraints = false
76 | self.view.addSubview(innerView)
77 |
78 | self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[scrollview]-|", options: .alignAllCenterY, metrics: nil, views: ["scrollview": innerView]))
79 | self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[scrollview]-|", options: .alignAllCenterY, metrics: nil, views: ["scrollview": innerView]))
80 |
81 | innerView.addSubview(self.flaps)
82 |
83 | self.flaps.centerXAnchor.constraint(equalTo: innerView.centerXAnchor).isActive = true
84 | self.flaps.centerYAnchor.constraint(equalTo: innerView.centerYAnchor).isActive = true
85 |
86 | self.flaps.widthAnchor.constraint(equalTo: innerView.widthAnchor).isActive = true
87 | //self.flaps.heightAnchor.constraint(lessThanOrEqualTo: innerView.heightAnchor).isActive = true
88 | self.flaps.heightAnchor.constraint(equalTo: self.flaps.widthAnchor, multiplier: 0.5).isActive = true
89 | self.flaps.heightAnchor.constraint(lessThanOrEqualTo: innerView.heightAnchor, multiplier: 0.5).isActive = true
90 |
91 |
92 | settingsButton.setImage(UIImage.init(systemName: "gear"), for: .normal)
93 | settingsButton.alpha = 0.0
94 | settingsButton.tintColor = .systemFill
95 | settingsButton.translatesAutoresizingMaskIntoConstraints = false
96 | settingsButton.isUserInteractionEnabled = true
97 | settingsButton.isEnabled = true
98 |
99 | innerView.addSubview(settingsButton)
100 | settingsButton.bottomAnchor.constraint(equalTo: innerView.bottomAnchor, constant: -16.0).isActive = true
101 | settingsButton.trailingAnchor.constraint(equalTo: innerView.trailingAnchor, constant: -16.0).isActive = true
102 | //settings.heightAnchor.constraint(equalToConstant: 24).isActive = true
103 | settingsButton.widthAnchor.constraint(equalTo: settingsButton.heightAnchor).isActive = true
104 | settingsButton.addTarget(self, action: #selector(openSettings(_:)), for: .touchUpInside)
105 |
106 | let tapScreenGesture = UITapGestureRecognizer(target: self, action: #selector(tapScreen(_:)))
107 | tapScreenGesture.numberOfTapsRequired = 1
108 | self.view.addGestureRecognizer(tapScreenGesture)
109 |
110 | self.flapText = self.currentTime()
111 | self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.updateTimer(timer:)), userInfo: nil, repeats: true)
112 | RunLoop.main.add(self.timer, forMode: .common)
113 |
114 | UserDefaults.standard.addObserver(self, forKeyPath: "background_color", options: .new, context: nil)
115 | UserDefaults.standard.addObserver(self, forKeyPath: "flap_color", options: .new, context: nil)
116 | UserDefaults.standard.addObserver(self, forKeyPath: "text_color", options: .new, context: nil)
117 | UserDefaults.standard.addObserver(self, forKeyPath: "font", options: .new, context: nil)
118 | }
119 |
120 | deinit {
121 | UserDefaults.standard.removeObserver(self, forKeyPath: "background_color")
122 | UserDefaults.standard.removeObserver(self, forKeyPath: "flap_color")
123 | UserDefaults.standard.removeObserver(self, forKeyPath: "text_color")
124 | UserDefaults.standard.removeObserver(self, forKeyPath: "font")
125 | }
126 |
127 | override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
128 | guard let key = keyPath else {
129 | return
130 | }
131 | self.didChangeKey(key)
132 | }
133 |
134 | @objc func tapScreen(_ sender: UITapGestureRecognizer) {
135 | print("did tap")
136 | self.showSettingsIcon()
137 | }
138 |
139 | @objc func showSettingsIcon() {
140 | print("showing icons")
141 | self.settingsButton.isEnabled = true
142 | self.settingsButton.isUserInteractionEnabled = true
143 | UIView.animate(withDuration: 0.5, delay: 0.0, options: [.allowUserInteraction, .beginFromCurrentState, .curveEaseInOut], animations: {
144 | self.settingsButton.alpha = 1.0
145 | }, completion: { didComplete in
146 | self.hideSettingsIcon(didComplete, delay: 5.0)
147 | })
148 | }
149 |
150 | @objc func openSettings(_ sender: UIButton) {
151 | let settings = SettingsNavigationController()
152 | settings.settingsDelegate = self
153 | self.present(settings, animated: true, completion: nil)
154 | }
155 |
156 | func currentTime() -> String {
157 | if Calendar.current.is24Hour {
158 | if UserDefaults.standard.bool(forKey: "showSeconds") {
159 | return Date().convertToLocaleDate(template: "HH:mm:ss")
160 | } else {
161 | return Date().convertToLocaleDate(template: "HH:mm")
162 | }
163 | } else {
164 | if UserDefaults.standard.bool(forKey: "showSeconds") {
165 | return Date().convertToLocaleDate(template: "h:mm:ss a")
166 | } else {
167 | return Date().convertToLocaleDate(template: "h:mm a")
168 | }
169 | }
170 | }
171 |
172 | @objc
173 | func updateTimer(timer: Timer) {
174 | if shouldSetTime {
175 | DispatchQueue.main.async {
176 |
177 | self.flapText = self.currentTime()
178 | self.flaps.setText(self.flapText, animated: true)
179 | }
180 | }
181 | }
182 |
183 | func numberOfFlapsInSplitflap(_ splitflap: Splitflap) -> Int {
184 | return self.flapText.count
185 | }
186 |
187 | func tokensInSplitflap(_ splitflap: Splitflap, flap: Int) -> [String] {
188 | // so this is the order in which things will actually flip
189 | if shouldSetTime {
190 | if Calendar.current.is24Hour {
191 | return SplitflapTokens.Numeric + [":"]
192 | } else {
193 | let map = SplitflapTokens.Numeric + " AMP:".map { String($0) }
194 | return map
195 | }
196 | }
197 | let map = SplitflapTokens.AlphanumericAndSpace + [":"]
198 | return map
199 | }
200 |
201 | func splitflap(_ splitflap: Splitflap, builderForFlapAtIndex index: Int) -> FlapViewBuilder {
202 | var background: UIColor = .secondarySystemBackground
203 | if let data = UserDefaults.standard.object(forKey: "flap_color") as? Data, let color = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) {
204 |
205 | background = color
206 | }
207 | var textColor: UIColor = .label
208 | if let data = UserDefaults.standard.object(forKey: "text_color") as? Data, let color = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) {
209 |
210 | textColor = color
211 | }
212 |
213 | let width = splitflap.bounds.width / CGFloat(self.numberOfFlapsInSplitflap(splitflap))
214 |
215 | var font = UIFont(name: "Courier", size: width)
216 |
217 | if let data = UserDefaults.standard.object(forKey: "font") as? Data, let color = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [UIFont.self], from: data) as? UIFont {
218 |
219 | font = UIFont(name: color.familyName, size: width)
220 | }
221 |
222 | return FlapViewBuilder { builder in
223 | builder.backgroundColor = background
224 | builder.textColor = textColor
225 | builder.lineColor = .opaqueSeparator
226 | builder.adjustsFontSizeToFitWidth = true
227 | builder.font = font
228 | }
229 | }
230 |
231 | func hideSettingsIcon(_ didComplete: Bool = true, delay: TimeInterval = 0.0) {
232 | print("did complete?", didComplete)
233 | if didComplete {
234 | UIView.animate(withDuration: 2.0, delay: delay, options: [.allowUserInteraction, .beginFromCurrentState], animations: {
235 | self.settingsButton.alpha = 0.011
236 | }, completion: { didComplete2 in
237 | print("did complete2:", didComplete2)
238 | if didComplete2 {
239 | self.settingsButton.isUserInteractionEnabled = false
240 | self.settingsButton.isEnabled = true
241 | }
242 | })
243 | }
244 | }
245 |
246 | override func viewDidAppear(_ animated: Bool) {
247 | super.viewDidAppear(animated)
248 | self.flaps.reload() // XXX remove once width fix is implemented
249 | UIView.animate(withDuration: 1.0, delay: 0.0, options: [.allowUserInteraction, .beginFromCurrentState], animations: {
250 | self.settingsButton.alpha = 1.0
251 | }, completion: { didComplete in
252 | self.hideSettingsIcon(didComplete, delay: 5.0)
253 | })
254 | }
255 |
256 | override var prefersStatusBarHidden: Bool {
257 | return true
258 | }
259 | }
260 |
261 | extension ViewController: SettingsControllerDelegate {
262 | func didChangeKey(_ key: String) {
263 | if key == "background_color" {
264 | if let data = UserDefaults.standard.object(forKey: key) as? Data, let color = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) {
265 | self.view.backgroundColor = color
266 | } else {
267 | self.view.backgroundColor = .systemBackground
268 | }
269 | }
270 |
271 | if key == "flap_color" || key == "text_color" || key == "font" {
272 | self.flaps.reload()
273 | }
274 | }
275 |
276 | func didCloseSettings() {
277 | self.showSettingsIcon()
278 | }
279 |
280 | func willCloseSettings() {
281 | self.showSettingsIcon()
282 | self.flaps.reload()
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/flipflap.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 52;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | F6B15A4D23F478AC001DB252 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6B15A4C23F478AC001DB252 /* UIViewController.swift */; };
11 | F6B15A5023F48797001DB252 /* EFColorPicker in Frameworks */ = {isa = PBXBuildFile; productRef = F6B15A4F23F48797001DB252 /* EFColorPicker */; };
12 | F6B15A5223F4CD3D001DB252 /* FontViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6B15A5123F4CD3D001DB252 /* FontViewController.swift */; };
13 | F6DAA11C2944F13F001F4C56 /* Splitflap in Frameworks */ = {isa = PBXBuildFile; productRef = F6DAA11B2944F13F001F4C56 /* Splitflap */; };
14 | F6F29FBE23EDDF19000AF82B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6F29FBD23EDDF19000AF82B /* AppDelegate.swift */; };
15 | F6F29FC023EDDF19000AF82B /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6F29FBF23EDDF19000AF82B /* SceneDelegate.swift */; };
16 | F6F29FC223EDDF19000AF82B /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6F29FC123EDDF19000AF82B /* ViewController.swift */; };
17 | F6F29FC823EDDF19000AF82B /* flipflap.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = F6F29FC623EDDF19000AF82B /* flipflap.xcdatamodeld */; };
18 | F6F29FCA23EDDF1C000AF82B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F6F29FC923EDDF1C000AF82B /* Assets.xcassets */; };
19 | F6F29FCD23EDDF1C000AF82B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F6F29FCB23EDDF1C000AF82B /* LaunchScreen.storyboard */; };
20 | F6F29FD823EDF77A000AF82B /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6F29FD723EDF77A000AF82B /* SettingsViewController.swift */; };
21 | /* End PBXBuildFile section */
22 |
23 | /* Begin PBXFileReference section */
24 | F6B15A4C23F478AC001DB252 /* UIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; };
25 | F6B15A5123F4CD3D001DB252 /* FontViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontViewController.swift; sourceTree = ""; };
26 | F6F29FBA23EDDF19000AF82B /* flipflap.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = flipflap.app; sourceTree = BUILT_PRODUCTS_DIR; };
27 | F6F29FBD23EDDF19000AF82B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
28 | F6F29FBF23EDDF19000AF82B /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
29 | F6F29FC123EDDF19000AF82B /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
30 | F6F29FC723EDDF19000AF82B /* flipflap.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = flipflap.xcdatamodel; sourceTree = ""; };
31 | F6F29FC923EDDF1C000AF82B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
32 | F6F29FCC23EDDF1C000AF82B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
33 | F6F29FCE23EDDF1C000AF82B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
34 | F6F29FD723EDF77A000AF82B /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; };
35 | /* End PBXFileReference section */
36 |
37 | /* Begin PBXFrameworksBuildPhase section */
38 | F6F29FB723EDDF19000AF82B /* Frameworks */ = {
39 | isa = PBXFrameworksBuildPhase;
40 | buildActionMask = 2147483647;
41 | files = (
42 | F6DAA11C2944F13F001F4C56 /* Splitflap in Frameworks */,
43 | F6B15A5023F48797001DB252 /* EFColorPicker in Frameworks */,
44 | );
45 | runOnlyForDeploymentPostprocessing = 0;
46 | };
47 | /* End PBXFrameworksBuildPhase section */
48 |
49 | /* Begin PBXGroup section */
50 | F653498D23EE136B004222A1 /* Frameworks */ = {
51 | isa = PBXGroup;
52 | children = (
53 | );
54 | name = Frameworks;
55 | sourceTree = "";
56 | };
57 | F6F29FB123EDDF18000AF82B = {
58 | isa = PBXGroup;
59 | children = (
60 | F6F29FBC23EDDF19000AF82B /* flipflap */,
61 | F6F29FBB23EDDF19000AF82B /* Products */,
62 | F653498D23EE136B004222A1 /* Frameworks */,
63 | );
64 | sourceTree = "";
65 | };
66 | F6F29FBB23EDDF19000AF82B /* Products */ = {
67 | isa = PBXGroup;
68 | children = (
69 | F6F29FBA23EDDF19000AF82B /* flipflap.app */,
70 | );
71 | name = Products;
72 | sourceTree = "";
73 | };
74 | F6F29FBC23EDDF19000AF82B /* flipflap */ = {
75 | isa = PBXGroup;
76 | children = (
77 | F6F29FBD23EDDF19000AF82B /* AppDelegate.swift */,
78 | F6F29FBF23EDDF19000AF82B /* SceneDelegate.swift */,
79 | F6B15A4C23F478AC001DB252 /* UIViewController.swift */,
80 | F6F29FC123EDDF19000AF82B /* ViewController.swift */,
81 | F6F29FD723EDF77A000AF82B /* SettingsViewController.swift */,
82 | F6B15A5123F4CD3D001DB252 /* FontViewController.swift */,
83 | F6F29FC923EDDF1C000AF82B /* Assets.xcassets */,
84 | F6F29FCB23EDDF1C000AF82B /* LaunchScreen.storyboard */,
85 | F6F29FCE23EDDF1C000AF82B /* Info.plist */,
86 | F6F29FC623EDDF19000AF82B /* flipflap.xcdatamodeld */,
87 | );
88 | path = flipflap;
89 | sourceTree = "";
90 | };
91 | /* End PBXGroup section */
92 |
93 | /* Begin PBXNativeTarget section */
94 | F6F29FB923EDDF19000AF82B /* flipflap */ = {
95 | isa = PBXNativeTarget;
96 | buildConfigurationList = F6F29FD123EDDF1C000AF82B /* Build configuration list for PBXNativeTarget "flipflap" */;
97 | buildPhases = (
98 | F6F29FB623EDDF19000AF82B /* Sources */,
99 | F6F29FB723EDDF19000AF82B /* Frameworks */,
100 | F6F29FB823EDDF19000AF82B /* Resources */,
101 | );
102 | buildRules = (
103 | );
104 | dependencies = (
105 | );
106 | name = flipflap;
107 | packageProductDependencies = (
108 | F6B15A4F23F48797001DB252 /* EFColorPicker */,
109 | F6DAA11B2944F13F001F4C56 /* Splitflap */,
110 | );
111 | productName = flipflap;
112 | productReference = F6F29FBA23EDDF19000AF82B /* flipflap.app */;
113 | productType = "com.apple.product-type.application";
114 | };
115 | /* End PBXNativeTarget section */
116 |
117 | /* Begin PBXProject section */
118 | F6F29FB223EDDF18000AF82B /* Project object */ = {
119 | isa = PBXProject;
120 | attributes = {
121 | LastSwiftUpdateCheck = 1130;
122 | LastUpgradeCheck = 1210;
123 | ORGANIZATIONNAME = twodayslate;
124 | TargetAttributes = {
125 | F6F29FB923EDDF19000AF82B = {
126 | CreatedOnToolsVersion = 11.3;
127 | };
128 | };
129 | };
130 | buildConfigurationList = F6F29FB523EDDF18000AF82B /* Build configuration list for PBXProject "flipflap" */;
131 | compatibilityVersion = "Xcode 9.3";
132 | developmentRegion = en;
133 | hasScannedForEncodings = 0;
134 | knownRegions = (
135 | en,
136 | Base,
137 | );
138 | mainGroup = F6F29FB123EDDF18000AF82B;
139 | packageReferences = (
140 | F6B15A4E23F48797001DB252 /* XCRemoteSwiftPackageReference "EFColorPicker" */,
141 | F6DAA11A2944F13F001F4C56 /* XCRemoteSwiftPackageReference "Splitflap" */,
142 | );
143 | productRefGroup = F6F29FBB23EDDF19000AF82B /* Products */;
144 | projectDirPath = "";
145 | projectRoot = "";
146 | targets = (
147 | F6F29FB923EDDF19000AF82B /* flipflap */,
148 | );
149 | };
150 | /* End PBXProject section */
151 |
152 | /* Begin PBXResourcesBuildPhase section */
153 | F6F29FB823EDDF19000AF82B /* Resources */ = {
154 | isa = PBXResourcesBuildPhase;
155 | buildActionMask = 2147483647;
156 | files = (
157 | F6F29FCD23EDDF1C000AF82B /* LaunchScreen.storyboard in Resources */,
158 | F6F29FCA23EDDF1C000AF82B /* Assets.xcassets in Resources */,
159 | );
160 | runOnlyForDeploymentPostprocessing = 0;
161 | };
162 | /* End PBXResourcesBuildPhase section */
163 |
164 | /* Begin PBXSourcesBuildPhase section */
165 | F6F29FB623EDDF19000AF82B /* Sources */ = {
166 | isa = PBXSourcesBuildPhase;
167 | buildActionMask = 2147483647;
168 | files = (
169 | F6F29FC223EDDF19000AF82B /* ViewController.swift in Sources */,
170 | F6B15A5223F4CD3D001DB252 /* FontViewController.swift in Sources */,
171 | F6F29FBE23EDDF19000AF82B /* AppDelegate.swift in Sources */,
172 | F6B15A4D23F478AC001DB252 /* UIViewController.swift in Sources */,
173 | F6F29FC023EDDF19000AF82B /* SceneDelegate.swift in Sources */,
174 | F6F29FD823EDF77A000AF82B /* SettingsViewController.swift in Sources */,
175 | F6F29FC823EDDF19000AF82B /* flipflap.xcdatamodeld in Sources */,
176 | );
177 | runOnlyForDeploymentPostprocessing = 0;
178 | };
179 | /* End PBXSourcesBuildPhase section */
180 |
181 | /* Begin PBXVariantGroup section */
182 | F6F29FCB23EDDF1C000AF82B /* LaunchScreen.storyboard */ = {
183 | isa = PBXVariantGroup;
184 | children = (
185 | F6F29FCC23EDDF1C000AF82B /* Base */,
186 | );
187 | name = LaunchScreen.storyboard;
188 | sourceTree = "";
189 | };
190 | /* End PBXVariantGroup section */
191 |
192 | /* Begin XCBuildConfiguration section */
193 | F6F29FCF23EDDF1C000AF82B /* Debug */ = {
194 | isa = XCBuildConfiguration;
195 | buildSettings = {
196 | ALWAYS_SEARCH_USER_PATHS = NO;
197 | CLANG_ANALYZER_NONNULL = YES;
198 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
199 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
200 | CLANG_CXX_LIBRARY = "libc++";
201 | CLANG_ENABLE_MODULES = YES;
202 | CLANG_ENABLE_OBJC_ARC = YES;
203 | CLANG_ENABLE_OBJC_WEAK = YES;
204 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
205 | CLANG_WARN_BOOL_CONVERSION = YES;
206 | CLANG_WARN_COMMA = YES;
207 | CLANG_WARN_CONSTANT_CONVERSION = YES;
208 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
209 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
210 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
211 | CLANG_WARN_EMPTY_BODY = YES;
212 | CLANG_WARN_ENUM_CONVERSION = YES;
213 | CLANG_WARN_INFINITE_RECURSION = YES;
214 | CLANG_WARN_INT_CONVERSION = YES;
215 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
216 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
217 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
218 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
219 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
220 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
221 | CLANG_WARN_STRICT_PROTOTYPES = YES;
222 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
223 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
224 | CLANG_WARN_UNREACHABLE_CODE = YES;
225 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
226 | COPY_PHASE_STRIP = NO;
227 | DEBUG_INFORMATION_FORMAT = dwarf;
228 | ENABLE_STRICT_OBJC_MSGSEND = YES;
229 | ENABLE_TESTABILITY = YES;
230 | GCC_C_LANGUAGE_STANDARD = gnu11;
231 | GCC_DYNAMIC_NO_PIC = NO;
232 | GCC_NO_COMMON_BLOCKS = YES;
233 | GCC_OPTIMIZATION_LEVEL = 0;
234 | GCC_PREPROCESSOR_DEFINITIONS = (
235 | "DEBUG=1",
236 | "$(inherited)",
237 | );
238 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
239 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
240 | GCC_WARN_UNDECLARED_SELECTOR = YES;
241 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
242 | GCC_WARN_UNUSED_FUNCTION = YES;
243 | GCC_WARN_UNUSED_VARIABLE = YES;
244 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
245 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
246 | MTL_FAST_MATH = YES;
247 | ONLY_ACTIVE_ARCH = YES;
248 | SDKROOT = iphoneos;
249 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
250 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
251 | };
252 | name = Debug;
253 | };
254 | F6F29FD023EDDF1C000AF82B /* Release */ = {
255 | isa = XCBuildConfiguration;
256 | buildSettings = {
257 | ALWAYS_SEARCH_USER_PATHS = NO;
258 | CLANG_ANALYZER_NONNULL = YES;
259 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
260 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
261 | CLANG_CXX_LIBRARY = "libc++";
262 | CLANG_ENABLE_MODULES = YES;
263 | CLANG_ENABLE_OBJC_ARC = YES;
264 | CLANG_ENABLE_OBJC_WEAK = YES;
265 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
266 | CLANG_WARN_BOOL_CONVERSION = YES;
267 | CLANG_WARN_COMMA = YES;
268 | CLANG_WARN_CONSTANT_CONVERSION = YES;
269 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
270 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
271 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
272 | CLANG_WARN_EMPTY_BODY = YES;
273 | CLANG_WARN_ENUM_CONVERSION = YES;
274 | CLANG_WARN_INFINITE_RECURSION = YES;
275 | CLANG_WARN_INT_CONVERSION = YES;
276 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
277 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
278 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
279 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
280 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
281 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
282 | CLANG_WARN_STRICT_PROTOTYPES = YES;
283 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
284 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
285 | CLANG_WARN_UNREACHABLE_CODE = YES;
286 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
287 | COPY_PHASE_STRIP = NO;
288 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
289 | ENABLE_NS_ASSERTIONS = NO;
290 | ENABLE_STRICT_OBJC_MSGSEND = YES;
291 | GCC_C_LANGUAGE_STANDARD = gnu11;
292 | GCC_NO_COMMON_BLOCKS = YES;
293 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
294 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
295 | GCC_WARN_UNDECLARED_SELECTOR = YES;
296 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
297 | GCC_WARN_UNUSED_FUNCTION = YES;
298 | GCC_WARN_UNUSED_VARIABLE = YES;
299 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
300 | MTL_ENABLE_DEBUG_INFO = NO;
301 | MTL_FAST_MATH = YES;
302 | SDKROOT = iphoneos;
303 | SWIFT_COMPILATION_MODE = wholemodule;
304 | SWIFT_OPTIMIZATION_LEVEL = "-O";
305 | VALIDATE_PRODUCT = YES;
306 | };
307 | name = Release;
308 | };
309 | F6F29FD223EDDF1C000AF82B /* Debug */ = {
310 | isa = XCBuildConfiguration;
311 | buildSettings = {
312 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
313 | CODE_SIGN_STYLE = Automatic;
314 | CURRENT_PROJECT_VERSION = 2;
315 | DEVELOPMENT_TEAM = C6L3992RFB;
316 | INFOPLIST_FILE = flipflap/Info.plist;
317 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
318 | LD_RUNPATH_SEARCH_PATHS = (
319 | "$(inherited)",
320 | "@executable_path/Frameworks",
321 | );
322 | MARKETING_VERSION = 1.1.2;
323 | PRODUCT_BUNDLE_IDENTIFIER = com.twodayslate.flipflap;
324 | PRODUCT_NAME = "$(TARGET_NAME)";
325 | SWIFT_VERSION = 5.0;
326 | TARGETED_DEVICE_FAMILY = "1,2";
327 | };
328 | name = Debug;
329 | };
330 | F6F29FD323EDDF1C000AF82B /* Release */ = {
331 | isa = XCBuildConfiguration;
332 | buildSettings = {
333 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
334 | CODE_SIGN_STYLE = Automatic;
335 | CURRENT_PROJECT_VERSION = 2;
336 | DEVELOPMENT_TEAM = C6L3992RFB;
337 | INFOPLIST_FILE = flipflap/Info.plist;
338 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
339 | LD_RUNPATH_SEARCH_PATHS = (
340 | "$(inherited)",
341 | "@executable_path/Frameworks",
342 | );
343 | MARKETING_VERSION = 1.1.2;
344 | PRODUCT_BUNDLE_IDENTIFIER = com.twodayslate.flipflap;
345 | PRODUCT_NAME = "$(TARGET_NAME)";
346 | SWIFT_VERSION = 5.0;
347 | TARGETED_DEVICE_FAMILY = "1,2";
348 | };
349 | name = Release;
350 | };
351 | /* End XCBuildConfiguration section */
352 |
353 | /* Begin XCConfigurationList section */
354 | F6F29FB523EDDF18000AF82B /* Build configuration list for PBXProject "flipflap" */ = {
355 | isa = XCConfigurationList;
356 | buildConfigurations = (
357 | F6F29FCF23EDDF1C000AF82B /* Debug */,
358 | F6F29FD023EDDF1C000AF82B /* Release */,
359 | );
360 | defaultConfigurationIsVisible = 0;
361 | defaultConfigurationName = Release;
362 | };
363 | F6F29FD123EDDF1C000AF82B /* Build configuration list for PBXNativeTarget "flipflap" */ = {
364 | isa = XCConfigurationList;
365 | buildConfigurations = (
366 | F6F29FD223EDDF1C000AF82B /* Debug */,
367 | F6F29FD323EDDF1C000AF82B /* Release */,
368 | );
369 | defaultConfigurationIsVisible = 0;
370 | defaultConfigurationName = Release;
371 | };
372 | /* End XCConfigurationList section */
373 |
374 | /* Begin XCRemoteSwiftPackageReference section */
375 | F6B15A4E23F48797001DB252 /* XCRemoteSwiftPackageReference "EFColorPicker" */ = {
376 | isa = XCRemoteSwiftPackageReference;
377 | repositoryURL = "https://github.com/EFPrefix/EFColorPicker";
378 | requirement = {
379 | kind = upToNextMajorVersion;
380 | minimumVersion = 5.2.2;
381 | };
382 | };
383 | F6DAA11A2944F13F001F4C56 /* XCRemoteSwiftPackageReference "Splitflap" */ = {
384 | isa = XCRemoteSwiftPackageReference;
385 | repositoryURL = "https://github.com/twodayslate/Splitflap";
386 | requirement = {
387 | branch = mainline;
388 | kind = branch;
389 | };
390 | };
391 | /* End XCRemoteSwiftPackageReference section */
392 |
393 | /* Begin XCSwiftPackageProductDependency section */
394 | F6B15A4F23F48797001DB252 /* EFColorPicker */ = {
395 | isa = XCSwiftPackageProductDependency;
396 | package = F6B15A4E23F48797001DB252 /* XCRemoteSwiftPackageReference "EFColorPicker" */;
397 | productName = EFColorPicker;
398 | };
399 | F6DAA11B2944F13F001F4C56 /* Splitflap */ = {
400 | isa = XCSwiftPackageProductDependency;
401 | package = F6DAA11A2944F13F001F4C56 /* XCRemoteSwiftPackageReference "Splitflap" */;
402 | productName = Splitflap;
403 | };
404 | /* End XCSwiftPackageProductDependency section */
405 |
406 | /* Begin XCVersionGroup section */
407 | F6F29FC623EDDF19000AF82B /* flipflap.xcdatamodeld */ = {
408 | isa = XCVersionGroup;
409 | children = (
410 | F6F29FC723EDDF19000AF82B /* flipflap.xcdatamodel */,
411 | );
412 | currentVersion = F6F29FC723EDDF19000AF82B /* flipflap.xcdatamodel */;
413 | path = flipflap.xcdatamodeld;
414 | sourceTree = "";
415 | versionGroupType = wrapper.xcdatamodel;
416 | };
417 | /* End XCVersionGroup section */
418 | };
419 | rootObject = F6F29FB223EDDF18000AF82B /* Project object */;
420 | }
421 |
--------------------------------------------------------------------------------
/flipflap/SettingsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsViewController.swift
3 | // flipflap
4 | //
5 | // Created by Zachary Gorak on 2/7/20.
6 | // Copyright © 2020 twodayslate. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import EFColorPicker
12 |
13 | protocol SettingsControllerDelegate {
14 | func willCloseSettings()
15 | func didCloseSettings()
16 | func didChangeKey(_ key: String)
17 | }
18 |
19 | class SettingsNavigationController: UINavigationController {
20 | var settingsDelegate: SettingsControllerDelegate? = nil {
21 | didSet {
22 | settings.settingsDelegate = self.settingsDelegate
23 | }
24 | }
25 |
26 | convenience init() {
27 | self.init(nibName: nil, bundle: nil)
28 | }
29 |
30 | let settings = SettingsViewController(style: .insetGrouped)
31 |
32 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
33 | super.init(nibName: nil, bundle: nil)
34 |
35 | self.viewControllers = [settings]
36 |
37 | self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .close, target: self, action: #selector(close(_:)))
38 | }
39 |
40 | override func viewDidLoad() {
41 | super.viewDidLoad()
42 | self.view.backgroundColor = .systemBackground
43 | }
44 |
45 | @objc func close(_ sender: Any?) {
46 | self.settingsDelegate?.willCloseSettings()
47 | self.dismiss(animated: true, completion: {
48 | self.settingsDelegate?.didCloseSettings()
49 | })
50 | }
51 |
52 | required init?(coder aDecoder: NSCoder) {
53 | fatalError("init(coder:) has not been implemented")
54 | }
55 | }
56 |
57 | class SettingsViewController: UITableViewController {
58 | var settingsDelegate: SettingsControllerDelegate? = nil
59 |
60 | override func viewDidLoad() {
61 | super.viewDidLoad()
62 | self.title = "Settings"
63 | self.view.backgroundColor = .systemBackground
64 | self.tableView.backgroundColor = .systemGroupedBackground
65 |
66 | self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .close, target: self.navigationController, action: #selector(SettingsNavigationController.close(_:)))
67 | }
68 |
69 | let themeSheet = UIAlertController(title: "Theme", message: nil, preferredStyle: .actionSheet)
70 |
71 | override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
72 | switch section {
73 | case 0:
74 | return "Appearance"
75 | default:
76 | return nil
77 | }
78 | }
79 |
80 | override func numberOfSections(in tableView: UITableView) -> Int {
81 | return 2
82 | }
83 |
84 | @objc func showSeconds(_ sender: UISwitch) {
85 | UserDefaults.standard.set(sender.isOn, forKey: "showSeconds")
86 | }
87 |
88 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
89 | var cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
90 | switch indexPath.section {
91 | case 0:
92 | cell = UITableViewCell(style: .value1, reuseIdentifier: "value1")
93 | cell.accessoryType = .disclosureIndicator
94 | switch indexPath.row {
95 | case 0:
96 | cell.textLabel?.text = "Theme"
97 |
98 | switch UserDefaults.standard.integer(forKey: "theme") {
99 | case 1:
100 | cell.detailTextLabel?.text = "Light"
101 | case 2:
102 | cell.detailTextLabel?.text = "Dark"
103 | case 3:
104 | cell.detailTextLabel?.text = "Custom"
105 | default:
106 | cell.detailTextLabel?.text = "System"
107 | }
108 | break
109 | case 1:
110 | if UserDefaults.standard.integer(forKey: "theme") == 3 {
111 | cell.textLabel?.text = "Background Color"
112 | var color = UIColor.systemBackground
113 |
114 | //colorView.backgroundColor = .red
115 | if let data = UserDefaults.standard.object(forKey: "background_color") as? Data, let dcolor = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) {
116 |
117 | color = dcolor
118 | }
119 |
120 | let size = CGSize(width: 24.0, height: 24.0)
121 | let renderer = UIGraphicsImageRenderer(size: size)
122 | let image = renderer.image(actions: { rendererContext in
123 | color.setFill()
124 | rendererContext.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height))
125 | })
126 | cell.imageView?.image = image
127 | cell.imageView?.layer.cornerRadius = 5.0
128 | cell.imageView?.layer.borderColor = UIColor.opaqueSeparator.cgColor
129 | cell.imageView?.layer.borderWidth = 1.0
130 | cell.imageView?.clipsToBounds = true
131 |
132 | } else {
133 | cell.textLabel?.text = "Font"
134 | cell.accessoryType = .disclosureIndicator
135 | if let data = UserDefaults.standard.object(forKey: "font") as? Data, let color = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [UIFont.self], from: data) as? UIFont {
136 |
137 | cell.detailTextLabel?.text = color.familyName
138 | } else {
139 | cell.detailTextLabel?.text = "Courier"
140 | }
141 | }
142 | case 2:
143 | if UserDefaults.standard.integer(forKey: "theme") == 3 {
144 | cell.textLabel?.text = "Flap Color"
145 | var color = UIColor.secondarySystemBackground
146 |
147 | //colorView.backgroundColor = .red
148 | if let data = UserDefaults.standard.object(forKey: "flap_color") as? Data, let dcolor = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) {
149 |
150 | color = dcolor
151 | }
152 |
153 | let size = CGSize(width: 24.0, height: 24.0)
154 | let renderer = UIGraphicsImageRenderer(size: size)
155 | let image = renderer.image(actions: { rendererContext in
156 | color.setFill()
157 | rendererContext.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height))
158 | })
159 | cell.imageView?.image = image
160 | cell.imageView?.layer.cornerRadius = 5.0
161 | cell.imageView?.layer.borderColor = UIColor.opaqueSeparator.cgColor
162 | cell.imageView?.layer.borderWidth = 1.0
163 | cell.imageView?.clipsToBounds = true
164 | } else {
165 | cell = UITableViewCell(style: .default, reuseIdentifier: "reset")
166 | cell.accessoryType = .none
167 | cell.textLabel?.text = "Reset to Default"
168 | cell.textLabel?.textColor = tableView.tintColor
169 | cell.textLabel?.textAlignment = .center
170 | }
171 |
172 | case 3:
173 | cell.textLabel?.text = "Text Color"
174 | var color = UIColor.label
175 |
176 | //colorView.backgroundColor = .red
177 | if let data = UserDefaults.standard.object(forKey: "text_color") as? Data, let dcolor = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) {
178 |
179 | color = dcolor
180 | }
181 |
182 | let size = CGSize(width: 24.0, height: 24.0)
183 | let renderer = UIGraphicsImageRenderer(size: size)
184 | let image = renderer.image(actions: { rendererContext in
185 | color.setFill()
186 | rendererContext.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height))
187 | })
188 | cell.imageView?.image = image
189 | cell.imageView?.layer.cornerRadius = 5.0
190 | cell.imageView?.layer.borderColor = UIColor.opaqueSeparator.cgColor
191 | cell.imageView?.layer.borderWidth = 1.0
192 | cell.imageView?.clipsToBounds = true
193 | case 4:
194 | cell.textLabel?.text = "Font"
195 | cell.accessoryType = .disclosureIndicator
196 | if let data = UserDefaults.standard.object(forKey: "font") as? Data, let color = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [UIFont.self], from: data) as? UIFont {
197 |
198 | cell.detailTextLabel?.text = color.familyName
199 | } else {
200 | cell.detailTextLabel?.text = "Courier"
201 | }
202 | case 5:
203 | cell.textLabel?.text = "Reset to Default"
204 | cell.accessoryType = .none
205 | cell.textLabel?.textColor = tableView.tintColor
206 | cell.textLabel?.textAlignment = .center
207 | default:
208 | break
209 | }
210 |
211 | break
212 | case 1:
213 | let secSwitch = UISwitch()
214 | cell.textLabel?.text = "Show seconds"
215 | secSwitch.setOn(UserDefaults.standard.bool(forKey: "showSeconds"), animated: false)
216 | secSwitch.addTarget(self, action: #selector(showSeconds(_:)), for: .valueChanged)
217 | cell.accessoryView = secSwitch
218 | cell.selectionStyle = .none
219 | default:
220 | break
221 | }
222 |
223 | return cell
224 | }
225 |
226 | var currentColorPicker: EFColorSelectionViewController? = nil
227 | var currentColorPickerKey: String? = nil
228 |
229 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
230 | switch indexPath.section {
231 | case 0:
232 | switch indexPath.row {
233 | case 0:
234 | if themeSheet.actions.count == 0 {
235 | let inappSafariAction = UIAlertAction(title: "System", style: .default, handler: { _ in
236 | print("Auto")
237 | UserDefaults.standard.set(0, forKey: "theme")
238 | UserDefaults.standard.synchronize()
239 | let cell = self.tableView.cellForRow(at: indexPath)
240 | cell?.detailTextLabel?.text = "System"
241 | self.clearCustomColors()
242 | self.tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
243 |
244 | })
245 | themeSheet.addAction(inappSafariAction)
246 | if #available(iOS 13.0, *) {
247 | let safariAction = UIAlertAction(title: "Light", style: .default, handler: { _ in
248 | print("Auto")
249 | UserDefaults.standard.set(1, forKey: "theme")
250 | UserDefaults.standard.synchronize()
251 | let cell = self.tableView.cellForRow(at: indexPath)
252 | cell?.detailTextLabel?.text = "Light"
253 | self.clearCustomColors()
254 | self.tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
255 | })
256 | themeSheet.addAction(safariAction)
257 | let darkAction = UIAlertAction(title: "Dark", style: .default, handler: { _ in
258 | print("Dark")
259 | UserDefaults.standard.set(2, forKey: "theme")
260 | UserDefaults.standard.synchronize()
261 | let cell = self.tableView.cellForRow(at: indexPath)
262 | cell?.detailTextLabel?.text = "Dark"
263 | self.clearCustomColors()
264 | self.tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
265 | })
266 | themeSheet.addAction(darkAction)
267 | let customAction = UIAlertAction(title: "Custom", style: .default, handler: { _ in
268 | UserDefaults.standard.set(3, forKey: "theme")
269 | UserDefaults.standard.synchronize()
270 | let cell = self.tableView.cellForRow(at: indexPath)
271 | cell?.detailTextLabel?.text = "Custom"
272 | self.tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
273 | })
274 | themeSheet.addAction(customAction)
275 | }
276 | themeSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
277 | }
278 |
279 | for (index, action) in themeSheet.actions.enumerated() {
280 | if UserDefaults.standard.integer(forKey: "theme") == index {
281 | action.setValue("true", forKey: "checked")
282 | } else {
283 | action.setValue("false", forKey: "checked")
284 | }
285 | }
286 | // swiftlint:disable:next line_length
287 | self.addActionSheetForiPad(actionSheet: themeSheet)
288 | present(themeSheet, animated: true) {
289 | tableView.deselectRow(at: indexPath, animated: true)
290 | }
291 | case 4:
292 | print("selected 4")
293 | let c = FontNavigationController()
294 | c.fontDelegate = self
295 | c.currentlySelectedFont = self.currentlySelectedFont
296 | self.present(c, animated: true, completion: {
297 | tableView.deselectRow(at: indexPath, animated: true)
298 | })
299 | break
300 | case 5:
301 | print("selected 5")
302 | tableView.deselectRow(at: indexPath, animated: true)
303 | self.resetToDefault()
304 | break
305 | default:
306 | if UserDefaults.standard.integer(forKey: "theme") == 3 {
307 |
308 | let colorSelectionController = EFColorSelectionViewController()
309 | colorSelectionController.isColorTextFieldHidden = false
310 | let navCtrl = UINavigationController(rootViewController: colorSelectionController)
311 | navCtrl.navigationBar.backgroundColor = UIColor.systemBackground
312 | navCtrl.navigationBar.isTranslucent = false
313 | navCtrl.modalPresentationStyle = UIModalPresentationStyle.popover
314 |
315 | navCtrl.popoverPresentationController?.sourceView = tableView.cellForRow(at: indexPath)
316 | navCtrl.popoverPresentationController?.sourceRect = tableView.cellForRow(at: indexPath)!.bounds
317 | navCtrl.preferredContentSize = colorSelectionController.view.systemLayoutSizeFitting(
318 | UIView.layoutFittingCompressedSize
319 | )
320 | if UIUserInterfaceSizeClass.compact == self.traitCollection.horizontalSizeClass {
321 |
322 | colorSelectionController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .close, target: self, action: #selector(dismissColorPicker(_:)))
323 |
324 | colorSelectionController.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Reset", style: .done, target: self, action: #selector(resetsColorPicker(_:)))
325 | }
326 |
327 | //colorSelectionController.delegate = self
328 |
329 | colorSelectionController.setMode(mode: .all)
330 | self.currentColorPicker = colorSelectionController
331 |
332 | switch indexPath.row {
333 | case 1:
334 | self.currentColorPickerKey = "background_color"
335 | if let data = UserDefaults.standard.object(forKey: "background_color") as? Data, let color = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) {
336 |
337 | colorSelectionController.color = color
338 | } else {
339 | colorSelectionController.color = .systemBackground
340 | }
341 | break
342 | case 2:
343 | self.currentColorPickerKey = "flap_color"
344 | if let data = UserDefaults.standard.object(forKey: "flap_color") as? Data, let color = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) {
345 |
346 | colorSelectionController.color = color
347 | } else {
348 | colorSelectionController.color = .secondarySystemBackground
349 | }
350 | break
351 | case 3:
352 | self.currentColorPickerKey = "text_color"
353 | if let data = UserDefaults.standard.object(forKey: "text_color") as? Data, let color = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) {
354 |
355 | colorSelectionController.color = color
356 | } else {
357 | colorSelectionController.color = .label
358 | }
359 | break
360 |
361 | default:
362 | break
363 | }
364 |
365 | present(navCtrl, animated: true) {
366 | tableView.deselectRow(at: indexPath, animated: true)
367 | }
368 | } else {
369 | switch indexPath.row {
370 | case 1:
371 | let c = FontNavigationController()
372 | c.fontDelegate = self
373 | c.currentlySelectedFont = self.currentlySelectedFont
374 | self.present(c, animated: true, completion: {
375 | tableView.deselectRow(at: indexPath, animated: true)
376 | })
377 | case 2:
378 | tableView.deselectRow(at: indexPath, animated: true)
379 | self.resetToDefault()
380 | default:
381 | break
382 | }
383 | }
384 | break
385 | }
386 | break
387 | default:
388 | break
389 | }
390 | }
391 |
392 | @objc func dismissColorPicker(_ sender: UIBarButtonItem?) {
393 | print(self.currentColorPicker?.color)
394 | guard let color = self.currentColorPicker?.color, let data = try? NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: false), let key = currentColorPickerKey else {
395 | return
396 | }
397 | UserDefaults.standard.set(data, forKey: key)
398 |
399 | self.tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
400 |
401 | self.currentColorPicker?.dismiss(animated: true, completion: nil)
402 | }
403 |
404 | func resetToDefault() {
405 | UserDefaults.standard.set(0, forKey: "theme")
406 | self.clearCustomColors()
407 | let font = UIFont(name: "Courier", size: 45.0)
408 | if let data = try? NSKeyedArchiver.archivedData(withRootObject: font, requiringSecureCoding: false) {
409 | UserDefaults.standard.set(data, forKey: "font")
410 | }
411 |
412 | self.tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
413 | }
414 |
415 | func clearCustomColors() {
416 | UserDefaults.standard.removeObject(forKey: "background_color")
417 | UserDefaults.standard.removeObject(forKey: "text_color")
418 | UserDefaults.standard.removeObject(forKey: "flap_color")
419 | self.settingsDelegate?.didChangeKey("background_color")
420 | self.settingsDelegate?.didChangeKey("text_color")
421 | self.settingsDelegate?.didChangeKey("flap_color")
422 | }
423 |
424 | @objc func resetsColorPicker(_ sender: UIBarButtonItem?) {
425 | guard let key = currentColorPickerKey else {
426 | return
427 | }
428 |
429 | var color = UIColor.systemBackground
430 | if key == "text_color" {
431 | color = .label
432 | } else if key == "flap_color" {
433 | color = .secondarySystemBackground
434 | }
435 | self.currentColorPicker?.color = color
436 |
437 | self.dismissColorPicker(sender)
438 | }
439 |
440 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
441 | switch section {
442 | case 0:
443 | if UserDefaults.standard.integer(forKey: "theme") == 3 {
444 | return 6
445 | }
446 | return 3
447 | default:
448 | return 1
449 | }
450 | }
451 | }
452 |
453 | extension SettingsViewController: FontControllerDelegate {
454 | var currentlySelectedFont: String? {
455 | if let data = UserDefaults.standard.object(forKey: "font") as? Data, let color = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [UIFont.self], from: data) as? UIFont {
456 |
457 | return color.familyName
458 | } else {
459 | return "Courier"
460 | }
461 | }
462 |
463 | func didChangeFont(name: String) {
464 | self.tableView.reloadData()
465 | }
466 |
467 |
468 | }
469 |
--------------------------------------------------------------------------------