├── Keyboard ├── CustomKeyboard │ ├── CustomKeyboard.swift │ └── CustomKeyboardLayout.swift ├── Info.plist ├── KeyPop │ ├── KeyMenu.swift │ ├── KeyMenuItem.swift │ └── KeyPop.swift ├── KeyboardLayoutEngine │ ├── KeyboardButton.swift │ ├── KeyboardLayout.swift │ └── KeyboardRow.swift ├── KeyboardViewController.swift └── Resources.xcassets │ ├── Backspace.imageset │ ├── Backspace.pdf │ └── Contents.json │ ├── Contents.json │ ├── Emoji.imageset │ ├── Contents.json │ └── Emoji.pdf │ ├── Globe.imageset │ ├── Contents.json │ └── Globe.pdf │ ├── ShiftOff.imageset │ ├── Contents.json │ └── ShiftOff.pdf │ ├── ShiftOn.imageset │ ├── Contents.json │ └── ShiftOn.pdf │ ├── ShiftOnce.imageset │ ├── Contents.json │ └── ShiftOnce.pdf │ └── Siri.imageset │ ├── Contents.json │ └── Siri.pdf ├── KeyboardLayoutEngine.podspec ├── KeyboardLayoutEngine.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── Cem.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── Keyboard.xcscheme │ ├── KeyboardLayoutEngine.xcscheme │ └── xcschememanagement.plist ├── KeyboardLayoutEngine.xcworkspace ├── contents.xcworkspacedata └── xcuserdata │ └── Cem.xcuserdatad │ ├── UserInterfaceState.xcuserstate │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── KeyboardLayoutEngine ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist └── ViewController.swift ├── KeyboardLayoutEngineTests ├── Info.plist └── KeyboardLayoutEngineTests.swift ├── KeyboardLayoutEngineUITests ├── Info.plist └── KeyboardLayoutEngineUITests.swift ├── LICENSE ├── Podfile ├── Podfile.lock ├── Pods ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── Cem.xcuserdatad │ │ └── xcschemes │ │ ├── Pods-Keyboard.xcscheme │ │ ├── Pods-KeyboardLayoutEngine.xcscheme │ │ ├── Pods-KeyboardLayoutEngineTests.xcscheme │ │ ├── Pods-KeyboardLayoutEngineUITests.xcscheme │ │ ├── Shadow.xcscheme │ │ └── xcschememanagement.plist ├── Shadow │ ├── LICENSE │ ├── README.md │ └── Shadow │ │ └── Shadow.swift └── Target Support Files │ ├── Pods-Keyboard │ ├── Info.plist │ ├── Pods-Keyboard-acknowledgements.markdown │ ├── Pods-Keyboard-acknowledgements.plist │ ├── Pods-Keyboard-dummy.m │ ├── Pods-Keyboard-frameworks.sh │ ├── Pods-Keyboard-resources.sh │ ├── Pods-Keyboard-umbrella.h │ ├── Pods-Keyboard.debug.xcconfig │ ├── Pods-Keyboard.modulemap │ └── Pods-Keyboard.release.xcconfig │ ├── Pods-KeyboardLayoutEngine │ ├── Info.plist │ ├── Pods-KeyboardLayoutEngine-acknowledgements.markdown │ ├── Pods-KeyboardLayoutEngine-acknowledgements.plist │ ├── Pods-KeyboardLayoutEngine-dummy.m │ ├── Pods-KeyboardLayoutEngine-frameworks.sh │ ├── Pods-KeyboardLayoutEngine-resources.sh │ ├── Pods-KeyboardLayoutEngine-umbrella.h │ ├── Pods-KeyboardLayoutEngine.debug.xcconfig │ ├── Pods-KeyboardLayoutEngine.modulemap │ └── Pods-KeyboardLayoutEngine.release.xcconfig │ ├── Pods-KeyboardLayoutEngineTests │ ├── Info.plist │ ├── Pods-KeyboardLayoutEngineTests-acknowledgements.markdown │ ├── Pods-KeyboardLayoutEngineTests-acknowledgements.plist │ ├── Pods-KeyboardLayoutEngineTests-dummy.m │ ├── Pods-KeyboardLayoutEngineTests-frameworks.sh │ ├── Pods-KeyboardLayoutEngineTests-resources.sh │ ├── Pods-KeyboardLayoutEngineTests-umbrella.h │ ├── Pods-KeyboardLayoutEngineTests.debug.xcconfig │ ├── Pods-KeyboardLayoutEngineTests.modulemap │ └── Pods-KeyboardLayoutEngineTests.release.xcconfig │ ├── Pods-KeyboardLayoutEngineUITests │ ├── Info.plist │ ├── Pods-KeyboardLayoutEngineUITests-acknowledgements.markdown │ ├── Pods-KeyboardLayoutEngineUITests-acknowledgements.plist │ ├── Pods-KeyboardLayoutEngineUITests-dummy.m │ ├── Pods-KeyboardLayoutEngineUITests-frameworks.sh │ ├── Pods-KeyboardLayoutEngineUITests-resources.sh │ ├── Pods-KeyboardLayoutEngineUITests-umbrella.h │ ├── Pods-KeyboardLayoutEngineUITests.debug.xcconfig │ ├── Pods-KeyboardLayoutEngineUITests.modulemap │ └── Pods-KeyboardLayoutEngineUITests.release.xcconfig │ └── Shadow │ ├── Info.plist │ ├── Shadow-dummy.m │ ├── Shadow-prefix.pch │ ├── Shadow-umbrella.h │ ├── Shadow.modulemap │ └── Shadow.xcconfig ├── README.md └── demo.gif /Keyboard/CustomKeyboard/CustomKeyboard.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomKeyboard.swift 3 | // KeyboardLayoutEngine 4 | // 5 | // Created by Cem Olcay on 11/05/16. 6 | // Copyright © 2016 Prototapp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // MARK: - CustomKeyboardDelegate 12 | @objc public protocol CustomKeyboardDelegate { 13 | optional func customKeyboard(customKeyboard: CustomKeyboard, keyboardButtonPressed keyboardButton: KeyboardButton) 14 | optional func customKeyboard(customKeyboard: CustomKeyboard, keyButtonPressed key: String) 15 | optional func customKeyboardSpaceButtonPressed(customKeyboard: CustomKeyboard) 16 | optional func customKeyboardBackspaceButtonPressed(customKeyboard: CustomKeyboard) 17 | optional func customKeyboardGlobeButtonPressed(customKeyboard: CustomKeyboard) 18 | optional func customKeyboardReturnButtonPressed(customKeyboard: CustomKeyboard) 19 | } 20 | 21 | // MARK: - CustomKeyboard 22 | public class CustomKeyboard: UIView, KeyboardLayoutDelegate { 23 | public var keyboardLayout = CustomKeyboardLayout() 24 | public weak var delegate: CustomKeyboardDelegate? 25 | 26 | // MARK: CustomKeyobardShiftState 27 | public enum CustomKeyboardShiftState { 28 | case Once 29 | case Off 30 | case On 31 | } 32 | 33 | // MARK: CustomKeyboardLayoutState 34 | public enum CustomKeyboardLayoutState { 35 | case Letters(shiftState: CustomKeyboardShiftState) 36 | case Numbers 37 | case Symbols 38 | } 39 | 40 | public private(set) var keyboardLayoutState: CustomKeyboardLayoutState = .Letters(shiftState: CustomKeyboardShiftState.Once) { 41 | didSet { 42 | keyboardLayoutStateDidChange(oldState: oldValue, newState: keyboardLayoutState) 43 | } 44 | } 45 | 46 | // MARK: Shift 47 | public var shiftToggleInterval: NSTimeInterval = 0.5 48 | private var shiftToggleTimer: NSTimer? 49 | 50 | // MARK: Backspace 51 | public var backspaceDeleteInterval: NSTimeInterval = 0.1 52 | public var backspaceAutoDeleteModeInterval: NSTimeInterval = 0.5 53 | private var backspaceDeleteTimer: NSTimer? 54 | private var backspaceAutoDeleteModeTimer: NSTimer? 55 | 56 | // MARK: KeyMenu 57 | public var keyMenuLocked: Bool = false 58 | public var keyMenuOpenTimer: NSTimer? 59 | public var keyMenuOpenTimeInterval: NSTimeInterval = 1 60 | public var keyMenuShowingKeyboardButton: KeyboardButton? { 61 | didSet { 62 | oldValue?.showKeyPop(show: false) 63 | oldValue?.showKeyMenu(show: false) 64 | keyMenuShowingKeyboardButton?.showKeyPop(show: false) 65 | keyMenuShowingKeyboardButton?.showKeyMenu(show: true) 66 | dispatch_after( 67 | dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))), 68 | dispatch_get_main_queue()) { [weak self] in 69 | self?.getCurrentKeyboardLayout().typingEnabled = self!.keyMenuShowingKeyboardButton == nil && self!.keyMenuLocked == false 70 | } 71 | } 72 | } 73 | 74 | // MARK: Init 75 | public init() { 76 | super.init(frame: CGRect.zero) 77 | defaultInit() 78 | } 79 | 80 | public override init(frame: CGRect) { 81 | super.init(frame: frame) 82 | defaultInit() 83 | } 84 | 85 | public required init?(coder aDecoder: NSCoder) { 86 | super.init(coder: aDecoder) 87 | defaultInit() 88 | } 89 | 90 | private func defaultInit() { 91 | keyboardLayout = CustomKeyboardLayout() 92 | keyboardLayoutStateDidChange(oldState: nil, newState: keyboardLayoutState) 93 | } 94 | 95 | // MARK: Layout 96 | public override func layoutSubviews() { 97 | super.layoutSubviews() 98 | 99 | getCurrentKeyboardLayout().frame = CGRect( 100 | x: 0, 101 | y: 0, 102 | width: frame.size.width, 103 | height: frame.size.height) 104 | } 105 | 106 | // MARK: KeyboardLayout 107 | public func getKeyboardLayout(ofState state: CustomKeyboardLayoutState) -> KeyboardLayout { 108 | switch state { 109 | case .Letters(let shiftState): 110 | switch shiftState { 111 | case .Once: 112 | return keyboardLayout.uppercase 113 | case .On: 114 | return keyboardLayout.uppercaseToggled 115 | case .Off: 116 | return keyboardLayout.lowercase 117 | } 118 | case .Numbers: 119 | return keyboardLayout.numbers 120 | case .Symbols: 121 | return keyboardLayout.symbols 122 | } 123 | } 124 | 125 | public func getCurrentKeyboardLayout() -> KeyboardLayout { 126 | return getKeyboardLayout(ofState: keyboardLayoutState) 127 | } 128 | 129 | public func enumerateKeyboardLayouts(enumerate: (KeyboardLayout) -> Void) { 130 | let layouts = [ 131 | keyboardLayout.uppercase, 132 | keyboardLayout.uppercaseToggled, 133 | keyboardLayout.lowercase, 134 | keyboardLayout.numbers, 135 | keyboardLayout.symbols, 136 | ] 137 | 138 | for layout in layouts { 139 | enumerate(layout) 140 | } 141 | } 142 | 143 | public func keyboardLayoutStateDidChange(oldState oldState: CustomKeyboardLayoutState?, newState: CustomKeyboardLayoutState) { 144 | // Remove old keyboard layout 145 | if let oldState = oldState { 146 | let oldKeyboardLayout = getKeyboardLayout(ofState: oldState) 147 | oldKeyboardLayout.delegate = nil 148 | oldKeyboardLayout.removeFromSuperview() 149 | } 150 | 151 | // Add new keyboard layout 152 | let newKeyboardLayout = getKeyboardLayout(ofState: newState) 153 | newKeyboardLayout.delegate = self 154 | addSubview(newKeyboardLayout) 155 | setNeedsLayout() 156 | } 157 | 158 | public func reload() { 159 | // Remove current 160 | let currentLayout = getCurrentKeyboardLayout() 161 | currentLayout.delegate = nil 162 | currentLayout.removeFromSuperview() 163 | // Reload layout 164 | keyboardLayout = CustomKeyboardLayout() 165 | keyboardLayoutStateDidChange(oldState: nil, newState: keyboardLayoutState) 166 | } 167 | 168 | // MARK: Capitalize 169 | public func switchToLetters(shiftState shift: CustomKeyboardShiftState) { 170 | keyboardLayoutState = .Letters(shiftState: shift) 171 | } 172 | 173 | public func capitalize() { 174 | switchToLetters(shiftState: .Once) 175 | } 176 | 177 | // MARK: Backspace Auto Delete 178 | private func startBackspaceAutoDeleteModeTimer() { 179 | backspaceAutoDeleteModeTimer = NSTimer.scheduledTimerWithTimeInterval( 180 | backspaceAutoDeleteModeInterval, 181 | target: self, 182 | selector: #selector(CustomKeyboard.startBackspaceAutoDeleteMode), 183 | userInfo: nil, 184 | repeats: false) 185 | } 186 | 187 | private func startBackspaceDeleteTimer() { 188 | backspaceDeleteTimer = NSTimer.scheduledTimerWithTimeInterval( 189 | backspaceDeleteInterval, 190 | target: self, 191 | selector: #selector(CustomKeyboard.autoDelete), 192 | userInfo: nil, 193 | repeats: true) 194 | } 195 | 196 | private func invalidateBackspaceAutoDeleteModeTimer() { 197 | backspaceAutoDeleteModeTimer?.invalidate() 198 | backspaceAutoDeleteModeTimer = nil 199 | } 200 | 201 | private func invalidateBackspaceDeleteTimer() { 202 | backspaceDeleteTimer?.invalidate() 203 | backspaceDeleteTimer = nil 204 | } 205 | 206 | internal func startBackspaceAutoDeleteMode() { 207 | invalidateBackspaceDeleteTimer() 208 | startBackspaceDeleteTimer() 209 | } 210 | 211 | internal func autoDelete() { 212 | delegate?.customKeyboardBackspaceButtonPressed?(self) 213 | } 214 | 215 | // MARK: Shift Toggle 216 | private func startShiftToggleTimer() { 217 | shiftToggleTimer = NSTimer.scheduledTimerWithTimeInterval( 218 | shiftToggleInterval, 219 | target: self, 220 | selector: #selector(CustomKeyboard.invalidateShiftToggleTimer), 221 | userInfo: nil, 222 | repeats: false) 223 | } 224 | 225 | internal func invalidateShiftToggleTimer() { 226 | shiftToggleTimer?.invalidate() 227 | shiftToggleTimer = nil 228 | } 229 | 230 | // MARK: KeyMenu Toggle 231 | private func startKeyMenuOpenTimer(forKeyboardButton keyboardButton: KeyboardButton) { 232 | keyMenuOpenTimer = NSTimer.scheduledTimerWithTimeInterval( 233 | keyMenuOpenTimeInterval, 234 | target: self, 235 | selector: #selector(CustomKeyboard.openKeyMenu(_:)), 236 | userInfo: keyboardButton, 237 | repeats: false) 238 | } 239 | 240 | private func invalidateKeyMenuOpenTimer() { 241 | keyMenuOpenTimer?.invalidate() 242 | keyMenuOpenTimer = nil 243 | } 244 | 245 | public func openKeyMenu(timer: NSTimer) { 246 | if let userInfo = timer.userInfo, keyboardButton = userInfo as? KeyboardButton { 247 | keyMenuShowingKeyboardButton = keyboardButton 248 | } 249 | } 250 | 251 | // MARK: KeyboardLayoutDelegate 252 | public func keyboardLayout(keyboardLayout: KeyboardLayout, didKeyPressStart keyboardButton: KeyboardButton) { 253 | invalidateBackspaceAutoDeleteModeTimer() 254 | invalidateBackspaceDeleteTimer() 255 | invalidateKeyMenuOpenTimer() 256 | 257 | // Backspace 258 | if keyboardButton.identifier == CustomKeyboardIdentifier.Backspace.rawValue { 259 | startBackspaceAutoDeleteModeTimer() 260 | } 261 | 262 | // KeyPop and KeyMenu 263 | if keyboardButton.style.keyPopType != nil { 264 | keyboardButton.showKeyPop(show: true) 265 | if keyboardButton.keyMenu != nil { 266 | startKeyMenuOpenTimer(forKeyboardButton: keyboardButton) 267 | } 268 | } else if keyboardButton.keyMenu != nil { 269 | keyMenuShowingKeyboardButton = keyboardButton 270 | keyMenuLocked = false 271 | } 272 | } 273 | 274 | public func keyboardLayout(keyboardLayout: KeyboardLayout, didKeyPressEnd keyboardButton: KeyboardButton) { 275 | delegate?.customKeyboard?(self, keyboardButtonPressed: keyboardButton) 276 | 277 | // If keyboard key is pressed notify no questions asked 278 | if case KeyboardButtonType.Key(let text) = keyboardButton.type { 279 | delegate?.customKeyboard?(self, keyButtonPressed: text) 280 | 281 | // If shift state was CustomKeyboardShiftState.Once then make keyboard layout lowercase 282 | if case CustomKeyboardLayoutState.Letters(let shiftState) = keyboardLayoutState where shiftState == CustomKeyboardShiftState.Once { 283 | keyboardLayoutState = CustomKeyboardLayoutState.Letters(shiftState: .Off) 284 | return 285 | } 286 | } 287 | 288 | // Chcek special keyboard buttons 289 | if let keyId = keyboardButton.identifier, identifier = CustomKeyboardIdentifier(rawValue: keyId) { 290 | switch identifier { 291 | 292 | // Notify special keys 293 | case .Backspace: 294 | delegate?.customKeyboardBackspaceButtonPressed?(self) 295 | case .Space: 296 | delegate?.customKeyboardSpaceButtonPressed?(self) 297 | case .Globe: 298 | delegate?.customKeyboardGlobeButtonPressed?(self) 299 | case .Return: 300 | delegate?.customKeyboardReturnButtonPressed?(self) 301 | 302 | // Update keyboard layout state 303 | case .Letters: 304 | keyboardLayoutState = .Letters(shiftState: .Off) 305 | case .Numbers: 306 | keyboardLayoutState = .Numbers 307 | case .Symbols: 308 | keyboardLayoutState = .Symbols 309 | 310 | // Update shift state 311 | case .ShiftOff: 312 | if shiftToggleTimer == nil { 313 | keyboardLayoutState = .Letters(shiftState: .Once) 314 | startShiftToggleTimer() 315 | } else { 316 | keyboardLayoutState = .Letters(shiftState: .On) 317 | invalidateShiftToggleTimer() 318 | } 319 | case .ShiftOnce: 320 | if shiftToggleTimer == nil { 321 | keyboardLayoutState = .Letters(shiftState: .Off) 322 | startShiftToggleTimer() 323 | } else { 324 | keyboardLayoutState = .Letters(shiftState: .On) 325 | invalidateShiftToggleTimer() 326 | } 327 | case .ShiftOn: 328 | if shiftToggleTimer == nil { 329 | keyboardLayoutState = .Letters(shiftState: .Off) 330 | } 331 | } 332 | } 333 | } 334 | 335 | public func keyboardLayout(keyboardLayout: KeyboardLayout, didTouchesBegin touches: Set) { 336 | // KeyMenu 337 | if let menu = keyMenuShowingKeyboardButton?.keyMenu, touch = touches.first { 338 | menu.updateSelection(touchLocation: touch.locationInView(self), inView: self) 339 | } 340 | } 341 | 342 | public func keyboardLayout(keyboardLayout: KeyboardLayout, didTouchesMove touches: Set) { 343 | // KeyMenu 344 | if let menu = keyMenuShowingKeyboardButton?.keyMenu, touch = touches.first { 345 | menu.updateSelection(touchLocation: touch.locationInView(self), inView: self) 346 | } 347 | } 348 | 349 | public func keyboardLayout(keyboardLayout: KeyboardLayout, didTouchesEnd touches: Set?) { 350 | invalidateBackspaceAutoDeleteModeTimer() 351 | invalidateBackspaceDeleteTimer() 352 | invalidateKeyMenuOpenTimer() 353 | 354 | // KeyMenu 355 | if let menu = keyMenuShowingKeyboardButton?.keyMenu, touch = touches?.first { 356 | menu.updateSelection(touchLocation: touch.locationInView(self), inView: self) 357 | // select item 358 | if menu.selectedIndex >= 0 { 359 | if let item = menu.items[safe: menu.selectedIndex] { 360 | item.action?(keyMenuItem: item) 361 | } 362 | keyMenuShowingKeyboardButton = nil 363 | keyMenuLocked = false 364 | } else { 365 | if keyMenuLocked { 366 | keyMenuShowingKeyboardButton = nil 367 | keyMenuLocked = false 368 | return 369 | } 370 | keyMenuLocked = true 371 | } 372 | } 373 | } 374 | 375 | public func keyboardLayout(keyboardLayout: KeyboardLayout, didTouchesCancel touches: Set?) { 376 | invalidateBackspaceAutoDeleteModeTimer() 377 | invalidateBackspaceDeleteTimer() 378 | invalidateKeyMenuOpenTimer() 379 | 380 | // KeyMenu 381 | if let menu = keyMenuShowingKeyboardButton?.keyMenu, touch = touches?.first { 382 | menu.updateSelection(touchLocation: touch.locationInView(self), inView: self) 383 | // select item 384 | if menu.selectedIndex >= 0 { 385 | if let item = menu.items[safe: menu.selectedIndex] { 386 | item.action?(keyMenuItem: item) 387 | } 388 | keyMenuShowingKeyboardButton = nil 389 | keyMenuLocked = false 390 | } else { 391 | if keyMenuLocked { 392 | keyMenuShowingKeyboardButton = nil 393 | keyMenuLocked = false 394 | getCurrentKeyboardLayout().typingEnabled = true 395 | return 396 | } 397 | keyMenuLocked = true 398 | } 399 | } 400 | } 401 | } 402 | -------------------------------------------------------------------------------- /Keyboard/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Keyboard 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSExtension 26 | 27 | NSExtensionAttributes 28 | 29 | IsASCIICapable 30 | 31 | PrefersRightToLeft 32 | 33 | PrimaryLanguage 34 | en-US 35 | RequestsOpenAccess 36 | 37 | 38 | NSExtensionPointIdentifier 39 | com.apple.keyboard-service 40 | NSExtensionPrincipalClass 41 | $(PRODUCT_MODULE_NAME).KeyboardViewController 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Keyboard/KeyPop/KeyMenu.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyMenu.swift 3 | // KeyboardLayoutEngine 4 | // 5 | // Created by Cem Olcay on 05/06/16. 6 | // Copyright © 2016 Prototapp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Shadow 11 | 12 | // MARK: - KeyMenuType 13 | public enum KeyMenuType { 14 | case Horizontal 15 | case Vertical 16 | } 17 | 18 | // MARK: - KeyMenuStyle 19 | public struct KeyMenuStyle { 20 | // MARK: Shadow 21 | public var shadow: Shadow? 22 | 23 | // MARK: Background Color 24 | public var backgroundColor: UIColor 25 | 26 | // MARK: Item Style 27 | public var itemSize: CGSize 28 | 29 | // MARK: Padding 30 | public var horizontalMenuItemPadding: CGFloat 31 | public var horizontalMenuLeftPadding: CGFloat 32 | public var horizontalMenuRightPadding: CGFloat 33 | 34 | // MARK: Init 35 | public init( 36 | shadow: Shadow? = nil, 37 | backgroundColor: UIColor? = nil, 38 | itemSize: CGSize? = nil, 39 | horizontalMenuItemPadding: CGFloat? = nil, 40 | horizontalMenuLeftPadding: CGFloat? = nil, 41 | horizontalMenuRightPadding: CGFloat? = nil) { 42 | self.shadow = shadow 43 | self.backgroundColor = backgroundColor ?? UIColor.whiteColor() 44 | self.itemSize = itemSize ?? CGSize(width: 40, height: 40) 45 | self.horizontalMenuItemPadding = horizontalMenuItemPadding ?? 5 46 | self.horizontalMenuLeftPadding = horizontalMenuLeftPadding ?? 5 47 | self.horizontalMenuRightPadding = horizontalMenuRightPadding ?? 5 48 | } 49 | } 50 | 51 | // MARK: - KeyMenu 52 | public class KeyMenu: UIView { 53 | public var items = [KeyMenuItem]() 54 | public var style = KeyMenuStyle() 55 | public var type = KeyMenuType.Horizontal 56 | 57 | private let titleLabelTag = 1 58 | 59 | public var selectedIndex: Int = -1 { 60 | didSet { 61 | if selectedIndex == -1 { 62 | for item in items { 63 | item.highlighted = false 64 | } 65 | } 66 | layoutIfNeeded() 67 | } 68 | } 69 | 70 | // MARK: Init 71 | public init(items: [KeyMenuItem], style: KeyMenuStyle, type: KeyMenuType) { 72 | super.init(frame: CGRect.zero) 73 | self.items = items 74 | self.style = style 75 | self.type = type 76 | 77 | backgroundColor = style.backgroundColor 78 | applyShadow(shadow: style.shadow) 79 | 80 | for item in items { 81 | addSubview(item) 82 | } 83 | } 84 | 85 | required public init?(coder aDecoder: NSCoder) { 86 | super.init(coder: aDecoder) 87 | } 88 | 89 | // MARK: Layout 90 | public override func layoutSubviews() { 91 | super.layoutSubviews() 92 | switch type { 93 | case .Horizontal: 94 | generateHorizontalMenu() 95 | case .Vertical: 96 | generateVerticalMenu() 97 | } 98 | } 99 | 100 | private func generateHorizontalMenu() { 101 | let verticalPadding = CGFloat(5) 102 | let paddings = style.horizontalMenuLeftPadding + 103 | style.horizontalMenuRightPadding + 104 | (CGFloat(items.count - 1) * style.horizontalMenuItemPadding) 105 | let itemsWidth = CGFloat(items.count) * style.itemSize.width 106 | frame = CGRect( 107 | x: 0, 108 | y: 0, 109 | width: itemsWidth + paddings, 110 | height: style.itemSize.height + (verticalPadding * 2)) 111 | 112 | var currentX = style.horizontalMenuLeftPadding 113 | for item in items { 114 | item.frame = CGRect( 115 | x: currentX, 116 | y: verticalPadding, 117 | width: style.itemSize.width, 118 | height: style.itemSize.height) 119 | currentX += item.frame.size.width + style.horizontalMenuItemPadding 120 | } 121 | } 122 | 123 | private func generateVerticalMenu() { 124 | frame = CGRect( 125 | x: 0, 126 | y: 0, 127 | width: style.itemSize.width, 128 | height: CGFloat(items.count) * style.itemSize.height) 129 | 130 | var currentY = CGFloat(0) 131 | for (index, item) in items.enumerate() { 132 | if index == items.count - 1 { 133 | item.separator = nil 134 | } 135 | item.frame = CGRect( 136 | x: 0, 137 | y: currentY, 138 | width: style.itemSize.width, 139 | height: style.itemSize.height) 140 | currentY += item.frame.size.height 141 | } 142 | } 143 | 144 | // MARK: Update Selection 145 | public func updateSelection(touchLocation location: CGPoint, inView view: UIView) { 146 | var tempIndex = -1 147 | for (index, item) in items.enumerate() { 148 | item.highlighted = CGRectContainsPoint(view.convertRect(item.frame, fromView: self), location) 149 | if item.highlighted { 150 | tempIndex = index 151 | } 152 | } 153 | selectedIndex = tempIndex 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /Keyboard/KeyPop/KeyMenuItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyMenuItem.swift 3 | // KeyboardLayoutEngine 4 | // 5 | // Created by Cem Olcay on 05/06/16. 6 | // Copyright © 2016 Prototapp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // MARK: KeyMenuItemStyle 12 | public struct KeyMenuItemStyle { 13 | // MARK: Background Color 14 | public var highlightedBackgroundColor: UIColor 15 | 16 | // MARK: Text Color 17 | public var textColor: UIColor 18 | public var highlightedTextColor: UIColor 19 | 20 | // MARK: Font 21 | public var font: UIFont 22 | public var highlightedFont: UIFont 23 | 24 | // MARK: Separator 25 | public var separatorColor: UIColor 26 | public var separatorWidth: CGFloat 27 | 28 | // MARK: Init 29 | public init( 30 | highlightedBackgroundColor: UIColor? = nil, 31 | textColor: UIColor? = nil, 32 | highlightedTextColor: UIColor? = nil, 33 | font: UIFont? = nil, 34 | highlightedFont: UIFont? = nil, 35 | separatorColor: UIColor? = nil, 36 | separatorWidth: CGFloat? = nil) { 37 | self.highlightedBackgroundColor = highlightedBackgroundColor ?? UIColor.blueColor() 38 | self.textColor = textColor ?? UIColor.blackColor() 39 | self.highlightedTextColor = highlightedTextColor ?? UIColor.whiteColor() 40 | self.font = font ?? UIFont.systemFontOfSize(15) 41 | self.highlightedFont = highlightedFont ?? UIFont.boldSystemFontOfSize(15) 42 | self.separatorColor = separatorColor ?? UIColor.blackColor() 43 | self.separatorWidth = separatorWidth ?? 1 44 | } 45 | } 46 | 47 | // MARK: - KeyMenuItem 48 | public typealias KeyMenuItemAction = (keyMenuItem: KeyMenuItem) -> Void 49 | 50 | public class KeyMenuItem: UIView { 51 | public var title: String? 52 | public var style = KeyMenuItemStyle() 53 | public var action: KeyMenuItemAction? 54 | 55 | public var highlighted: Bool = false { 56 | didSet { 57 | setNeedsLayout() 58 | } 59 | } 60 | 61 | public var titleLabel: UILabel? 62 | public var separator: CALayer? 63 | 64 | // MARK: Init 65 | public init( 66 | title: String? = "", 67 | style: KeyMenuItemStyle = KeyMenuItemStyle(), 68 | action: KeyMenuItemAction? = nil) { 69 | super.init(frame: CGRect.zero) 70 | self.title = title 71 | self.style = style 72 | self.action = action 73 | 74 | titleLabel = UILabel() 75 | titleLabel?.text = title 76 | titleLabel?.textAlignment = .Center 77 | addSubview(titleLabel!) 78 | 79 | separator = CALayer() 80 | separator?.backgroundColor = style.separatorColor.CGColor 81 | layer.addSublayer(separator!) 82 | } 83 | 84 | required public init?(coder aDecoder: NSCoder) { 85 | super.init(coder: aDecoder) 86 | } 87 | 88 | // MARK: Layout 89 | public override func layoutSubviews() { 90 | super.layoutSubviews() 91 | titleLabel?.frame = CGRect( 92 | x: 0, 93 | y: 0, 94 | width: frame.size.width, 95 | height: frame.size.height) 96 | 97 | separator?.frame = CGRect( 98 | x: 0, 99 | y: frame.size.height - style.separatorWidth, 100 | width: frame.size.width, 101 | height: style.separatorWidth) 102 | 103 | if highlighted { 104 | titleLabel?.textColor = style.highlightedTextColor 105 | titleLabel?.font = style.highlightedFont 106 | titleLabel?.backgroundColor = style.highlightedBackgroundColor 107 | } else { 108 | titleLabel?.textColor = style.textColor 109 | titleLabel?.font = style.font 110 | titleLabel?.backgroundColor = UIColor.clearColor() 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Keyboard/KeyPop/KeyPop.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyPop.swift 3 | // KeyboardLayoutEngine 4 | // 5 | // Created by Cem Olcay on 05/06/16. 6 | // Copyright © 2016 Prototapp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Shadow 11 | 12 | // MARK: - KeyPopType 13 | public enum KeyPopType { 14 | case Default 15 | case Left 16 | case Right 17 | } 18 | 19 | // MARK: - KeyPopStyle 20 | public struct KeyPopStyle { 21 | public var backgroundColor: UIColor 22 | public var shadow: Shadow? 23 | public var widthMultiplier: CGFloat 24 | public var heightMultiplier: CGFloat 25 | public var font: UIFont 26 | public var textColor: UIColor 27 | public var contentInset: CGSize 28 | public var contentOffset: CGSize 29 | 30 | public init( 31 | backgroundColor: UIColor? = nil, 32 | shadow: Shadow? = nil, 33 | widthMultiplier: CGFloat? = nil, 34 | heightMultiplier: CGFloat? = nil, 35 | font: UIFont? = nil, 36 | textColor: UIColor? = nil, 37 | contentInset: CGSize? = nil, 38 | contentOffset: CGSize? = nil) { 39 | self.backgroundColor = backgroundColor ?? UIColor.whiteColor() 40 | self.shadow = shadow ?? nil 41 | self.widthMultiplier = widthMultiplier ?? 1.2 42 | self.heightMultiplier = heightMultiplier ?? 1.2 43 | self.font = font ?? UIFont.systemFontOfSize(15) 44 | self.textColor = textColor ?? UIColor.blackColor() 45 | self.contentInset = contentInset ?? CGSize(width: 5, height: 5) 46 | self.contentOffset = contentOffset ?? CGSize.zero 47 | } 48 | } 49 | 50 | // MARK: - KeyPop 51 | public class KeyPop: UIView { 52 | public var type: KeyPopType = .Default 53 | public var style: KeyPopStyle = KeyPopStyle() 54 | private(set) var keyboardButton: KeyboardButton? 55 | private(set) var contentView: UIView? 56 | 57 | // MARK: Init 58 | public init(referenceButton keyboardButton: KeyboardButton, style: KeyPopStyle = KeyPopStyle()) { 59 | super.init(frame: CGRect.zero) 60 | self.style = style 61 | self.keyboardButton = keyboardButton 62 | userInteractionEnabled = false 63 | 64 | switch keyboardButton.type { 65 | case .Key(let text): 66 | let label = UILabel() 67 | label.text = text 68 | label.textColor = style.textColor 69 | label.textAlignment = .Center 70 | label.adjustsFontSizeToFitWidth = true 71 | label.minimumScaleFactor = 0.5 72 | if let textLabel = keyboardButton.textLabel { 73 | label.font = textLabel.font.fontWithSize(textLabel.font.pointSize * style.widthMultiplier) 74 | } else { 75 | label.font = style.font.fontWithSize(style.font.pointSize * style.widthMultiplier) 76 | } 77 | addSubview(label) 78 | contentView = label 79 | case .Text(let text): 80 | let label = UILabel() 81 | label.text = text 82 | label.textColor = style.textColor 83 | label.textAlignment = .Center 84 | label.adjustsFontSizeToFitWidth = true 85 | label.minimumScaleFactor = 0.5 86 | if let textLabel = keyboardButton.textLabel { 87 | label.font = textLabel.font.fontWithSize(textLabel.font.pointSize * style.widthMultiplier) 88 | } else { 89 | label.font = style.font.fontWithSize(style.font.pointSize * style.widthMultiplier) 90 | } 91 | contentView = label 92 | addSubview(label) 93 | case .Image(let image): 94 | let imageView = UIImageView() 95 | imageView.contentMode = .ScaleAspectFit 96 | imageView.image = image 97 | contentView = imageView 98 | addSubview(imageView) 99 | } 100 | } 101 | 102 | required public init?(coder aDecoder: NSCoder) { 103 | super.init(coder: aDecoder) 104 | } 105 | 106 | // MARK: Layout 107 | public override func layoutSubviews() { 108 | super.layoutSubviews() 109 | contentView?.frame = CGRect( 110 | x: style.contentInset.width + style.contentOffset.width, 111 | y: style.contentInset.height + style.contentOffset.height, 112 | width: frame.size.width - (style.contentInset.width * 2), 113 | height: frame.size.height - (style.contentInset.height * 2)) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Keyboard/KeyboardLayoutEngine/KeyboardButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardButton.swift 3 | // KeyboardLayoutEngine 4 | // 5 | // Created by Cem Olcay on 06/05/16. 6 | // Copyright © 2016 Prototapp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Shadow 11 | 12 | // MARK: - KeyboardButtonType 13 | public enum KeyboardButtonType { 14 | case Key(String) 15 | case Text(String) 16 | case Image(UIImage?) 17 | } 18 | 19 | // MARK: - KeyboardButtonWidth 20 | public enum KeyboardButtonWidth { 21 | case Dynamic 22 | case Static(width: CGFloat) 23 | case Relative(percent: CGFloat) 24 | } 25 | 26 | // MARK: - KeyboardButtonStyle 27 | public struct KeyboardButtonStyle { 28 | public var backgroundColor: UIColor 29 | public var cornerRadius: CGFloat 30 | 31 | // Border 32 | public var borderColor: UIColor 33 | public var borderWidth: CGFloat 34 | 35 | // Shadow 36 | public var shadow: Shadow? 37 | 38 | // Text 39 | public var textColor: UIColor 40 | public var font: UIFont 41 | public var textOffsetY: CGFloat 42 | 43 | // Image 44 | public var imageSize: CGFloat? 45 | public var tintColor: UIColor 46 | 47 | // KeyPop 48 | public var keyPopType: KeyPopType? 49 | public var keyPopWidthMultiplier: CGFloat 50 | public var keyPopHeightMultiplier: CGFloat 51 | public var keyPopContainerView: UIView? 52 | 53 | public init( 54 | backgroundColor: UIColor? = nil, 55 | cornerRadius: CGFloat? = nil, 56 | borderColor: UIColor? = nil, 57 | borderWidth: CGFloat? = nil, 58 | shadow: Shadow? = nil, 59 | textColor: UIColor? = nil, 60 | font: UIFont? = nil, 61 | textOffsetY: CGFloat? = nil, 62 | imageSize: CGFloat? = nil, 63 | tintColor: UIColor = UIColor.blackColor(), 64 | keyPopType: KeyPopType? = nil, 65 | keyPopWidthMultiplier: CGFloat? = nil, 66 | keyPopHeightMultiplier: CGFloat? = nil, 67 | keyPopContainerView: UIView? = nil) { 68 | self.backgroundColor = backgroundColor ?? UIColor.whiteColor() 69 | self.cornerRadius = cornerRadius ?? 5 70 | self.borderColor = borderColor ?? UIColor.clearColor() 71 | self.borderWidth = borderWidth ?? 0 72 | self.shadow = shadow 73 | self.textColor = textColor ?? UIColor.blackColor() 74 | self.font = font ?? UIFont.systemFontOfSize(21) 75 | self.textOffsetY = textOffsetY ?? 0 76 | self.imageSize = imageSize 77 | self.tintColor = tintColor 78 | self.keyPopType = keyPopType 79 | self.keyPopWidthMultiplier = keyPopWidthMultiplier ?? 1.5 80 | self.keyPopHeightMultiplier = keyPopHeightMultiplier ?? 1.1 81 | self.keyPopContainerView = keyPopContainerView 82 | } 83 | } 84 | 85 | // MARK: - KeyboardButton 86 | public var KeyboardButtonPopupViewTag: Int = 101 87 | public var KeyboardButtonMenuViewTag: Int = 102 88 | 89 | public class KeyboardButton: UIView { 90 | public var type: KeyboardButtonType = .Key("") { didSet { reload() } } 91 | public var widthInRow: KeyboardButtonWidth = .Dynamic { didSet { reload() } } 92 | public var style: KeyboardButtonStyle! { didSet { reload() } } 93 | public var keyMenu: KeyMenu? 94 | 95 | public var textLabel: UILabel? 96 | public var imageView: UIImageView? 97 | 98 | public var identifier: String? 99 | public var hitRangeInsets: UIEdgeInsets = UIEdgeInsetsZero 100 | 101 | // MARK: Init 102 | public init( 103 | type: KeyboardButtonType, 104 | style: KeyboardButtonStyle, 105 | width: KeyboardButtonWidth = .Dynamic, 106 | menu: KeyMenu? = nil, 107 | identifier: String? = nil) { 108 | 109 | super.init(frame: CGRect.zero) 110 | self.type = type 111 | self.style = style 112 | self.widthInRow = width 113 | self.identifier = identifier 114 | reload() 115 | } 116 | 117 | public required init?(coder aDecoder: NSCoder) { 118 | super.init(coder: aDecoder) 119 | reload() 120 | } 121 | 122 | private func reload() { 123 | userInteractionEnabled = true 124 | backgroundColor = style.backgroundColor 125 | layer.cornerRadius = style.cornerRadius 126 | 127 | // border 128 | layer.borderColor = style.borderColor.CGColor 129 | layer.borderWidth = style.borderWidth 130 | 131 | // content 132 | textLabel?.removeFromSuperview() 133 | textLabel = nil 134 | imageView?.removeFromSuperview() 135 | imageView = nil 136 | 137 | switch type { 138 | case .Key(let text): 139 | textLabel = UILabel() 140 | textLabel?.text = text 141 | textLabel?.textColor = style.textColor 142 | textLabel?.font = style.font 143 | textLabel?.textAlignment = .Center 144 | textLabel?.translatesAutoresizingMaskIntoConstraints = false 145 | textLabel?.adjustsFontSizeToFitWidth = true 146 | textLabel?.minimumScaleFactor = 0.5 147 | addSubview(textLabel!) 148 | case .Text(let text): 149 | textLabel = UILabel() 150 | textLabel?.text = text 151 | textLabel?.textColor = style.textColor 152 | textLabel?.font = style.font 153 | textLabel?.textAlignment = .Center 154 | textLabel?.translatesAutoresizingMaskIntoConstraints = false 155 | textLabel?.adjustsFontSizeToFitWidth = true 156 | textLabel?.minimumScaleFactor = 0.5 157 | addSubview(textLabel!) 158 | case .Image(let image): 159 | imageView = UIImageView(image: image) 160 | imageView?.contentMode = .ScaleAspectFit 161 | imageView?.tintColor = style.tintColor 162 | addSubview(imageView!) 163 | } 164 | } 165 | 166 | // MARK: Layout 167 | public override func layoutSubviews() { 168 | super.layoutSubviews() 169 | var padding = CGFloat(0) 170 | applyShadow(shadow: style.shadow) 171 | 172 | textLabel?.frame = CGRect( 173 | x: padding, 174 | y: padding + style.textOffsetY, 175 | width: frame.size.width - (padding * 2), 176 | height: frame.size.height - (padding * 2)) 177 | 178 | if let imageSize = style.imageSize { 179 | padding = (min(frame.size.height, frame.size.width) - imageSize) / 2 180 | } 181 | 182 | imageView?.frame = CGRect( 183 | x: padding, 184 | y: padding, 185 | width: frame.size.width - (padding * 2), 186 | height: frame.size.height - (padding * 2)) 187 | } 188 | 189 | // MARK: KeyPop 190 | public func showKeyPop(show show: Bool) { 191 | if style.keyPopType == nil { 192 | return 193 | } 194 | 195 | let view = style.keyPopContainerView ?? self 196 | if show { 197 | if view.viewWithTag(KeyboardButtonPopupViewTag) != nil { return } 198 | let popup = createKeyPop() 199 | popup.tag = KeyboardButtonPopupViewTag 200 | popup.frame.origin = convertPoint(popup.frame.origin, toView: view) 201 | view.addSubview(popup) 202 | } else { 203 | if let popup = view.viewWithTag(KeyboardButtonPopupViewTag) { 204 | popup.removeFromSuperview() 205 | } 206 | } 207 | } 208 | 209 | private func createKeyPop() -> UIView { 210 | let padding = CGFloat(5) 211 | let popStyle = KeyPopStyle( 212 | widthMultiplier: style.keyPopWidthMultiplier, 213 | heightMultiplier: style.keyPopHeightMultiplier) 214 | let content = KeyPop(referenceButton: self, style: popStyle) 215 | let contentWidth = frame.size.width * content.style.widthMultiplier 216 | 217 | var contentX = CGFloat(0) 218 | var contentRoundCorners = UIRectCorner.AllCorners 219 | switch style.keyPopType! { 220 | case .Default: 221 | contentX = (contentWidth - frame.size.width) / -2.0 222 | case .Right: 223 | contentX = frame.size.width - contentWidth 224 | contentRoundCorners = [.TopLeft, .TopRight, .BottomLeft] 225 | case .Left: 226 | contentX = 0 227 | contentRoundCorners = [.TopLeft, .TopRight, .BottomRight] 228 | } 229 | 230 | content.frame = CGRect( 231 | x: contentX, 232 | y: 0, 233 | width: contentWidth, 234 | height: frame.size.height * content.style.heightMultiplier) 235 | content.frame.origin.y = -(content.frame.size.height + padding) 236 | 237 | let bottomRect = CGRect( 238 | x: 0, 239 | y: -padding - 1, // a little hack for filling the gap 240 | width: frame.size.width, 241 | height: frame.size.height + padding) 242 | 243 | let path = UIBezierPath( 244 | roundedRect: content.frame, 245 | byRoundingCorners: contentRoundCorners, 246 | cornerRadii: CGSize( 247 | width: style.cornerRadius * style.keyPopWidthMultiplier, 248 | height: style.cornerRadius * style.keyPopHeightMultiplier)) 249 | path.appendPath(UIBezierPath( 250 | roundedRect: bottomRect, 251 | byRoundingCorners: [.BottomLeft, .BottomRight], 252 | cornerRadii: CGSize( 253 | width: style.cornerRadius, 254 | height: style.cornerRadius))) 255 | 256 | let mask = CAShapeLayer() 257 | mask.path = path.CGPath 258 | mask.fillColor = popStyle.backgroundColor.CGColor 259 | 260 | let popup = UIView( 261 | frame: CGRect( 262 | x: 0, 263 | y: 0, 264 | width: content.frame.size.width, 265 | height: content.frame.size.height + padding + frame.size.height)) 266 | popup.addSubview(content) 267 | popup.layer.applyShadow(shadow: popStyle.shadow) 268 | popup.layer.insertSublayer(mask, atIndex: 0) 269 | 270 | return popup 271 | } 272 | 273 | // MARK: KeyMenu 274 | public func showKeyMenu(show show: Bool) { 275 | if keyMenu == nil { 276 | return 277 | } 278 | 279 | let view = style.keyPopContainerView ?? self 280 | if show { 281 | if view.viewWithTag(KeyboardButtonMenuViewTag) != nil { return } 282 | keyMenu?.selectedIndex = -1 283 | let menu = createKeyMenu() 284 | menu.tag = KeyboardButtonMenuViewTag 285 | view.addSubview(menu) 286 | } else { 287 | if let menu = view.viewWithTag(KeyboardButtonMenuViewTag) { 288 | menu.removeFromSuperview() 289 | } 290 | } 291 | } 292 | 293 | private func createKeyMenu() -> UIView { 294 | guard let content = keyMenu else { return UIView() } 295 | let padding = CGFloat(5) 296 | content.frame.origin.y = -(content.frame.size.height + padding) 297 | content.layer.cornerRadius = style.cornerRadius * style.keyPopWidthMultiplier 298 | content.clipsToBounds = true 299 | 300 | let bottomRect = CGRect( 301 | x: 0, 302 | y: -padding - 1, // a little hack for filling the gap 303 | width: frame.size.width, 304 | height: frame.size.height + padding) 305 | 306 | let path = UIBezierPath( 307 | roundedRect: content.frame, 308 | byRoundingCorners: [.TopLeft, .TopRight, .BottomRight], 309 | cornerRadii: CGSize( 310 | width: style.cornerRadius * style.keyPopWidthMultiplier, 311 | height: style.cornerRadius * style.keyPopHeightMultiplier)) 312 | path.appendPath(UIBezierPath( 313 | roundedRect: bottomRect, 314 | byRoundingCorners: [.BottomLeft, .BottomRight], 315 | cornerRadii: CGSize( 316 | width: style.cornerRadius, 317 | height: style.cornerRadius))) 318 | 319 | let mask = CAShapeLayer() 320 | mask.path = path.CGPath 321 | mask.fillColor = content.style.backgroundColor.CGColor 322 | mask.applyShadow(shadow: content.style.shadow) 323 | 324 | let popup = UIView( 325 | frame: CGRect( 326 | x: 0, 327 | y: 0, 328 | width: content.frame.size.width, 329 | height: content.frame.size.height + padding + frame.size.height)) 330 | popup.addSubview(content) 331 | popup.layer.insertSublayer(mask, atIndex: 0) 332 | return popup 333 | } 334 | 335 | // MARK: Hit Test 336 | public override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool { 337 | let hitFrame = UIEdgeInsetsInsetRect(bounds, hitRangeInsets) 338 | return CGRectContainsPoint(hitFrame, point) 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /Keyboard/KeyboardLayoutEngine/KeyboardLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardLayout.swift 3 | // KeyboardLayoutEngine 4 | // 5 | // Created by Cem Olcay on 06/05/16. 6 | // Copyright © 2016 Prototapp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // MARK: - Array Extension 12 | extension CollectionType { 13 | /// Returns the element at the specified index iff it is within bounds, otherwise nil. 14 | subscript (safe index: Index) -> Generator.Element? { 15 | return indices.contains(index) ? self[index] : nil 16 | } 17 | } 18 | 19 | // MARK: - UITouch Extension 20 | internal extension UITouch { 21 | internal var key: String { 22 | return "\(unsafeAddressOf(self))" 23 | } 24 | } 25 | 26 | // MARK: - KeyboardButtonTouch 27 | internal class KeyboardButtonTouch { 28 | internal var touch: String 29 | internal var button: KeyboardButton 30 | 31 | internal init(touch: UITouch, button: KeyboardButton) { 32 | self.touch = touch.key 33 | self.button = button 34 | } 35 | } 36 | 37 | // MARK: - KeyboardLayoutDelegate 38 | @objc public protocol KeyboardLayoutDelegate { 39 | // Key Press Events 40 | optional func keyboardLayout(keyboardLayout: KeyboardLayout, didKeyPressStart keyboardButton: KeyboardButton) 41 | optional func keyboardLayout(keyboardLayout: KeyboardLayout, didKeyPressEnd keyboardButton: KeyboardButton) 42 | optional func keyboardLayout(keyboardLayout: KeyboardLayout, didDraggedInFrom oldKeyboardButton: KeyboardButton, to newKeyboardButton: KeyboardButton) 43 | // Touch Events 44 | optional func keyboardLayout(keyboardLayout: KeyboardLayout, didTouchesBegin touches: Set) 45 | optional func keyboardLayout(keyboardLayout: KeyboardLayout, didTouchesMove touches: Set) 46 | optional func keyboardLayout(keyboardLayout: KeyboardLayout, didTouchesEnd touches: Set?) 47 | optional func keyboardLayout(keyboardLayout: KeyboardLayout, didTouchesCancel touches: Set?) 48 | } 49 | 50 | // MARK: - KeyboardLayoutStyle 51 | public struct KeyboardLayoutStyle { 52 | public var backgroundColor: UIColor 53 | 54 | public init(backgroundColor: UIColor? = nil) { 55 | self.backgroundColor = backgroundColor ?? UIColor(red: 208.0/255.0, green: 213.0/255.0, blue: 219.0/255.0, alpha: 1) 56 | } 57 | } 58 | 59 | // MARK: - KeyboardLayout 60 | public class KeyboardLayout: UIView { 61 | public var style: KeyboardLayoutStyle! 62 | public var rows: [KeyboardRow]! 63 | 64 | public weak var delegate: KeyboardLayoutDelegate? 65 | 66 | private var isPortrait: Bool { 67 | return UIScreen.mainScreen().bounds.size.width < UIScreen.mainScreen().bounds.size.height 68 | } 69 | 70 | public var typingEnabled: Bool = true 71 | private var currentTouches: [KeyboardButtonTouch] = [] 72 | 73 | // MARK: Init 74 | public init(style: KeyboardLayoutStyle, rows: [KeyboardRow]) { 75 | super.init(frame: CGRect.zero) 76 | self.style = style 77 | self.rows = rows 78 | 79 | for row in rows { 80 | addSubview(row) 81 | } 82 | } 83 | 84 | public required init?(coder aDecoder: NSCoder) { 85 | super.init(coder: aDecoder) 86 | } 87 | 88 | // MARK: Layout 89 | public override func layoutSubviews() { 90 | super.layoutSubviews() 91 | superview?.backgroundColor = style.backgroundColor 92 | 93 | let optimumRowHeight = getOptimumRowHeight() 94 | var currentY: CGFloat = 0 95 | for row in rows { 96 | row.isPortrait = isPortrait 97 | currentY += isPortrait ? row.style.topPadding : row.style.topPaddingLandscape 98 | row.frame = CGRect( 99 | x: 0, 100 | y: currentY, 101 | width: frame.size.width, 102 | height: optimumRowHeight) 103 | currentY += optimumRowHeight + (isPortrait ? row.style.bottomPadding : row.style.bottomPaddingLandscape) 104 | } 105 | } 106 | 107 | private func getRowPaddings() -> CGFloat { 108 | var total = CGFloat(0) 109 | for row in rows { 110 | total += isPortrait ? row.style.topPadding + row.style.bottomPadding : row.style.topPaddingLandscape + row.style.bottomPaddingLandscape 111 | } 112 | return total 113 | } 114 | 115 | private func getOptimumRowHeight() -> CGFloat { 116 | let height = frame.size.height 117 | let totalPaddings = getRowPaddings() 118 | return max(0, (height - totalPaddings) / CGFloat(rows.count)) 119 | } 120 | 121 | // MARK: Manage Buttons 122 | public func getKeyboardButton(atRowIndex rowIndex: Int, buttonIndex: Int) -> KeyboardButton? { 123 | if let row = rows[safe: rowIndex], let button = row.characters[safe: buttonIndex] as? KeyboardButton { 124 | return button 125 | } 126 | return nil 127 | } 128 | 129 | public func getKeyboardButton(withIdentifier identifier: String) -> KeyboardButton? { 130 | for row in rows { 131 | for button in row.characters { 132 | if let button = button as? KeyboardButton where button.identifier == identifier { 133 | return button 134 | } 135 | } 136 | } 137 | return nil 138 | } 139 | 140 | public func addKeyboardButton(keyboardButton button: KeyboardButton, rowAtIndex: Int, buttonIndex: Int?) { 141 | if let row = rows[safe: rowAtIndex] { 142 | if let index = buttonIndex where buttonIndex < row.characters.count { 143 | row.characters.insert(button, atIndex: index) 144 | } else { 145 | row.characters.append(button) 146 | } 147 | row.addSubview(button) 148 | } 149 | } 150 | 151 | public func removeKeyboardButton(atRowIndex rowIndex: Int, buttonIndex: Int) -> Bool { 152 | if let row = rows[safe: rowIndex], let button = row.characters[safe: buttonIndex] { 153 | row.characters.removeAtIndex(buttonIndex) 154 | button.removeFromSuperview() 155 | return true 156 | } 157 | return false 158 | } 159 | 160 | // MARK: Touch Handling 161 | public override func touchesBegan(touches: Set, withEvent event: UIEvent?) { 162 | super.touchesBegan(touches, withEvent: event) 163 | delegate?.keyboardLayout?(self, didTouchesBegin: touches) 164 | 165 | if !typingEnabled { 166 | return 167 | } 168 | 169 | // Add valid touches that hit a `KeyboardButton` to `currentTouches` dictionary 170 | for touch in touches { 171 | if let button = hitTest(touch.locationInView(self), withEvent: nil) as? KeyboardButton { 172 | if currentTouches.map({ $0.touch }).contains(touch.key) == false { 173 | currentTouches.append(KeyboardButtonTouch(touch: touch, button: button)) 174 | } 175 | } 176 | } 177 | 178 | // Send key press start and end events ordered from current touches 179 | for (index, currentTouch) in currentTouches.enumerate() { 180 | if index == max(0, currentTouches.count - 1) { 181 | delegate?.keyboardLayout?(self, didKeyPressStart: currentTouch.button) 182 | currentTouch.button.showKeyPop(show: true) 183 | } else { 184 | delegate?.keyboardLayout?(self, didKeyPressEnd: currentTouch.button) 185 | currentTouch.button.showKeyPop(show: false) 186 | } 187 | } 188 | 189 | if currentTouches.count > 1 { 190 | currentTouches.removeRange(0.., withEvent event: UIEvent?) { 195 | super.touchesMoved(touches, withEvent: event) 196 | delegate?.keyboardLayout?(self, didTouchesMove: touches) 197 | 198 | if !typingEnabled { 199 | return 200 | } 201 | 202 | for touch in touches { 203 | if let currentTouch = currentTouches.filter({ $0.touch == touch.key }).first, 204 | button = hitTest(touch.locationInView(self), withEvent: nil) as? KeyboardButton { 205 | if currentTouch.button != button { 206 | delegate?.keyboardLayout?(self, didDraggedInFrom: currentTouch.button, to: button) 207 | currentTouch.button.showKeyPop(show: false) 208 | currentTouch.button = button 209 | currentTouch.button.showKeyPop(show: true) 210 | } 211 | } 212 | } 213 | } 214 | 215 | public override func touchesEnded(touches: Set, withEvent event: UIEvent?) { 216 | super.touchesEnded(touches, withEvent: event) 217 | delegate?.keyboardLayout?(self, didTouchesEnd: touches) 218 | 219 | if !typingEnabled { 220 | return 221 | } 222 | 223 | for touch in touches { 224 | if let currentTouch = currentTouches.filter({ $0.touch == touch.key }).first { 225 | currentTouch.button.showKeyPop(show: false) 226 | delegate?.keyboardLayout?(self, didKeyPressEnd: currentTouch.button) 227 | currentTouches = currentTouches.filter({ $0.touch != touch.key }) 228 | } 229 | } 230 | } 231 | 232 | public override func touchesCancelled(touches: Set?, withEvent event: UIEvent?) { 233 | super.touchesCancelled(touches, withEvent: event) 234 | currentTouches.forEach({ $0.button.showKeyPop(show: false) }) 235 | currentTouches = [] 236 | delegate?.keyboardLayout?(self, didTouchesCancel: touches) 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /Keyboard/KeyboardLayoutEngine/KeyboardRow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardRow.swift 3 | // KeyboardLayoutEngine 4 | // 5 | // Created by Cem Olcay on 06/05/16. 6 | // Copyright © 2016 Prototapp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // MARK: - KeyboardRowStyle 12 | public struct KeyboardRowStyle { 13 | public var leadingPadding: CGFloat 14 | public var leadingPaddingLandscape: CGFloat 15 | 16 | public var trailingPadding: CGFloat 17 | public var trailingPaddingLandscape: CGFloat 18 | 19 | public var topPadding: CGFloat 20 | public var topPaddingLandscape: CGFloat 21 | 22 | public var bottomPadding: CGFloat 23 | public var bottomPaddingLandscape: CGFloat 24 | 25 | public var buttonsPadding: CGFloat 26 | public var buttonsPaddingLandscape: CGFloat 27 | 28 | public init( 29 | leadingPadding: CGFloat? = nil, 30 | leadingPaddingLandscape: CGFloat? = nil, 31 | trailingPadding: CGFloat? = nil, 32 | trailingPaddingLandscape: CGFloat? = nil, 33 | topPadding: CGFloat? = nil, 34 | topPaddingLandscape: CGFloat? = nil, 35 | bottomPadding: CGFloat? = nil, 36 | bottomPaddingLandscape: CGFloat? = nil, 37 | buttonsPadding: CGFloat? = nil, 38 | buttonsPaddingLandscape: CGFloat? = nil) { 39 | 40 | self.leadingPadding = leadingPadding ?? 3 41 | self.leadingPaddingLandscape = leadingPaddingLandscape ?? leadingPadding ?? 3 42 | self.trailingPadding = trailingPadding ?? 3 43 | self.trailingPaddingLandscape = trailingPaddingLandscape ?? trailingPadding ?? 3 44 | self.topPadding = topPadding ?? 6 45 | self.topPaddingLandscape = topPaddingLandscape ?? topPadding ?? 6 46 | self.bottomPadding = bottomPadding ?? 6 47 | self.bottomPaddingLandscape = bottomPaddingLandscape ?? bottomPadding ?? 4 48 | self.buttonsPadding = buttonsPadding ?? 6 49 | self.buttonsPaddingLandscape = buttonsPaddingLandscape ?? buttonsPadding ?? 5 50 | } 51 | } 52 | 53 | // MARK: - KeyboardRow 54 | public class KeyboardRow: UIView { 55 | public var style: KeyboardRowStyle! 56 | /// Characters should be eighter `KeyboardButton` or `KeyboardRow` 57 | public var characters: [AnyObject]! 58 | /// Managed by KeyboardLayout 59 | internal var isPortrait: Bool = true 60 | /// Managed by self, returns parent `KeyboardRow` if exists. 61 | public private(set) var parentRow: KeyboardRow? 62 | 63 | public var buttonHitRangeInsets: UIEdgeInsets { 64 | return UIEdgeInsets( 65 | top: isPortrait ? -style.topPadding : -style.topPaddingLandscape, 66 | left: -(style.buttonsPadding / 2.0), 67 | bottom: isPortrait ? -style.bottomPadding : -style.bottomPaddingLandscape, 68 | right: -(style.buttonsPadding / 2.0)) 69 | } 70 | 71 | // MARK: Init 72 | public init(style: KeyboardRowStyle, characters: [AnyObject]) { 73 | assert(characters.filter({ !(($0 is KeyboardButton) || ($0 is KeyboardRow)) }).count <= 0) 74 | super.init(frame: CGRect.zero) 75 | 76 | self.style = style 77 | self.characters = characters 78 | 79 | for character in self.characters { 80 | if let character = character as? KeyboardButton { 81 | addSubview(character) 82 | } 83 | if let row = character as? KeyboardRow { 84 | addSubview(row) 85 | } 86 | } 87 | } 88 | 89 | public required init?(coder aDecoder: NSCoder) { 90 | super.init(coder: aDecoder) 91 | } 92 | 93 | // MARK: Paddings 94 | 95 | func getLeadingPadding() -> CGFloat { 96 | return isPortrait ? style.leadingPadding : style.leadingPaddingLandscape ?? style.leadingPadding 97 | } 98 | 99 | func getTrailingPadding() -> CGFloat { 100 | return isPortrait ? style.trailingPadding : style.trailingPaddingLandscape ?? style.trailingPadding 101 | } 102 | 103 | func getButtonsPadding() -> CGFloat { 104 | return isPortrait ? style.buttonsPadding : style.buttonsPaddingLandscape ?? style.buttonsPadding 105 | } 106 | 107 | // MARK: Layout 108 | public override func layoutSubviews() { 109 | super.layoutSubviews() 110 | let optimumButtonWidth = getOptimumButtonWidth() 111 | var currentX = getLeadingPadding() 112 | for character in characters { 113 | if let character = character as? KeyboardButton { 114 | character.frame = CGRect( 115 | x: currentX, 116 | y: 0, 117 | width: getWidthForKeyboardButton(character), 118 | height: frame.size.height) 119 | currentX += character.frame.size.width + getButtonsPadding() 120 | // Set hit range 121 | character.hitRangeInsets = buttonHitRangeInsets 122 | if character == characters.first as? KeyboardButton && parentRow == nil { 123 | character.hitRangeInsets.left -= 20 124 | } else if character == characters.last as? KeyboardButton && parentRow == nil { 125 | character.hitRangeInsets.right -= 20 126 | } 127 | } 128 | if let childRow = character as? KeyboardRow { 129 | childRow.parentRow = self 130 | childRow.isPortrait = isPortrait 131 | childRow.frame = CGRect( 132 | x: currentX, 133 | y: 0, 134 | width: childRow.getLeadingPadding() + optimumButtonWidth + childRow.getTrailingPadding(), 135 | height: frame.size.height) 136 | currentX += childRow.frame.size.width + getButtonsPadding() 137 | } 138 | } 139 | currentX += getTrailingPadding() 140 | } 141 | 142 | private func getRelativeWidthForPercent(percent: CGFloat) -> CGFloat { 143 | let buttonsPadding = max(0, CGFloat(characters.count - 1)) * getButtonsPadding() 144 | let totalPadding = buttonsPadding + getLeadingPadding() + getTrailingPadding() 145 | let cleanWidth = frame.size.width - totalPadding 146 | return cleanWidth * percent 147 | } 148 | 149 | private func getWidthForKeyboardButton(button: KeyboardButton) -> CGFloat { 150 | switch button.widthInRow { 151 | case .Dynamic: 152 | return getOptimumButtonWidth() 153 | case .Static(let width): 154 | return width 155 | case .Relative(let percent): 156 | return getRelativeWidthForPercent(percent) 157 | } 158 | } 159 | 160 | private func getOptimumButtonWidth() -> CGFloat { 161 | var charactersWithDynamicWidthCount: Int = 0 162 | var totalStaticWidthButtonsWidth: CGFloat = 0 163 | var totalChildRowPadding: CGFloat = 0 164 | 165 | for character in characters { 166 | if let button = character as? KeyboardButton { 167 | switch button.widthInRow { 168 | case .Dynamic: 169 | charactersWithDynamicWidthCount += 1 170 | case .Static(let width): 171 | totalStaticWidthButtonsWidth += width 172 | case .Relative(let percent): 173 | totalStaticWidthButtonsWidth += getRelativeWidthForPercent(percent) 174 | break 175 | } 176 | } else if let row = character as? KeyboardRow { 177 | totalChildRowPadding += row.getLeadingPadding() + row.getTrailingPadding() 178 | charactersWithDynamicWidthCount += 1 179 | } 180 | } 181 | 182 | let width = frame.size.width 183 | let totalButtonPadding: CGFloat = max(0, CGFloat(characters.count - 1) * getButtonsPadding()) 184 | let totalPadding = totalButtonPadding + 185 | totalStaticWidthButtonsWidth + 186 | totalChildRowPadding + 187 | getLeadingPadding() + 188 | getTrailingPadding() 189 | let opt = (width - totalPadding) / CGFloat(charactersWithDynamicWidthCount) 190 | return opt 191 | } 192 | 193 | // MARK: Hit Test 194 | public override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool { 195 | if UIEdgeInsetsEqualToEdgeInsets(buttonHitRangeInsets, UIEdgeInsetsZero) { 196 | return super.pointInside(point, withEvent: event) 197 | } 198 | 199 | let hitFrame = UIEdgeInsetsInsetRect(bounds, buttonHitRangeInsets) 200 | return CGRectContainsPoint(hitFrame, point) 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /Keyboard/KeyboardViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardViewController.swift 3 | // Keyboard 4 | // 5 | // Created by Cem Olcay on 06/05/16. 6 | // Copyright © 2016 Prototapp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class KeyboardViewController: UIInputViewController, CustomKeyboardDelegate { 12 | var customKeyboard: CustomKeyboard! 13 | 14 | // MARK: - Lifecycle 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | setupKeyboard() 18 | } 19 | 20 | private func setupKeyboard() { 21 | customKeyboard = CustomKeyboard() 22 | customKeyboard.delegate = self 23 | customKeyboard.translatesAutoresizingMaskIntoConstraints = false 24 | view.addSubview(customKeyboard) 25 | 26 | // Autolayout 27 | if #available(iOSApplicationExtension 9.0, *) { 28 | customKeyboard.leftAnchor.constraintEqualToAnchor(view.leftAnchor).active = true 29 | customKeyboard.rightAnchor.constraintEqualToAnchor(view.rightAnchor).active = true 30 | customKeyboard.topAnchor.constraintEqualToAnchor(view.topAnchor).active = true 31 | customKeyboard.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor).active = true 32 | } else { 33 | // Fallback on earlier versions 34 | } 35 | 36 | // This is how you add extra buttons to layouts for customising CustomKeyboard without even subclass it! 37 | let customButton = KeyboardButton( 38 | type: .Text("🕶"), 39 | style: CustomKeyboardKeyButtonStyle, 40 | width: .Static(width: 40), 41 | identifier: "customButton") 42 | customKeyboard.keyboardLayout.symbols.addKeyboardButton( 43 | keyboardButton: customButton, 44 | rowAtIndex: 3, 45 | buttonIndex: 2) 46 | 47 | // This is how you add a menu 48 | let globeButtons = [ 49 | customKeyboard.keyboardLayout.uppercase.getKeyboardButton(atRowIndex: 3, buttonIndex: 1), 50 | customKeyboard.keyboardLayout.uppercaseToggled.getKeyboardButton(atRowIndex: 3, buttonIndex: 1), 51 | customKeyboard.keyboardLayout.lowercase.getKeyboardButton(atRowIndex: 3, buttonIndex: 1), 52 | customKeyboard.keyboardLayout.numbers.getKeyboardButton(atRowIndex: 3, buttonIndex: 1), 53 | customKeyboard.keyboardLayout.symbols.getKeyboardButton(atRowIndex: 3, buttonIndex: 1), 54 | ] 55 | 56 | let menuItemStyle = KeyMenuItemStyle( 57 | separatorColor: UIColor(red: 210.0/255.0, green: 213.0/255.0, blue: 219.0/255.0, alpha: 1), 58 | separatorWidth: 0.5) 59 | 60 | for globeButton in globeButtons { 61 | let menu = KeyMenu( 62 | items: [ 63 | KeyMenuItem(title: "Switch Keyboard", style: menuItemStyle, action: { _ in self.advanceToNextInputMode() }), 64 | KeyMenuItem(title: "Settings", style: menuItemStyle, action: { _ in print("settings pressed") }), 65 | KeyMenuItem(title: "About Us", style: menuItemStyle, action: { _ in print("about pressed") }), 66 | ], 67 | style: KeyMenuStyle(itemSize: CGSize(width: 150, height: 40)), 68 | type: .Vertical) 69 | globeButton?.keyMenu = menu 70 | } 71 | } 72 | 73 | // MARK: CustomKeyboardDelegate 74 | func customKeyboard(customKeyboard: CustomKeyboard, keyboardButtonPressed keyboardButton: KeyboardButton) { 75 | if customKeyboard == self.customKeyboard { 76 | if keyboardButton.identifier == "customButton" { 77 | print("custom button pressed") 78 | } 79 | } 80 | } 81 | 82 | func customKeyboard(customKeyboard: CustomKeyboard, keyButtonPressed key: String) { 83 | if customKeyboard == self.customKeyboard { 84 | textDocumentProxy.insertText(key) 85 | } 86 | } 87 | 88 | func customKeyboardSpaceButtonPressed(customKeyboard: CustomKeyboard) { 89 | if customKeyboard == self.customKeyboard { 90 | textDocumentProxy.insertText(" ") 91 | } 92 | } 93 | 94 | func customKeyboardBackspaceButtonPressed(customKeyboard: CustomKeyboard) { 95 | if customKeyboard == self.customKeyboard { 96 | textDocumentProxy.deleteBackward() 97 | } 98 | } 99 | 100 | func customKeyboardReturnButtonPressed(customKeyboard: CustomKeyboard) { 101 | if customKeyboard == self.customKeyboard { 102 | textDocumentProxy.insertText("\n") 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/Backspace.imageset/Backspace.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cemolcay/KeyboardLayoutEngine/b53eaf37e6c84926e7e68e46370b4e165a0c39de/Keyboard/Resources.xcassets/Backspace.imageset/Backspace.pdf -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/Backspace.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Backspace.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/Emoji.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Emoji.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/Emoji.imageset/Emoji.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cemolcay/KeyboardLayoutEngine/b53eaf37e6c84926e7e68e46370b4e165a0c39de/Keyboard/Resources.xcassets/Emoji.imageset/Emoji.pdf -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/Globe.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Globe.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/Globe.imageset/Globe.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cemolcay/KeyboardLayoutEngine/b53eaf37e6c84926e7e68e46370b4e165a0c39de/Keyboard/Resources.xcassets/Globe.imageset/Globe.pdf -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/ShiftOff.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ShiftOff.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/ShiftOff.imageset/ShiftOff.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cemolcay/KeyboardLayoutEngine/b53eaf37e6c84926e7e68e46370b4e165a0c39de/Keyboard/Resources.xcassets/ShiftOff.imageset/ShiftOff.pdf -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/ShiftOn.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ShiftOn.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/ShiftOn.imageset/ShiftOn.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cemolcay/KeyboardLayoutEngine/b53eaf37e6c84926e7e68e46370b4e165a0c39de/Keyboard/Resources.xcassets/ShiftOn.imageset/ShiftOn.pdf -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/ShiftOnce.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ShiftOnce.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/ShiftOnce.imageset/ShiftOnce.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cemolcay/KeyboardLayoutEngine/b53eaf37e6c84926e7e68e46370b4e165a0c39de/Keyboard/Resources.xcassets/ShiftOnce.imageset/ShiftOnce.pdf -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/Siri.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Siri.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Keyboard/Resources.xcassets/Siri.imageset/Siri.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cemolcay/KeyboardLayoutEngine/b53eaf37e6c84926e7e68e46370b4e165a0c39de/Keyboard/Resources.xcassets/Siri.imageset/Siri.pdf -------------------------------------------------------------------------------- /KeyboardLayoutEngine.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint KeyboardLayoutEngine.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "KeyboardLayoutEngine" 19 | s.version = "0.9.9" 20 | s.summary = "⌨️ Simplest custom keyboard generator for iOS ever!" 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | s.description = <<-DESC 28 | KeyboardLayoutEngine 29 | === 30 | 31 | ⌨️ Simplest custom keyboard generator for iOS ever! 32 | 33 | `KeyboardLayoutEngine` is all about laying out keyboard buttons dynamically in a rectangle in a custom style easily but in fashion of original keyboard. For the sake of flexiblity, KeyboardLayoutEngine provides: 34 | 35 | * `KeyboardLayout`: For laying out rows with custom paddings, colors. 36 | * `KeyboardRow`: For laying out buttons or another set of KeyboardRow's inside. 37 | * `KeyboardButton`: For rendering buttons in rows. Also provides flexible width, type and other very useful API's for flexiblty. 38 | * They are also `UIView`s and handles their layout in their `layoutSubviews` function. 39 | * They are faster than autolayout yet they can adopt perfectly any `CGFrame` you want apply a keyboard layout. 40 | * That means they are play very well with orientation changes. (Layout for size class and/or orientation support is on the way.) 41 | * `KeyboardLayoutStyle`, `KeyboardRowStyle` and `KeyboardButtonStyle` structs handles pretty much everything about styling. 42 | * `KeyboardLayoutDelegate` for inform about button presses. 43 | * Also `DefaultKeyboard` provided out of box, a good start point for figuring out how it works other than being of fully functional original keyboard. 44 | 45 | Install 46 | ---- 47 | #### CocoaPods 48 | 49 | ``` ruby 50 | use_frameworks! 51 | # Target Keyboard 52 | pod 'KeyboardLayoutEngine' 53 | ``` 54 | 55 | Usage 56 | ---- 57 | 58 | * Describe your keyboard with custom styles, rows and buttons with either text or image in it. 59 | * Checkout the [DefaultKeyboardLayout](https://github.com/cemolcay/KeyboardLayoutEngine/blob/master/Keyboard/DefaultKeyboard/DefaultKeyboardLayout.swift) for detailed usage. 60 | 61 | ``` swift 62 | let keyboardLayout = KeyboardLayout( 63 | style: DefaultKeyboardLayoutStyle, 64 | rows: [ 65 | KeyboardRow( 66 | style: DefaultKeyboardRowStyle, 67 | characters: [ 68 | KeyboardButton(type: .Key("Q"), style: DefaultKeyboardKeyButtonStyle), 69 | KeyboardButton(type: .Key("W"), style: DefaultKeyboardKeyButtonStyle), 70 | KeyboardButton(type: .Key("E"), style: DefaultKeyboardKeyButtonStyle), 71 | KeyboardButton(type: .Key("R"), style: DefaultKeyboardKeyButtonStyle), 72 | KeyboardButton(type: .Key("T"), style: DefaultKeyboardKeyButtonStyle), 73 | KeyboardButton(type: .Key("Y"), style: DefaultKeyboardKeyButtonStyle), 74 | KeyboardButton(type: .Key("U"), style: DefaultKeyboardKeyButtonStyle), 75 | KeyboardButton(type: .Key("I"), style: DefaultKeyboardKeyButtonStyle), 76 | KeyboardButton(type: .Key("O"), style: DefaultKeyboardKeyButtonStyle), 77 | KeyboardButton(type: .Key("P"), style: DefaultKeyboardKeyButtonStyle), 78 | ] 79 | ) 80 | ] 81 | ) 82 | 83 | override func viewDidLoad() { 84 | super.viewDidLoad() 85 | view.addSubview(keyboardLayout) 86 | } 87 | 88 | override func viewDidLayoutSubviews() { 89 | super.viewDidLayoutSubviews() 90 | keyboardLayout.setNeedsLayout() 91 | } 92 | ``` 93 | 94 | KeyboardLayoutDelegate 95 | ---- 96 | 97 | * Implement `KeyboardLayoutDelegate` for get information about the button presses. 98 | 99 | ``` swift 100 | @objc public protocol KeyboardLayoutDelegate { 101 | optional func keyboardLayoutDidStartPressingButton(keyboardLayout: KeyboardLayout, keyboardButton: KeyboardButton) 102 | optional func keyboardLayoutDidPressButton(keyboardLayout: KeyboardLayout, keyboardButton: KeyboardButton) 103 | } 104 | ``` 105 | 106 | KeyboardButtonWidth 107 | ---- 108 | 109 | ``` swift 110 | public enum KeyboardButtonWidth { 111 | case Dynamic 112 | case Static(width: CGFloat) 113 | case Relative(percent: CGFloat) 114 | } 115 | ``` 116 | 117 | * Laying out buttons in rows are important. Since rows can their child rows, calculating right sizes for buttons and rows done by button types. 118 | * If you leave `.Dynamic` which is default by the way, every button in a row, it will calculate their width by `KeyboardRowStyle.buttonPadding` and total width of row and figure out equal widths with equal buttonPaddings. 119 | * Static will be static width obviusly. 120 | * Relative is an interesting one, which takes a value between [0, 1], fills percent of parent row, smartly calculated. 121 | 122 | KeyboardButtonType 123 | ---- 124 | 125 | ``` swift 126 | public enum KeyboardButtonType { 127 | case Key(String) 128 | case Text(String) 129 | case Image(UIImage?) 130 | } 131 | ``` 132 | 133 | * A button can be `Key`, `Text` or `Image`. 134 | * Key case might be useful for `textDocumentProxy.insertText`operation. 135 | * Text case might be useful for buttons like "space", "return", "ABC", "123" or any string include emojis. 136 | * Image case might be useful for buttons like "shift", "backspace", "switch keyboard" etc. 137 | 138 | Styling 139 | ---- 140 | 141 | * Every style struct has their default values in taste of original keyboard. 142 | * If you dont assign a value in `init` function of a style struct, it will be loaded with its default value. 143 | 144 | KeyboardLayoutStyle 145 | ---- 146 | Definition: 147 | 148 | ``` swift 149 | public struct KeyboardLayoutStyle { 150 | public var topPadding: CGFloat 151 | public var bottomPadding: CGFloat 152 | public var rowPadding: CGFloat 153 | public var backgroundColor: UIColor 154 | } 155 | ``` 156 | 157 | Example: 158 | 159 | ``` swift 160 | let DefaultKeyboardLayoutStyle = KeyboardLayoutStyle( 161 | topPadding: 10, 162 | bottomPadding: 5, 163 | rowPadding: 13, 164 | backgroundColor: UIColor(red: 208.0/255.0, green: 213.0/255.0, blue: 219.0/255.0, alpha: 1)) 165 | ``` 166 | 167 | KeyboardRowStyle 168 | ---- 169 | 170 | Definition: 171 | 172 | 173 | ``` swift 174 | public struct KeyboardRowStyle { 175 | public var leadingPadding: CGFloat 176 | public var trailingPadding: CGFloat 177 | public var buttonsPadding: CGFloat 178 | } 179 | ``` 180 | 181 | Example: 182 | 183 | ``` swift 184 | let DefaultKeyboardRowStyle = KeyboardRowStyle( 185 | leadingPadding: 5, 186 | trailingPadding: 5, 187 | buttonsPadding: 6) 188 | ``` 189 | 190 | KeyboardButtonStyle 191 | ---- 192 | 193 | Definition: 194 | 195 | ``` swift 196 | public struct KeyboardButtonStyle { 197 | public var backgroundColor: UIColor 198 | public var cornerRadius: CGFloat 199 | 200 | // Border 201 | public var borderColor: UIColor 202 | public var borderWidth: CGFloat 203 | 204 | // Shadow 205 | public var shadowColor: UIColor 206 | public var shadowOpacity: Float 207 | public var shadowOffset: CGSize 208 | public var shadowRadius: CGFloat 209 | public var shadowPath: UIBezierPath? 210 | 211 | // Text 212 | public var textColor: UIColor 213 | public var font: UIFont 214 | 215 | // Image 216 | public var imageSize: CGFloat? 217 | 218 | // Popup 219 | public var showsPopup: Bool 220 | public var popupWidthMultiplier: CGFloat 221 | public var popupHeightMultiplier: CGFloat 222 | } 223 | ``` 224 | 225 | Example: 226 | 227 | ``` swift 228 | let DefaultKeyboardDarkImageButtonStyle = KeyboardButtonStyle( 229 | backgroundColor: UIColor(red: 180.0/255.0, green: 188.0/255.0, blue: 201.0/255.0, alpha: 1), 230 | imageSize: 18, 231 | showsPopup: false) 232 | ``` 233 | 234 | DefaultKeyboard 235 | ---- 236 | 237 | Default iOS Keyboard implementation with `KeyboardLayoutEngine`. 238 | 239 | * Shift toggle mechanism 240 | * Backspace mechanisim 241 | * Key button popups 242 | * `textDocumentProxy` integrations with `DefaultKeyboardDelegate` 243 | * Ridiculusly easy implementation in `KeyboardViewController` 244 | * Change default styles before initilze it and you have your fully functional custumised standard English QWERTY keyboard! 245 | 246 | ``` swift 247 | override func viewDidLoad() { 248 | super.viewDidLoad() 249 | DefaultKeyboardLayoutStyle.backgroundColor = UIColor.redColor() 250 | DefaultKeyboardRowStyle.buttonsPadding = 5 251 | defaultKeyboard = DefaultKeyboard() 252 | defaultKeyboard.delegate = self 253 | view.addSubview(defaultKeyboard) 254 | } 255 | ``` 256 | 257 | #### DefaultKeyboard styles 258 | 259 | * DefaultKeyboardLayoutStyle: `KeyboardLayoutStyle` 260 | * DefaultKeyboardRowStyle: `KeyboardRowStyle` 261 | * DefaultKeyboardSecondRowStyle: `KeyboardRowStyle` 262 | * DefaultKeyboardChildRowStyle: `KeyboardRowStyle` 263 | * DefaultKeyboardSpaceButtonStyle: `KeyboardButtonStyle` 264 | * DefaultKeyboardBackspaceButtonStyle: `KeyboardButtonStyle` 265 | * DefaultKeyboardShiftButtonStyle: `KeyboardButtonStyle` 266 | * DefaultKeyboardGlobeButtonStyle: `KeyboardButtonStyle` 267 | * DefaultKeyboardReturnButtonStyle: `KeyboardButtonStyle` 268 | * DefaultKeyboardNumbersButtonStyle: `KeyboardButtonStyle` 269 | * DefaultKeyboardKeyButtonStyle: `KeyboardButtonStyle` 270 | 271 | DefaultKeyboardDelegate 272 | ---- 273 | 274 | * Provides information about key and special button presses. 275 | 276 | ``` swift 277 | @objc public protocol DefaultKeyboardDelegate { 278 | optional func defaultKeyboardDidPressKeyButton(defaultKeyboard: DefaultKeyboard, key: String) 279 | optional func defaultKeyboardDidPressSpaceButton(defaultKeyboard: DefaultKeyboard) 280 | optional func defaultKeyboardDidPressBackspaceButton(defaultKeyboard: DefaultKeyboard) 281 | optional func defaultKeyboardDidPressGlobeButton(defaultKeyboard: DefaultKeyboard) 282 | optional func defaultKeyboardDidPressReturnButton(defaultKeyboard: DefaultKeyboard) 283 | } 284 | ``` 285 | DESC 286 | 287 | s.homepage = "https://github.com/cemolcay/KeyboardLayoutEngine" 288 | # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 289 | 290 | 291 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 292 | # 293 | # Licensing your code is important. See http://choosealicense.com for more info. 294 | # CocoaPods will detect a license file if there is a named LICENSE* 295 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 296 | # 297 | 298 | s.license = "MIT" 299 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 300 | 301 | 302 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 303 | # 304 | # Specify the authors of the library, with email addresses. Email addresses 305 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 306 | # accepts just a name if you'd rather not provide an email address. 307 | # 308 | # Specify a social_media_url where others can refer to, for example a twitter 309 | # profile URL. 310 | # 311 | 312 | s.author = { "cemolcay" => "ccemolcay@gmail.com" } 313 | # Or just: s.author = "cemolcay" 314 | # s.authors = { "cemolcay" => "ccemolcay@gmail.com" } 315 | # s.social_media_url = "http://twitter.com/cemolcay" 316 | 317 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 318 | # 319 | # If this Pod runs only on iOS or OS X, then specify the platform and 320 | # the deployment target. You can optionally include the target after the platform. 321 | # 322 | 323 | # s.platform = :ios 324 | s.platform = :ios, "8.0" 325 | 326 | # When using multiple platforms 327 | # s.ios.deployment_target = "5.0" 328 | # s.osx.deployment_target = "10.7" 329 | # s.watchos.deployment_target = "2.0" 330 | # s.tvos.deployment_target = "9.0" 331 | 332 | 333 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 334 | # 335 | # Specify the location from where the source should be retrieved. 336 | # Supports git, hg, bzr, svn and HTTP. 337 | # 338 | 339 | s.source = { :git => "https://github.com/cemolcay/KeyboardLayoutEngine.git", :tag => "0.9.9" } 340 | 341 | 342 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 343 | # 344 | # CocoaPods is smart about how it includes source code. For source files 345 | # giving a folder will include any swift, h, m, mm, c & cpp files. 346 | # For header files it will include any header in the folder. 347 | # Not including the public_header_files will make all headers public. 348 | # 349 | 350 | s.source_files = "Keyboard/KeyboardLayoutEngine/*.swift", "Keyboard/CustomKeyboard/*.swift", "Keyboard/KeyPop/*.swift" 351 | 352 | # s.public_header_files = "Classes/**/*.h" 353 | 354 | 355 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 356 | # 357 | # A list of resources included with the Pod. These are copied into the 358 | # target bundle with a build phase script. Anything else will be cleaned. 359 | # You can preserve files from being cleaned, please don't preserve 360 | # non-essential files like tests, examples and documentation. 361 | # 362 | 363 | # s.resource = "icon.png" 364 | s.resources = "Keyboard/Resources.xcassets" 365 | 366 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 367 | 368 | 369 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 370 | # 371 | # Link your library with frameworks, or libraries. Libraries do not include 372 | # the lib prefix of their name. 373 | # 374 | 375 | s.framework = "UIKit" 376 | # s.frameworks = "SomeFramework", "AnotherFramework" 377 | 378 | # s.library = "iconv" 379 | # s.libraries = "iconv", "xml2" 380 | 381 | 382 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 383 | # 384 | # If your library depends on compiler flags you can set them in the xcconfig hash 385 | # where they will only apply to your library. If you depend on other Podspecs 386 | # you can include multiple dependencies to ensure it works. 387 | 388 | s.requires_arc = true 389 | 390 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 391 | s.dependency "Shadow" 392 | 393 | end 394 | -------------------------------------------------------------------------------- /KeyboardLayoutEngine.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /KeyboardLayoutEngine.xcodeproj/xcuserdata/Cem.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /KeyboardLayoutEngine.xcodeproj/xcuserdata/Cem.xcuserdatad/xcschemes/Keyboard.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 10 | 16 | 22 | 23 | 24 | 30 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 70 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 90 | 92 | 98 | 99 | 100 | 101 | 103 | 104 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /KeyboardLayoutEngine.xcodeproj/xcuserdata/Cem.xcuserdatad/xcschemes/KeyboardLayoutEngine.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /KeyboardLayoutEngine.xcodeproj/xcuserdata/Cem.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Keyboard.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | KeyboardLayoutEngine.xcscheme 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | B2FBDCC71CDD2224008D6100 21 | 22 | primary 23 | 24 | 25 | B2FBDCDB1CDD2224008D6100 26 | 27 | primary 28 | 29 | 30 | B2FBDCE61CDD2224008D6100 31 | 32 | primary 33 | 34 | 35 | B2FBDCFC1CDD2292008D6100 36 | 37 | primary 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /KeyboardLayoutEngine.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /KeyboardLayoutEngine.xcworkspace/xcuserdata/Cem.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cemolcay/KeyboardLayoutEngine/b53eaf37e6c84926e7e68e46370b4e165a0c39de/KeyboardLayoutEngine.xcworkspace/xcuserdata/Cem.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /KeyboardLayoutEngine.xcworkspace/xcuserdata/Cem.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /KeyboardLayoutEngine/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // KeyboardLayoutEngine 4 | // 5 | // Created by Cem Olcay on 06/05/16. 6 | // Copyright © 2016 Prototapp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /KeyboardLayoutEngine/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /KeyboardLayoutEngine/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 | 27 | 28 | -------------------------------------------------------------------------------- /KeyboardLayoutEngine/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /KeyboardLayoutEngine/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /KeyboardLayoutEngine/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // KeyboardLayoutEngine 4 | // 5 | // Created by Cem Olcay on 06/05/16. 6 | // Copyright © 2016 Prototapp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /KeyboardLayoutEngineTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /KeyboardLayoutEngineTests/KeyboardLayoutEngineTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardLayoutEngineTests.swift 3 | // KeyboardLayoutEngineTests 4 | // 5 | // Created by Cem Olcay on 06/05/16. 6 | // Copyright © 2016 Prototapp. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import KeyboardLayoutEngine 11 | 12 | class KeyboardLayoutEngineTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /KeyboardLayoutEngineUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /KeyboardLayoutEngineUITests/KeyboardLayoutEngineUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardLayoutEngineUITests.swift 3 | // KeyboardLayoutEngineUITests 4 | // 5 | // Created by Cem Olcay on 06/05/16. 6 | // Copyright © 2016 Prototapp. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class KeyboardLayoutEngineUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2016, Cem Olcay 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is 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 16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '8.0' 3 | # Uncomment this line if you're using Swift 4 | use_frameworks! 5 | 6 | target 'KeyboardLayoutEngine' do 7 | 8 | end 9 | 10 | target 'KeyboardLayoutEngineTests' do 11 | 12 | end 13 | 14 | target 'KeyboardLayoutEngineUITests' do 15 | 16 | end 17 | 18 | target 'Keyboard' do 19 | pod 'Shadow' 20 | end 21 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Shadow (0.2) 3 | 4 | DEPENDENCIES: 5 | - Shadow 6 | 7 | SPEC CHECKSUMS: 8 | Shadow: 0fd870dd3de3ec5226315203f25ba54453547060 9 | 10 | PODFILE CHECKSUM: df543bce1ee7f6451a25cf49c2b49af81bcb189e 11 | 12 | COCOAPODS: 1.0.1 13 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Shadow (0.2) 3 | 4 | DEPENDENCIES: 5 | - Shadow 6 | 7 | SPEC CHECKSUMS: 8 | Shadow: 0fd870dd3de3ec5226315203f25ba54453547060 9 | 10 | PODFILE CHECKSUM: df543bce1ee7f6451a25cf49c2b49af81bcb189e 11 | 12 | COCOAPODS: 1.0.1 13 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/Cem.xcuserdatad/xcschemes/Pods-Keyboard.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/Cem.xcuserdatad/xcschemes/Pods-KeyboardLayoutEngine.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/Cem.xcuserdatad/xcschemes/Pods-KeyboardLayoutEngineTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/Cem.xcuserdatad/xcschemes/Pods-KeyboardLayoutEngineUITests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/Cem.xcuserdatad/xcschemes/Shadow.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/Cem.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pods-Keyboard.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Pods-KeyboardLayoutEngine.xcscheme 13 | 14 | isShown 15 | 16 | 17 | Pods-KeyboardLayoutEngineTests.xcscheme 18 | 19 | isShown 20 | 21 | 22 | Pods-KeyboardLayoutEngineUITests.xcscheme 23 | 24 | isShown 25 | 26 | 27 | Shadow.xcscheme 28 | 29 | isShown 30 | 31 | 32 | 33 | SuppressBuildableAutocreation 34 | 35 | 00FAA7EC280B13D71F901C8A858C312A 36 | 37 | primary 38 | 39 | 40 | 06DC6E84AB12018A5EAB0905E5ED2E11 41 | 42 | primary 43 | 44 | 45 | 36F46FCC52674D2BCA61E373388DC87F 46 | 47 | primary 48 | 49 | 50 | 8C1145D9AFD788479A50BCC77D5A2B31 51 | 52 | primary 53 | 54 | 55 | 973AC4F4116E2FFD939C2891116C1E50 56 | 57 | primary 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Pods/Shadow/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2016, Cem Olcay 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is 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 16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /Pods/Shadow/README.md: -------------------------------------------------------------------------------- 1 | Shadow 2 | === 3 | 4 | A simple object for adding/removing shadows from your `CALayer`s or `UIView`s. 5 | You don't need to define or edit all shadow properties line by line anymore. 6 | 7 | Install 8 | ---- 9 | 10 | #### CocoaPods 11 | 12 | ``` ruby 13 | use_frameworks! 14 | pod 'Shadow' 15 | ``` 16 | 17 | Usage 18 | ---- 19 | 20 | ``` swift 21 | // Create default shadow. 22 | let shadow = Shadow() 23 | // Add shadow 24 | view.applyShadow(shadow: shadow) 25 | // Remove shadow 26 | view.applyShadow(shadow: nil) 27 | ``` 28 | -------------------------------------------------------------------------------- /Pods/Shadow/Shadow/Shadow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Shadow.swift 3 | // Shadow 4 | // 5 | // Created by Cem Olcay on 05/06/16. 6 | // Copyright © 2016 Prototapp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Defines a shadow object for `CALayer` shadows. 12 | /// Uses UIKit objects instead of CoreGraphics ones that CALayer uses. 13 | public struct Shadow { 14 | /// Color of the shadow. Default is `UIColor.grayColor()`. 15 | public var color: UIColor 16 | /// Radius of the shadow. Default is 1. 17 | public var radius: CGFloat 18 | /// Opacity of the shadow. Default is 0.5 19 | public var opacity: Float 20 | /// Offset of the shadow. Default is `CGSize(width: 0, height: 1)` 21 | public var offset: CGSize 22 | /// Optional bezier path of shadow. 23 | public var path: UIBezierPath? 24 | 25 | /// Initilizes `Shadow` with default values or optional given values. 26 | public init( 27 | color: UIColor? = nil, 28 | radius: CGFloat? = nil, 29 | opacity: Float? = nil, 30 | offset: CGSize? = nil, 31 | path: UIBezierPath? = nil) { 32 | self.color = color ?? UIColor.grayColor() 33 | self.radius = radius ?? 5 34 | self.opacity = opacity ?? 1 35 | self.offset = offset ?? CGSize(width: 0, height: 1) 36 | self.path = path 37 | } 38 | } 39 | 40 | /// Public extension of CALayer for applying or removing `Shadow`. 41 | public extension CALayer { 42 | /// Applys shadow if given object is not nil. 43 | /// Removes shadow if given object is nil. 44 | public func applyShadow(shadow shadow: Shadow? = nil) { 45 | shadowColor = shadow?.color.CGColor ?? UIColor.clearColor().CGColor 46 | shadowOpacity = shadow?.opacity ?? 0 47 | if let shadow = shadow { 48 | if let path = shadow.path { 49 | shadowRadius = shadow.radius ?? 0 50 | shadowOffset = shadow.offset ?? CGSize.zero 51 | shadowPath = path.CGPath 52 | } else { 53 | var shadowRect = bounds 54 | shadowRect.origin = CGPoint( 55 | x: bounds.origin.x + shadow.offset.width, 56 | y: bounds.origin.y + shadow.offset.height) 57 | let path = UIBezierPath( 58 | roundedRect: shadowRect, 59 | cornerRadius: shadow.radius) 60 | shadowPath = path.CGPath 61 | shadowRadius = 0 62 | shadowOffset = CGSize.zero 63 | } 64 | } else { 65 | shadowRadius = 0 66 | shadowOffset = CGSize.zero 67 | shadowPath = nil 68 | } 69 | } 70 | } 71 | 72 | /// Public extension of UIView for applying or removing `Shadow` 73 | public extension UIView { 74 | /// Applys shadow on its layer if given object is not nil. 75 | /// Removes shadow on its layer if given object is nil. 76 | public func applyShadow(shadow shadow: Shadow? = nil) { 77 | layer.applyShadow(shadow: shadow) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Keyboard/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Keyboard/Pods-Keyboard-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Shadow 5 | 6 | Copyright (C) 2016, Cem Olcay 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a 9 | copy of this software and associated documentation files (the "Software"), 10 | to deal in the Software without restriction, including without limitation 11 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | and/or sell copies of the Software, and to permit persons to whom the 13 | Software is 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 21 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 | DEALINGS IN THE SOFTWARE. 25 | 26 | 27 | Generated by CocoaPods - https://cocoapods.org 28 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Keyboard/Pods-Keyboard-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) 2016, Cem Olcay 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a 20 | copy of this software and associated documentation files (the "Software"), 21 | to deal in the Software without restriction, including without limitation 22 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 23 | and/or sell copies of the Software, and to permit persons to whom the 24 | Software is 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 32 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 34 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 35 | DEALINGS IN THE SOFTWARE. 36 | 37 | 38 | Title 39 | Shadow 40 | Type 41 | PSGroupSpecifier 42 | 43 | 44 | FooterText 45 | Generated by CocoaPods - https://cocoapods.org 46 | Title 47 | 48 | Type 49 | PSGroupSpecifier 50 | 51 | 52 | StringsTable 53 | Acknowledgements 54 | Title 55 | Acknowledgements 56 | 57 | 58 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Keyboard/Pods-Keyboard-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Keyboard : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Keyboard 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Keyboard/Pods-Keyboard-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | 86 | if [[ "$CONFIGURATION" == "Debug" ]]; then 87 | install_framework "$BUILT_PRODUCTS_DIR/Shadow/Shadow.framework" 88 | fi 89 | if [[ "$CONFIGURATION" == "Release" ]]; then 90 | install_framework "$BUILT_PRODUCTS_DIR/Shadow/Shadow.framework" 91 | fi 92 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Keyboard/Pods-Keyboard-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | *) 22 | TARGET_DEVICE_ARGS="--target-device mac" 23 | ;; 24 | esac 25 | 26 | realpath() { 27 | DIRECTORY="$(cd "${1%/*}" && pwd)" 28 | FILENAME="${1##*/}" 29 | echo "$DIRECTORY/$FILENAME" 30 | } 31 | 32 | install_resource() 33 | { 34 | if [[ "$1" = /* ]] ; then 35 | RESOURCE_PATH="$1" 36 | else 37 | RESOURCE_PATH="${PODS_ROOT}/$1" 38 | fi 39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 40 | cat << EOM 41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 42 | EOM 43 | exit 1 44 | fi 45 | case $RESOURCE_PATH in 46 | *.storyboard) 47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 49 | ;; 50 | *.xib) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.framework) 55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 59 | ;; 60 | *.xcdatamodel) 61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 63 | ;; 64 | *.xcdatamodeld) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 67 | ;; 68 | *.xcmappingmodel) 69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 71 | ;; 72 | *.xcassets) 73 | ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH") 74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 75 | ;; 76 | *) 77 | echo "$RESOURCE_PATH" 78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 79 | ;; 80 | esac 81 | } 82 | 83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | fi 89 | rm -f "$RESOURCES_TO_COPY" 90 | 91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 92 | then 93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 95 | while read line; do 96 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 97 | XCASSET_FILES+=("$line") 98 | fi 99 | done <<<"$OTHER_XCASSETS" 100 | 101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 102 | fi 103 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Keyboard/Pods-Keyboard-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | FOUNDATION_EXPORT double Pods_KeyboardVersionNumber; 5 | FOUNDATION_EXPORT const unsigned char Pods_KeyboardVersionString[]; 6 | 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Keyboard/Pods-Keyboard.debug.xcconfig: -------------------------------------------------------------------------------- 1 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Shadow" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Shadow/Shadow.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Shadow" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Keyboard/Pods-Keyboard.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_Keyboard { 2 | umbrella header "Pods-Keyboard-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Keyboard/Pods-Keyboard.release.xcconfig: -------------------------------------------------------------------------------- 1 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Shadow" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Shadow/Shadow.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Shadow" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngine/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngine/Pods-KeyboardLayoutEngine-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngine/Pods-KeyboardLayoutEngine-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 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngine/Pods-KeyboardLayoutEngine-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_KeyboardLayoutEngine : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_KeyboardLayoutEngine 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngine/Pods-KeyboardLayoutEngine-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngine/Pods-KeyboardLayoutEngine-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | *) 22 | TARGET_DEVICE_ARGS="--target-device mac" 23 | ;; 24 | esac 25 | 26 | realpath() { 27 | DIRECTORY="$(cd "${1%/*}" && pwd)" 28 | FILENAME="${1##*/}" 29 | echo "$DIRECTORY/$FILENAME" 30 | } 31 | 32 | install_resource() 33 | { 34 | if [[ "$1" = /* ]] ; then 35 | RESOURCE_PATH="$1" 36 | else 37 | RESOURCE_PATH="${PODS_ROOT}/$1" 38 | fi 39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 40 | cat << EOM 41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 42 | EOM 43 | exit 1 44 | fi 45 | case $RESOURCE_PATH in 46 | *.storyboard) 47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 49 | ;; 50 | *.xib) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.framework) 55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 59 | ;; 60 | *.xcdatamodel) 61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 63 | ;; 64 | *.xcdatamodeld) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 67 | ;; 68 | *.xcmappingmodel) 69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 71 | ;; 72 | *.xcassets) 73 | ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH") 74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 75 | ;; 76 | *) 77 | echo "$RESOURCE_PATH" 78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 79 | ;; 80 | esac 81 | } 82 | 83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | fi 89 | rm -f "$RESOURCES_TO_COPY" 90 | 91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 92 | then 93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 95 | while read line; do 96 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 97 | XCASSET_FILES+=("$line") 98 | fi 99 | done <<<"$OTHER_XCASSETS" 100 | 101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 102 | fi 103 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngine/Pods-KeyboardLayoutEngine-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | FOUNDATION_EXPORT double Pods_KeyboardLayoutEngineVersionNumber; 5 | FOUNDATION_EXPORT const unsigned char Pods_KeyboardLayoutEngineVersionString[]; 6 | 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngine/Pods-KeyboardLayoutEngine.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 3 | PODS_BUILD_DIR = $BUILD_DIR 4 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 5 | PODS_ROOT = ${SRCROOT}/Pods 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngine/Pods-KeyboardLayoutEngine.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_KeyboardLayoutEngine { 2 | umbrella header "Pods-KeyboardLayoutEngine-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngine/Pods-KeyboardLayoutEngine.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 3 | PODS_BUILD_DIR = $BUILD_DIR 4 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 5 | PODS_ROOT = ${SRCROOT}/Pods 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineTests/Pods-KeyboardLayoutEngineTests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineTests/Pods-KeyboardLayoutEngineTests-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 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineTests/Pods-KeyboardLayoutEngineTests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_KeyboardLayoutEngineTests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_KeyboardLayoutEngineTests 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineTests/Pods-KeyboardLayoutEngineTests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineTests/Pods-KeyboardLayoutEngineTests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | *) 22 | TARGET_DEVICE_ARGS="--target-device mac" 23 | ;; 24 | esac 25 | 26 | realpath() { 27 | DIRECTORY="$(cd "${1%/*}" && pwd)" 28 | FILENAME="${1##*/}" 29 | echo "$DIRECTORY/$FILENAME" 30 | } 31 | 32 | install_resource() 33 | { 34 | if [[ "$1" = /* ]] ; then 35 | RESOURCE_PATH="$1" 36 | else 37 | RESOURCE_PATH="${PODS_ROOT}/$1" 38 | fi 39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 40 | cat << EOM 41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 42 | EOM 43 | exit 1 44 | fi 45 | case $RESOURCE_PATH in 46 | *.storyboard) 47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 49 | ;; 50 | *.xib) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.framework) 55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 59 | ;; 60 | *.xcdatamodel) 61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 63 | ;; 64 | *.xcdatamodeld) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 67 | ;; 68 | *.xcmappingmodel) 69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 71 | ;; 72 | *.xcassets) 73 | ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH") 74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 75 | ;; 76 | *) 77 | echo "$RESOURCE_PATH" 78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 79 | ;; 80 | esac 81 | } 82 | 83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | fi 89 | rm -f "$RESOURCES_TO_COPY" 90 | 91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 92 | then 93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 95 | while read line; do 96 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 97 | XCASSET_FILES+=("$line") 98 | fi 99 | done <<<"$OTHER_XCASSETS" 100 | 101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 102 | fi 103 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineTests/Pods-KeyboardLayoutEngineTests-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | FOUNDATION_EXPORT double Pods_KeyboardLayoutEngineTestsVersionNumber; 5 | FOUNDATION_EXPORT const unsigned char Pods_KeyboardLayoutEngineTestsVersionString[]; 6 | 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineTests/Pods-KeyboardLayoutEngineTests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 3 | PODS_BUILD_DIR = $BUILD_DIR 4 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 5 | PODS_ROOT = ${SRCROOT}/Pods 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineTests/Pods-KeyboardLayoutEngineTests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_KeyboardLayoutEngineTests { 2 | umbrella header "Pods-KeyboardLayoutEngineTests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineTests/Pods-KeyboardLayoutEngineTests.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 3 | PODS_BUILD_DIR = $BUILD_DIR 4 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 5 | PODS_ROOT = ${SRCROOT}/Pods 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineUITests/Pods-KeyboardLayoutEngineUITests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineUITests/Pods-KeyboardLayoutEngineUITests-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 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineUITests/Pods-KeyboardLayoutEngineUITests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_KeyboardLayoutEngineUITests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_KeyboardLayoutEngineUITests 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineUITests/Pods-KeyboardLayoutEngineUITests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineUITests/Pods-KeyboardLayoutEngineUITests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | *) 22 | TARGET_DEVICE_ARGS="--target-device mac" 23 | ;; 24 | esac 25 | 26 | realpath() { 27 | DIRECTORY="$(cd "${1%/*}" && pwd)" 28 | FILENAME="${1##*/}" 29 | echo "$DIRECTORY/$FILENAME" 30 | } 31 | 32 | install_resource() 33 | { 34 | if [[ "$1" = /* ]] ; then 35 | RESOURCE_PATH="$1" 36 | else 37 | RESOURCE_PATH="${PODS_ROOT}/$1" 38 | fi 39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 40 | cat << EOM 41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 42 | EOM 43 | exit 1 44 | fi 45 | case $RESOURCE_PATH in 46 | *.storyboard) 47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 49 | ;; 50 | *.xib) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.framework) 55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 59 | ;; 60 | *.xcdatamodel) 61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 63 | ;; 64 | *.xcdatamodeld) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 67 | ;; 68 | *.xcmappingmodel) 69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 71 | ;; 72 | *.xcassets) 73 | ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH") 74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 75 | ;; 76 | *) 77 | echo "$RESOURCE_PATH" 78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 79 | ;; 80 | esac 81 | } 82 | 83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | fi 89 | rm -f "$RESOURCES_TO_COPY" 90 | 91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 92 | then 93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 95 | while read line; do 96 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 97 | XCASSET_FILES+=("$line") 98 | fi 99 | done <<<"$OTHER_XCASSETS" 100 | 101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 102 | fi 103 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineUITests/Pods-KeyboardLayoutEngineUITests-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | FOUNDATION_EXPORT double Pods_KeyboardLayoutEngineUITestsVersionNumber; 5 | FOUNDATION_EXPORT const unsigned char Pods_KeyboardLayoutEngineUITestsVersionString[]; 6 | 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineUITests/Pods-KeyboardLayoutEngineUITests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 3 | PODS_BUILD_DIR = $BUILD_DIR 4 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 5 | PODS_ROOT = ${SRCROOT}/Pods 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineUITests/Pods-KeyboardLayoutEngineUITests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_KeyboardLayoutEngineUITests { 2 | umbrella header "Pods-KeyboardLayoutEngineUITests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-KeyboardLayoutEngineUITests/Pods-KeyboardLayoutEngineUITests.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 3 | PODS_BUILD_DIR = $BUILD_DIR 4 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 5 | PODS_ROOT = ${SRCROOT}/Pods 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Shadow/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.2.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Shadow/Shadow-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Shadow : NSObject 3 | @end 4 | @implementation PodsDummy_Shadow 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Shadow/Shadow-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Shadow/Shadow-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | FOUNDATION_EXPORT double ShadowVersionNumber; 5 | FOUNDATION_EXPORT const unsigned char ShadowVersionString[]; 6 | 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Shadow/Shadow.modulemap: -------------------------------------------------------------------------------- 1 | framework module Shadow { 2 | umbrella header "Shadow-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Shadow/Shadow.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Shadow 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = -framework "UIKit" 5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | KeyboardLayoutEngine 2 | === 3 | 4 | [![Version](https://img.shields.io/cocoapods/v/KeyboardLayoutEngine.svg?style=flat)](http://cocoapods.org/pods/KeyboardLayoutEngine) 5 | [![License](https://img.shields.io/cocoapods/l/KeyboardLayoutEngine.svg?style=flat)](http://cocoapods.org/pods/KeyboardLayoutEngine) 6 | 7 | ⌨️ The most simple custom keyboard generator for iOS ever! 8 | 9 | ![alt tag](https://raw.githubusercontent.com/cemolcay/KeyboardLayoutEngine/master/demo.gif) 10 | 11 | `KeyboardLayoutEngine` is all about laying out keyboard buttons dynamically in a rectangle with a custom style easily. For the sake of flexibility, KeyboardLayoutEngine provides: 12 | 13 | * `KeyboardLayout`: For laying out rows with custom paddings, colors. 14 | * `KeyboardRow`: For laying out buttons or another set of KeyboardRow's inside. 15 | * `KeyboardButton`: For rendering buttons in rows. Also provides flexible width, type and other very useful API's. 16 | * They are also `UIView`s and handles their layout in their `layoutSubviews` function. 17 | * They are faster than autolayout yet they can adopt perfectly any `CGFrame` you want apply a keyboard layout. 18 | * That means they are play very well with orientation changes. (Layout for size class and/or orientation support is on the way.) 19 | * `KeyboardLayoutStyle`, `KeyboardRowStyle` and `KeyboardButtonStyle` structs handles pretty much everything about styling. 20 | * `KeyboardLayoutDelegate` for inform about button presses. 21 | * Also `CustomKeyboard` provided out of box, a good start point for figuring out how it works other than being of fully functional original keyboard. 22 | 23 | Install 24 | ---- 25 | #### CocoaPods 26 | 27 | ``` ruby 28 | use_frameworks! 29 | # Target Keyboard 30 | pod 'KeyboardLayoutEngine' 31 | ``` 32 | 33 | Usage 34 | ---- 35 | 36 | * Describe your keyboard with custom styles, rows and buttons with either text or image in it. 37 | * Checkout the [CustomKeyboardLayout](https://github.com/cemolcay/KeyboardLayoutEngine/blob/master/Keyboard/DefaultKeyboard/CustomKeyboardLayout.swift) for detailed usage. 38 | 39 | ``` swift 40 | let keyboardLayout = KeyboardLayout( 41 | style: CustomKeyboardLayoutStyle, 42 | rows: [ 43 | KeyboardRow( 44 | style: CustomKeyboardRowStyle, 45 | characters: [ 46 | KeyboardButton(type: .Key("Q"), style: CustomKeyboardKeyButtonStyle), 47 | KeyboardButton(type: .Key("W"), style: CustomKeyboardKeyButtonStyle), 48 | KeyboardButton(type: .Key("E"), style: CustomKeyboardKeyButtonStyle), 49 | KeyboardButton(type: .Key("R"), style: CustomKeyboardKeyButtonStyle), 50 | KeyboardButton(type: .Key("T"), style: CustomKeyboardKeyButtonStyle), 51 | KeyboardButton(type: .Key("Y"), style: CustomKeyboardKeyButtonStyle), 52 | KeyboardButton(type: .Key("U"), style: CustomKeyboardKeyButtonStyle), 53 | KeyboardButton(type: .Key("I"), style: CustomKeyboardKeyButtonStyle), 54 | KeyboardButton(type: .Key("O"), style: CustomKeyboardKeyButtonStyle), 55 | KeyboardButton(type: .Key("P"), style: CustomKeyboardKeyButtonStyle), 56 | ] 57 | ) 58 | ] 59 | ) 60 | 61 | override func viewDidLoad() { 62 | super.viewDidLoad() 63 | view.addSubview(keyboardLayout) 64 | } 65 | 66 | override func viewDidLayoutSubviews() { 67 | super.viewDidLayoutSubviews() 68 | keyboardLayout.setNeedsLayout() 69 | } 70 | ``` 71 | 72 | KeyboardLayoutDelegate 73 | ---- 74 | 75 | * Implement `KeyboardLayoutDelegate` for get information about the button presses. 76 | 77 | ``` swift 78 | @objc public protocol KeyboardLayoutDelegate { 79 | // Key Press Events 80 | optional func keyboardLayout(keyboardLayout: KeyboardLayout, didKeyPressStart keyboardButton: KeyboardButton) 81 | optional func keyboardLayout(keyboardLayout: KeyboardLayout, didKeyPressEnd keyboardButton: KeyboardButton) 82 | optional func keyboardLayout(keyboardLayout: KeyboardLayout, didDraggedIn fromKeyboardButton: KeyboardButton, toKeyboardButton: KeyboardButton) 83 | // Touch Events 84 | optional func keyboardLayout(keyboardLayout: KeyboardLayout, didTouchesBegin touches: Set) 85 | optional func keyboardLayout(keyboardLayout: KeyboardLayout, didTouchesMove touches: Set) 86 | optional func keyboardLayout(keyboardLayout: KeyboardLayout, didTouchesEnd touches: Set?) 87 | optional func keyboardLayout(keyboardLayout: KeyboardLayout, didTouchesCancel touches: Set?) 88 | } 89 | ``` 90 | 91 | KeyboardButtonWidth 92 | ---- 93 | 94 | ``` swift 95 | public enum KeyboardButtonWidth { 96 | case Dynamic 97 | case Static(width: CGFloat) 98 | case Relative(percent: CGFloat) 99 | } 100 | ``` 101 | 102 | * Laying out buttons in rows are important. Since rows can their child rows, calculating right sizes for buttons and rows done by button types. 103 | * If you leave `.Dynamic` which is default by the way, every button in a row, it will calculate their width by `KeyboardRowStyle.buttonPadding` and total width of row and figure out equal widths with equal buttonPaddings. 104 | * Static will be static width obviusly. 105 | * Relative is an interesting one, which takes a value between [0, 1], fills percent of parent row, smartly calculated. 106 | 107 | KeyboardButtonType 108 | ---- 109 | 110 | ``` swift 111 | public enum KeyboardButtonType { 112 | case Key(String) 113 | case Text(String) 114 | case Image(UIImage?) 115 | } 116 | ``` 117 | 118 | * A button can be `Key`, `Text` or `Image`. 119 | * Key case might be useful for `textDocumentProxy.insertText`operation. 120 | * Text case might be useful for buttons like "space", "return", "ABC", "123" or any string include emojis. 121 | * Image case might be useful for buttons like "shift", "backspace", "switch keyboard" etc. 122 | 123 | Styling 124 | ---- 125 | 126 | * Every style struct has their default values in taste of original keyboard. 127 | * If you dont assign a value in `init` function of a style struct, it will be loaded with its default value. 128 | 129 | KeyboardLayoutStyle 130 | ---- 131 | Definition: 132 | 133 | ``` swift 134 | public struct KeyboardLayoutStyle { 135 | public var topPadding: CGFloat 136 | public var bottomPadding: CGFloat 137 | public var rowPadding: CGFloat 138 | public var backgroundColor: UIColor 139 | } 140 | ``` 141 | 142 | Example: 143 | 144 | ``` swift 145 | let CustomKeyboardLayoutStyle = KeyboardLayoutStyle( 146 | topPadding: 10, 147 | bottomPadding: 5, 148 | rowPadding: 13, 149 | backgroundColor: UIColor(red: 208.0/255.0, green: 213.0/255.0, blue: 219.0/255.0, alpha: 1)) 150 | ``` 151 | 152 | KeyboardRowStyle 153 | ---- 154 | 155 | Definition: 156 | 157 | 158 | ``` swift 159 | public struct KeyboardRowStyle { 160 | public var leadingPadding: CGFloat 161 | public var trailingPadding: CGFloat 162 | public var buttonsPadding: CGFloat 163 | } 164 | ``` 165 | 166 | Example: 167 | 168 | ``` swift 169 | let CustomKeyboardRowStyle = KeyboardRowStyle( 170 | leadingPadding: 5, 171 | trailingPadding: 5, 172 | buttonsPadding: 6) 173 | ``` 174 | 175 | KeyboardButtonStyle 176 | ---- 177 | 178 | Definition: 179 | 180 | ``` swift 181 | public struct KeyboardButtonStyle { 182 | public var backgroundColor: UIColor 183 | public var cornerRadius: CGFloat 184 | 185 | // Border 186 | public var borderColor: UIColor 187 | public var borderWidth: CGFloat 188 | 189 | // Shadow 190 | public var shadowColor: UIColor 191 | public var shadowOpacity: Float 192 | public var shadowOffset: CGSize 193 | public var shadowRadius: CGFloat 194 | public var shadowPath: UIBezierPath? 195 | 196 | // Text 197 | public var textColor: UIColor 198 | public var font: UIFont 199 | 200 | // Image 201 | public var imageSize: CGFloat? 202 | 203 | // Popup 204 | public var showsPopup: Bool 205 | public var popupWidthMultiplier: CGFloat 206 | public var popupHeightMultiplier: CGFloat 207 | } 208 | ``` 209 | 210 | Example: 211 | 212 | ``` swift 213 | let CustomKeyboardDarkImageButtonStyle = KeyboardButtonStyle( 214 | backgroundColor: UIColor(red: 180.0/255.0, green: 188.0/255.0, blue: 201.0/255.0, alpha: 1), 215 | imageSize: 18, 216 | showsPopup: false) 217 | ``` 218 | 219 | CustomKeyboard 220 | ---- 221 | 222 | Default iOS Keyboard implementation with `KeyboardLayoutEngine`. 223 | 224 | * Shift toggle mechanism 225 | * Backspace mechanism 226 | * Key button popups 227 | * `textDocumentProxy` integrations with `CustomKeyboardDelegate` 228 | * Ridiculusly easy implementation in `KeyboardViewController` 229 | * Change default styles before initialize it and you have your fully functional customized standard English QWERTY keyboard! 230 | 231 | ``` swift 232 | override func viewDidLoad() { 233 | super.viewDidLoad() 234 | CustomKeyboardLayoutStyle.backgroundColor = UIColor.redColor() 235 | CustomKeyboardRowStyle.buttonsPadding = 5 236 | customKeyboard = CustomKeyboard() 237 | customKeyboard.delegate = self 238 | view.addSubview(customKeyboard) 239 | } 240 | ``` 241 | 242 | #### CustomKeyboard styles 243 | 244 | * CustomKeyboardLayoutStyle: `KeyboardLayoutStyle` 245 | * CustomKeyboardRowStyle: `KeyboardRowStyle` 246 | * CustomKeyboardSecondRowStyle: `KeyboardRowStyle` 247 | * CustomKeyboardChildRowStyle: `KeyboardRowStyle` 248 | * CustomKeyboardSpaceButtonStyle: `KeyboardButtonStyle` 249 | * CustomKeyboardBackspaceButtonStyle: `KeyboardButtonStyle` 250 | * CustomKeyboardShiftButtonStyle: `KeyboardButtonStyle` 251 | * CustomKeyboardGlobeButtonStyle: `KeyboardButtonStyle` 252 | * CustomKeyboardReturnButtonStyle: `KeyboardButtonStyle` 253 | * CustomKeyboardNumbersButtonStyle: `KeyboardButtonStyle` 254 | * CustomKeyboardKeyButtonStyle: `KeyboardButtonStyle` 255 | 256 | CustomKeyboardDelegate 257 | ---- 258 | 259 | * Provides information about key and special button presses. 260 | 261 | ``` swift 262 | @objc public protocol CustomKeyboardDelegate { 263 | optional func customKeyboard(customKeyboard: CustomKeyboard, keyboardButtonPressed keyboardButton: KeyboardButton) 264 | optional func customKeyboard(customKeyboard: CustomKeyboard, keyButtonPressed key: String) 265 | optional func customKeyboardSpaceButtonPressed(customKeyboard: CustomKeyboard) 266 | optional func customKeyboardBackspaceButtonPressed(customKeyboard: CustomKeyboard) 267 | optional func customKeyboardGlobeButtonPressed(customKeyboard: CustomKeyboard) 268 | optional func customKeyboardReturnButtonPressed(customKeyboard: CustomKeyboard) 269 | } 270 | ``` 271 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cemolcay/KeyboardLayoutEngine/b53eaf37e6c84926e7e68e46370b4e165a0c39de/demo.gif --------------------------------------------------------------------------------