├── LICENSE ├── Podfile ├── Podfile.lock ├── Pods ├── Alamofire │ ├── LICENSE │ ├── README.md │ └── Source │ │ ├── AFError.swift │ │ ├── Alamofire.swift │ │ ├── DispatchQueue+Alamofire.swift │ │ ├── MultipartFormData.swift │ │ ├── NetworkReachabilityManager.swift │ │ ├── Notifications.swift │ │ ├── ParameterEncoding.swift │ │ ├── Request.swift │ │ ├── Response.swift │ │ ├── ResponseSerialization.swift │ │ ├── Result.swift │ │ ├── ServerTrustPolicy.swift │ │ ├── SessionDelegate.swift │ │ ├── SessionManager.swift │ │ ├── TaskDelegate.swift │ │ ├── Timeline.swift │ │ └── Validation.swift ├── Headers │ └── Public │ │ └── Alamofire │ │ ├── Alamofire-umbrella.h │ │ └── Alamofire.modulemap ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ ├── pjhubs.xcuserdatad │ │ └── xcschemes │ │ │ ├── Alamofire.xcscheme │ │ │ ├── Pods-Unicorn.xcscheme │ │ │ └── xcschememanagement.plist │ │ └── yi.xcuserdatad │ │ └── xcschemes │ │ ├── Alamofire.xcscheme │ │ ├── Pods-Unicorn.xcscheme │ │ └── xcschememanagement.plist └── Target Support Files │ ├── Alamofire │ ├── Alamofire-dummy.m │ ├── Alamofire-prefix.pch │ ├── Alamofire-umbrella.h │ ├── Alamofire.modulemap │ └── Alamofire.xcconfig │ └── Pods-Unicorn │ ├── Pods-Unicorn-acknowledgements.markdown │ ├── Pods-Unicorn-acknowledgements.plist │ ├── Pods-Unicorn-dummy.m │ ├── Pods-Unicorn-umbrella.h │ ├── Pods-Unicorn.debug.xcconfig │ ├── Pods-Unicorn.modulemap │ └── Pods-Unicorn.release.xcconfig ├── Unicorn.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── pjhubs.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ └── Unicorn.xcscheme └── xcuserdata │ ├── pjhubs.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ └── xcschememanagement.plist │ └── yi.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── Unicorn.xcworkspace ├── contents.xcworkspacedata ├── xcshareddata │ └── IDEWorkspaceChecks.plist └── xcuserdata │ ├── pjhubs.xcuserdatad │ └── UserInterfaceState.xcuserstate │ └── yi.xcuserdatad │ ├── UserInterfaceState.xcuserstate │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist └── Unicorn ├── AppDelegate.swift ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json ├── Contents.json ├── back.imageset │ ├── Contents.json │ └── back.png ├── home-0.imageset │ ├── Contents.json │ └── home-0@2x.png ├── home-1.imageset │ ├── Contents.json │ └── home-1@2x.png ├── home-2.imageset │ ├── Contents.json │ └── home-2@2x.png ├── home-3.imageset │ ├── Contents.json │ └── home-5@2x的.png ├── home-4.imageset │ ├── Contents.json │ └── pen.png ├── profile.imageset │ ├── Contents.json │ └── profile.png ├── return.imageset │ ├── Contents.json │ └── return.png ├── story_maker_close.imageset │ ├── Contents.json │ ├── story maker_close@2x.png │ └── story maker_close@3x.png ├── 贴纸0.imageset │ ├── Contents.json │ └── img.png ├── 贴纸1.imageset │ ├── Contents.json │ └── 贴纸01.png ├── 贴纸10.imageset │ ├── Contents.json │ └── 贴纸10.png ├── 贴纸11.imageset │ ├── Contents.json │ └── 贴纸11.png ├── 贴纸12.imageset │ ├── Contents.json │ └── 贴纸12.png ├── 贴纸13.imageset │ ├── Contents.json │ └── 贴纸13.png ├── 贴纸14.imageset │ ├── Contents.json │ └── 贴纸14.png ├── 贴纸15.imageset │ ├── Contents.json │ └── 贴纸15.png ├── 贴纸16.imageset │ ├── Contents.json │ └── 贴纸16.png ├── 贴纸17.imageset │ ├── Contents.json │ └── 贴纸17.png ├── 贴纸2.imageset │ ├── Contents.json │ └── 贴纸02.png ├── 贴纸3.imageset │ ├── Contents.json │ └── 贴纸03.png ├── 贴纸4.imageset │ ├── Contents.json │ └── 贴纸04.png ├── 贴纸5.imageset │ ├── Contents.json │ └── 贴纸05.png ├── 贴纸6.imageset │ ├── Contents.json │ └── 贴纸06.png ├── 贴纸7.imageset │ ├── Contents.json │ └── 贴纸07.png ├── 贴纸8.imageset │ ├── Contents.json │ └── 贴纸08.png └── 贴纸9.imageset │ ├── Contents.json │ └── 贴纸09.png ├── Base.lproj └── LaunchScreen.storyboard ├── Component ├── .DS_Store ├── Base │ ├── BottomView │ │ ├── UNLineCollectionView.swift │ │ └── UNLineCollectionViewCell.swift │ └── UNTouchView.swift ├── Brush │ ├── Canva │ │ └── UNCanvaView.swift │ └── UNBrushView.swift ├── Network │ └── Network.swift ├── Note │ ├── Sevices │ │ └── Note.swift │ ├── UNNoteCreateViewController.swift │ └── UNNoteViewController.swift ├── Sticker │ ├── Services │ │ └── StickerService.swift │ ├── UNStickerComponentView.swift │ └── UNStickerView.swift ├── Text │ ├── .DS_Store │ ├── Component │ │ ├── Color │ │ │ ├── ColorPicker │ │ │ │ ├── EFColorComponentView.swift │ │ │ │ ├── EFColorWheelView.swift │ │ │ │ ├── EFControl.swift │ │ │ │ ├── EFHSBView.swift │ │ │ │ ├── EFSliderView.swift │ │ │ │ └── EFThumbView.swift │ │ │ └── UNBottomColorViewController.swift │ │ ├── Font │ │ │ ├── UNBottomFontsTableViewController.storyboard │ │ │ └── UNBottomFontsTableViewController.swift │ │ └── Size │ │ │ ├── UNBottomSizeViewController.swift │ │ │ └── UNBottomSizeViewController.xib │ └── ViewController │ │ └── UNTextViewController.swift ├── UNContentViewController.swift └── User │ ├── .DS_Store │ ├── Login │ ├── UNUserLoginViewController.storyboard │ └── UNUserLoginViewController.swift │ └── User.swift ├── Config └── Define.swift ├── Extension ├── UIColor.swift └── UIView.swift ├── Fonts ├── FZLuXTJW.TTF ├── FZQingFSJW_Cu.TTF ├── FZZJ-FOJW.TTF ├── FZZJ-HTKSJW.TTF ├── FZZJ-MSMLJW.TTF └── FZZJ-ZJJYBKTJW.TTF └── Info.plist /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 PJHubs 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform:'ios','12.0' 2 | target:'Unicorn' do 3 | 4 | pod 'Alamofire' 5 | 6 | end 7 | 8 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.8.1) 3 | 4 | DEPENDENCIES: 5 | - Alamofire 6 | 7 | SPEC REPOS: 8 | https://github.com/cocoapods/specs.git: 9 | - Alamofire 10 | 11 | SPEC CHECKSUMS: 12 | Alamofire: 16ce2c353fb72865124ddae8a57c5942388f4f11 13 | 14 | PODFILE CHECKSUM: b6dcc0f4318b87af1009c05073e28e014256fab2 15 | 16 | COCOAPODS: 1.7.1 17 | -------------------------------------------------------------------------------- /Pods/Alamofire/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/DispatchQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Dispatch 26 | import Foundation 27 | 28 | extension DispatchQueue { 29 | static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) } 30 | static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) } 31 | static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) } 32 | static var background: DispatchQueue { return DispatchQueue.global(qos: .background) } 33 | 34 | func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { 35 | asyncAfter(deadline: .now() + delay, execute: closure) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/NetworkReachabilityManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkReachabilityManager.swift 3 | // 4 | // Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #if !os(watchOS) 26 | 27 | import Foundation 28 | import SystemConfiguration 29 | 30 | /// The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both WWAN and 31 | /// WiFi network interfaces. 32 | /// 33 | /// Reachability can be used to determine background information about why a network operation failed, or to retry 34 | /// network requests when a connection is established. It should not be used to prevent a user from initiating a network 35 | /// request, as it's possible that an initial request may be required to establish reachability. 36 | open class NetworkReachabilityManager { 37 | /// Defines the various states of network reachability. 38 | /// 39 | /// - unknown: It is unknown whether the network is reachable. 40 | /// - notReachable: The network is not reachable. 41 | /// - reachable: The network is reachable. 42 | public enum NetworkReachabilityStatus { 43 | case unknown 44 | case notReachable 45 | case reachable(ConnectionType) 46 | } 47 | 48 | /// Defines the various connection types detected by reachability flags. 49 | /// 50 | /// - ethernetOrWiFi: The connection type is either over Ethernet or WiFi. 51 | /// - wwan: The connection type is a WWAN connection. 52 | public enum ConnectionType { 53 | case ethernetOrWiFi 54 | case wwan 55 | } 56 | 57 | /// A closure executed when the network reachability status changes. The closure takes a single argument: the 58 | /// network reachability status. 59 | public typealias Listener = (NetworkReachabilityStatus) -> Void 60 | 61 | // MARK: - Properties 62 | 63 | /// Whether the network is currently reachable. 64 | open var isReachable: Bool { return isReachableOnWWAN || isReachableOnEthernetOrWiFi } 65 | 66 | /// Whether the network is currently reachable over the WWAN interface. 67 | open var isReachableOnWWAN: Bool { return networkReachabilityStatus == .reachable(.wwan) } 68 | 69 | /// Whether the network is currently reachable over Ethernet or WiFi interface. 70 | open var isReachableOnEthernetOrWiFi: Bool { return networkReachabilityStatus == .reachable(.ethernetOrWiFi) } 71 | 72 | /// The current network reachability status. 73 | open var networkReachabilityStatus: NetworkReachabilityStatus { 74 | guard let flags = self.flags else { return .unknown } 75 | return networkReachabilityStatusForFlags(flags) 76 | } 77 | 78 | /// The dispatch queue to execute the `listener` closure on. 79 | open var listenerQueue: DispatchQueue = DispatchQueue.main 80 | 81 | /// A closure executed when the network reachability status changes. 82 | open var listener: Listener? 83 | 84 | open var flags: SCNetworkReachabilityFlags? { 85 | var flags = SCNetworkReachabilityFlags() 86 | 87 | if SCNetworkReachabilityGetFlags(reachability, &flags) { 88 | return flags 89 | } 90 | 91 | return nil 92 | } 93 | 94 | private let reachability: SCNetworkReachability 95 | open var previousFlags: SCNetworkReachabilityFlags 96 | 97 | // MARK: - Initialization 98 | 99 | /// Creates a `NetworkReachabilityManager` instance with the specified host. 100 | /// 101 | /// - parameter host: The host used to evaluate network reachability. 102 | /// 103 | /// - returns: The new `NetworkReachabilityManager` instance. 104 | public convenience init?(host: String) { 105 | guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil } 106 | self.init(reachability: reachability) 107 | } 108 | 109 | /// Creates a `NetworkReachabilityManager` instance that monitors the address 0.0.0.0. 110 | /// 111 | /// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing 112 | /// status of the device, both IPv4 and IPv6. 113 | /// 114 | /// - returns: The new `NetworkReachabilityManager` instance. 115 | public convenience init?() { 116 | var address = sockaddr_in() 117 | address.sin_len = UInt8(MemoryLayout.size) 118 | address.sin_family = sa_family_t(AF_INET) 119 | 120 | guard let reachability = withUnsafePointer(to: &address, { pointer in 121 | return pointer.withMemoryRebound(to: sockaddr.self, capacity: MemoryLayout.size) { 122 | return SCNetworkReachabilityCreateWithAddress(nil, $0) 123 | } 124 | }) else { return nil } 125 | 126 | self.init(reachability: reachability) 127 | } 128 | 129 | private init(reachability: SCNetworkReachability) { 130 | self.reachability = reachability 131 | 132 | // Set the previous flags to an unreserved value to represent unknown status 133 | self.previousFlags = SCNetworkReachabilityFlags(rawValue: 1 << 30) 134 | } 135 | 136 | deinit { 137 | stopListening() 138 | } 139 | 140 | // MARK: - Listening 141 | 142 | /// Starts listening for changes in network reachability status. 143 | /// 144 | /// - returns: `true` if listening was started successfully, `false` otherwise. 145 | @discardableResult 146 | open func startListening() -> Bool { 147 | var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) 148 | context.info = Unmanaged.passUnretained(self).toOpaque() 149 | 150 | let callbackEnabled = SCNetworkReachabilitySetCallback( 151 | reachability, 152 | { (_, flags, info) in 153 | let reachability = Unmanaged.fromOpaque(info!).takeUnretainedValue() 154 | reachability.notifyListener(flags) 155 | }, 156 | &context 157 | ) 158 | 159 | let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue) 160 | 161 | listenerQueue.async { 162 | guard let flags = self.flags else { return } 163 | self.notifyListener(flags) 164 | } 165 | 166 | return callbackEnabled && queueEnabled 167 | } 168 | 169 | /// Stops listening for changes in network reachability status. 170 | open func stopListening() { 171 | SCNetworkReachabilitySetCallback(reachability, nil, nil) 172 | SCNetworkReachabilitySetDispatchQueue(reachability, nil) 173 | } 174 | 175 | // MARK: - Internal - Listener Notification 176 | 177 | func notifyListener(_ flags: SCNetworkReachabilityFlags) { 178 | guard previousFlags != flags else { return } 179 | previousFlags = flags 180 | 181 | listener?(networkReachabilityStatusForFlags(flags)) 182 | } 183 | 184 | // MARK: - Internal - Network Reachability Status 185 | 186 | func networkReachabilityStatusForFlags(_ flags: SCNetworkReachabilityFlags) -> NetworkReachabilityStatus { 187 | guard isNetworkReachable(with: flags) else { return .notReachable } 188 | 189 | var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi) 190 | 191 | #if os(iOS) 192 | if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) } 193 | #endif 194 | 195 | return networkStatus 196 | } 197 | 198 | func isNetworkReachable(with flags: SCNetworkReachabilityFlags) -> Bool { 199 | let isReachable = flags.contains(.reachable) 200 | let needsConnection = flags.contains(.connectionRequired) 201 | let canConnectAutomatically = flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic) 202 | let canConnectWithoutUserInteraction = canConnectAutomatically && !flags.contains(.interventionRequired) 203 | 204 | return isReachable && (!needsConnection || canConnectWithoutUserInteraction) 205 | } 206 | } 207 | 208 | // MARK: - 209 | 210 | extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {} 211 | 212 | /// Returns whether the two network reachability status values are equal. 213 | /// 214 | /// - parameter lhs: The left-hand side value to compare. 215 | /// - parameter rhs: The right-hand side value to compare. 216 | /// 217 | /// - returns: `true` if the two values are equal, `false` otherwise. 218 | public func ==( 219 | lhs: NetworkReachabilityManager.NetworkReachabilityStatus, 220 | rhs: NetworkReachabilityManager.NetworkReachabilityStatus) 221 | -> Bool 222 | { 223 | switch (lhs, rhs) { 224 | case (.unknown, .unknown): 225 | return true 226 | case (.notReachable, .notReachable): 227 | return true 228 | case let (.reachable(lhsConnectionType), .reachable(rhsConnectionType)): 229 | return lhsConnectionType == rhsConnectionType 230 | default: 231 | return false 232 | } 233 | } 234 | 235 | #endif 236 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Notifications.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Notifications.swift 3 | // 4 | // Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension Notification.Name { 28 | /// Used as a namespace for all `URLSessionTask` related notifications. 29 | public struct Task { 30 | /// Posted when a `URLSessionTask` is resumed. The notification `object` contains the resumed `URLSessionTask`. 31 | public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume") 32 | 33 | /// Posted when a `URLSessionTask` is suspended. The notification `object` contains the suspended `URLSessionTask`. 34 | public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend") 35 | 36 | /// Posted when a `URLSessionTask` is cancelled. The notification `object` contains the cancelled `URLSessionTask`. 37 | public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel") 38 | 39 | /// Posted when a `URLSessionTask` is completed. The notification `object` contains the completed `URLSessionTask`. 40 | public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete") 41 | } 42 | } 43 | 44 | // MARK: - 45 | 46 | extension Notification { 47 | /// Used as a namespace for all `Notification` user info dictionary keys. 48 | public struct Key { 49 | /// User info dictionary key representing the `URLSessionTask` associated with the notification. 50 | public static let Task = "org.alamofire.notification.key.task" 51 | 52 | /// User info dictionary key representing the responseData associated with the notification. 53 | public static let ResponseData = "org.alamofire.notification.key.responseData" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Timeline.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Timeline.swift 3 | // 4 | // Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Responsible for computing the timing metrics for the complete lifecycle of a `Request`. 28 | public struct Timeline { 29 | /// The time the request was initialized. 30 | public let requestStartTime: CFAbsoluteTime 31 | 32 | /// The time the first bytes were received from or sent to the server. 33 | public let initialResponseTime: CFAbsoluteTime 34 | 35 | /// The time when the request was completed. 36 | public let requestCompletedTime: CFAbsoluteTime 37 | 38 | /// The time when the response serialization was completed. 39 | public let serializationCompletedTime: CFAbsoluteTime 40 | 41 | /// The time interval in seconds from the time the request started to the initial response from the server. 42 | public let latency: TimeInterval 43 | 44 | /// The time interval in seconds from the time the request started to the time the request completed. 45 | public let requestDuration: TimeInterval 46 | 47 | /// The time interval in seconds from the time the request completed to the time response serialization completed. 48 | public let serializationDuration: TimeInterval 49 | 50 | /// The time interval in seconds from the time the request started to the time response serialization completed. 51 | public let totalDuration: TimeInterval 52 | 53 | /// Creates a new `Timeline` instance with the specified request times. 54 | /// 55 | /// - parameter requestStartTime: The time the request was initialized. Defaults to `0.0`. 56 | /// - parameter initialResponseTime: The time the first bytes were received from or sent to the server. 57 | /// Defaults to `0.0`. 58 | /// - parameter requestCompletedTime: The time when the request was completed. Defaults to `0.0`. 59 | /// - parameter serializationCompletedTime: The time when the response serialization was completed. Defaults 60 | /// to `0.0`. 61 | /// 62 | /// - returns: The new `Timeline` instance. 63 | public init( 64 | requestStartTime: CFAbsoluteTime = 0.0, 65 | initialResponseTime: CFAbsoluteTime = 0.0, 66 | requestCompletedTime: CFAbsoluteTime = 0.0, 67 | serializationCompletedTime: CFAbsoluteTime = 0.0) 68 | { 69 | self.requestStartTime = requestStartTime 70 | self.initialResponseTime = initialResponseTime 71 | self.requestCompletedTime = requestCompletedTime 72 | self.serializationCompletedTime = serializationCompletedTime 73 | 74 | self.latency = initialResponseTime - requestStartTime 75 | self.requestDuration = requestCompletedTime - requestStartTime 76 | self.serializationDuration = serializationCompletedTime - requestCompletedTime 77 | self.totalDuration = serializationCompletedTime - requestStartTime 78 | } 79 | } 80 | 81 | // MARK: - CustomStringConvertible 82 | 83 | extension Timeline: CustomStringConvertible { 84 | /// The textual representation used when written to an output stream, which includes the latency, the request 85 | /// duration and the total duration. 86 | public var description: String { 87 | let latency = String(format: "%.3f", self.latency) 88 | let requestDuration = String(format: "%.3f", self.requestDuration) 89 | let serializationDuration = String(format: "%.3f", self.serializationDuration) 90 | let totalDuration = String(format: "%.3f", self.totalDuration) 91 | 92 | // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is 93 | // fixed, we should move back to string interpolation by reverting commit 7d4a43b1. 94 | let timings = [ 95 | "\"Latency\": " + latency + " secs", 96 | "\"Request Duration\": " + requestDuration + " secs", 97 | "\"Serialization Duration\": " + serializationDuration + " secs", 98 | "\"Total Duration\": " + totalDuration + " secs" 99 | ] 100 | 101 | return "Timeline: { " + timings.joined(separator: ", ") + " }" 102 | } 103 | } 104 | 105 | // MARK: - CustomDebugStringConvertible 106 | 107 | extension Timeline: CustomDebugStringConvertible { 108 | /// The textual representation used when written to an output stream, which includes the request start time, the 109 | /// initial response time, the request completed time, the serialization completed time, the latency, the request 110 | /// duration and the total duration. 111 | public var debugDescription: String { 112 | let requestStartTime = String(format: "%.3f", self.requestStartTime) 113 | let initialResponseTime = String(format: "%.3f", self.initialResponseTime) 114 | let requestCompletedTime = String(format: "%.3f", self.requestCompletedTime) 115 | let serializationCompletedTime = String(format: "%.3f", self.serializationCompletedTime) 116 | let latency = String(format: "%.3f", self.latency) 117 | let requestDuration = String(format: "%.3f", self.requestDuration) 118 | let serializationDuration = String(format: "%.3f", self.serializationDuration) 119 | let totalDuration = String(format: "%.3f", self.totalDuration) 120 | 121 | // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is 122 | // fixed, we should move back to string interpolation by reverting commit 7d4a43b1. 123 | let timings = [ 124 | "\"Request Start Time\": " + requestStartTime, 125 | "\"Initial Response Time\": " + initialResponseTime, 126 | "\"Request Completed Time\": " + requestCompletedTime, 127 | "\"Serialization Completed Time\": " + serializationCompletedTime, 128 | "\"Latency\": " + latency + " secs", 129 | "\"Request Duration\": " + requestDuration + " secs", 130 | "\"Serialization Duration\": " + serializationDuration + " secs", 131 | "\"Total Duration\": " + totalDuration + " secs" 132 | ] 133 | 134 | return "Timeline: { " + timings.joined(separator: ", ") + " }" 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Pods/Headers/Public/Alamofire/Alamofire-umbrella.h: -------------------------------------------------------------------------------- 1 | ../../../Target Support Files/Alamofire/Alamofire-umbrella.h -------------------------------------------------------------------------------- /Pods/Headers/Public/Alamofire/Alamofire.modulemap: -------------------------------------------------------------------------------- 1 | ../../../Target Support Files/Alamofire/Alamofire.modulemap -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.8.1) 3 | 4 | DEPENDENCIES: 5 | - Alamofire 6 | 7 | SPEC REPOS: 8 | https://github.com/cocoapods/specs.git: 9 | - Alamofire 10 | 11 | SPEC CHECKSUMS: 12 | Alamofire: 16ce2c353fb72865124ddae8a57c5942388f4f11 13 | 14 | PODFILE CHECKSUM: b6dcc0f4318b87af1009c05073e28e014256fab2 15 | 16 | COCOAPODS: 1.7.1 17 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcschemes/Alamofire.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcschemes/Pods-Unicorn.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Alamofire.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Pods-Unicorn.xcscheme 13 | 14 | isShown 15 | 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/yi.xcuserdatad/xcschemes/Alamofire.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/yi.xcuserdatad/xcschemes/Pods-Unicorn.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/yi.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Alamofire.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 0 13 | 14 | Pods-Unicorn.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 1 20 | 21 | 22 | SuppressBuildableAutocreation 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Alamofire : NSObject 3 | @end 4 | @implementation PodsDummy_Alamofire 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double AlamofireVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.modulemap: -------------------------------------------------------------------------------- 1 | module Alamofire { 2 | umbrella header "Alamofire-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -import-underlying-module -Xcc -fmodule-map-file="${SRCROOT}/${MODULEMAP_FILE}" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Unicorn/Pods-Unicorn-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Alamofire 5 | 6 | Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - https://cocoapods.org 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Unicorn/Pods-Unicorn-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | Alamofire 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Generated by CocoaPods - https://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Unicorn/Pods-Unicorn-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Unicorn : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Unicorn 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Unicorn/Pods-Unicorn-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_UnicornVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_UnicornVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Unicorn/Pods-Unicorn.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" 4 | OTHER_CFLAGS = $(inherited) -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.modulemap" 5 | OTHER_LDFLAGS = $(inherited) -ObjC -l"Alamofire" 6 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.modulemap" 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | SWIFT_INCLUDE_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Unicorn/Pods-Unicorn.modulemap: -------------------------------------------------------------------------------- 1 | module Pods_Unicorn { 2 | umbrella header "Pods-Unicorn-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Unicorn/Pods-Unicorn.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" 4 | OTHER_CFLAGS = $(inherited) -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.modulemap" 5 | OTHER_LDFLAGS = $(inherited) -ObjC -l"Alamofire" 6 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.modulemap" 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | SWIFT_INCLUDE_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" 12 | -------------------------------------------------------------------------------- /Unicorn.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Unicorn.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Unicorn.xcodeproj/project.xcworkspace/xcuserdata/pjhubs.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn.xcodeproj/project.xcworkspace/xcuserdata/pjhubs.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Unicorn.xcodeproj/xcshareddata/xcschemes/Unicorn.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Unicorn.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Unicorn.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Unicorn.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 2 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Unicorn.xcodeproj/xcuserdata/yi.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Unicorn.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 2 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 0CDB646E2247CDB600ED48D0 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Unicorn.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Unicorn.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Unicorn.xcworkspace/xcuserdata/pjhubs.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn.xcworkspace/xcuserdata/pjhubs.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Unicorn.xcworkspace/xcuserdata/yi.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn.xcworkspace/xcuserdata/yi.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Unicorn.xcworkspace/xcuserdata/yi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Unicorn/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/3/24. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | // 规定主类必须要遵从协议 `UIApplicationDelegate`,该 `UIApplicationDelegate` 依赖 `UIResponder`。 14 | // UIApplicationDelegate 协议用来管理应用状态。 UIResponder 协议用来响应用户点击(UI事件)。 15 | 16 | var window: UIWindow? 17 | 18 | // 加载完成 伴随选项, 19 | func application(_ application: UIApplication, 20 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 21 | 22 | // 对象 实例化 手帐本-首页 23 | let home = UNNoteViewController() 24 | // Navigation Controller导航视图控制器(用来控制页面,栈的形式,先进后出) 25 | let nav = UINavigationController(rootViewController: home) 26 | 27 | // 创建UIWindow,设置界限属性 主屏幕宽高 坐标 28 | window = UIWindow(frame: UIScreen.main.bounds) 29 | // 设置根视图控制器 30 | window?.rootViewController = nav 31 | // 成为 keyWindow 并显示 32 | window?.makeKeyAndVisible() 33 | return true 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "back.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/back.imageset/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/back.imageset/back.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/home-0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home-0@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/home-0.imageset/home-0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/home-0.imageset/home-0@2x.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/home-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home-1@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/home-1.imageset/home-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/home-1.imageset/home-1@2x.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/home-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home-2@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/home-2.imageset/home-2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/home-2.imageset/home-2@2x.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/home-3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home-5@2x的.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/home-3.imageset/home-5@2x的.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/home-3.imageset/home-5@2x的.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/home-4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pen.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/home-4.imageset/pen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/home-4.imageset/pen.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/profile.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "profile.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/profile.imageset/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/profile.imageset/profile.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/return.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "return.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/return.imageset/return.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/return.imageset/return.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/story_maker_close.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "story maker_close@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "story maker_close@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/story_maker_close.imageset/story maker_close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/story_maker_close.imageset/story maker_close@2x.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/story_maker_close.imageset/story maker_close@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/story_maker_close.imageset/story maker_close@3x.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸0.imageset/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸0.imageset/img.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸01.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸1.imageset/贴纸01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸1.imageset/贴纸01.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸10.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸10.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸10.imageset/贴纸10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸10.imageset/贴纸10.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸11.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸11.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸11.imageset/贴纸11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸11.imageset/贴纸11.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸12.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸12.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸12.imageset/贴纸12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸12.imageset/贴纸12.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸13.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸13.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸13.imageset/贴纸13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸13.imageset/贴纸13.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸14.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸14.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸14.imageset/贴纸14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸14.imageset/贴纸14.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸15.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸15.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸15.imageset/贴纸15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸15.imageset/贴纸15.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸16.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸16.imageset/贴纸16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸16.imageset/贴纸16.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸17.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸17.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸17.imageset/贴纸17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸17.imageset/贴纸17.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸02.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸2.imageset/贴纸02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸2.imageset/贴纸02.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸03.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸3.imageset/贴纸03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸3.imageset/贴纸03.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸04.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸4.imageset/贴纸04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸4.imageset/贴纸04.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸05.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸5.imageset/贴纸05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸5.imageset/贴纸05.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸06.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸6.imageset/贴纸06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸6.imageset/贴纸06.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸07.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸7.imageset/贴纸07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸7.imageset/贴纸07.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸8.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸08.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸8.imageset/贴纸08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸8.imageset/贴纸08.png -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸9.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "贴纸09.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Unicorn/Assets.xcassets/贴纸9.imageset/贴纸09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Assets.xcassets/贴纸9.imageset/贴纸09.png -------------------------------------------------------------------------------- /Unicorn/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 | -------------------------------------------------------------------------------- /Unicorn/Component/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Component/.DS_Store -------------------------------------------------------------------------------- /Unicorn/Component/Base/BottomView/UNLineCollectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UNShowContentCollectionView.swift 3 | // WWDC19 4 | // 5 | // Created by PJHubs on 2019/3/16. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // 线性 集合视图:底部选择器(下栏按钮)类 12 | class UNLineCollectionView: UICollectionView { 13 | // cell 标识符 14 | private let cellIdentifier = "UNLineCollectionViewCell" 15 | // 字符串数组。reloadData刷新数据 16 | var viewModels: [String]? {didSet{ reloadData()}} 17 | // 颜色数组 (reloadData 给数据的时候 刷新数据) 18 | var viewColorModels: [UIColor]? {didSet{ reloadData()}} 19 | // 每一个cell中心的x 20 | var cellCenterXs = [CGFloat]() 21 | // 不赋值的话 默认为text 22 | var lineType: LineType = .text 23 | // 资源文件Assets里的 图标的名称 24 | var iconTitle = "home-" 25 | var iconCount = 5 26 | // 闭包-反向传值 27 | var cellSelected: ((Int) -> Void)? 28 | // 长按闭包 29 | var longCellSelected: ((Int) -> Void)? 30 | 31 | // 重载 初始化方法 32 | override init(frame: CGRect, 33 | collectionViewLayout layout: UICollectionViewLayout) { 34 | super.init(frame: frame, 35 | collectionViewLayout: layout) 36 | // 初始化视图 37 | initView() 38 | } 39 | 40 | // 初始化。aDecoder解码器。(与上述一体) 41 | required init?(coder aDecoder: NSCoder) { 42 | super.init(coder: aDecoder) 43 | } 44 | 45 | private func initView() { 46 | // 展示水平滚动器 47 | showsHorizontalScrollIndicator = false 48 | // 是否分页功能(否的话,滑动就停止了,没有分页的流畅效果) 49 | isPagingEnabled = true 50 | 51 | // 用这个类:PJLineCollectionView,来处理这个视图: UICollectionView 52 | delegate = self 53 | // 自己处理自己的数据源 54 | dataSource = self 55 | 56 | // 要将cell注册进视图里,给cell标识符 57 | register(UNLineCollectionViewCell.self, 58 | forCellWithReuseIdentifier: "UNLineCollectionViewCell") 59 | } 60 | } 61 | 62 | // Delegate 代理操作 63 | extension UNLineCollectionView: UICollectionViewDelegate { 64 | // didSelectItemAt 选中一个Item (点击按钮的响应事件) 65 | func collectionView(_ collectionView: UICollectionView, 66 | didSelectItemAt indexPath: IndexPath) { 67 | // 闭包反向传值(传给父视图) 68 | cellSelected?(indexPath.row) 69 | } 70 | } 71 | 72 | // DataSource 数据源 73 | extension UNLineCollectionView: UICollectionViewDataSource { 74 | // 设置组内有几个内容 numberOfItemsInSection。section 一条为一个组 75 | func collectionView(_ collectionView: UICollectionView, 76 | numberOfItemsInSection section: Int) -> Int { 77 | // 判断一下类型,因为选颜色和下栏按钮的形式不一样。 78 | // 所以分为四类。返回的数据源的个数不一样。 79 | switch lineType { 80 | case .text: 81 | // let常量。判空。 82 | guard let viewModels = viewModels else { return 0 } 83 | // 返回数据源个数 84 | return viewModels.count 85 | case .color: 86 | guard let viewColorModels = viewColorModels else { return 0 } 87 | return viewColorModels.count 88 | case .icon: 89 | return iconCount 90 | case .cover: 91 | guard let viewColorModels = viewColorModels else { return 0 } 92 | return viewColorModels.count 93 | } 94 | 95 | } 96 | 97 | // 每一个cell里的Item。定义内容。 98 | func collectionView(_ collectionView: UICollectionView, 99 | cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 100 | // 取队列可重用的cell。 101 | // withReuseIdentifier 伴随着重用标识符。 强转类型:as! PJLineCollectionViewCell。 102 | // 索引路径 indexPath 103 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "UNLineCollectionViewCell", for: indexPath) as! UNLineCollectionViewCell 104 | // 区别类型。 105 | cell.type = lineType 106 | 107 | switch lineType { 108 | case .text: 109 | // 给viewModel 赋值数据源。(viewModels:字符串数组-按钮名称) 110 | cell.viewModel = viewModels![indexPath.row] 111 | case .color: 112 | // 给viewColorModel 赋值数据源。(viewColorModels:UIColor数组) 113 | cell.viewColorModel = viewColorModels![indexPath.row] 114 | case .icon: 115 | cell.image = UIImage(named: iconTitle + "\(indexPath.row)") 116 | case .cover: //封面 117 | // indexPath 索引路径 118 | cell.viewColorModel = viewColorModels![indexPath.row] 119 | cell.viewModel = viewModels![indexPath.row] 120 | cell.layer.shadowColor = UIColor.black.cgColor 121 | // 阴影偏移 122 | cell.layer.shadowOffset = CGSize(width: 0, height: 0) 123 | // 阴影透明度 124 | cell.layer.shadowOpacity = 0.4 125 | } 126 | // 把cell的中心值传出,用于设置点击后的气泡位置 127 | cellCenterXs.append(cell.center.x) 128 | 129 | return cell 130 | } 131 | } 132 | 133 | // 枚举 134 | extension UNLineCollectionView { 135 | enum LineType { 136 | case text 137 | case color 138 | case icon 139 | case cover 140 | } 141 | } 142 | 143 | -------------------------------------------------------------------------------- /Unicorn/Component/Base/BottomView/UNLineCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UNLineCollectionViewCell.swift 3 | // WWDC19 4 | // 5 | // Created by PJHubs on 2019/3/16. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class UNLineCollectionViewCell: UICollectionViewCell { 12 | // 字符串类型的viewModel 13 | var viewModel = "" { 14 | // 已经赋值 15 | didSet { 16 | setViewModel() 17 | } 18 | } 19 | // 把颜色设置为背景颜色 20 | var viewColorModel: UIColor? { 21 | didSet { 22 | backgroundColor = viewColorModel 23 | } 24 | } 25 | // 把图片设置为图标 26 | var image: UIImage? { didSet{iconImageView?.image = image }} 27 | // 设置类型 28 | var type: UNLineCollectionView.LineType = .text {didSet{setType()}} 29 | 30 | // tipsLabel:cell里的文字 31 | private var tipsLabel: UILabel? 32 | // iconImageView:cell里的view 33 | private var iconImageView: UIImageView? 34 | 35 | // CGRect类 有 xy宽高属性 36 | override init(frame: CGRect) { 37 | super.init(frame: frame) 38 | initView() 39 | } 40 | 41 | // 初始化编码器 42 | required init?(coder aDecoder: NSCoder) { 43 | super.init(coder: aDecoder) 44 | } 45 | 46 | private func initView() { 47 | // tipsLabel:cell里的文字 48 | let tipLabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: height)) 49 | tipLabel.text = viewModel 50 | // 文本对齐方式 51 | tipLabel.textAlignment = .center 52 | tipLabel.textColor = .white 53 | self.tipsLabel = tipLabel 54 | // 添加子视图 55 | addSubview(tipLabel) 56 | 57 | // iconImageView:cell里的view 58 | let iconImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height)) 59 | addSubview(iconImageView) 60 | self.iconImageView = iconImageView 61 | // 内容模式 = Fit 匹配(按比例合适) 62 | iconImageView.contentMode = .scaleAspectFit 63 | } 64 | 65 | private func setViewModel() { 66 | tipsLabel!.text = viewModel 67 | } 68 | 69 | private func setType() { 70 | switch type { 71 | 72 | // 字(字体部分下方栏) 73 | case .text: 74 | // 文本 75 | tipsLabel?.isHidden = false 76 | // View 77 | iconImageView?.isHidden = true 78 | 79 | // 颜色 80 | case .color: 81 | tipsLabel?.isHidden = true 82 | iconImageView?.isHidden = true 83 | backgroundColor = viewColorModel 84 | // 圆角 85 | layer.cornerRadius = width / 2 86 | 87 | // 图标 88 | case .icon: 89 | iconImageView?.isHidden = false 90 | // scale 缩放,将图标缩放0.7倍 91 | iconImageView?.transform = CGAffineTransform(scaleX: 0.7, y: 0.7) 92 | 93 | // 封面 94 | case .cover: 95 | iconImageView?.isHidden = true 96 | tipsLabel?.isHidden = false 97 | tipsLabel?.height = 20 98 | tipsLabel?.left = 5 99 | // width = width - 5 100 | tipsLabel?.width -= 5 101 | // 手帐本高度-字高度-10(距离手帐本底部10) 102 | tipsLabel?.top = height - tipsLabel!.height - 10 103 | tipsLabel?.textAlignment = .left 104 | // 封面底色 105 | backgroundColor = viewColorModel 106 | layer.cornerRadius = 10 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Unicorn/Component/Base/UNTouchView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UNTouchView.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/3/25. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | // 贴纸基类 共有的 方法 属性 10 | import UIKit 11 | 12 | class UNTouchView: UIView { 13 | private var previousRotation: CGFloat = 0 14 | 15 | // 是否被选中 16 | var isSelected = false { 17 | didSet { 18 | // 选中时,关闭按钮 不隐藏。未选中时,隐藏 19 | if isSelected { 20 | closeButton.isHidden = false 21 | } else { 22 | closeButton.isHidden = true 23 | } 24 | } 25 | } 26 | // 代理 27 | var viewDelegate: UNTouchViewProtocol? 28 | 29 | private var closeButton = UIButton() 30 | 31 | // 初始化方法 32 | override init(frame: CGRect) { 33 | // 初始化父类 34 | super.init(frame: frame) 35 | // 初始化本类 36 | initView() 37 | } 38 | 39 | 40 | override func layoutSubviews() { 41 | initLayout() 42 | } 43 | 44 | // (必要的)与上面的是一体的 45 | required init?(coder aDecoder: NSCoder) { 46 | fatalError("init(coder:) has not been implemented") 47 | } 48 | 49 | // 私有 方法 初始化 50 | private func initView() { 51 | // 是否允许用户交互 Interaction交互 52 | isUserInteractionEnabled = true 53 | 54 | // 设置在 normal 情况下的背景图片 55 | 56 | // 关闭按钮(UIImage的名称) 57 | closeButton.setImage(UIImage(named: "story_maker_close"), for: .normal) 58 | closeButton.backgroundColor = .black 59 | // 圆角半径 60 | closeButton.layer.cornerRadius = 12 61 | // 裁剪(切割超出圆角范围的子视图) 62 | closeButton.layer.masksToBounds = true 63 | // 不点击时 按钮隐藏 64 | closeButton.isHidden = true 65 | // 添加触摸事件(按钮目标事件)(事件是给谁代理的-本类), 两者省略selector(用来选择方法的选择器),状态(按钮按下后才可执行)) 66 | closeButton.addTarget(self, action: .close, for: .touchUpInside) 67 | 68 | // pinchGesture 缩放手势(受理-当前类,调用方法) 69 | let pinchGesture = UIPinchGestureRecognizer(target: self, action: .pinch) 70 | pinchGesture.delegate = self 71 | // 给当前UNSticerView类 ,添加手势识别器 72 | addGestureRecognizer(pinchGesture) 73 | 74 | // rotateGesture 旋转手势 75 | let rotateGesture = UIRotationGestureRecognizer(target: self, action: .rotate) 76 | rotateGesture.delegate = self 77 | // 给当前UNSticerView类 ,添加手势识别器 78 | addGestureRecognizer(rotateGesture) 79 | 80 | // panGesture 拖拽/平移手势 81 | let panGesture = UIPanGestureRecognizer(target: self, action: .pan) 82 | panGesture.delegate = self 83 | // 给当前UNSticerView类 ,添加手势识别器 84 | addGestureRecognizer(panGesture) 85 | 86 | // TapGesture 单击/点击手势 87 | let tapGesture = UITapGestureRecognizer(target: self, action: .doubleTap) 88 | // 双击,设置所需的点击次数 89 | tapGesture.numberOfTapsRequired = 2 90 | // 给当前UNSticerView类 ,添加手势识别器 91 | addGestureRecognizer(tapGesture) 92 | } 93 | 94 | 95 | // 添加进视图(关闭按钮要在贴纸左上角) 96 | private func initLayout() { 97 | closeButton.frame = CGRect(x: 0, y: 0, width: 24, height: 24) 98 | // 位置-在零点 99 | closeButton.center = .zero 100 | addSubview(closeButton) 101 | } 102 | 103 | // 关闭按钮的点击方法 (点击关闭按钮 移除贴纸) 104 | @objc 105 | // 文件私有(文件内可调用这个方法)。 【 文件私有的范围 > 类私有 】 106 | fileprivate func closeButtonClick() { 107 | // 此处考虑到在删除时提示用户。。 108 | // 是否遵守代理 109 | if viewDelegate != nil { 110 | // 遵守。执行方法 111 | viewDelegate!.UNTouchViewCloseButtonClick(sticker: self) 112 | } else { 113 | // 不遵守。移除当前视图 114 | removeFromSuperview() 115 | } 116 | } 117 | 118 | // pinchGesture 缩放手势 119 | // 缩放的方法(文件私有)。 gesture手势 :UI缩放手势识别器 120 | @objc 121 | fileprivate func pinchImage(gesture: UIPinchGestureRecognizer) { 122 | // 当前手势 状态 改变中 123 | if gesture.state == .changed { 124 | // 当前矩阵2D变换 缩放通过(手势缩放的参数) 125 | transform = transform.scaledBy(x: gesture.scale, y: gesture.scale) 126 | // 要复原到1(原尺寸),不要叠加放大(放大3缩小1) 127 | gesture.scale = 1 128 | } 129 | } 130 | 131 | // rotateGesture 旋转手势 132 | // 旋转的方法(文件私有)。 gesture手势 :UI旋转手势识别器 133 | @objc 134 | fileprivate func rotateImage(gesture: UIRotationGestureRecognizer) { 135 | if gesture.state == .changed { 136 | // 该手势中包含 手势旋转的弧度 137 | transform = transform.rotated(by: gesture.rotation) 138 | // 0为弧度制(要跟角度转换) 139 | gesture.rotation = 0 140 | } 141 | } 142 | 143 | // panGesture 拖拽/平移手势 144 | // 平移的方法(文件私有)。 gesture手势 :UI平移手势识别器 145 | @objc 146 | fileprivate func panImage(gesture: UIPanGestureRecognizer) { 147 | if gesture.state == .changed { 148 | // 坐标转换至父视图坐标 149 | let gesturePosition = gesture.translation(in: superview) 150 | // 用移动距离与原位置坐标计算。 gesturePosition.x 已经带正负了 151 | center = CGPoint(x: center.x + gesturePosition.x, y: center.y + gesturePosition.y) 152 | // .zero 为 CGPoint(x: 0, y: 0)的简写, 位置坐标回0 153 | gesture.setTranslation(.zero, in: superview) 154 | } 155 | } 156 | 157 | // 双击动作(UI点击手势识别器) 158 | @objc 159 | fileprivate func doubleTapGesture(tap: UITapGestureRecognizer) { 160 | // 状态 双击结束后 161 | if tap.state == .ended { 162 | // 翻转 90度 163 | let ratation = CGFloat(Double.pi / 2.0) 164 | // 变换 旋转角度 = 之前的旋转角度 + 旋转 165 | transform = CGAffineTransform(rotationAngle: previousRotation + ratation) 166 | previousRotation += ratation 167 | } 168 | } 169 | } 170 | 171 | // Selector的私有拓展 selector:用来选择方法的选择器 (使用时简便) 172 | private extension Selector { 173 | static let close = #selector(UNTouchView.closeButtonClick) 174 | static let pinch = #selector(UNStickerView.pinchImage(gesture:)) 175 | static let rotate = #selector(UNStickerView.rotateImage(gesture:)) 176 | static let pan = #selector(UNStickerView.panImage(gesture:)) 177 | static let doubleTap = #selector(UNStickerView.doubleTapGesture(tap:)) 178 | } 179 | 180 | extension UNTouchView: UIGestureRecognizerDelegate { 181 | 182 | // 是否允许多手势(是否在执行当前手势时 允许其他手势发生) 183 | func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { 184 | return true 185 | } 186 | } 187 | 188 | // 协议 (通过协议向外传值) 189 | protocol UNTouchViewProtocol { 190 | // 定义协议里的方法(但 是VC去实现方法,决定方法的作用) 191 | func UNTouchViewCloseButtonClick(sticker: UNTouchView) 192 | } 193 | 194 | // 拓展 协议。(属于实现 所以不可写在定义里) 195 | private extension UNTouchViewProtocol { 196 | func UNTouchViewCloseButtonClick(sticker: UNTouchView) {} 197 | } 198 | 199 | -------------------------------------------------------------------------------- /Unicorn/Component/Brush/Canva/UNCanvaView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UNCanvaView.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/4/28. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // 画布视图 12 | class UNCanvaView: UIView { 13 | /// 告知系统当前 UIView.layer = CAShapeLayer 14 | override class var layerClass : AnyClass { 15 | /* CAShapeLayer属于QuartzCore框架,是在坐标系内绘制贝塞尔曲线的 16 | 含有属性:指定图像的路径,填充颜色、描边颜色等样式属性 */ 17 | return CAShapeLayer.self 18 | } 19 | 20 | /// 设置画笔 21 | // 通过 UNBrush结构体 完成 22 | func brunsh(brush: UNBrushView.UNBrush?) { 23 | let shapeLayer = self.layer as! CAShapeLayer 24 | // 设置填充颜色 25 | shapeLayer.fillColor = UIColor.clear.cgColor 26 | // 线拐点的样式(拐点圆滑) 27 | shapeLayer.lineJoin = .round 28 | // 线端点的样式(端点圆滑) 29 | shapeLayer.lineCap = .round 30 | 31 | if brush != nil { 32 | // 描边的宽度,默认为1 33 | shapeLayer.lineWidth = brush!.width 34 | // 描边颜色 35 | shapeLayer.strokeColor = brush!.color.cgColor 36 | // 路径 37 | shapeLayer.path = brush!.bezierPath.cgPath 38 | } else { 39 | shapeLayer.path = nil 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Unicorn/Component/Brush/UNBrushView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UNBrushView.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/4/28. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class UNBrushView: UIView { 12 | /// 画笔颜色 13 | var brushColor: UIColor = .black 14 | /// 画笔宽度 15 | var brushWidth: CGFloat = 3 16 | 17 | /// 笔画数 (UNBrush含有颜色 宽度等属性) 18 | private(set) var brushes = [UNBrush]() 19 | 20 | /// 绘制层。【画布视图】UNCanvaView含有画笔属性 21 | private var canvaView = UNCanvaView() 22 | /// 显示层(画笔画完生成图片所占view) 23 | private var bgView = UIImageView() 24 | /// 存储绘制一次贝塞尔曲线的点集合 25 | private var points = Array(repeating: CGPoint(), count: 5) 26 | /// 标识当前存储的点索引 27 | private var pointIndex = 0 28 | /// 可重做集合 29 | private var redoDatas = [Data]() 30 | /// 可撤回集合 31 | private var undoDatas = [Data]() 32 | /// 底部功能栏 33 | private var bottomView = UIView() 34 | 35 | 36 | override init(frame: CGRect) { 37 | super.init(frame: frame) 38 | initView() 39 | } 40 | 41 | // deinit { 42 | // clean() 43 | // } 44 | // 45 | // private func clean() { 46 | // bgView.image = nil 47 | // brushes.removeAll() 48 | // redoDatas.removeAll() 49 | // } 50 | // 51 | 52 | // 解码器 53 | required init?(coder aDecoder: NSCoder) { 54 | fatalError("init(coder:) has not been implemented") 55 | } 56 | 57 | private func initView() { 58 | // 底部视图(底部功能栏) 59 | bottomView.frame = CGRect(x: 0, y: height - 50, width: width, height: 50) 60 | addSubview(bottomView) 61 | bottomView.backgroundColor = .lightGray 62 | 63 | // 显示层:画笔画完后生成图片,所占view(是UIImageView) 64 | bgView.frame = CGRect(x: 0, y: 0, width: width, height: height) 65 | addSubview(bgView) 66 | // 画布视图。canvaView是UNCanvaView类 含有画笔属性 67 | canvaView.frame = CGRect(x: 0, y: 0, width: width, height: height) 68 | bgView.addSubview(canvaView) 69 | 70 | /// 撤回 71 | let undoButton = UIButton(frame: CGRect(x: 0, y: 0, width: bottomView.height, height: bottomView.height)) 72 | bottomView.addSubview(undoButton) 73 | // 图标 74 | undoButton.setImage(UIImage(named: "back"), for: .normal) 75 | undoButton.addTarget(self, action: #selector(undo), for: .touchUpInside) 76 | 77 | /// 重做 78 | let redoButton = UIButton(frame: CGRect(x: undoButton.right + 10, y: 0, width: bottomView.height, height: bottomView.height)) 79 | bottomView.addSubview(redoButton) 80 | // 图标 81 | redoButton.setImage(UIImage(named: "return"), for: .normal) 82 | redoButton.addTarget(self, action: #selector(redo), for: .touchUpInside) 83 | 84 | /// 颜色选择 85 | // 集合视图布局 86 | let collectionViewLayout = UICollectionViewFlowLayout() 87 | let itemW = bottomView.height * 0.7 88 | // 数量 89 | let itemCount = CGFloat(5) 90 | // 内部 91 | let innerW = (screenWidth - itemCount * 50) / itemCount 92 | // 宽高大小 93 | collectionViewLayout.itemSize = CGSize(width: itemW , height: itemW) 94 | // 最小行间距 95 | collectionViewLayout.minimumLineSpacing = innerW 96 | // 最小项间距 97 | collectionViewLayout.minimumInteritemSpacing = 10 98 | // 滚动方向 水平 99 | collectionViewLayout.scrollDirection = .horizontal 100 | // 距离四周的间距 101 | collectionViewLayout.sectionInset = UIEdgeInsets(top: 0, left: innerW / 2, bottom: 0, right: innerW / 2) 102 | 103 | // 集合视图 位置xy宽高 redo重做(颜色在redo的右边) 104 | let collectionView = UNLineCollectionView(frame: CGRect(x: redoButton.right, y: 0, width: bottomView.width - redoButton.right, height: bottomView.height), collectionViewLayout: collectionViewLayout) 105 | collectionView.backgroundColor = .clear 106 | // 线性集合视图类型 107 | collectionView.lineType = .color 108 | collectionView.viewColorModels = [.black, .red, .gray, .green, .purple, .blue] 109 | bottomView.addSubview(collectionView) 110 | 111 | // 选中cell,重复点击 宽度增加 112 | collectionView.cellSelected = { cellIndex in 113 | // viewColorModels 颜色数组 114 | // 判断颜色是否一样(笔刷颜色-当前选择颜色) 115 | if self.brushColor == collectionView.viewColorModels![cellIndex] { 116 | self.brushWidth += 1 117 | } else { 118 | self.brushWidth = 3 119 | } 120 | // 把画笔颜色赋值回去 121 | self.brushColor = collectionView.viewColorModels![cellIndex] 122 | } 123 | 124 | // “画布视图”canvaView 里会使用UNBrush结构体 125 | // 画笔颜色、粗细赋值后,给“画布视图”canvaView 126 | } 127 | 128 | 129 | // undo 撤回 130 | @objc 131 | private func undo() { 132 | // undoDatas 可撤回集合 数量 133 | guard undoDatas.count != 0 else { return } 134 | 135 | // 如果是撤回集合中只有 1 个数据,则说明撤回后为空 136 | if undoDatas.count == 1 { 137 | // 重做 redo append 添加 138 | redoDatas.append(undoDatas.last!) 139 | // 撤回 undo 清空 140 | undoDatas.removeLast() 141 | // 清空图片视图 142 | bgView.image = nil 143 | } else { 144 | // 把 3 给 redo 145 | redoDatas.append(undoDatas.last!) 146 | // 从 undo 移除 3. 还剩 2 1 147 | undoDatas.removeLast() 148 | // 清空图片视图 149 | bgView.image = nil 150 | // 把 2 给图片视图 151 | bgView.image = UIImage(data: undoDatas.last!) 152 | } 153 | } 154 | 155 | // redo 重做 156 | @objc 157 | private func redo() { 158 | if redoDatas.count > 0 { 159 | // 先赋值,再移除(redo的last给图片视图) 160 | bgView.image = UIImage(data: redoDatas.last!) 161 | // redo的last 给 undo撤回数组 162 | undoDatas.append(redoDatas.last!) 163 | // 从redo重做 移除last 164 | redoDatas.removeLast() 165 | } 166 | } 167 | 168 | // --- 绘制起始点 --- 169 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 170 | // 获取起始点 171 | let beginPoint = touches.first?.location(in: self) 172 | // 贝塞尔曲线 173 | let bezierPath = UIBezierPath() 174 | // 路径起点 175 | bezierPath.move(to: beginPoint!) 176 | let brush = UNBrush(color: brushColor, width: brushWidth, bezierPath: bezierPath) 177 | // 所有画笔的集合(里面有 画笔的颜色宽度路径属性) 178 | brushes.append(brush) 179 | // 三次方贝塞尔曲线的起始点,这一次的三次贝塞尔画完了,连接下一次的 180 | pointIndex = 0 181 | points[0] = beginPoint! 182 | } 183 | 184 | override func touchesMoved(_ touches: Set, with event: UIEvent?) { 185 | let movedPoint = touches.first?.location(in: self) 186 | // brushes中含有画笔属性 187 | let brush = brushes.last 188 | 189 | // 拿到一个点后++,存储点 190 | pointIndex += 1 191 | points[pointIndex] = movedPoint! 192 | 193 | if pointIndex == 4 { 194 | // 4为下一个控制点 195 | let p_x = (points[2].x + points[4].x) / 2 196 | let p_y = (points[2].y + points[4].y) / 2 197 | points[3] = CGPoint(x: p_x, y: p_y) 198 | 199 | // 绘制 “三次贝塞尔曲线” 200 | // 绘制三次贝塞尔曲线的关键方法,以三个点画一段曲线. 一般和 `moveToPoint:` 配合使用. 201 | // 起始点为 pts[0] 设置,终止点位为 pts[3],控制点 1 的坐标 pts[1],控制点2的坐标是 pts[2] 202 | 203 | // 移到起始点 204 | brush?.bezierPath.move(to: points[0]) 205 | // 添加曲线 206 | brush?.bezierPath.addCurve(to: points[3], 207 | controlPoint1: points[1], 208 | controlPoint2: points[2]) 209 | 210 | // 延续三次贝塞尔曲线点 211 | // 终点->起点。 212 | points[0] = points[3] 213 | // 控制点 214 | points[1] = points[4] 215 | pointIndex = 1 216 | } 217 | // 画布 218 | canvaView.brunsh(brush: brush) 219 | } 220 | 221 | override func touchesEnded(_ touches: Set, with event: UIEvent?) { 222 | // 开启图形上下文(“创建容器”) 223 | UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), false, 0) 224 | // UIGraphics API ,GetCurrentContext获取当前上下文 225 | let context = UIGraphicsGetCurrentContext() 226 | // 把view的layer放进上下文 227 | bgView.layer.render(in: context!) 228 | // 获取图片,来自当前开启的上下文(笔迹变成图片) 229 | let bgImage = UIGraphicsGetImageFromCurrentImageContext() 230 | // 关闭当前打开的上下文 231 | UIGraphicsEndImageContext() 232 | 233 | bgView.image = bgImage 234 | // 转成png类型的二进制数据 235 | undoDatas.append(bgImage!.pngData()!) 236 | // 每次画完都要从画布上清除最后一笔 237 | canvaView.brunsh(brush: nil) 238 | } 239 | 240 | func drawImage() -> UIImage { 241 | // 开启图形上下文 242 | UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), false, UIScreen.main.scale) 243 | // 填充该上下文到背景View中 244 | bgView.layer.render(in: UIGraphicsGetCurrentContext()!) 245 | // 获取图片,来自当前开启的上下文 246 | let bgImage = UIGraphicsGetImageFromCurrentImageContext() 247 | // 关闭当前打开的上下文 248 | UIGraphicsEndImageContext() 249 | 250 | return bgImage! 251 | } 252 | } 253 | 254 | // 画笔结构体 255 | extension UNBrushView { 256 | struct UNBrush { 257 | // 画笔颜色 258 | var color: UIColor 259 | // 画笔宽度 260 | var width: CGFloat 261 | // 画笔路径 262 | var bezierPath: UIBezierPath 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /Unicorn/Component/Network/Network.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/5/7. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // 网络请求封装的类 12 | 13 | class Network { 14 | // 单例 15 | static let shared = Network() 16 | 17 | // complateHandler 证明结果返回成功,failedHandler 返回失败 18 | func get( urlString: String, 19 | params: [String: String], 20 | complateHandler: @escaping ((Any) -> Void), 21 | failedHandler: @escaping ((Error?) -> Void)) { 22 | // 临时url 例如:"http://localhost:8080/api/sticker?bookId=1" 23 | var tempUrlString = urlString + "?" 24 | for d in params { 25 | let dString = d.key + "=" + d.value + "&" 26 | tempUrlString += dString 27 | } 28 | // 移除 29 | tempUrlString.removeLast() 30 | 31 | 32 | let url = URL(string: tempUrlString)! 33 | 34 | // url请求,把token信息带上,发送请求 35 | var request = URLRequest(url: url) 36 | // request.setValue("application/json", forHTTPHeaderField: "Content-Type") 37 | if User.shared.viewModel.token != nil { 38 | request.setValue("Bearer " + User.shared.viewModel.token!, forHTTPHeaderField: "Authorization") 39 | } 40 | // request.httpBody = try! JSONSerialization.data(withJSONObject: params, options: .prettyPrinted) 41 | 42 | let session = URLSession(configuration: URLSessionConfiguration.default) 43 | 44 | // 发送http请求,拿到数据 45 | let task = session.dataTask(with: request) { (responseData, urlResponseData, error) in 46 | // guard error != nil else { failedHandler(error) } 47 | // 加上 do-catch 48 | // responseData 响应数据 49 | if responseData?.count == 0 { 50 | // 证明结果返回成功 51 | complateHandler(0) 52 | } else { 53 | if responseData != nil { 54 | let resDict = try? JSONSerialization.jsonObject(with: responseData!, options: .allowFragments) 55 | if resDict != nil { 56 | complateHandler(resDict!) 57 | } 58 | } 59 | } 60 | } 61 | task.resume() 62 | } 63 | 64 | // post “更新” ... 构造 http请求体 65 | func post(urlString: String, 66 | params: [String: Any], 67 | complateHandler: @escaping ((Any) -> Void), 68 | failedHandler: @escaping ((Error?) -> Void)) { 69 | let url = URL(string: urlString)! 70 | var request = URLRequest(url: url) 71 | request.httpMethod = "POST" 72 | request.setValue("application/json", forHTTPHeaderField: "Content-Type") 73 | if User.shared.viewModel.token != nil { 74 | request.setValue("Bearer " + User.shared.viewModel.token!, forHTTPHeaderField: "Authorization") 75 | } 76 | request.httpBody = try! JSONSerialization.data(withJSONObject: params, options: .prettyPrinted) 77 | 78 | let session = URLSession(configuration: URLSessionConfiguration.default) 79 | 80 | let task = session.dataTask(with: request) { (responseData, urlResponseData, error) in 81 | // guard error != nil else { failedHandler(error) } 82 | 83 | // 加上 do-catch 84 | if responseData?.count != 0 { 85 | if responseData != nil { 86 | let resDict = try? JSONSerialization.jsonObject(with: responseData!, options: .allowFragments) 87 | if resDict != nil { 88 | complateHandler(resDict!) 89 | } 90 | } 91 | } else { 92 | complateHandler("") 93 | } 94 | } 95 | task.resume() 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /Unicorn/Component/Note/Sevices/Note.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NoteSevice.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/5/10. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // 提供增删改查服务(“M”) 12 | 13 | class Note { 14 | 15 | // 单例(此类就这一个对象) 16 | static let shared = Note() 17 | 18 | // @escaping 逃逸闭包。 19 | func create(viewModel: ViewModel, 20 | // “完成”闭包 证明结果返回成功 21 | compalteHandler: @escaping (() -> Void), 22 | // “失败”闭包 证明结果返回失败 23 | failedHandler: @escaping ((Error) -> Void)) { 24 | // 【字典】【key:value】赋值,API需要,coverColor是拼接好的颜色字符串 25 | let params = [ 26 | "userId": User.shared.viewModel.uid!, 27 | "coverTitle": viewModel.coverTitle, 28 | "coverColor": viewModel.coverColor 29 | ] as [String : Any] // 强转类型 30 | Network.shared.post(urlString: "http://localhost:8080/api/note", params: params, complateHandler: { _ in 31 | // 证明结果返回成功 32 | compalteHandler() 33 | }) { 34 | // 证明结果返回失败 35 | failedHandler($0!) 36 | } 37 | } 38 | 39 | // 获取所有手帐本 , escaping异步方法 40 | func get(compalteHandler: @escaping (([[String: Any]]) -> Void), 41 | failedHandler: @escaping ((Error) -> Void)) { 42 | // API需要,下面的url接口需要 43 | let params = [ 44 | "userId": String(User.shared.viewModel.uid!), 45 | ] 46 | Network.shared.get(urlString: "http://localhost:8080/api/note", params: params, complateHandler: { 47 | print($0) 48 | // 证明结果返回成功 49 | compalteHandler($0 as! Array>) 50 | }) { 51 | // 证明结果返回失败 52 | failedHandler($0!) 53 | } 54 | } 55 | 56 | func delete(bookId: String, 57 | compalteHandler: @escaping (() -> Void), 58 | failedHandler: @escaping ((Error) -> Void)) { 59 | let params = [ 60 | "bookId": bookId, 61 | ] 62 | 63 | // 调用get方法。 64 | Network.shared.get(urlString: "http://localhost:8080/api/note/delete", params: params, complateHandler: { 65 | print($0) 66 | compalteHandler() 67 | }) { 68 | failedHandler($0!) 69 | } 70 | } 71 | } 72 | 73 | extension Note { 74 | // 视图模型结构体 75 | struct ViewModel { 76 | // 手帐本 名称 77 | var coverTitle: String 78 | // 手帐本 颜色 79 | var coverColor: String 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Unicorn/Component/Note/UNNoteCreateViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UNNoteCreateViewController.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/5/10. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // 手帐本创建 12 | class UNNoteCreateViewController: UIViewController { 13 | 14 | private var noteView = UIView() 15 | // 手帐本 标题 标签(中间的名称) 16 | private var noteTitleLabel = UILabel() 17 | // 底部 集合视图 18 | private var bottomCollectionView: UNLineCollectionView? 19 | // 设置手帐名称 20 | private var noteTitleString = "新的手帐本" { 21 | didSet { noteTitleLabel.text = noteTitleString } 22 | } 23 | 24 | // 页面加载完了 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | initView() 28 | } 29 | 30 | private func initView() { 31 | // 导航项 32 | navigationItem.title = "创建新手帐薄" 33 | view.backgroundColor = .white 34 | // 导航右侧按钮-“Done” (导航栏按钮项) 35 | navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(done)) 36 | 37 | // 手帐本 位置 38 | noteView.frame = CGRect(x: view.width * 0.15, y: navigationHeight + 20, width: view.width * 0.7, height: view.height * 0.6) 39 | noteView.backgroundColor = .red 40 | noteView.layer.cornerRadius = 10 41 | view.addSubview(noteView) 42 | 43 | // 手帐本名称(本子中间位置) 44 | noteTitleLabel.frame = CGRect(x: 0, y: 0, width: noteView.width, height: 20) 45 | view.addSubview(noteTitleLabel) 46 | noteTitleLabel.text = noteTitleString 47 | noteTitleLabel.textColor = .white 48 | noteTitleLabel.center = noteView.center 49 | noteTitleLabel.textAlignment = .center 50 | 51 | // 集合视图布局 = 流布局 52 | let collectionViewLayout = UICollectionViewFlowLayout() 53 | 54 | let itemW = view.width / CGFloat(2) 55 | collectionViewLayout.itemSize = CGSize(width: itemW , height: itemW) 56 | // 最小项间距 57 | collectionViewLayout.minimumInteritemSpacing = 10 58 | // 滚动方向 水平 59 | collectionViewLayout.scrollDirection = .horizontal 60 | // 底部 集合视图 61 | let collectionView = UNLineCollectionView(frame: CGRect(x: 0, y: view.height - bottomSafeAreaHeight - 64, width: view.width, height: 64), collectionViewLayout: collectionViewLayout) 62 | collectionView.backgroundColor = .darkGray 63 | collectionView.viewModels = ["标题", "颜色"] 64 | bottomCollectionView = collectionView 65 | view.addSubview(collectionView) 66 | 67 | // cell被选中之后 68 | collectionView.cellSelected = { cellIndex in 69 | switch cellIndex { 70 | // 选中“标题” 71 | case 0: 72 | // 弹出提示框,message正文,preferredStyle弹窗样式 73 | let alertController = UIAlertController(title: "请输入手帐薄标题", 74 | message: nil, 75 | preferredStyle: .alert) 76 | let ok = UIAlertAction(title: "确定", style: .default, handler: { _ in 77 | // 传数据给 78 | self.noteTitleString = alertController.textFields![0].text ?? "" 79 | }) 80 | // 输入框提示符 81 | alertController.addTextField(configurationHandler: { 82 | $0.placeholder = self.noteTitleString 83 | }) 84 | alertController.addAction(ok) 85 | // 从底部弹出 86 | self.present(alertController, animated: true, completion: nil) 87 | 88 | case 1: self.present(self.colorBottomView, 89 | animated: true, 90 | completion: nil) 91 | default: break 92 | } 93 | } 94 | } 95 | 96 | @objc 97 | private func done() { 98 | var red: CGFloat = 0 99 | var green: CGFloat = 0 100 | var blue: CGFloat = 0 101 | // 取地址 颜色,要存进后端 102 | noteView.backgroundColor?.getRed(&red, green: &green, blue: &blue, alpha: nil) 103 | let colorString = "\(red),\(green),\(blue)" 104 | // colorString封面颜色拼接为字符串,传出coverColor 105 | let viewModel = Note.ViewModel(coverTitle: noteTitleString, coverColor: colorString) 106 | // Note creat里拿到viewModel,也就是coverColor拼接好的字符串 等 107 | Note.shared.create(viewModel: viewModel, compalteHandler: { 108 | DispatchQueue.main.async { 109 | self.navigationController?.popViewController(animated: true) 110 | } 111 | }) { 112 | print($0) 113 | } 114 | } 115 | 116 | /// 颜色 117 | private var colorBottomView: UNBottomColorViewController { 118 | get { 119 | let colorPopover = UNBottomColorViewController() 120 | colorPopover.colors = [.red, .black, .gray, .green, .darkGray] 121 | colorPopover.currentColor = self.noteView.backgroundColor 122 | colorPopover.preferredContentSize = CGSize(width: 200, height: 280) 123 | colorPopover.modalPresentationStyle = .popover 124 | 125 | let colorPopoverPVC = colorPopover.popoverPresentationController 126 | colorPopoverPVC?.sourceView = self.bottomCollectionView 127 | colorPopoverPVC?.sourceRect = CGRect(x: bottomCollectionView!.cellCenterXs[1], y: 0, width: 0, height: 0) 128 | colorPopoverPVC?.permittedArrowDirections = .down 129 | colorPopoverPVC?.delegate = self 130 | colorPopoverPVC?.backgroundColor = .white 131 | 132 | colorPopover.colorChange = { color in 133 | self.noteView.backgroundColor = color 134 | } 135 | colorPopover.bottomColorChange = { 136 | self.noteView.backgroundColor = $0 137 | } 138 | 139 | return colorPopover 140 | } 141 | } 142 | } 143 | 144 | 145 | extension UNNoteCreateViewController: UIPopoverPresentationControllerDelegate { 146 | // 自适应 147 | func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle { 148 | return .none 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /Unicorn/Component/Note/UNNoteViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UNNoteViewController.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/5/10. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class UNNoteViewController: UIViewController { 12 | 13 | // coverId 手帐封面id(用数组存了封面了id) 14 | private var coverIds = [Int]() 15 | // 封面-集合视图 16 | private var collectionView: UNLineCollectionView? 17 | // 布尔值(是否选中删除按钮) 18 | private var deleteSelected = false 19 | private var deleteBarItemButton = UIBarButtonItem() 20 | // 页面加载完了 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | initView() 24 | } 25 | 26 | // 初始化视图 27 | private func initView() { 28 | // 页面的标题 29 | navigationItem.title = "Unicorn 手帐" 30 | if User.shared.viewModel.nickname != nil { 31 | navigationItem.title = User.shared.viewModel.nickname! 32 | } 33 | view.backgroundColor = .white 34 | // 顶部标题-颜色 文字什么的颜色 35 | navigationController?.navigationBar.tintColor = .black 36 | 37 | // deleteNote 方法 【删除】手帐本 38 | deleteBarItemButton = UIBarButtonItem(title: "删除", style: .plain, target: self, action: #selector(deleteNote)) 39 | 40 | // 顶部 右侧bar按钮(使用系统图标,target谁响应方法,动作:addNote方法) 41 | // rightBarButtonItems 按钮数组。-- [删除,添加] -- 【添加】addNote方法 新建手帐本 42 | navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addNote)), deleteBarItemButton] 43 | // 顶部 左侧 【退出登录】(使用系统图标)-- logout方法 44 | navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .rewind, target: self, action: #selector(logout)) 45 | 46 | // 集合视图 布局(所有手帐本) 47 | let collectionViewLayout = UICollectionViewFlowLayout() 48 | let itemW = view.width * 0.4 49 | // 数量(一行两个) 50 | let itemCount = CGFloat(2) 51 | // 内部间距 52 | let innerW = (view.width - itemCount * itemW) / itemCount 53 | // 每一个封面的大小,长是宽的1.7倍 54 | collectionViewLayout.itemSize = CGSize(width: itemW, height: itemW * 1.7) 55 | // 最小行间距 56 | collectionViewLayout.minimumLineSpacing = innerW 57 | // 最小项艰巨 58 | collectionViewLayout.minimumInteritemSpacing = 10 59 | // 滚动方向 = 垂直 60 | collectionViewLayout.scrollDirection = .vertical 61 | // 距离四周的距离 62 | collectionViewLayout.sectionInset = UIEdgeInsets(top: innerW / 2, left: innerW / 2, bottom: 20, right: innerW / 2) 63 | 64 | // 线性集合视图 设置xy宽高(手帐本) 65 | let collectionView = UNLineCollectionView(frame: CGRect(x: 0, y: navigationHeight, width: view.width, height: view.height - navigationHeight - bottomSafeAreaHeight), collectionViewLayout: collectionViewLayout) 66 | 67 | self.collectionView = collectionView 68 | collectionView.backgroundColor = .clear 69 | // lineType 线性集合视图 cover封面类型 70 | collectionView.lineType = .cover 71 | view.addSubview(collectionView) 72 | 73 | // ----- 手帐本的点击事件。----- 74 | // 选择一个cell,跳到所选择的手帐页面。点击后返回的是手帐本的cellIndex 75 | collectionView.cellSelected = { cellIndex in 76 | // 用deleteSelected属性判断是否点击了“删除” 77 | if self.deleteSelected { 78 | // 取到手帐本数组中该手帐本的index,赋值。 79 | // 进入手帐本界面,就会给所有手帐的coverIds赋值。具体的在getAllNote方法中。 80 | let bookId = self.coverIds[cellIndex] 81 | // 用被赋值的bookId 82 | Note.shared.delete(bookId: String(bookId), compalteHandler: { 83 | // 调用getAllNote 84 | self.getAllNote() 85 | }, failedHandler: { 86 | // 失败,仅显示在控制台终端 87 | print($0.localizedDescription) 88 | }) 89 | } else { // 没有点击删除,则正常进入 90 | let vc = UNContentViewController() 91 | // 数据源,coverId封面id取出给这里的bookId 92 | vc.bookId = self.coverIds[cellIndex] 93 | // 导航控制器 视图控制器 94 | self.navigationController?.pushViewController(vc, animated: true) 95 | } 96 | } 97 | } 98 | 99 | // 视图出现 100 | override func viewDidAppear(_ animated: Bool) { 101 | super.viewDidAppear(animated) 102 | // 若token为空 103 | if User.shared.viewModel.token == nil { 104 | // 弹出登陆界面(bundle“沙盒”,nil默认当前文件夹) 105 | let sb = UIStoryboard(name: "UNUserLoginViewController", bundle: nil) 106 | // 实例化ViewController 107 | let vc = sb.instantiateViewController(withIdentifier: "UNUserLoginViewController") 108 | // 界面从底部弹出(是否用动画true,弹出结束nil) 109 | present(vc, animated: true, completion: nil) 110 | return 111 | } 112 | 113 | getAllNote() 114 | } 115 | 116 | func getAllNote() { 117 | // 获取所有的手帐本 - 调用get方法 118 | Note.shared.get(compalteHandler: { 119 | // 用数组存储标题、背景颜色 120 | var titles = [String]() 121 | var colors = [UIColor]() 122 | 123 | // 刷新时,要移除之前的coverIds数据(类比1231234)。下面重新赋值 124 | self.coverIds.removeAll() 125 | 126 | // 从服务端获取数据 - 遍历数据 127 | // d:返回数组里的每一个实体 128 | for d: Dictionary in $0 { 129 | // 添加 封面名称 为"coverTitle"。取实体d中的coverTitle,强转为String(titles被前面设置为[String]) 130 | titles.append(d["coverTitle"] as! String) 131 | // 把id传到coverIds里面 132 | self.coverIds.append(d["id"] as! Int) 133 | // 颜色字符串 134 | let colorString = d["coverColor"] as! String 135 | // 颜色分割字符串, 分割 分割符。要转成rgb色值 136 | let colorSplitString = colorString.split(separator: ",") 137 | // colorSplitString是数组,通过获取数组的第n位获取数值 138 | if colorString != "" { 139 | let color = UIColor.rgb(CGFloat(Double(String(colorSplitString[0]))!) * 255, CGFloat(Double(String(colorSplitString[1]))!) * 255, CGFloat(Double(String(colorSplitString[2]))!) * 255) 140 | // 添加 颜色 141 | colors.append(color) 142 | } 143 | } 144 | 145 | // 队列,全局队列 回主线程刷新UI 146 | DispatchQueue.main.async { 147 | // 赋值数据源,给viewColorModels,里的颜色数组 148 | self.collectionView!.viewColorModels = colors 149 | // 赋值数据源,给viewModels,里的字符串数组 150 | self.collectionView!.viewModels = titles 151 | } 152 | }) { 153 | print($0) 154 | } 155 | } 156 | 157 | // 添加/新建手帐本 方法 158 | @objc 159 | private func addNote() { 160 | // 初始化控制器 161 | let vc = UNNoteCreateViewController() 162 | // 导航栏控制器 “push”(右侧向左推进)。用push方法使界面加入 -- 新建手帐本界面 163 | navigationController?.pushViewController(vc, animated: true) 164 | } 165 | 166 | @objc 167 | // 删除手帐本 168 | private func deleteNote() { 169 | 170 | // 是否编辑 取反。(删除时 按钮的选中与否) 171 | deleteSelected = !deleteSelected 172 | 173 | if deleteSelected { 174 | deleteBarItemButton.tintColor = .red 175 | } else { 176 | deleteBarItemButton.tintColor = .black 177 | } 178 | } 179 | 180 | // 退出登录 181 | @objc 182 | private func logout() { 183 | User.shared.logout { 184 | // 队列 185 | DispatchQueue.main.async { 186 | // 弹出登陆界面(bundle“沙盒”,nil默认当前文件夹) 187 | self.present(UIStoryboard(name: "UNUserLoginViewController", bundle: nil).instantiateViewController(withIdentifier: "UNUserLoginViewController"), animated: true, completion: nil) 188 | // 是否用动画true,弹出结束nil 189 | } 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /Unicorn/Component/Sticker/Services/StickerService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StickerService.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/5/10. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class Sticker { 12 | static let shared = Sticker() 13 | 14 | func create(viewModel: ViewModel, 15 | complateHandler: @escaping (() -> Void), 16 | failedHanler: @escaping ((Error) -> Void)) { 17 | var params = [ 18 | "x": viewModel.x, 19 | "y": viewModel.y, 20 | "w": viewModel.w, 21 | "h": viewModel.h, 22 | "rotate": viewModel.rotate, 23 | "type": viewModel.type, 24 | ] as [String : Any] 25 | 26 | if viewModel.defaultIndex != nil { 27 | params["defaultIndex"] = viewModel.defaultIndex! 28 | } 29 | 30 | if viewModel.data != nil { 31 | params["data"] = viewModel.data! 32 | } 33 | 34 | if viewModel.bookId != nil { 35 | params["bookId"] = viewModel.bookId! 36 | } 37 | 38 | Network.shared.post(urlString: "http://localhost:8080/api/sticker", params: params, complateHandler: { 39 | print($0) 40 | complateHandler() 41 | }) { 42 | failedHanler($0!) 43 | } 44 | } 45 | 46 | func get(bookId: Int, 47 | complateHandler: @escaping (([[String: Any]]) -> Void), 48 | failedHanler: @escaping ((Error) -> Void)) { 49 | Network.shared.get(urlString: "http://localhost:8080/api/sticker", params: ["bookId": String(bookId)], complateHandler: { 50 | complateHandler($0 as! [[String : Any]]) 51 | }) { 52 | print($0!) 53 | } 54 | } 55 | 56 | func update(viewModel: ViewModel, 57 | complateHandler: @escaping (() -> Void), 58 | failedHanler: @escaping ((Error) -> Void)) { 59 | var params = [ 60 | "x": viewModel.x, 61 | "y": viewModel.y, 62 | "w": viewModel.w, 63 | "h": viewModel.h, 64 | "rotate": viewModel.rotate, 65 | "bookId": viewModel.bookId!, 66 | "type": viewModel.type, 67 | "id": viewModel.id! 68 | ] as [String : Any] 69 | 70 | if viewModel.defaultIndex != nil { 71 | params["defaultIndex"] = viewModel.defaultIndex! 72 | } 73 | 74 | if viewModel.data != nil { 75 | params["data"] = viewModel.data! 76 | } 77 | 78 | Network.shared.post(urlString: "http://localhost:8080/api/sticker/update", params: params, complateHandler: { 79 | print($0) 80 | complateHandler() 81 | }) { 82 | failedHanler($0!) 83 | } 84 | } 85 | } 86 | 87 | extension Sticker { 88 | struct ViewModel: Codable { 89 | var id: Int? 90 | var x: CGFloat 91 | var y: CGFloat 92 | var w: CGFloat 93 | var h: CGFloat 94 | var rotate: CGFloat 95 | var type: Int 96 | var data: Data? 97 | var bookId: Int? 98 | var defaultIndex: Int? 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Unicorn/Component/Sticker/UNStickerComponentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UNStickerComponentView.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/4/29. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class UNStickerComponentView: UIView { 12 | var sticker: ((UNStickerView) -> Void)? 13 | 14 | override init(frame: CGRect) { 15 | super.init(frame: frame) 16 | initView() 17 | } 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | fatalError("init(coder:) has not been implemented") 21 | } 22 | 23 | // “贴纸”界面初始化 24 | private func initView() { 25 | backgroundColor = .lightGray 26 | 27 | let collectionViewLayout = UICollectionViewFlowLayout() 28 | let itemW = 70 29 | let itemCount = CGFloat(5) 30 | let innerW = (width - itemCount * 50) / itemCount 31 | collectionViewLayout.itemSize = CGSize(width: itemW , height: itemW) 32 | collectionViewLayout.minimumLineSpacing = innerW 33 | collectionViewLayout.minimumInteritemSpacing = 10 34 | collectionViewLayout.scrollDirection = .vertical 35 | // 距离四周的间距 36 | collectionViewLayout.sectionInset = UIEdgeInsets(top: innerW / 2, left: innerW / 2, bottom: 0, right: innerW / 2) 37 | 38 | let collectionView = UNLineCollectionView(frame: CGRect(x: 0, y: 0, width: width, height: height), collectionViewLayout: collectionViewLayout) 39 | collectionView.backgroundColor = .clear 40 | collectionView.lineType = .icon 41 | // iconTitle里设置默认为“home-”开头,但这里设为“贴纸”开头的 图(文件夹Assets里的资源) 42 | collectionView.iconTitle = "贴纸" 43 | collectionView.iconCount = 17 44 | addSubview(collectionView) 45 | 46 | collectionView.cellSelected = { cellIndex in 47 | let stickerImage = UIImage(named: collectionView.iconTitle + "\(cellIndex)") 48 | let sticker = UNStickerView() 49 | sticker.width = 100 50 | sticker.height = 100 51 | sticker.imgViewModel = UNStickerView.ImageStickerViewModel(image: stickerImage!) 52 | self.sticker?(sticker) 53 | } 54 | 55 | 56 | // 顶左、右圆角 57 | let maskPath = UIBezierPath(roundedRect: self.bounds, 58 | byRoundingCorners: [UIRectCorner.topLeft, UIRectCorner.topRight], 59 | cornerRadii: CGSize(width: 15, height: 15)) 60 | let maskLayer = CAShapeLayer() 61 | maskLayer.frame = self.bounds 62 | maskLayer.path = maskPath.cgPath 63 | self.layer.mask = maskLayer 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /Unicorn/Component/Sticker/UNStickerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UNSticerView.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/3/25. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | // 封装基类(StickerView) 继承于UNTouchView贴纸基类(可用手势..) 13 | class UNStickerView: UNTouchView { 14 | // 贴纸类型(两种类型:默认类型-贴纸; 用户自定义类型-照片、文字) 15 | var stickerType: StickerType = .default 16 | // 贴纸id 17 | var id: Int? 18 | 19 | // 照片贴纸数据源 20 | var imgViewModel: ImageStickerViewModel? { 21 | didSet { 22 | imageStickerView.image = imgViewModel!.image 23 | stickerType = .custom //照片、文字被认为是自定义类型 24 | } 25 | } 26 | 27 | // 文字贴纸数据源。文字模型 28 | var textViewModel: TextStickerViewModel? { 29 | didSet { 30 | // textStickerView里 --> UNLabel里就会修改属性 31 | textStickerView.viewModel = textViewModel! 32 | stickerType = .custom 33 | } 34 | } 35 | 36 | // 图片视图 37 | lazy var imageStickerView: UIImageView = { 38 | let imageStickerView = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height)) 39 | // 贴纸边缘 亮灰色 40 | imageStickerView.layer.borderColor = UIColor.lightGray.cgColor 41 | // 内容模式 - 按比例全充满 42 | imageStickerView.contentMode = .scaleAspectFit 43 | // imageStickerView.layer.masksToBounds = true 44 | addSubview(imageStickerView) 45 | return imageStickerView 46 | }() 47 | 48 | // 文字/标签视图 49 | // UNLabel里 赋值方法就会被执行。就会修改属性。所以,生成的文字贴纸是Label 50 | lazy var textStickerView: UNLabel = { 51 | let textStickerView = UNLabel(frame: CGRect(x: 0, y: 0, width: width, height: height)) 52 | // 换行模式 53 | textStickerView.lineBreakMode = .byWordWrapping 54 | // 0 相当于不限制行数 55 | textStickerView.numberOfLines = 0 56 | addSubview(textStickerView) 57 | return textStickerView 58 | }() 59 | 60 | override var isSelected: Bool { 61 | didSet { 62 | if isSelected { 63 | imageStickerView.layer.borderWidth = 1 64 | } else { 65 | imageStickerView.layer.borderWidth = 0 66 | } 67 | } 68 | } 69 | } 70 | 71 | extension UNStickerView { 72 | // 照片数据源-结构体 73 | struct ImageStickerViewModel { 74 | let image: UIImage 75 | } 76 | 77 | // 文字贴纸数据源类型-结构体 78 | struct TextStickerViewModel { 79 | let text: String 80 | let textColor: UIColor 81 | let textFont: UIFont 82 | } 83 | 84 | // 贴纸类型(默认类型-贴纸;自定义类型-文字、照片)- 结构体 85 | enum StickerType: Int { 86 | case `default` = 0 87 | case custom //自定义类型 88 | } 89 | } 90 | 91 | 92 | class UNLabel: UILabel { 93 | var viewModel: UNStickerView.TextStickerViewModel? { 94 | didSet { 95 | // 字体 96 | font = viewModel?.textFont 97 | // 颜色 98 | textColor = viewModel?.textColor 99 | // 文本内容 100 | text = viewModel?.text 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Unicorn/Component/Text/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Component/Text/.DS_Store -------------------------------------------------------------------------------- /Unicorn/Component/Text/Component/Color/ColorPicker/EFColorComponentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFColorComponentView.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/28. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | // The view to edit a color component. 30 | public class EFColorComponentView: UIControl { 31 | 32 | // Indicates if the user touches the control at the moment 33 | var isTouched: Bool { 34 | return slider.isTouched 35 | } 36 | 37 | let EFColorComponentViewSpacing: CGFloat = 5.0 38 | let EFColorComponentLabelWidth: CGFloat = 30.0 39 | 40 | // The title. 41 | var title: String { 42 | get { 43 | return label.text ?? "" 44 | } 45 | set { 46 | label.text = newValue 47 | } 48 | } 49 | 50 | // The current value. The default value is 0.0. 51 | var value: CGFloat { 52 | get { 53 | return slider.value 54 | } 55 | set { 56 | slider.setValue(value: newValue) 57 | } 58 | } 59 | 60 | // The minimum value. The default value is 0.0. 61 | var minimumValue: CGFloat { 62 | get { 63 | return slider.minimumValue 64 | } 65 | set { 66 | slider.minimumValue = newValue 67 | } 68 | } 69 | 70 | // The maximum value. The default value is 255.0. 71 | var maximumValue: CGFloat { 72 | get { 73 | return slider.maximumValue 74 | } 75 | set { 76 | slider.maximumValue = newValue 77 | } 78 | } 79 | 80 | // The format string to use apply for textfield value. \c %.f by default. 81 | var format: String = "%.f" 82 | 83 | private let label: UILabel = UILabel() 84 | private let slider: EFSliderView = EFSliderView() // The color slider to edit color component. 85 | 86 | override open class var requiresConstraintBasedLayout: Bool { 87 | get { 88 | return true 89 | } 90 | } 91 | 92 | override init(frame: CGRect) { 93 | super.init(frame: frame) 94 | ef_baseInit() 95 | } 96 | 97 | required public init?(coder aDecoder: NSCoder) { 98 | super.init(coder: aDecoder) 99 | ef_baseInit() 100 | } 101 | 102 | // Sets the array of CGColorRef objects defining the color of each gradient stop on a slider's track. 103 | // The location of each gradient stop is evaluated with formula: i * width_of_the_track / number_of_colors. 104 | // @param colors An array of CGColorRef objects. 105 | func setColors(colors: [UIColor]) { 106 | 107 | if colors.count <= 1 { 108 | fatalError("‘colors: [CGColor]’ at least need to have 2 elements") 109 | } 110 | 111 | slider.setColors(colors: colors) 112 | } 113 | 114 | // MARK:- Private methods 115 | private func ef_baseInit() { 116 | self.accessibilityLabel = "color_component_view" 117 | 118 | label.translatesAutoresizingMaskIntoConstraints = false 119 | label.adjustsFontSizeToFitWidth = true 120 | self.addSubview(label) 121 | 122 | slider.maximumValue = EFRGBColorComponentMaxValue 123 | slider.translatesAutoresizingMaskIntoConstraints = false 124 | self.addSubview(slider) 125 | 126 | self.value = 0.0 127 | 128 | slider.addTarget(self, action: #selector(ef_didChangeSliderValue(sender:)), for: UIControl.Event.valueChanged) 129 | 130 | self.ef_installConstraints() 131 | } 132 | 133 | @objc private func ef_didChangeSliderValue(sender: EFSliderView) { 134 | self.value = sender.value 135 | self.sendActions(for: UIControl.Event.valueChanged) 136 | } 137 | 138 | private func ef_installConstraints() { 139 | let views: [String : Any] = [ 140 | "label" : label, 141 | "slider" : slider 142 | ] 143 | let metrics: [String : Any] = [ 144 | "spacing" : EFColorComponentViewSpacing, 145 | "label_width" : EFColorComponentLabelWidth 146 | ] 147 | self.addConstraints( 148 | NSLayoutConstraint.constraints( 149 | withVisualFormat: "H:|[label(label_width)]-spacing-[slider]-spacing-|", 150 | options: NSLayoutConstraint.FormatOptions.alignAllCenterY, 151 | metrics: metrics, 152 | views: views 153 | ) 154 | ) 155 | self.addConstraints( 156 | NSLayoutConstraint.constraints( 157 | withVisualFormat: "V:|[label]|", 158 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 159 | metrics: nil, 160 | views: views 161 | ) 162 | ) 163 | } 164 | 165 | private func ef_remakeConstraints() { 166 | // Remove all old constraints 167 | let views: [String : Any] = [ 168 | "label" : label, 169 | "slider" : slider 170 | ] 171 | let metrics: [String : Any] = [ 172 | "spacing" : EFColorComponentViewSpacing, 173 | "label_width" : EFColorComponentLabelWidth 174 | ] 175 | self.removeConstraints( 176 | NSLayoutConstraint.constraints( 177 | withVisualFormat: "H:|[label(label_width)]-spacing-[slider]-spacing-|", 178 | options: NSLayoutConstraint.FormatOptions.alignAllCenterY, 179 | metrics: metrics, 180 | views: views 181 | ) 182 | ) 183 | self.removeConstraints( 184 | NSLayoutConstraint.constraints( 185 | withVisualFormat: "V:|[label]|", 186 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 187 | metrics: nil, 188 | views: views 189 | ) 190 | ) 191 | 192 | // Readd control 193 | for control in [label, slider] { 194 | control.removeFromSuperview() 195 | self.addSubview(control) 196 | } 197 | 198 | // Add new constraints 199 | ef_installConstraints() 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /Unicorn/Component/Text/Component/Color/ColorPicker/EFColorWheelView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFColorWheelView.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/29. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | import CoreGraphics 29 | 30 | 31 | public class EFColorWheelView: UIControl { 32 | /// 色相 33 | var hue: CGFloat = 0.0 { 34 | didSet { 35 | self.setSelectedPoint(point: ef_selectedPoint()) 36 | self.setNeedsDisplay() 37 | } 38 | } 39 | 40 | /// 饱和度 41 | var saturation: CGFloat = 0.0 { 42 | didSet { 43 | self.setSelectedPoint(point: ef_selectedPoint()) 44 | self.setNeedsDisplay() 45 | } 46 | } 47 | 48 | /// 亮度 49 | var brightness: CGFloat = 1.0 { 50 | didSet { 51 | self.setSelectedPoint(point: ef_selectedPoint()) 52 | self.setNeedsDisplay() 53 | } 54 | } 55 | 56 | /// 指示圈 57 | private lazy var indicatorLayer: CALayer = { 58 | let dimension: CGFloat = 33 59 | let edgeColor = UIColor(white: 0.9, alpha: 0.8) 60 | 61 | let indicatorLayer = CALayer() 62 | indicatorLayer.cornerRadius = dimension / 2 63 | indicatorLayer.borderColor = edgeColor.cgColor 64 | indicatorLayer.borderWidth = 2 65 | indicatorLayer.backgroundColor = UIColor.white.cgColor 66 | indicatorLayer.bounds = CGRect(x: 0, y: 0, width: dimension, height: dimension) 67 | indicatorLayer.position = CGPoint(x: self.bounds.width / 2, y: self.bounds.height / 2) 68 | indicatorLayer.shadowColor = UIColor.black.cgColor 69 | indicatorLayer.shadowOffset = CGSize.zero 70 | indicatorLayer.shadowRadius = 1 71 | indicatorLayer.shadowOpacity = 0.5 72 | return indicatorLayer 73 | }() 74 | 75 | // 基于约束的布局是懒加载的,只有在添加约束的情况下才会触发 `updateConstraints` 方法,但如果把所有约束布局代码写在其中,则系统并不知道是否是基于约束的布局,使用该方法强行告诉系统进行调用 76 | override open class var requiresConstraintBasedLayout: Bool { 77 | get { 78 | return true 79 | } 80 | } 81 | 82 | override init(frame: CGRect) { 83 | super.init(frame: frame) 84 | 85 | self.layer.delegate = self 86 | self.layer.addSublayer(self.indicatorLayer) 87 | } 88 | 89 | required public init?(coder aDecoder: NSCoder) { 90 | fatalError("init(coder:) has not been implemented") 91 | } 92 | 93 | override public func touchesBegan(_ touches: Set, with event: UIEvent?) { 94 | if let position: CGPoint = touches.first?.location(in: self) { 95 | self.onTouchEventWithPosition(point: position) 96 | } 97 | } 98 | 99 | override public func touchesMoved(_ touches: Set, with event: UIEvent?) { 100 | if let position: CGPoint = touches.first?.location(in: self) { 101 | self.onTouchEventWithPosition(point: position) 102 | } 103 | } 104 | 105 | override public func touchesEnded(_ touches: Set, with event: UIEvent?) { 106 | if let position: CGPoint = touches.first?.location(in: self) { 107 | self.onTouchEventWithPosition(point: position) 108 | } 109 | } 110 | 111 | private func onTouchEventWithPosition(point: CGPoint) { 112 | let radius: CGFloat = self.bounds.width / 2 113 | 114 | let mx = Double(radius - point.x) 115 | let my = Double(radius - point.y) 116 | let dist: CGFloat = CGFloat(sqrt(mx * mx + my * my)) 117 | 118 | if dist <= radius { 119 | self.ef_colorWheelValueWithPosition(position: point, hue: &hue, saturation: &saturation) 120 | self.setSelectedPoint(point: point) 121 | self.sendActions(for: UIControl.Event.valueChanged) 122 | } 123 | } 124 | 125 | func setSelectedPoint(point: CGPoint) { 126 | let selectedColor: UIColor = UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: 1) 127 | 128 | CATransaction.begin() 129 | CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) 130 | self.indicatorLayer.position = point 131 | self.indicatorLayer.backgroundColor = selectedColor.cgColor 132 | CATransaction.commit() 133 | } 134 | 135 | // MARK:- CALayerDelegate methods 136 | override public func display(_ layer: CALayer) { 137 | let dimension: CGFloat = min(self.frame.width, self.frame.height) 138 | guard let bitmapData = CFDataCreateMutable(nil, 0) else { 139 | return 140 | } 141 | 142 | // 造了一个位图,按像素进行颜色填充 143 | CFDataSetLength(bitmapData, CFIndex(dimension * dimension * 4)) 144 | self.ef_colorWheelBitmap( 145 | bitmap: CFDataGetMutableBytePtr(bitmapData), 146 | withSize: CGSize(width: dimension, height: dimension) 147 | ) 148 | if let image = self.ef_imageWithRGBAData(data: bitmapData, width: Int(dimension), height: Int(dimension)) { 149 | self.layer.contents = image 150 | } 151 | } 152 | 153 | override public func layoutSublayers(of layer: CALayer) { 154 | if layer == self.layer { 155 | self.setSelectedPoint(point: self.ef_selectedPoint()) 156 | self.layer.setNeedsDisplay() 157 | } 158 | } 159 | 160 | // MARK:- Private methods 161 | private func ef_selectedPoint() -> CGPoint { 162 | let dimension: CGFloat = min(self.frame.width, self.frame.height) 163 | 164 | let radius: CGFloat = saturation * dimension / 2 165 | let x: CGFloat = dimension / 2 + radius * CGFloat(cos(Double(hue) * Double.pi * 2.0)) 166 | let y: CGFloat = dimension / 2 + radius * CGFloat(sin(Double(hue) * Double.pi * 2.0)) 167 | 168 | return CGPoint(x: x, y: y) 169 | } 170 | 171 | private func ef_colorWheelBitmap(bitmap: UnsafeMutablePointer?, withSize size: CGSize) { 172 | if size.width <= 0 || size.height <= 0 { 173 | return 174 | } 175 | 176 | // 填充每一个像素的内容 177 | for y in 0 ..< Int(size.width) { 178 | for x in 0 ..< Int(size.height) { 179 | var hue: CGFloat = 0, saturation: CGFloat = 0, a: CGFloat = 0.0 180 | self.ef_colorWheelValueWithPosition(position: CGPoint(x: x, y: y), hue: &hue, saturation: &saturation) 181 | 182 | var rgb: RGB = RGB(1, 1, 1, 1) 183 | if saturation < 1.0 { 184 | // Antialias the edge of the circle. 185 | if saturation > 0.99 { 186 | a = (1.0 - saturation) * 100 187 | } else { 188 | a = 1.0 189 | } 190 | 191 | let hsb: HSB = HSB(hue, saturation, brightness, a) 192 | rgb = EFHSB2RGB(hsb: hsb) 193 | } 194 | 195 | // RGBA 四通道 196 | let i: Int = 4 * (x + y * Int(size.width)) 197 | bitmap?[i] = UInt8(rgb.red * 0xff) 198 | bitmap?[i + 1] = UInt8(rgb.green * 0xff) 199 | bitmap?[i + 2] = UInt8(rgb.blue * 0xff) 200 | bitmap?[i + 3] = UInt8(rgb.alpha * 0xff) 201 | } 202 | } 203 | } 204 | 205 | private func ef_colorWheelValueWithPosition(position: CGPoint, hue: inout CGFloat, saturation: inout CGFloat) { 206 | let c: Int = Int(self.bounds.width / 2) 207 | let dx: CGFloat = (position.x - CGFloat(c)) / CGFloat(c) 208 | let dy: CGFloat = (position.y - CGFloat(c)) / CGFloat(c) 209 | let d: CGFloat = CGFloat(sqrt(Double(dx * dx + dy * dy))) 210 | 211 | saturation = d 212 | 213 | if d == 0 { 214 | hue = 0 215 | } else { 216 | hue = acos(dx / d) / CGFloat.pi / 2.0 217 | 218 | if dy < 0 { 219 | hue = 1.0 - hue 220 | } 221 | } 222 | } 223 | 224 | private func ef_imageWithRGBAData(data: CFData, width: Int, height: Int) -> CGImage? { 225 | guard let dataProvider = CGDataProvider(data: data) else { 226 | return nil 227 | } 228 | let colorSpace = CGColorSpaceCreateDeviceRGB() 229 | 230 | let imageRef = CGImage( 231 | width: width, 232 | height: height, 233 | bitsPerComponent: 8, 234 | bitsPerPixel: 32, 235 | bytesPerRow: width * 4, 236 | space: colorSpace, 237 | bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue), 238 | provider: dataProvider, 239 | decode: nil, 240 | shouldInterpolate: false, 241 | intent: CGColorRenderingIntent.defaultIntent 242 | ) 243 | return imageRef 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /Unicorn/Component/Text/Component/Color/ColorPicker/EFControl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFControl.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/28. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | public class EFControl: UIControl { 30 | 31 | // Edge inset values are applied to a view bounds to shrink or expand the touchable area. 32 | var hitTestEdgeInsets: UIEdgeInsets = UIEdgeInsets.zero 33 | 34 | override public func point(inside point: CGPoint, with event: UIEvent?) -> Bool { 35 | if self.hitTestEdgeInsets == UIEdgeInsets.zero 36 | || !self.isEnabled 37 | || self.isHidden 38 | || !self.isUserInteractionEnabled 39 | || 0 == self.alpha { 40 | return super.point(inside: point, with: event) 41 | } 42 | 43 | let hitFrame: CGRect = self.bounds.inset(by: self.hitTestEdgeInsets) 44 | return hitFrame.contains(point) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Unicorn/Component/Text/Component/Color/ColorPicker/EFHSBView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFHSBView.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/29. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | // The view to edit HSB color components. 30 | class EFHSBView: UIView { 31 | 32 | let EFColorSampleViewHeight: CGFloat = 30.0 33 | let EFViewMargin: CGFloat = 10.0 34 | let EFColorWheelDimension: CGFloat = 120.0 35 | 36 | private let colorWheel: EFColorWheelView = EFColorWheelView() 37 | let brightnessView: EFColorComponentView = EFColorComponentView() 38 | 39 | /// 白色 40 | private var colorComponents: HSB = HSB(1, 1, 1, 1) 41 | private var layoutConstraints: [NSLayoutConstraint] = [] 42 | 43 | weak var delegate: EFHSBViewDelegate? 44 | 45 | public var color: UIColor { 46 | get { 47 | return UIColor( 48 | hue: colorComponents.hue, 49 | saturation: colorComponents.saturation, 50 | brightness: colorComponents.brightness, 51 | alpha: colorComponents.alpha 52 | ) 53 | } 54 | set { 55 | colorComponents = EFRGB2HSB(rgb: EFRGBColorComponents(color: newValue)) 56 | self.reloadData() 57 | } 58 | } 59 | 60 | override init(frame: CGRect) { 61 | super.init(frame: frame) 62 | self.ef_baseInit() 63 | } 64 | 65 | required init?(coder aDecoder: NSCoder) { 66 | fatalError("init(coder:) has not been implemented") 67 | } 68 | 69 | func reloadData() { 70 | self.ef_reloadViewsWithColorComponents(colorComponents: colorComponents) 71 | self.colorWheel.display(self.colorWheel.layer) 72 | } 73 | 74 | override public func updateConstraints() { 75 | self.ef_updateConstraints() 76 | super.updateConstraints() 77 | } 78 | 79 | // MARK:- Private methods 80 | private func ef_baseInit() { 81 | // 使用约束布局时要设置为 false,取消 frame 布局自动转化为约束布局 82 | colorWheel.translatesAutoresizingMaskIntoConstraints = false 83 | self.addSubview(colorWheel) 84 | 85 | brightnessView.title = NSLocalizedString("亮度", comment: "") 86 | brightnessView.value = 1 87 | brightnessView.maximumValue = EFHSBColorComponentMaxValue 88 | brightnessView.format = "%.2f" 89 | brightnessView.translatesAutoresizingMaskIntoConstraints = false 90 | brightnessView.setColors(colors: [UIColor.black, UIColor.white]) 91 | self.addSubview(brightnessView) 92 | 93 | colorWheel.addTarget( 94 | self, action: #selector(ef_colorDidChangeValue(sender:)), for: UIControl.Event.valueChanged 95 | ) 96 | brightnessView.addTarget( 97 | self, action: #selector(ef_brightnessDidChangeValue(sender:)), for: UIControl.Event.valueChanged 98 | ) 99 | 100 | self.setNeedsUpdateConstraints() 101 | } 102 | 103 | private func ef_updateConstraints() { 104 | // remove all constraints first 105 | if !layoutConstraints.isEmpty { 106 | self.removeConstraints(layoutConstraints) 107 | } 108 | 109 | layoutConstraints = UIUserInterfaceSizeClass.compact == self.traitCollection.verticalSizeClass 110 | ? self.ef_constraintsForCompactVerticalSizeClass() 111 | : self.ef_constraintsForRegularVerticalSizeClass() 112 | 113 | self.addConstraints(layoutConstraints) 114 | } 115 | 116 | private func ef_constraintsForRegularVerticalSizeClass() -> [NSLayoutConstraint] { 117 | let metrics = [ 118 | "margin" : EFViewMargin, 119 | "color_wheel_dimension" : EFColorWheelDimension 120 | ] 121 | let views = [ 122 | "colorWheel" : colorWheel, 123 | "brightnessView" : brightnessView 124 | ] 125 | 126 | var layoutConstraints: [NSLayoutConstraint] = [] 127 | let visualFormats = [ 128 | "H:|-margin-[colorWheel(>=color_wheel_dimension)]-margin-|", 129 | "H:|-margin-[brightnessView]-margin-|", 130 | "V:|-margin-[colorWheel]-[brightnessView]-|" 131 | 132 | ] 133 | for visualFormat in visualFormats { 134 | layoutConstraints.append( 135 | contentsOf: NSLayoutConstraint.constraints( 136 | withVisualFormat: visualFormat, 137 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 138 | metrics: metrics, 139 | views: views 140 | ) 141 | ) 142 | } 143 | layoutConstraints.append( 144 | NSLayoutConstraint( 145 | item: colorWheel, 146 | attribute: NSLayoutConstraint.Attribute.width, 147 | relatedBy: NSLayoutConstraint.Relation.equal, 148 | toItem: colorWheel, 149 | attribute: NSLayoutConstraint.Attribute.height, 150 | multiplier: 1, 151 | constant: 0) 152 | ) 153 | return layoutConstraints 154 | } 155 | 156 | private func ef_constraintsForCompactVerticalSizeClass() -> [NSLayoutConstraint] { 157 | let metrics = [ 158 | "margin" : EFViewMargin, 159 | "color_wheel_dimension" : EFColorWheelDimension 160 | ] 161 | let views = [ 162 | "colorWheel" : colorWheel, 163 | "brightnessView" : brightnessView 164 | ] 165 | 166 | var layoutConstraints: [NSLayoutConstraint] = [] 167 | let visualFormats = [ 168 | "H:|-margin-[colorWheel(>=color_wheel_dimension)]-margin-[brightnessView]-(margin@500)-|", 169 | "V:|-margin-[colorWheel]-(margin@500)-|" 170 | ] 171 | for visualFormat in visualFormats { 172 | layoutConstraints.append( 173 | contentsOf: NSLayoutConstraint.constraints( 174 | withVisualFormat: visualFormat, 175 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 176 | metrics: metrics, 177 | views: views 178 | ) 179 | ) 180 | } 181 | layoutConstraints.append( 182 | NSLayoutConstraint( 183 | item: colorWheel, 184 | attribute: NSLayoutConstraint.Attribute.width, 185 | relatedBy: NSLayoutConstraint.Relation.equal, 186 | toItem: colorWheel, 187 | attribute: NSLayoutConstraint.Attribute.height, 188 | multiplier: 1, 189 | constant: 0) 190 | ) 191 | layoutConstraints.append( 192 | NSLayoutConstraint( 193 | item: brightnessView, 194 | attribute: NSLayoutConstraint.Attribute.centerY, 195 | relatedBy: NSLayoutConstraint.Relation.equal, 196 | toItem: self, 197 | attribute: NSLayoutConstraint.Attribute.centerY, 198 | multiplier: 1, 199 | constant: 0) 200 | ) 201 | return layoutConstraints 202 | } 203 | 204 | private func ef_reloadViewsWithColorComponents(colorComponents: HSB) { 205 | colorWheel.hue = colorComponents.hue 206 | colorWheel.saturation = colorComponents.saturation 207 | colorWheel.brightness = colorComponents.brightness 208 | self.ef_updateSlidersWithColorComponents(colorComponents: colorComponents) 209 | } 210 | 211 | private func ef_updateSlidersWithColorComponents(colorComponents: HSB) { 212 | brightnessView.value = colorComponents.brightness 213 | } 214 | 215 | @objc private func ef_colorDidChangeValue(sender: EFColorWheelView) { 216 | colorComponents.hue = sender.hue 217 | colorComponents.saturation = sender.saturation 218 | self.delegate?.colorView(self, didChangeColor: self.color) 219 | self.reloadData() 220 | } 221 | 222 | @objc private func ef_brightnessDidChangeValue(sender: EFColorComponentView) { 223 | colorComponents.brightness = sender.value 224 | self.colorWheel.brightness = sender.value 225 | self.delegate?.colorView(self, didChangeColor: self.color) 226 | self.reloadData() 227 | } 228 | } 229 | 230 | 231 | protocol EFHSBViewDelegate: class { 232 | func colorView(_ colorView: EFHSBView, 233 | didChangeColor color: UIColor) 234 | } 235 | 236 | extension EFHSBViewDelegate { 237 | func colorView(_ colorView: EFHSBView, 238 | didChangeColor color: UIColor) {} 239 | } 240 | -------------------------------------------------------------------------------- /Unicorn/Component/Text/Component/Color/ColorPicker/EFSliderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFSliderView.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/28. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | import CoreGraphics 29 | import QuartzCore 30 | import UIKit 31 | 32 | public class EFSliderView: EFControl { 33 | 34 | let EFSliderViewHeight: CGFloat = 28.0 35 | let EFSliderViewMinWidth: CGFloat = 150.0 36 | let EFSliderViewTrackHeight: CGFloat = 6.0 37 | let EFThumbViewEdgeInset: CGFloat = -10.0 38 | 39 | private let thumbView: EFThumbView = EFThumbView() 40 | private let trackLayer: CAGradientLayer = CAGradientLayer() 41 | 42 | // The slider's current value. The default value is 0.0. 43 | private(set) var value: CGFloat = 0 44 | 45 | // The minimum value of the slider. The default value is 0.0. 46 | var minimumValue: CGFloat = 0 47 | 48 | // The maximum value of the slider. The default value is 1.0. 49 | var maximumValue: CGFloat = 1 50 | 51 | // Indicates if the user touches the control at the moment 52 | var isTouched = false 53 | 54 | override init(frame: CGRect) { 55 | super.init(frame: frame) 56 | 57 | self.accessibilityLabel = "color_slider" 58 | 59 | minimumValue = 0.0 60 | maximumValue = 1.0 61 | value = 0.0 62 | 63 | self.layer.delegate = self 64 | 65 | trackLayer.cornerRadius = EFSliderViewTrackHeight / 2.0 66 | trackLayer.startPoint = CGPoint(x: 0.0, y: 0.5) 67 | trackLayer.endPoint = CGPoint(x: 1.0, y: 0.5) 68 | self.layer.addSublayer(trackLayer) 69 | 70 | thumbView.hitTestEdgeInsets = UIEdgeInsets( 71 | top: EFThumbViewEdgeInset, left: EFThumbViewEdgeInset, 72 | bottom: EFThumbViewEdgeInset, right: EFThumbViewEdgeInset 73 | ) 74 | thumbView.gestureRecognizer.addTarget(self, action: #selector(ef_didPanThumbView(gestureRecognizer:))) 75 | self.addSubview(thumbView) 76 | 77 | let color = UIColor.blue 78 | self.setColors(colors: [color, color]) 79 | } 80 | 81 | required public init?(coder aDecoder: NSCoder) { 82 | fatalError("init(coder:) has not been implemented") 83 | } 84 | 85 | override open class var requiresConstraintBasedLayout: Bool { 86 | get { 87 | return true 88 | } 89 | } 90 | 91 | override public var intrinsicContentSize: CGSize { 92 | get { 93 | return CGSize(width: EFSliderViewMinWidth, height: EFSliderViewHeight) 94 | } 95 | } 96 | 97 | func setValue(value: CGFloat) { 98 | if (value < minimumValue) { 99 | self.value = minimumValue 100 | } else if (value > maximumValue) { 101 | self.value = maximumValue 102 | } else { 103 | self.value = value 104 | } 105 | 106 | self.ef_updateThumbPositionWithValue(value: self.value) 107 | } 108 | 109 | // Sets the array of CGColorRef objects defining the color of each gradient stop on the track. 110 | // The location of each gradient stop is evaluated with formula: i * width_of_the_track / number_of_colors. 111 | // @param colors An array of CGColorRef objects. 112 | func setColors(colors: [UIColor]) { 113 | let cgColors = colors.map { color -> CGColor in 114 | return color.cgColor 115 | } 116 | if cgColors.count <= 1 { 117 | fatalError("‘colors: [CGColor]’ at least need to have 2 elements") 118 | } 119 | trackLayer.colors = cgColors 120 | self.ef_updateLocations() 121 | } 122 | 123 | override public func layoutSubviews() { 124 | self.ef_updateThumbPositionWithValue(value: self.value) 125 | self.ef_updateTrackLayer() 126 | } 127 | 128 | // MARK:- UIControl touch tracking events 129 | @objc func ef_didPanThumbView(gestureRecognizer: UIPanGestureRecognizer) { 130 | if gestureRecognizer.state == UIGestureRecognizer.State.ended { 131 | self.isTouched = false 132 | } else if gestureRecognizer.state == UIGestureRecognizer.State.began { 133 | self.isTouched = true 134 | } 135 | 136 | if gestureRecognizer.state != UIGestureRecognizer.State.began 137 | && gestureRecognizer.state != UIGestureRecognizer.State.changed { 138 | return 139 | } 140 | 141 | let translation = gestureRecognizer.translation(in: self) 142 | gestureRecognizer.setTranslation(CGPoint.zero, in: self) 143 | 144 | self.ef_setValueWithTranslation(translation: translation.x) 145 | } 146 | 147 | func ef_updateTrackLayer() { 148 | let height: CGFloat = EFSliderViewHeight 149 | let width: CGFloat = self.bounds.width 150 | 151 | CATransaction.begin() 152 | CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) 153 | trackLayer.bounds = CGRect(x: 0, y: 0, width: width, height: EFSliderViewTrackHeight) 154 | trackLayer.position = CGPoint(x: self.bounds.width / 2, y: height / 2) 155 | CATransaction.commit() 156 | } 157 | 158 | // MARK:- Private methods 159 | private func ef_setValueWithTranslation(translation: CGFloat) { 160 | let width: CGFloat = self.bounds.width - thumbView.bounds.width 161 | let valueRange: CGFloat = maximumValue - minimumValue 162 | let value: CGFloat = self.value + valueRange * translation / width 163 | 164 | self.setValue(value: value) 165 | self.sendActions(for: UIControl.Event.valueChanged) 166 | } 167 | 168 | private func ef_updateLocations() { 169 | let size: Int = trackLayer.colors?.count ?? 2 170 | if size == trackLayer.locations?.count { 171 | return 172 | } 173 | 174 | let step: CGFloat = 1.0 / (CGFloat(size) - 1) 175 | var locations: [NSNumber] = [0] 176 | 177 | var i: Int = 1 178 | while i < size - 1 { 179 | locations.append(NSNumber(value: Double(CGFloat(i) * step))) 180 | i += 1 181 | } 182 | 183 | locations.append(1.0) 184 | trackLayer.locations = locations 185 | } 186 | 187 | private func ef_updateThumbPositionWithValue(value: CGFloat) { 188 | let thumbWidth: CGFloat = thumbView.bounds.width 189 | let thumbHeight: CGFloat = thumbView.bounds.height 190 | let width: CGFloat = self.bounds.width - thumbWidth 191 | 192 | if width == 0 { 193 | return 194 | } 195 | 196 | let percentage: CGFloat = (value - minimumValue) / (maximumValue - minimumValue) 197 | let position: CGFloat = width * percentage 198 | thumbView.frame = CGRect(x: position, y: 0, width: thumbWidth, height: thumbHeight) 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /Unicorn/Component/Text/Component/Color/ColorPicker/EFThumbView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFThumbView.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/28. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | public class EFThumbView: EFControl { 30 | 31 | private(set) var gestureRecognizer: UIGestureRecognizer = UIPanGestureRecognizer(target: nil, action: nil) 32 | 33 | private let EFSliderViewThumbDimension: CGFloat = 28.0 34 | private let thumbLayer: CALayer = CALayer() 35 | 36 | override init(frame: CGRect) { 37 | super.init( 38 | frame: CGRect( 39 | x: frame.origin.x, y: frame.origin.y, 40 | width: EFSliderViewThumbDimension, height: EFSliderViewThumbDimension 41 | ) 42 | ) 43 | 44 | self.thumbLayer.borderColor = UIColor.lightGray.withAlphaComponent(0.4).cgColor 45 | self.thumbLayer.borderWidth = 0.5 46 | self.thumbLayer.cornerRadius = EFSliderViewThumbDimension / 2 47 | self.thumbLayer.backgroundColor = UIColor.white.cgColor 48 | self.thumbLayer.shadowColor = UIColor.black.cgColor 49 | self.thumbLayer.shadowOffset = CGSize(width: 0, height: 3) 50 | self.thumbLayer.shadowRadius = 2 51 | self.thumbLayer.shadowOpacity = 0.3 52 | self.layer.addSublayer(self.thumbLayer) 53 | 54 | self.addGestureRecognizer(self.gestureRecognizer) 55 | } 56 | 57 | required public init?(coder aDecoder: NSCoder) { 58 | fatalError("init(coder:) has not been implemented") 59 | } 60 | 61 | override public func layoutSublayers(of layer: CALayer) { 62 | if layer != self.layer { 63 | return 64 | } 65 | 66 | CATransaction.begin() 67 | CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) 68 | self.thumbLayer.bounds = CGRect( 69 | x: 0, y: 0, width: EFSliderViewThumbDimension, height: EFSliderViewThumbDimension 70 | ) 71 | self.thumbLayer.position = CGPoint(x: EFSliderViewThumbDimension / 2, y: EFSliderViewThumbDimension / 2) 72 | CATransaction.commit() 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Unicorn/Component/Text/Component/Color/UNBottomColorViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UNBottomColorViewController.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/4/5. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class UNBottomColorViewController: UIViewController { 12 | // 底部一行供选择的颜色 13 | var colors: [UIColor]? { 14 | didSet { 15 | colorCollectionView?.viewColorModels = colors 16 | } 17 | } 18 | // 圆盘颜色 19 | var colorChange: ((UIColor) -> Void)? 20 | // 底部颜色变化(从底部选中的颜色) 21 | var bottomColorChange: ((UIColor) -> Void)? 22 | // 当前颜色 23 | var currentColor: UIColor? 24 | 25 | private var colorCollectionView: UNLineCollectionView? 26 | 27 | override func viewDidLoad() { 28 | super.viewDidLoad() 29 | initView() 30 | } 31 | 32 | private func initView() { 33 | // EFColorPicker颜色选择器 34 | // 设置HSB色彩圆盘。H色相,S饱和度,B亮度(调用EFHSBView库-色彩圆盘) 35 | let colorView = EFHSBView(frame: CGRect(x: 0, y: 0, width: 200, height: 250)) 36 | // 当前的颜色(保证下次点开颜色盘时还定留在上次选择的颜色) 37 | if currentColor != nil { 38 | colorView.color = currentColor! 39 | } 40 | // 操作 代理 41 | colorView.delegate = self 42 | // 把色盘加进气泡视图里 43 | view.addSubview(colorView) 44 | 45 | // 下方固定颜色的布局 -- 【颜色数组】的集合视图 46 | let collectionViewLayout = UICollectionViewFlowLayout() 47 | let itemW = 25 48 | // 内部间距(固定颜色之间的间距) 49 | let innerW = CGFloat((200 - 5 * itemW) / 5) 50 | collectionViewLayout.itemSize = CGSize(width: itemW , height: itemW) 51 | // 最小行间距 52 | collectionViewLayout.minimumLineSpacing = innerW 53 | // 最小项间距 54 | collectionViewLayout.minimumInteritemSpacing = 10 55 | // 滚动方向 水平 56 | collectionViewLayout.scrollDirection = .horizontal 57 | // 距离四周的间距 58 | collectionViewLayout.sectionInset = UIEdgeInsets(top: 0, left: innerW / 2, bottom: 0, right: innerW / 2) 59 | 60 | // 线性集合视图(色盘下方的几个固定颜色选项) 61 | let colorCollectionView = UNLineCollectionView(frame: CGRect(x: 0, y: colorView.bottom, width: 200, height: 30), collectionViewLayout: collectionViewLayout) 62 | // 局部变量给全局变量 63 | self.colorCollectionView = colorCollectionView 64 | colorCollectionView.backgroundColor = .white 65 | // 线性集合视图类型 66 | colorCollectionView.lineType = .color 67 | // viewColorModels 颜色数组 68 | // 颜色是UNBottomColorViewController本类父类设置的。固定颜色数组。 69 | colorCollectionView.viewColorModels = colors 70 | // 获取到被点击的cell(用反向闭包传值,获取到用户点击的颜色) 71 | colorCollectionView.cellSelected = { selectedIndex in 72 | guard self.colors != nil else { return } 73 | // 根据索引位置判断用哪个颜色(再用闭包将色值传出) 74 | self.bottomColorChange?(self.colors![selectedIndex]) 75 | } 76 | view.addSubview(colorCollectionView) 77 | } 78 | } 79 | 80 | // 改变颜色的操作,EFHSBViewDelegate协议 获取颜色 81 | extension UNBottomColorViewController: EFHSBViewDelegate { 82 | // 当颜色改变时 调用该闭包(didChangeColor已经改变颜色) 83 | func colorView(_ colorView: EFHSBView, didChangeColor color: UIColor) { 84 | colorChange?(color) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Unicorn/Component/Text/Component/Font/UNBottomFontsTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UNBottomFontsTableViewController.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/4/4. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // 点击下栏按钮后弹出的选项:是列表视图 UITableViewController 12 | class UNBottomFontsTableViewController: UITableViewController { 13 | // 字体数据源(字符串数组)(字符串数据源 传进来) 14 | var fonts = [String]() {didSet{ tableView.reloadData() }} 15 | var cellSelected: ((Int) -> Void)? 16 | 17 | // 加载完视图 18 | override func viewDidLoad() { 19 | // override重载,要调用父视图的方法(super) 20 | super.viewDidLoad() 21 | // 要将cell注册进视图里,给cell标识符 22 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: "reuseIdentifier") 23 | } 24 | 25 | // 设置Section里内容的个数 26 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 27 | return 6 28 | } 29 | 30 | // 每一个cell里的Item。定义内容。 31 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 32 | // 要将cell注册进视图里,给cell标识符 33 | let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) 34 | cell.textLabel?.text = "这是字体" 35 | // 赋值字体。(字体数据源数组)字体 - 根据cell下标索引取fonts数据源里的数据 36 | cell.textLabel?.font = UIFont(name: fonts[indexPath.row], size: 20) 37 | return cell 38 | } 39 | 40 | // (点击字体的响应事件) 41 | // didSelectRowAt 已经选择了的Row,通过索引来判断 42 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 43 | cellSelected?(indexPath.row) 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Unicorn/Component/Text/Component/Size/UNBottomSizeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UNBottomSizeViewController.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/4/5. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class UNBottomSizeViewController: UIViewController { 12 | 13 | var size: CGFloat? 14 | var maxSize: CGFloat? 15 | var minSize: CGFloat? 16 | 17 | // xib画出气泡弹窗样式 UISlider滑动条为【大小】的调节样式; 18 | @IBOutlet weak var sizeSlider: UISlider! 19 | // UILabel 调节下方的文字(字号的数字) 20 | @IBOutlet private weak var sizeLabel: UILabel! 21 | 22 | // 反向闭包传值(用来给父视图传 文字大小的数值) 23 | var sizeChange: ((CGFloat) -> Void)? 24 | 25 | // 重载父视图 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | initView() 29 | } 30 | 31 | // 根据调节 改变数值(xib可设置,给横线调节组件sender 绑定value change状态) 32 | // 改变数值时 调用 33 | @IBAction func sizeSliderTouching(_ sender: UISlider) { 34 | // 获取“横线”sender的值(取到UISlider的值) 35 | sizeLabel.text = "大小:\(Int(sender.value))" 36 | // sizeChange闭包,反向传值。获取到值传出 37 | // Float类型,需要整数,所以用ceilf向上取整。例如:23.111 -> 24 38 | sizeChange?(CGFloat(ceilf(sender.value))) 39 | } 40 | 41 | // xib要初始化好给view,而storyboard可以直接读出来用 42 | // 初始化 气泡的view。 43 | private func initView() { 44 | // UNBottomSizeViewController.xib 初始化好给view 45 | let v = Bundle.main.loadNibNamed("UNBottomSizeViewController", owner: self, options: nil)?.first as! UIView 46 | // 父视图view的宽和高给v 47 | v.frame = view.frame 48 | 49 | // 可以设置Slider字号数值滑动条的最大值最小值(也可用xib写) 50 | if maxSize != nil { 51 | sizeSlider.maximumValue = Float(maxSize!) 52 | } 53 | 54 | if minSize != nil { 55 | sizeSlider.minimumValue = Float(minSize!) 56 | } 57 | 58 | // size必须有值 59 | if size != nil { 60 | // 读当前大小 61 | sizeLabel.text = "大小:\(Int(size!))" 62 | // 保证下一次点开时还是之前选择的文字大小 63 | sizeSlider.value = Float(size!) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Unicorn/Component/Text/Component/Size/UNBottomSizeViewController.xib: -------------------------------------------------------------------------------- 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 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Unicorn/Component/User/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Component/User/.DS_Store -------------------------------------------------------------------------------- /Unicorn/Component/User/Login/UNUserLoginViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UNUserLoginViewController.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/5/7. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class UNUserLoginViewController: UIViewController { 12 | 13 | @IBOutlet weak var phoneNumberTextField: UITextField! 14 | @IBOutlet weak var passwordTextField: UITextField! 15 | @IBOutlet weak var loginButton: UIButton! 16 | @IBOutlet weak var registerButton: UIButton! 17 | @IBOutlet weak var tipsLabel: UILabel! 18 | 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | initView() 23 | } 24 | 25 | private func initView() { 26 | 27 | } 28 | 29 | // 点击登录按钮 30 | @IBAction func loginButtonTapped(_ sender: Any) { 31 | guard phoneNumberTextField.text != nil else { return } 32 | guard passwordTextField.text != nil else { return } 33 | 34 | guard phoneNumberTextField.text?.count == 11 else { 35 | self.tipsLabel.isHidden = false 36 | self.tipsLabel.text = "手机号错误" 37 | return 38 | } 39 | 40 | // 调用login方法。传信息给User。complateHandler完成后处理。 41 | User.shared.login(username: phoneNumberTextField.text!, password: passwordTextField.text!, complateHandler: { tipsString in 42 | // tipsString 用户不存在时返回报错信息 43 | if tipsString != nil { 44 | // 主线程更新 UI 45 | DispatchQueue.main.async { 46 | // login方法中 可返回异常的原因 47 | self.tipsLabel.isHidden = false 48 | self.tipsLabel.text = tipsString 49 | } 50 | } else { 51 | // 如果用户存在,页面从上往下消失。 52 | self.dismiss(animated: true, completion: nil) 53 | } 54 | }) { 55 | print($0) 56 | } 57 | } 58 | 59 | // 点击注册按钮 60 | @IBAction func registerButtonTapped(_ sender: Any) { 61 | guard phoneNumberTextField.text != nil else { return } 62 | guard passwordTextField.text != nil else { return } 63 | 64 | guard phoneNumberTextField.text?.count == 11 else { 65 | self.tipsLabel.isHidden = false 66 | self.tipsLabel.text = "手机号错误" 67 | return 68 | } 69 | 70 | // 调用register方法。传信息给User。complateHandler完成后处理。 71 | User.shared.register(username: phoneNumberTextField.text!, password: passwordTextField.text!, complateHandler: { tipsString in 72 | // tipsString 注册不成功时 返回报错信息 73 | if tipsString != nil { 74 | // 主线程更新 UI 75 | DispatchQueue.main.async { 76 | // register方法中 可返回异常的原因 77 | self.tipsLabel.isHidden = false 78 | self.tipsLabel.text = tipsString 79 | } 80 | } else { 81 | // 注册成功 界面消失 82 | self.dismiss(animated: true, completion: nil) 83 | } 84 | }) { 85 | print($0) 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /Unicorn/Component/User/User.swift: -------------------------------------------------------------------------------- 1 | // 2 | // User.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/5/7. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class User { 12 | static let shared = User() 13 | var viewModel = ViewModel() 14 | // 账户路径 , 用来获取沙盒位置 15 | static let userAccountPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first! 16 | 17 | // 从沙盒中读取 18 | init() { 19 | let viewModel = readBySandBox() 20 | if viewModel != nil { 21 | self.viewModel = viewModel! 22 | } 23 | } 24 | 25 | // 登录:complateHandler 结果返回成功,failedHandler 结果返回失败 26 | func login(username: String, 27 | password: String, 28 | // 完成的处理回调 29 | complateHandler: @escaping ((String?) -> Void), 30 | // 失败的处理回调 31 | failedHandler: @escaping ((Error) -> Void)) { 32 | // key:value 字典。拼接参数。 33 | let params = [ 34 | "phoneNumber": username, 35 | "password": password, 36 | "nickname": "", 37 | ] 38 | 39 | // 用Network网络请求,构造http请求体。传参。 40 | // 调用post请求。“http...”-后端登录接口的api。 41 | Network.shared.post(urlString: "http://localhost:8080/login", 42 | // 参数:参数的集合 43 | params: params, 44 | // 完成的处理回调(在此处理 请求完服务端后返回的数据) 45 | complateHandler: { 46 | // 把第0个参数,强转为字典类型key,value 47 | let dataDict = $0 as! Dictionary 48 | // 例如此处是取key为"error"的 49 | if dataDict["error"] != nil { 50 | // 如果存在异常返回原因 String类型。调用系统的... 51 | complateHandler((dataDict["reason"] as! String)) 52 | } else { 53 | // 如果合法没有error,直接获取用户所输入的 54 | self.viewModel.phoneNumber = username 55 | // 服务端返回的token 56 | self.viewModel.token = (dataDict["token"] as! String) 57 | // 返回的“Token”里有所有信息 58 | self.viewModel.uid = (dataDict["userId"] as! Int) 59 | self.saveToSandBox() 60 | // 结果返回成功 61 | complateHandler(nil) 62 | } 63 | }) { 64 | print($0!) 65 | failedHandler($0!) 66 | } 67 | } 68 | 69 | func details() { 70 | 71 | } 72 | 73 | // 注册,结果返回成功/失败 74 | func register(username: String, 75 | password: String, 76 | // 完成的处理回调 77 | complateHandler: @escaping ((String?) -> Void), 78 | // 失败的处理回调 79 | failedHandler: @escaping ((Error) -> Void)) { 80 | // key:value 字典。拼接参数。 81 | let params = [ 82 | "phoneNumber": username, 83 | "password": password, 84 | "nickname": "", 85 | ] 86 | 87 | // 用Network网络请求,构造http请求体 88 | // 调用post请求。“http...”-后端注册接口的api。 89 | Network.shared.post(urlString: "http://localhost:8080/register", 90 | // 参数:参数的集合 91 | params: params, 92 | // 完成的处理回调(在此处理 请求完服务端后返回的数据) 93 | complateHandler: { (dataArr) in 94 | // 注册成功后 直接登录 95 | self.login(username: username, 96 | password: password, 97 | complateHandler: { 98 | complateHandler($0) 99 | }, failedHandler: { 100 | failedHandler($0) 101 | }) 102 | }) { 103 | failedHandler($0!) 104 | } 105 | } 106 | 107 | // 退出登录,发送请求给服务器 清除Token,客户端再清除Token 108 | func logout(complateHandler: @escaping (() -> Void)) { 109 | Network.shared.get(urlString: "http://localhost:8080/api/user/logout", 110 | params: [:], 111 | complateHandler: { (data) in 112 | // token: nil 清除token 113 | self.viewModel = ViewModel(uid: nil, token: nil, nickname: nil, phoneNumber: nil, password: nil) 114 | self.saveToSandBox() 115 | complateHandler() 116 | }) { 117 | print($0!) 118 | } 119 | } 120 | 121 | func update() { 122 | 123 | } 124 | } 125 | 126 | extension User { 127 | struct ViewModel: Codable { 128 | var uid: Int? 129 | var token: String? 130 | var nickname: String? 131 | var phoneNumber: String? 132 | var password: String? 133 | } 134 | } 135 | 136 | extension User { 137 | /// 保存到沙盒中 138 | func saveToSandBox() { 139 | // 编码器。将数据转为二进制流 才可存入沙盒 140 | let encoder = JSONEncoder() 141 | if let BeerData = try? encoder.encode(self.viewModel) { 142 | let url = URL.init(fileURLWithPath: User.userAccountPath).appendingPathComponent("userData.data") 143 | do { 144 | try BeerData.write(to: url) 145 | print("完成了对数据的二进制化归档") 146 | } catch { 147 | print("saveToSandBox \(error.localizedDescription)") 148 | assert(true, "saveToSandBox \(error.localizedDescription)") 149 | } 150 | } 151 | } 152 | 153 | /// 从沙盒中读取 154 | func readBySandBox() -> ViewModel? { 155 | let url = URL.init(fileURLWithPath: User.userAccountPath).appendingPathComponent("userData.data") 156 | do { 157 | let data = try FileHandle.init(forReadingFrom: url) 158 | do { 159 | return try JSONDecoder().decode(ViewModel.self, from: data.readDataToEndOfFile()) 160 | } catch { 161 | assert(true, "readBySandBox JSONDecoder \(error.localizedDescription)") 162 | } 163 | } catch { 164 | assert(true, "readBySandBox \(error.localizedDescription)") 165 | } 166 | return nil 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /Unicorn/Config/Define.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Define.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/4/2. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreGraphics 11 | 12 | 13 | let screenWidth = UIScreen.main.bounds.size.width 14 | let screentHeight = UIScreen.main.bounds.size.height 15 | // 底部安全距离 16 | let bottomSafeAreaHeight = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0.0 17 | 18 | //顶部的安全距离 19 | let topSafeAreaHeight = UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0.0 20 | 21 | //状态栏高度 22 | let statusBarHeight = UIApplication.shared.statusBarFrame.height; 23 | 24 | //导航栏高度 25 | let navigationHeight = 44 + topSafeAreaHeight 26 | 27 | 28 | func heightForString(textView: UITextView, textWidth: CGFloat) -> CGFloat { 29 | //获取想要的view的size 这边是固定宽度为textview的宽度 高度自适应 30 | let getSize:CGSize = textView.sizeThatFits(CGSize.init(width: textWidth, height: CGFloat(MAXFLOAT))) 31 | //获取textView的高度 32 | return getSize.height 33 | } 34 | 35 | private let colorComponentValueRange = (CGFloat(0.0) ... CGFloat(1.0)) 36 | 37 | // The structure to represent a color in the Red-Green-Blue-Alpha color space. 38 | struct RGB { 39 | var red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat 40 | 41 | init(_ red: CGFloat, _ green: CGFloat, _ blue: CGFloat, _ alpha: CGFloat) { 42 | self.red = red 43 | self.green = green 44 | self.blue = blue 45 | self.alpha = alpha 46 | } 47 | } 48 | 49 | // The structure to represent a color in the hue-saturation-brightness color space. 50 | struct HSB { 51 | var hue: CGFloat, saturation: CGFloat, brightness: CGFloat, alpha: CGFloat 52 | 53 | init(_ hue: CGFloat, _ saturation: CGFloat, _ brightness: CGFloat, _ alpha: CGFloat) { 54 | self.hue = hue 55 | self.saturation = saturation 56 | self.brightness = brightness 57 | self.alpha = alpha 58 | } 59 | } 60 | 61 | // The maximum value of the RGB color components. 62 | let EFRGBColorComponentMaxValue: CGFloat = 255.0 63 | 64 | // The maximum value of the alpha component. 65 | let EFAlphaComponentMaxValue: CGFloat = 100.0 66 | 67 | // The maximum value of the HSB color components. 68 | let EFHSBColorComponentMaxValue: CGFloat = 1.0 69 | 70 | // Converts an RGB color value to HSV. 71 | // Assumes r, g, and b are contained in the set [0, 1] and 72 | // returns h, s, and b in the set [0, 1]. 73 | // @param rgb The rgb color values 74 | // @return The hsb color values 75 | func EFRGB2HSB(rgb: RGB) -> HSB { 76 | let rd = Double(rgb.red) 77 | let gd = Double(rgb.green) 78 | let bd = Double(rgb.blue) 79 | let max = fmax (rd, fmax(gd, bd)) 80 | let min = fmin(rd, fmin(gd, bd)) 81 | var h = 0.0, b = max 82 | 83 | let d = max - min 84 | 85 | let s = max == 0 ? 0 : d / max 86 | 87 | if max == min { 88 | h = 0 // achromatic 89 | } else { 90 | if max == rd { 91 | h = (gd - bd) / d + (gd < bd ? 6 : 0) 92 | } else if max == gd { 93 | h = (bd - rd) / d + 2 94 | } else if max == bd { 95 | h = (rd - gd) / d + 4 96 | } 97 | 98 | h /= 6 99 | } 100 | 101 | return HSB(CGFloat(h), CGFloat(s), CGFloat(b), CGFloat(rgb.alpha)) 102 | } 103 | 104 | // Converts an HSB color value to RGB. 105 | // Assumes h, s, and b are contained in the set [0, 1] and 106 | // returns r, g, and b in the set [0, 255]. 107 | // @param outRGB The rgb color values 108 | // @return The hsb color values 109 | func EFHSB2RGB(hsb: HSB) -> RGB { 110 | var r: CGFloat = 0.0, g: CGFloat = 0.0, b: CGFloat = 0.0 111 | 112 | let i: Int = Int(hsb.hue * 6) 113 | let f = hsb.hue * 6 - CGFloat(i) 114 | let p = hsb.brightness * (1 - hsb.saturation) 115 | let q = hsb.brightness * (1 - f * hsb.saturation) 116 | let t = hsb.brightness * (1 - (1 - f) * hsb.saturation) 117 | 118 | switch i % 6 { 119 | case 0: 120 | r = hsb.brightness 121 | g = t 122 | b = p 123 | break 124 | case 1: 125 | r = q 126 | g = hsb.brightness 127 | b = p 128 | break 129 | case 2: 130 | r = p 131 | g = hsb.brightness 132 | b = t 133 | break 134 | case 3: 135 | r = p 136 | g = q 137 | b = hsb.brightness 138 | break 139 | case 4: 140 | r = t 141 | g = p 142 | b = hsb.brightness 143 | break 144 | case 5: 145 | r = hsb.brightness 146 | g = p 147 | b = q 148 | break 149 | default: 150 | break 151 | } 152 | return RGB(r, g, b, hsb.alpha) 153 | } 154 | 155 | // Returns the rgb values of the color components. 156 | // @param color The color value. 157 | // @return The values of the color components (including alpha). 158 | func EFRGBColorComponents(color: UIColor) -> RGB { 159 | var result = RGB(1, 1, 1, 1) 160 | guard let colorSpaceModel: CGColorSpaceModel = color.cgColor.colorSpace?.model else { 161 | return result 162 | } 163 | 164 | if (CGColorSpaceModel.rgb != colorSpaceModel && CGColorSpaceModel.monochrome != colorSpaceModel) { 165 | return result 166 | } 167 | 168 | guard let components = color.cgColor.components else { 169 | return result 170 | } 171 | 172 | if CGColorSpaceModel.monochrome == colorSpaceModel { 173 | result.red = components[0].clamped(to: colorComponentValueRange) 174 | result.green = components[0].clamped(to: colorComponentValueRange) 175 | result.blue = components[0].clamped(to: colorComponentValueRange) 176 | result.alpha = components[1].clamped(to: colorComponentValueRange) 177 | } else { 178 | result.red = components[0].clamped(to: colorComponentValueRange) 179 | result.green = components[1].clamped(to: colorComponentValueRange) 180 | result.blue = components[2].clamped(to: colorComponentValueRange) 181 | result.alpha = components[3].clamped(to: colorComponentValueRange) 182 | } 183 | 184 | return result 185 | } 186 | 187 | // Converts hex string to the UIColor representation. 188 | // @param color The color value. 189 | // @return The hex string color value. 190 | func EFHexStringFromColor(color: UIColor) -> String? { 191 | guard let colorSpaceModel: CGColorSpaceModel = color.cgColor.colorSpace?.model else { 192 | return nil 193 | } 194 | 195 | if (CGColorSpaceModel.rgb != colorSpaceModel && CGColorSpaceModel.monochrome != colorSpaceModel) { 196 | return nil 197 | } 198 | 199 | guard let components = color.cgColor.components else { 200 | return nil 201 | } 202 | var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 203 | 204 | if CGColorSpaceModel.monochrome == colorSpaceModel { 205 | red = components[0].clamped(to: colorComponentValueRange) 206 | green = components[0].clamped(to: colorComponentValueRange) 207 | blue = components[0].clamped(to: colorComponentValueRange) 208 | alpha = components[1].clamped(to: colorComponentValueRange) 209 | } else { 210 | red = components[0].clamped(to: colorComponentValueRange) 211 | green = components[1].clamped(to: colorComponentValueRange) 212 | blue = components[2].clamped(to: colorComponentValueRange) 213 | alpha = components[3].clamped(to: colorComponentValueRange) 214 | } 215 | 216 | return String( 217 | format: "#%02lX%02lX%02lX%02lX", 218 | UInt64(red * EFRGBColorComponentMaxValue), 219 | UInt64(green * EFRGBColorComponentMaxValue), 220 | UInt64(blue * EFRGBColorComponentMaxValue), 221 | UInt64(alpha * EFRGBColorComponentMaxValue) 222 | ) 223 | } 224 | 225 | // Converts UIColor value to the hex string. 226 | // @param hexString The hex string color value. 227 | // @return The color value. 228 | func EFColorFromHexString(hexColor: String) -> UIColor? { 229 | if !hexColor.hasPrefix("#") { 230 | return nil 231 | } 232 | 233 | let scanner = Scanner(string: hexColor) 234 | scanner.charactersToBeSkipped = CharacterSet(charactersIn: "#") 235 | 236 | var hexNum: UInt32 = 0 237 | if !scanner.scanHexInt32(&hexNum) { 238 | return nil 239 | } 240 | 241 | let r: CGFloat = CGFloat((hexNum >> 24) & 0xFF) 242 | let g: CGFloat = CGFloat((hexNum >> 16) & 0xFF) 243 | let b: CGFloat = CGFloat((hexNum >> 8) & 0xFF) 244 | let a: CGFloat = CGFloat((hexNum) & 0xFF) 245 | 246 | return UIColor( 247 | red: r / EFRGBColorComponentMaxValue, 248 | green: g / EFRGBColorComponentMaxValue, 249 | blue: b / EFRGBColorComponentMaxValue, 250 | alpha: a / EFRGBColorComponentMaxValue 251 | ) 252 | } 253 | 254 | extension Comparable { 255 | func clamped(to limits: ClosedRange) -> Self { 256 | return min(max(self, limits.lowerBound), limits.upperBound) 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /Unicorn/Extension/UIColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/3/25. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | // iOS 浮点数 : CGFloat 13 | class func rgb(_ r: CGFloat, _ g: CGFloat, _ b: CGFloat) -> UIColor { 14 | return UIColor(red: r / 255.0, green: g / 255.0, blue: b / 255.0, alpha: 1) 15 | } 16 | 17 | class func rgba(_ r: CGFloat, _ g: CGFloat, _ b: CGFloat, _ a: CGFloat) -> UIColor { 18 | return UIColor(red: r / 255.0, green: g / 255.0, blue: b / 255.0, alpha: a) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Unicorn/Extension/UIView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView.swift 3 | // Unicorn 4 | // 5 | // Created by PJHubs on 2019/3/25. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIView { 12 | 13 | static private let UNSCREEN_SCALE = UIScreen.main.scale 14 | 15 | private func getPixintegral(pointValue: CGFloat) -> CGFloat { 16 | return round(pointValue * UIView.UNSCREEN_SCALE) / UIView.UNSCREEN_SCALE 17 | } 18 | 19 | public var x: CGFloat { 20 | get { 21 | return self.frame.origin.x 22 | } 23 | set(x) { 24 | self.frame = CGRect.init( 25 | x: getPixintegral(pointValue: x), 26 | y: self.y, 27 | width: self.width, 28 | height: self.height 29 | ) 30 | } 31 | } 32 | 33 | public var y: CGFloat { 34 | get { 35 | return self.frame.origin.y 36 | } 37 | set(y) { 38 | self.frame = CGRect.init( 39 | x: self.x, 40 | y: getPixintegral(pointValue: y), 41 | width: self.width, 42 | height: self.height 43 | ) 44 | } 45 | } 46 | 47 | public var width: CGFloat { 48 | get { 49 | return self.frame.size.width 50 | } 51 | set(width) { 52 | self.frame = CGRect.init( 53 | x: self.x, 54 | y: self.y, 55 | width: getPixintegral(pointValue: width), 56 | height: self.height 57 | ) 58 | } 59 | } 60 | 61 | public var height: CGFloat { 62 | get { 63 | return self.frame.size.height 64 | } 65 | set (height) { 66 | self.frame = CGRect.init( 67 | x: self.x, 68 | y: self.y, 69 | width: self.width, 70 | height: getPixintegral(pointValue: height) 71 | ) 72 | } 73 | } 74 | 75 | public var bottom: CGFloat { 76 | get { 77 | return self.y + self.height 78 | } 79 | set(bottom) { 80 | self.y = bottom - self.height 81 | } 82 | } 83 | 84 | public var right: CGFloat { 85 | get { 86 | return self.x + self.width 87 | } 88 | set (right) { 89 | self.x = right - self.width 90 | } 91 | } 92 | 93 | public var left: CGFloat { 94 | get { 95 | return self.x 96 | } 97 | set(left) { 98 | self.x = left 99 | } 100 | } 101 | 102 | public var top: CGFloat { 103 | get { 104 | return self.y 105 | } 106 | set(top) { 107 | self.y = top 108 | } 109 | } 110 | 111 | public var centerX: CGFloat { 112 | get { 113 | return self.center.x 114 | } 115 | set(centerX) { 116 | self.center = CGPoint.init( 117 | x: getPixintegral(pointValue: centerX), 118 | y: self.center.y 119 | ) 120 | } 121 | } 122 | 123 | public var centerY: CGFloat { 124 | get { 125 | return self.center.y 126 | } 127 | set (centerY) { 128 | self.center = CGPoint.init(x: self.center.x, y: getPixintegral(pointValue: centerY)) 129 | } 130 | } 131 | 132 | } 133 | 134 | -------------------------------------------------------------------------------- /Unicorn/Fonts/FZLuXTJW.TTF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Fonts/FZLuXTJW.TTF -------------------------------------------------------------------------------- /Unicorn/Fonts/FZQingFSJW_Cu.TTF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Fonts/FZQingFSJW_Cu.TTF -------------------------------------------------------------------------------- /Unicorn/Fonts/FZZJ-FOJW.TTF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Fonts/FZZJ-FOJW.TTF -------------------------------------------------------------------------------- /Unicorn/Fonts/FZZJ-HTKSJW.TTF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Fonts/FZZJ-HTKSJW.TTF -------------------------------------------------------------------------------- /Unicorn/Fonts/FZZJ-MSMLJW.TTF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Fonts/FZZJ-MSMLJW.TTF -------------------------------------------------------------------------------- /Unicorn/Fonts/FZZJ-ZJJYBKTJW.TTF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/Unicorn-iOS/fe155d9c8faa8a592966c20b8425a8e38241e3d1/Unicorn/Fonts/FZZJ-ZJJYBKTJW.TTF -------------------------------------------------------------------------------- /Unicorn/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPhotoLibraryAddUsageDescription 6 | 获取您的相册权限来存储手帐 7 | NSAppTransportSecurity 8 | 9 | NSAllowsArbitraryLoads 10 | 11 | 12 | UIAppFonts 13 | 14 | FZLuXTJW.TTF 15 | FZQingFSJW_Cu.TTF 16 | FZZJ-FOJW.TTF 17 | FZZJ-HTKSJW.TTF 18 | FZZJ-MSMLJW.TTF 19 | FZZJ-ZJJYBKTJW.TTF 20 | 21 | CFBundleDevelopmentRegion 22 | $(DEVELOPMENT_LANGUAGE) 23 | CFBundleExecutable 24 | $(EXECUTABLE_NAME) 25 | CFBundleIdentifier 26 | $(PRODUCT_BUNDLE_IDENTIFIER) 27 | CFBundleInfoDictionaryVersion 28 | 6.0 29 | CFBundleName 30 | $(PRODUCT_NAME) 31 | CFBundlePackageType 32 | APPL 33 | CFBundleShortVersionString 34 | 1.0 35 | CFBundleVersion 36 | 1 37 | LSRequiresIPhoneOS 38 | 39 | UILaunchStoryboardName 40 | LaunchScreen 41 | UIRequiredDeviceCapabilities 42 | 43 | armv7 44 | 45 | UISupportedInterfaceOrientations 46 | 47 | UIInterfaceOrientationPortrait 48 | 49 | UISupportedInterfaceOrientations~ipad 50 | 51 | UIInterfaceOrientationPortrait 52 | UIInterfaceOrientationPortraitUpsideDown 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | 57 | 58 | --------------------------------------------------------------------------------