├── sample.gif ├── DesireKeyboard ├── Assets.xcassets │ ├── Contents.json │ ├── App Icon & Top Shelf Image.brandassets │ │ ├── App Icon - Large.imagestack │ │ │ ├── Back.imagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ ├── Front.imagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ ├── Middle.imagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── App Icon - Small.imagestack │ │ │ ├── Back.imagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ ├── Front.imagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ ├── Middle.imagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Top Shelf Image.imageset │ │ │ └── Contents.json │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Info.plist ├── ViewController.swift ├── AppDelegate.swift ├── CandidateCell.xib ├── DesireKeyboardView.xib ├── Base.lproj │ └── Main.storyboard ├── DesireKeyboardView.swift └── SwiftyJSON.swift ├── README.md └── DesireKeyboard.xcodeproj ├── xcuserdata └── tomoya.hirano.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── xcschememanagement.plist │ └── DesireKeyboard.xcscheme ├── project.xcworkspace ├── contents.xcworkspacedata └── xcuserdata │ └── tomoya.hirano.xcuserdatad │ └── UserInterfaceState.xcuserstate └── project.pbxproj /sample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noppefoxwolf/DesireKeyboard/HEAD/sample.gif -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DesireKeyboard 2 | 3 | ## Awesome keyboard for tvOS. 4 | 5 | ![](https://raw.githubusercontent.com/noppefoxwolf/DesireKeyboard/master/sample.gif) 6 | 7 | inspired to PS4 motion keyboard. 8 | -------------------------------------------------------------------------------- /DesireKeyboard.xcodeproj/xcuserdata/tomoya.hirano.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /DesireKeyboard.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DesireKeyboard.xcodeproj/project.xcworkspace/xcuserdata/tomoya.hirano.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noppefoxwolf/DesireKeyboard/HEAD/DesireKeyboard.xcodeproj/project.xcworkspace/xcuserdata/tomoya.hirano.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "landscape", 5 | "idiom" : "tv", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "9.0", 8 | "scale" : "1x" 9 | } 10 | ], 11 | "info" : { 12 | "version" : 1, 13 | "author" : "xcode" 14 | } 15 | } -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "layers" : [ 3 | { 4 | "filename" : "Front.imagestacklayer" 5 | }, 6 | { 7 | "filename" : "Middle.imagestacklayer" 8 | }, 9 | { 10 | "filename" : "Back.imagestacklayer" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "layers" : [ 3 | { 4 | "filename" : "Front.imagestacklayer" 5 | }, 6 | { 7 | "filename" : "Middle.imagestacklayer" 8 | }, 9 | { 10 | "filename" : "Back.imagestacklayer" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /DesireKeyboard.xcodeproj/xcuserdata/tomoya.hirano.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | DesireKeyboard.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | D795D73C1BF4AC8D00E4E68A 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /DesireKeyboard/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets" : [ 3 | { 4 | "size" : "1280x768", 5 | "idiom" : "tv", 6 | "filename" : "App Icon - Large.imagestack", 7 | "role" : "primary-app-icon" 8 | }, 9 | { 10 | "size" : "400x240", 11 | "idiom" : "tv", 12 | "filename" : "App Icon - Small.imagestack", 13 | "role" : "primary-app-icon" 14 | }, 15 | { 16 | "size" : "1920x720", 17 | "idiom" : "tv", 18 | "filename" : "Top Shelf Image.imageset", 19 | "role" : "top-shelf-image" 20 | } 21 | ], 22 | "info" : { 23 | "version" : 1, 24 | "author" : "xcode" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /DesireKeyboard/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 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | arm64 30 | 31 | NSAppTransportSecurity 32 | 33 | NSAllowsArbitraryLoads 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /DesireKeyboard/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DesireKeyboard 4 | // 5 | // Created by Tomoya Hirano on 2015/11/12. 6 | // Copyright © 2015年 Tomoya Hirano. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import GameKit 11 | 12 | class ViewController: GCEventViewController { 13 | 14 | 15 | @IBOutlet weak var keyboardView: DesireKeyboardView! 16 | @IBOutlet weak var textField: UITextField! 17 | // var controller:GCController? 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | keyboardView.targetInput = textField 22 | 23 | // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupController) name:GCControllerDidConnectNotification object:nil]; 24 | 25 | NSNotificationCenter.defaultCenter().addObserver(self, selector: "setupController:", name: GCControllerDidConnectNotification, object: nil) 26 | 27 | 28 | } 29 | 30 | func setupController(notification:NSNotification){ 31 | if let controller = GCController.controllers().first?.microGamepad?.controller { 32 | controller.motion?.valueChangedHandler = {(motion) in 33 | print(motion.attitude) 34 | } 35 | // controller.microGamepad?.dpad.xAxis.valueChangedHandler = { (_) in 36 | // print(controller.microGamepad?.dpad.xAxis.value) 37 | // } 38 | // controller.microGamepad?.dpad.yAxis.valueChangedHandler = { (_) in 39 | // 40 | // } 41 | // controller.motion?.valueChangedHandler = {(motion:GCMotion)in 42 | // print(motion.gravity.x) 43 | // } 44 | } 45 | } 46 | 47 | 48 | override func didReceiveMemoryWarning() { 49 | super.didReceiveMemoryWarning() 50 | } 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /DesireKeyboard/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DesireKeyboard 4 | // 5 | // Created by Tomoya Hirano on 2015/11/12. 6 | // Copyright © 2015年 Tomoya Hirano. 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 | -------------------------------------------------------------------------------- /DesireKeyboard/CandidateCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /DesireKeyboard.xcodeproj/xcuserdata/tomoya.hirano.xcuserdatad/xcschemes/DesireKeyboard.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /DesireKeyboard/DesireKeyboardView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /DesireKeyboard/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /DesireKeyboard.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D76C2D241BF4CCEF00AC59EC /* DesireKeyboardView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D76C2D231BF4CCEF00AC59EC /* DesireKeyboardView.xib */; }; 11 | D79352051BF4DB4C002197FF /* CandidateCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D79352041BF4DB4C002197FF /* CandidateCell.xib */; }; 12 | D79352071BF57C05002197FF /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79352061BF57C05002197FF /* SwiftyJSON.swift */; }; 13 | D795D7411BF4AC8D00E4E68A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D795D7401BF4AC8D00E4E68A /* AppDelegate.swift */; }; 14 | D795D7431BF4AC8D00E4E68A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D795D7421BF4AC8D00E4E68A /* ViewController.swift */; }; 15 | D795D7461BF4AC8D00E4E68A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D795D7441BF4AC8D00E4E68A /* Main.storyboard */; }; 16 | D795D7481BF4AC8D00E4E68A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D795D7471BF4AC8D00E4E68A /* Assets.xcassets */; }; 17 | D7EC88691BF4AD21002ECD9D /* DesireKeyboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EC88681BF4AD21002ECD9D /* DesireKeyboardView.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | D76C2D231BF4CCEF00AC59EC /* DesireKeyboardView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DesireKeyboardView.xib; sourceTree = ""; }; 22 | D79352041BF4DB4C002197FF /* CandidateCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CandidateCell.xib; sourceTree = ""; }; 23 | D79352061BF57C05002197FF /* SwiftyJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftyJSON.swift; sourceTree = ""; }; 24 | D795D73D1BF4AC8D00E4E68A /* DesireKeyboard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DesireKeyboard.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | D795D7401BF4AC8D00E4E68A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 26 | D795D7421BF4AC8D00E4E68A /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 27 | D795D7451BF4AC8D00E4E68A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | D795D7471BF4AC8D00E4E68A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 29 | D795D7491BF4AC8D00E4E68A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | D7EC88681BF4AD21002ECD9D /* DesireKeyboardView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DesireKeyboardView.swift; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | D795D73A1BF4AC8D00E4E68A /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXFrameworksBuildPhase section */ 42 | 43 | /* Begin PBXGroup section */ 44 | D795D7341BF4AC8D00E4E68A = { 45 | isa = PBXGroup; 46 | children = ( 47 | D795D73F1BF4AC8D00E4E68A /* DesireKeyboard */, 48 | D795D73E1BF4AC8D00E4E68A /* Products */, 49 | ); 50 | sourceTree = ""; 51 | }; 52 | D795D73E1BF4AC8D00E4E68A /* Products */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | D795D73D1BF4AC8D00E4E68A /* DesireKeyboard.app */, 56 | ); 57 | name = Products; 58 | sourceTree = ""; 59 | }; 60 | D795D73F1BF4AC8D00E4E68A /* DesireKeyboard */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | D7EC88671BF4AD0E002ECD9D /* Lib */, 64 | D795D7401BF4AC8D00E4E68A /* AppDelegate.swift */, 65 | D795D7421BF4AC8D00E4E68A /* ViewController.swift */, 66 | D795D7441BF4AC8D00E4E68A /* Main.storyboard */, 67 | D795D7471BF4AC8D00E4E68A /* Assets.xcassets */, 68 | D795D7491BF4AC8D00E4E68A /* Info.plist */, 69 | ); 70 | path = DesireKeyboard; 71 | sourceTree = ""; 72 | }; 73 | D7EC88671BF4AD0E002ECD9D /* Lib */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | D79352061BF57C05002197FF /* SwiftyJSON.swift */, 77 | D7EC88681BF4AD21002ECD9D /* DesireKeyboardView.swift */, 78 | D76C2D231BF4CCEF00AC59EC /* DesireKeyboardView.xib */, 79 | D79352041BF4DB4C002197FF /* CandidateCell.xib */, 80 | ); 81 | name = Lib; 82 | sourceTree = ""; 83 | }; 84 | /* End PBXGroup section */ 85 | 86 | /* Begin PBXNativeTarget section */ 87 | D795D73C1BF4AC8D00E4E68A /* DesireKeyboard */ = { 88 | isa = PBXNativeTarget; 89 | buildConfigurationList = D795D74C1BF4AC8D00E4E68A /* Build configuration list for PBXNativeTarget "DesireKeyboard" */; 90 | buildPhases = ( 91 | D795D7391BF4AC8D00E4E68A /* Sources */, 92 | D795D73A1BF4AC8D00E4E68A /* Frameworks */, 93 | D795D73B1BF4AC8D00E4E68A /* Resources */, 94 | ); 95 | buildRules = ( 96 | ); 97 | dependencies = ( 98 | ); 99 | name = DesireKeyboard; 100 | productName = DesireKeyboard; 101 | productReference = D795D73D1BF4AC8D00E4E68A /* DesireKeyboard.app */; 102 | productType = "com.apple.product-type.application"; 103 | }; 104 | /* End PBXNativeTarget section */ 105 | 106 | /* Begin PBXProject section */ 107 | D795D7351BF4AC8D00E4E68A /* Project object */ = { 108 | isa = PBXProject; 109 | attributes = { 110 | LastSwiftUpdateCheck = 0710; 111 | LastUpgradeCheck = 0710; 112 | ORGANIZATIONNAME = "Tomoya Hirano"; 113 | TargetAttributes = { 114 | D795D73C1BF4AC8D00E4E68A = { 115 | CreatedOnToolsVersion = 7.1; 116 | }; 117 | }; 118 | }; 119 | buildConfigurationList = D795D7381BF4AC8D00E4E68A /* Build configuration list for PBXProject "DesireKeyboard" */; 120 | compatibilityVersion = "Xcode 3.2"; 121 | developmentRegion = English; 122 | hasScannedForEncodings = 0; 123 | knownRegions = ( 124 | en, 125 | Base, 126 | ); 127 | mainGroup = D795D7341BF4AC8D00E4E68A; 128 | productRefGroup = D795D73E1BF4AC8D00E4E68A /* Products */; 129 | projectDirPath = ""; 130 | projectRoot = ""; 131 | targets = ( 132 | D795D73C1BF4AC8D00E4E68A /* DesireKeyboard */, 133 | ); 134 | }; 135 | /* End PBXProject section */ 136 | 137 | /* Begin PBXResourcesBuildPhase section */ 138 | D795D73B1BF4AC8D00E4E68A /* Resources */ = { 139 | isa = PBXResourcesBuildPhase; 140 | buildActionMask = 2147483647; 141 | files = ( 142 | D795D7481BF4AC8D00E4E68A /* Assets.xcassets in Resources */, 143 | D795D7461BF4AC8D00E4E68A /* Main.storyboard in Resources */, 144 | D76C2D241BF4CCEF00AC59EC /* DesireKeyboardView.xib in Resources */, 145 | D79352051BF4DB4C002197FF /* CandidateCell.xib in Resources */, 146 | ); 147 | runOnlyForDeploymentPostprocessing = 0; 148 | }; 149 | /* End PBXResourcesBuildPhase section */ 150 | 151 | /* Begin PBXSourcesBuildPhase section */ 152 | D795D7391BF4AC8D00E4E68A /* Sources */ = { 153 | isa = PBXSourcesBuildPhase; 154 | buildActionMask = 2147483647; 155 | files = ( 156 | D795D7431BF4AC8D00E4E68A /* ViewController.swift in Sources */, 157 | D7EC88691BF4AD21002ECD9D /* DesireKeyboardView.swift in Sources */, 158 | D795D7411BF4AC8D00E4E68A /* AppDelegate.swift in Sources */, 159 | D79352071BF57C05002197FF /* SwiftyJSON.swift in Sources */, 160 | ); 161 | runOnlyForDeploymentPostprocessing = 0; 162 | }; 163 | /* End PBXSourcesBuildPhase section */ 164 | 165 | /* Begin PBXVariantGroup section */ 166 | D795D7441BF4AC8D00E4E68A /* Main.storyboard */ = { 167 | isa = PBXVariantGroup; 168 | children = ( 169 | D795D7451BF4AC8D00E4E68A /* Base */, 170 | ); 171 | name = Main.storyboard; 172 | sourceTree = ""; 173 | }; 174 | /* End PBXVariantGroup section */ 175 | 176 | /* Begin XCBuildConfiguration section */ 177 | D795D74A1BF4AC8D00E4E68A /* Debug */ = { 178 | isa = XCBuildConfiguration; 179 | buildSettings = { 180 | ALWAYS_SEARCH_USER_PATHS = NO; 181 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 182 | CLANG_CXX_LIBRARY = "libc++"; 183 | CLANG_ENABLE_MODULES = YES; 184 | CLANG_ENABLE_OBJC_ARC = YES; 185 | CLANG_WARN_BOOL_CONVERSION = YES; 186 | CLANG_WARN_CONSTANT_CONVERSION = YES; 187 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 188 | CLANG_WARN_EMPTY_BODY = YES; 189 | CLANG_WARN_ENUM_CONVERSION = YES; 190 | CLANG_WARN_INT_CONVERSION = YES; 191 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 192 | CLANG_WARN_UNREACHABLE_CODE = YES; 193 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 194 | COPY_PHASE_STRIP = NO; 195 | DEBUG_INFORMATION_FORMAT = dwarf; 196 | ENABLE_STRICT_OBJC_MSGSEND = YES; 197 | ENABLE_TESTABILITY = YES; 198 | GCC_C_LANGUAGE_STANDARD = gnu99; 199 | GCC_DYNAMIC_NO_PIC = NO; 200 | GCC_NO_COMMON_BLOCKS = YES; 201 | GCC_OPTIMIZATION_LEVEL = 0; 202 | GCC_PREPROCESSOR_DEFINITIONS = ( 203 | "DEBUG=1", 204 | "$(inherited)", 205 | ); 206 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 207 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 208 | GCC_WARN_UNDECLARED_SELECTOR = YES; 209 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 210 | GCC_WARN_UNUSED_FUNCTION = YES; 211 | GCC_WARN_UNUSED_VARIABLE = YES; 212 | MTL_ENABLE_DEBUG_INFO = YES; 213 | ONLY_ACTIVE_ARCH = YES; 214 | SDKROOT = appletvos; 215 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 216 | TARGETED_DEVICE_FAMILY = 3; 217 | TVOS_DEPLOYMENT_TARGET = 9.0; 218 | }; 219 | name = Debug; 220 | }; 221 | D795D74B1BF4AC8D00E4E68A /* Release */ = { 222 | isa = XCBuildConfiguration; 223 | buildSettings = { 224 | ALWAYS_SEARCH_USER_PATHS = NO; 225 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 226 | CLANG_CXX_LIBRARY = "libc++"; 227 | CLANG_ENABLE_MODULES = YES; 228 | CLANG_ENABLE_OBJC_ARC = YES; 229 | CLANG_WARN_BOOL_CONVERSION = YES; 230 | CLANG_WARN_CONSTANT_CONVERSION = YES; 231 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 232 | CLANG_WARN_EMPTY_BODY = YES; 233 | CLANG_WARN_ENUM_CONVERSION = YES; 234 | CLANG_WARN_INT_CONVERSION = YES; 235 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 236 | CLANG_WARN_UNREACHABLE_CODE = YES; 237 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 238 | COPY_PHASE_STRIP = NO; 239 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 240 | ENABLE_NS_ASSERTIONS = NO; 241 | ENABLE_STRICT_OBJC_MSGSEND = YES; 242 | GCC_C_LANGUAGE_STANDARD = gnu99; 243 | GCC_NO_COMMON_BLOCKS = YES; 244 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 245 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 246 | GCC_WARN_UNDECLARED_SELECTOR = YES; 247 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 248 | GCC_WARN_UNUSED_FUNCTION = YES; 249 | GCC_WARN_UNUSED_VARIABLE = YES; 250 | MTL_ENABLE_DEBUG_INFO = NO; 251 | SDKROOT = appletvos; 252 | TARGETED_DEVICE_FAMILY = 3; 253 | TVOS_DEPLOYMENT_TARGET = 9.0; 254 | VALIDATE_PRODUCT = YES; 255 | }; 256 | name = Release; 257 | }; 258 | D795D74D1BF4AC8D00E4E68A /* Debug */ = { 259 | isa = XCBuildConfiguration; 260 | buildSettings = { 261 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; 262 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 263 | INFOPLIST_FILE = DesireKeyboard/Info.plist; 264 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 265 | PRODUCT_BUNDLE_IDENTIFIER = com.noppe.DesireKeyboard; 266 | PRODUCT_NAME = "$(TARGET_NAME)"; 267 | }; 268 | name = Debug; 269 | }; 270 | D795D74E1BF4AC8D00E4E68A /* Release */ = { 271 | isa = XCBuildConfiguration; 272 | buildSettings = { 273 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; 274 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 275 | INFOPLIST_FILE = DesireKeyboard/Info.plist; 276 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 277 | PRODUCT_BUNDLE_IDENTIFIER = com.noppe.DesireKeyboard; 278 | PRODUCT_NAME = "$(TARGET_NAME)"; 279 | }; 280 | name = Release; 281 | }; 282 | /* End XCBuildConfiguration section */ 283 | 284 | /* Begin XCConfigurationList section */ 285 | D795D7381BF4AC8D00E4E68A /* Build configuration list for PBXProject "DesireKeyboard" */ = { 286 | isa = XCConfigurationList; 287 | buildConfigurations = ( 288 | D795D74A1BF4AC8D00E4E68A /* Debug */, 289 | D795D74B1BF4AC8D00E4E68A /* Release */, 290 | ); 291 | defaultConfigurationIsVisible = 0; 292 | defaultConfigurationName = Release; 293 | }; 294 | D795D74C1BF4AC8D00E4E68A /* Build configuration list for PBXNativeTarget "DesireKeyboard" */ = { 295 | isa = XCConfigurationList; 296 | buildConfigurations = ( 297 | D795D74D1BF4AC8D00E4E68A /* Debug */, 298 | D795D74E1BF4AC8D00E4E68A /* Release */, 299 | ); 300 | defaultConfigurationIsVisible = 0; 301 | defaultConfigurationName = Release; 302 | }; 303 | /* End XCConfigurationList section */ 304 | }; 305 | rootObject = D795D7351BF4AC8D00E4E68A /* Project object */; 306 | } 307 | -------------------------------------------------------------------------------- /DesireKeyboard/DesireKeyboardView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DesireKeyboardView.swift 3 | // DesireKeyboard 4 | // 5 | // Created by Tomoya Hirano on 2015/11/12. 6 | // Copyright © 2015年 Tomoya Hirano. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | protocol DesireKeyboardKeysViewDelegate{ 18 | func didSelectWord(word:String) 19 | func didPressDeleteKey() 20 | func didPressEnterKey() 21 | func didPressSpaceKey() 22 | } 23 | 24 | class PointerView:UIView { 25 | override init(frame: CGRect) { 26 | super.init(frame: frame) 27 | backgroundColor = UIColor(red:0.58, green:0.82, blue:0.86, alpha:0.5) 28 | layer.cornerRadius = frame.width/2 29 | layer.borderWidth = 2 30 | layer.borderColor = UIColor(red:0.58, green:0.82, blue:0.86, alpha:1).CGColor 31 | } 32 | 33 | required init?(coder aDecoder: NSCoder) { 34 | fatalError("init(coder:) has not been implemented") 35 | } 36 | } 37 | 38 | class DesireKeyboardView: UIView,UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout,DesireKeyboardKeysViewDelegate { 39 | 40 | @IBOutlet weak var collectionView: UICollectionView! 41 | @IBOutlet weak var keysView: DesireKeyboardKeysView! 42 | 43 | var targetInput:UITextField? 44 | var pointerView = PointerView() 45 | 46 | required init?(coder aDecoder: NSCoder) { 47 | super.init(coder: aDecoder) 48 | customViewCommonInit() 49 | } 50 | 51 | override init(frame: CGRect) { 52 | super.init(frame: frame) 53 | } 54 | 55 | var replace_words:[String:String] = [ 56 | "あ":"ぁ","い":"ぃ","う":"ぅ","え":"ぇ","お":"ぉ", 57 | "や":"ゃ","ゆ":"ゅ","よ":"ょ","わ":"ゎ", 58 | "か":"が","き":"ぎ","く":"ぐ","け":"げ","こ":"ご", 59 | "さ":"ざ","し":"じ","す":"ず","せ":"ぜ","そ":"ぞ", 60 | "た":"だ","ち":"ぢ","つ":"づ","て":"で","と":"ど", 61 | "ば":"ぱ","び":"ぴ","ぶ":"ぷ","べ":"ぺ","ぼ":"ぽ", 62 | "は":"ば","ひ":"び","ふ":"ぶ","へ":"べ","ほ":"ぼ", 63 | ] 64 | 65 | var reverse_words:[String:String] = [ 66 | "ぁ":"あ","ぃ":"い","ぅ":"う","ぇ":"え","ぉ":"お", 67 | "ゃ":"や","ゅ":"ゆ","ょ":"よ","ゎ":"わ", 68 | "が":"か","ぎ":"き","ぐ":"く","げ":"け","ご":"こ", 69 | "ざ":"さ","じ":"し","ず":"す","ぜ":"せ","ぞ":"そ", 70 | "だ":"た","ぢ":"ち","づ":"つ","で":"て","ど":"と", 71 | "ぱ":"は","ぴ":"ひ","ぷ":"ふ","ぺ":"へ","ぽ":"ほ", 72 | "ば":"は","び":"ひ","ぶ":"ふ","べ":"へ","ぼ":"ほ", 73 | ] 74 | 75 | func customViewCommonInit(){ 76 | let view = NSBundle.mainBundle().loadNibNamed("DesireKeyboardView", owner: self, options: nil).first as! UIView 77 | view.frame = self.bounds 78 | view.translatesAutoresizingMaskIntoConstraints = true 79 | view.autoresizingMask = [.FlexibleWidth,.FlexibleHeight] 80 | view.backgroundColor = UIColor(red:0.08, green:0.08, blue:0.11, alpha:1) 81 | self.addSubview(view) 82 | 83 | let cellNib = UINib(nibName: "CandidateCell", bundle: nil) 84 | collectionView.registerNib(cellNib, forCellWithReuseIdentifier: "cell") 85 | collectionView.delegate = self 86 | collectionView.dataSource = self 87 | 88 | 89 | layer.cornerRadius = 6 90 | layer.masksToBounds = true 91 | layer.borderColor = UIColor.darkGrayColor().CGColor 92 | layer.borderWidth = 2 93 | 94 | keysView.delegate = self 95 | 96 | pointerView = PointerView(frame:CGRectMake(0,0,bounds.width/12,bounds.width/12)) 97 | pointerView.hidden = true 98 | addSubview(pointerView) 99 | } 100 | 101 | var candidats:JSON? 102 | 103 | func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { 104 | let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CandidateCell 105 | cell.textLabel.text = candidats![0][1][indexPath.row].string 106 | return cell 107 | } 108 | 109 | func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { 110 | return 1 111 | } 112 | 113 | func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 114 | return candidats?[0][1].count ?? 0 115 | } 116 | 117 | func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { 118 | collectionView.deselectItemAtIndexPath(indexPath, animated: false) 119 | let targetString = candidats![0][0].string 120 | let replaceString = candidats![0][1][indexPath.row].string 121 | targetInput?.text = targetInput?.text!.stringByReplacingOccurrencesOfString(targetString! 122 | , withString: replaceString!) 123 | inputWord = "" 124 | candidats = nil 125 | collectionView.reloadData() 126 | } 127 | 128 | var inputWord:String = "" 129 | func didSelectWord(word: String) { 130 | if word == "、。" { 131 | if inputWord.characters.last == "、"{ 132 | inputWord = inputWord.chopSuffix() 133 | targetInput?.text = targetInput?.text!.chopSuffix() 134 | inputWord += "。" 135 | targetInput?.text = (targetInput?.text)! + "。" 136 | }else if inputWord.characters.last == "。"{ 137 | inputWord = inputWord.chopSuffix() 138 | targetInput?.text = targetInput?.text!.chopSuffix() 139 | inputWord += "、" 140 | targetInput?.text = (targetInput?.text)! + "、" 141 | }else{ 142 | inputWord += "、" 143 | targetInput?.text = (targetInput?.text)! + "、" 144 | } 145 | }else if word == "小゛゜" { 146 | if let lastWord = inputWord.characters.last { 147 | if let c = replace_words["\(lastWord)"] { 148 | inputWord = inputWord.chopSuffix() 149 | inputWord += c 150 | targetInput?.text = targetInput?.text!.chopSuffix() 151 | targetInput?.text = (targetInput?.text)! + c 152 | }else if let c = reverse_words["\(lastWord)"]{ 153 | inputWord = inputWord.chopSuffix() 154 | inputWord += c 155 | targetInput?.text = targetInput?.text!.chopSuffix() 156 | targetInput?.text = (targetInput?.text)! + c 157 | } 158 | } 159 | }else if word == "?!" { 160 | if inputWord.characters.last == "?"{ 161 | inputWord = inputWord.chopSuffix() 162 | targetInput?.text = targetInput?.text!.chopSuffix() 163 | inputWord += "!" 164 | targetInput?.text = (targetInput?.text)! + "!" 165 | }else if inputWord.characters.last == "!"{ 166 | inputWord = inputWord.chopSuffix() 167 | targetInput?.text = targetInput?.text!.chopSuffix() 168 | inputWord += "?" 169 | targetInput?.text = (targetInput?.text)! + "?" 170 | }else{ 171 | inputWord += "?" 172 | targetInput?.text = (targetInput?.text)! + "?" 173 | } 174 | }else{ 175 | inputWord += word 176 | targetInput?.text = (targetInput?.text)! + word 177 | getCandidate(inputWord) 178 | } 179 | } 180 | func didPressDeleteKey() { 181 | inputWord = inputWord.chopSuffix() 182 | targetInput?.text = targetInput?.text?.chopSuffix() 183 | getCandidate(inputWord) 184 | } 185 | func didPressEnterKey() { 186 | candidats = nil 187 | inputWord = "" 188 | self.collectionView.reloadData() 189 | } 190 | func didPressSpaceKey() { 191 | targetInput?.text = (targetInput?.text)! + " " 192 | } 193 | 194 | func getCandidate(words:String){ 195 | let urlString = "http://www.google.com/transliterate?" 196 | let params:[String:String] = ["langpair":"ja-Hira|ja","text":words] 197 | let url = NSURL(string: urlString + params.urlEncodedQueryStringWithEncoding) 198 | let config = NSURLSessionConfiguration.defaultSessionConfiguration() 199 | let session = NSURLSession(configuration: config) 200 | let req = NSMutableURLRequest(URL: url!) 201 | let task = session.dataTaskWithRequest(req, completionHandler: { 202 | (data, resp, err) in 203 | self.candidats = JSON(data: data!) 204 | dispatch_async(dispatch_get_main_queue(), { () -> Void in 205 | self.collectionView.reloadData() 206 | }) 207 | }) 208 | task.resume() 209 | } 210 | 211 | override func touchesBegan(touches: Set, withEvent event: UIEvent?) { 212 | super.touchesBegan(touches, withEvent: event) 213 | pointerView.hidden = false 214 | pointerView.center = CGPointMake(bounds.width/2, bounds.height/2) 215 | } 216 | 217 | override func touchesEnded(touches: Set, withEvent event: UIEvent?) { 218 | super.touchesEnded(touches, withEvent: event) 219 | pointerView.hidden = true 220 | keysView.deselectAllButtons() 221 | } 222 | 223 | override func touchesMoved(touches: Set, withEvent event: UIEvent?) { 224 | super.touchesMoved(touches, withEvent: event) 225 | keysView.deselectAllButtons() 226 | let pos = touches.first?.locationInView(self) 227 | pointerView.center = pos! 228 | let v = keysView.hitTest(keysView.convertPoint(pos!, fromView: self), withEvent: nil) 229 | if let button = v as? KeyButton { 230 | button.hilight = true 231 | } 232 | 233 | (collectionView.visibleCells() as! [CandidateCell]).map({$0.hilight = false}) 234 | if let index = collectionView.indexPathForItemAtPoint(collectionView.convertPoint(pos!, fromView: self)) { 235 | if let cell = collectionView.cellForItemAtIndexPath(index) as? CandidateCell { 236 | cell.hilight = true 237 | } 238 | } 239 | } 240 | 241 | override func pressesBegan(presses: Set, withEvent event: UIPressesEvent?) { 242 | super.pressesBegan(presses, withEvent: event) 243 | if presses.first?.type == UIPressType.Select { 244 | let v = keysView.hitTest(keysView.convertPoint(pointerView.center, fromView: self), withEvent: nil) 245 | if let button = v as? KeyButton { 246 | button.selectAction() 247 | } 248 | if let index = collectionView.indexPathForItemAtPoint(collectionView.convertPoint(pointerView.center, fromView: self)) { 249 | collectionView.delegate?.collectionView!(collectionView, didSelectItemAtIndexPath: index) 250 | } 251 | } 252 | } 253 | 254 | override func canBecomeFocused() -> Bool { 255 | return true 256 | } 257 | 258 | func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { 259 | return CGSizeMake(collectionView.bounds.width/7, collectionView.bounds.height - 20) 260 | } 261 | } 262 | 263 | extension String { 264 | // URLエンコード 265 | var urlEncodedStringWithEncoding:String{ 266 | let charactersToBeEscaped = ":/?&=;+!@#$()',*" as CFStringRef 267 | let charactersToLeaveUnescaped = "[]." as CFStringRef 268 | 269 | let raw = self 270 | 271 | let result = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, raw, charactersToLeaveUnescaped, charactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)) as NSString 272 | 273 | return result as String 274 | } 275 | 276 | func chopSuffix(count: Int = 1) -> String { 277 | if self.characters.count == 0 { 278 | return self 279 | } 280 | return self.substringToIndex(self.endIndex.advancedBy(-count)) 281 | } 282 | } 283 | 284 | 285 | extension Dictionary { 286 | // Dictionary内のデータをエンコード 287 | var urlEncodedQueryStringWithEncoding:String{ 288 | var parts = [String]() 289 | for (key, value) in self { 290 | let keyString = (key as! String).urlEncodedStringWithEncoding 291 | let valueString = (value as! String).urlEncodedStringWithEncoding 292 | let query = "\(keyString)=\(valueString)" as String 293 | parts.append(query) 294 | } 295 | return parts.joinWithSeparator("&") as String 296 | } 297 | } 298 | 299 | 300 | 301 | class DesireKeyboardKeysView: UIView { 302 | required init?(coder aDecoder: NSCoder) { 303 | super.init(coder: aDecoder) 304 | userInteractionEnabled = true 305 | backgroundColor = UIColor.clearColor() 306 | setupKeys() 307 | } 308 | 309 | var delegate:DesireKeyboardKeysViewDelegate? = nil 310 | 311 | override init(frame: CGRect) { 312 | super.init(frame: frame) 313 | } 314 | 315 | override func layoutSubviews() { 316 | super.layoutSubviews() 317 | layoutKeyButtons() 318 | } 319 | 320 | var words = [ 321 | ["あ","い","う","え","お"], 322 | ["か","き","く","け","こ"], 323 | ["さ","し","す","せ","そ"], 324 | ["た","ち","つ","て","と"], 325 | ["な","に","ぬ","ね","の"], 326 | ["は","ひ","ふ","へ","ほ"], 327 | ["ま","み","む","め","も"], 328 | ["や","ゆ","よ","、。","?!"], 329 | ["ら","り","る","れ","ろ"], 330 | ["わ","を","ん","ー","小゛゜"] 331 | ] 332 | 333 | 334 | var keyButtons:[[KeyButton]] = [[]] 335 | let space = KeyButton(frame: CGRectZero) 336 | let delete = KeyButton(frame: CGRectZero) 337 | let enter = KeyButton(frame: CGRectZero) 338 | 339 | func setupKeys(){ 340 | for (_, column) in words.enumerate() { 341 | var keyColumn:[KeyButton] = [] 342 | for (_, word) in column.enumerate() { 343 | let key = KeyButton(frame: CGRectZero) 344 | key.text = word 345 | addSubview(key) 346 | keyColumn.append(key) 347 | } 348 | keyButtons.append(keyColumn) 349 | } 350 | space.text = "スペース" 351 | space.selectedAction = {(_) in 352 | self.delegate?.didPressSpaceKey() 353 | } 354 | addSubview(space) 355 | delete.text = "⇐" 356 | delete.selectedAction = {(_) in 357 | self.delegate?.didPressDeleteKey() 358 | } 359 | addSubview(delete) 360 | enter.text = "確定" 361 | enter.selectedAction = {(_) in 362 | self.delegate?.didPressEnterKey() 363 | } 364 | addSubview(enter) 365 | } 366 | 367 | func deselectAllButtons(){ 368 | for column in keyButtons { 369 | for key in column { 370 | key.hilight = false 371 | } 372 | } 373 | space.hilight = false 374 | delete.hilight = false 375 | enter.hilight = false 376 | } 377 | 378 | func layoutKeyButtons(){ 379 | let keyWidth:CGFloat = bounds.width/10.0 - 2 380 | let keyHeight:CGFloat = bounds.height/7.0 - 2 381 | for (i,column) in keyButtons.enumerate() { 382 | for (j,button) in column.enumerate() { 383 | button.frame = CGRectMake(bounds.width - CGFloat(i)*(keyWidth+2), CGFloat(j)*(keyHeight+2), keyWidth, keyHeight) 384 | button.selectedAction = { (word) in 385 | self.delegate?.didSelectWord(word!) 386 | } 387 | } 388 | } 389 | space.frame = CGRectMake((keyWidth+2)*3, (keyHeight+2)*5, keyWidth*4, keyHeight) 390 | delete.frame = CGRectMake((keyWidth+2)*8, (keyHeight+2)*5, keyWidth*2, keyHeight) 391 | enter.frame = CGRectMake((keyWidth+2)*8, (keyHeight+2)*6, keyWidth*2, keyHeight) 392 | } 393 | } 394 | 395 | class CandidateCell: UICollectionViewCell { 396 | @IBOutlet weak var textLabel: UILabel! 397 | 398 | var hilight:Bool = false{ 399 | didSet{ 400 | if hilight { 401 | self.backgroundColor = UIColor.darkGrayColor() 402 | }else{ 403 | self.backgroundColor = UIColor.clearColor() 404 | } 405 | } 406 | } 407 | 408 | override init(frame: CGRect){ 409 | super.init(frame: frame) 410 | } 411 | required init?(coder aDecoder: NSCoder){ 412 | super.init(coder: aDecoder) 413 | backgroundColor = UIColor.clearColor() 414 | } 415 | 416 | override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) { 417 | super.didUpdateFocusInContext(context, withAnimationCoordinator: coordinator) 418 | if self.focused { 419 | self.backgroundColor = UIColor.darkGrayColor() 420 | }else{ 421 | self.backgroundColor = UIColor.clearColor() 422 | } 423 | } 424 | } 425 | 426 | class KeyButton: UILabel { 427 | 428 | var unfocusBorderColor = UIColor(red:0.19, green:0.2, blue:0.22, alpha:1).CGColor 429 | var focusBorderColor = UIColor(red:0.58, green:0.82, blue:0.86, alpha:1).CGColor 430 | var hilight:Bool = false{ 431 | didSet{ 432 | if hilight { 433 | self.layer.borderColor = focusBorderColor 434 | }else{ 435 | self.layer.borderColor = unfocusBorderColor 436 | } 437 | } 438 | } 439 | 440 | override init(frame: CGRect) { 441 | super.init(frame: frame) 442 | userInteractionEnabled = true 443 | backgroundColor = UIColor(red:0.14, green:0.14, blue:0.14, alpha:1) 444 | layer.cornerRadius = 6 445 | layer.borderColor = unfocusBorderColor 446 | layer.borderWidth = 4 447 | layer.masksToBounds = true 448 | textColor = UIColor.whiteColor() 449 | font = UIFont.boldSystemFontOfSize(24) 450 | textAlignment = .Center 451 | } 452 | 453 | var selectedAction:((word:String?) -> Void)? 454 | 455 | required init?(coder aDecoder: NSCoder) { 456 | fatalError("init(coder:) has not been implemented") 457 | } 458 | 459 | override func canBecomeFocused() -> Bool { 460 | return false 461 | } 462 | 463 | override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) { 464 | super.didUpdateFocusInContext(context, withAnimationCoordinator: coordinator) 465 | if self.focused { 466 | self.layer.borderColor = focusBorderColor 467 | }else{ 468 | self.layer.borderColor = unfocusBorderColor 469 | } 470 | } 471 | 472 | override func pressesBegan(presses: Set, withEvent event: UIPressesEvent?) { 473 | super.pressesBegan(presses, withEvent: event) 474 | if presses.first?.type == UIPressType.Select { 475 | selectedAction?(word: text) 476 | } 477 | } 478 | 479 | func selectAction(){ 480 | selectedAction?(word: text) 481 | } 482 | } 483 | 484 | -------------------------------------------------------------------------------- /DesireKeyboard/SwiftyJSON.swift: -------------------------------------------------------------------------------- 1 | // SwiftyJSON.swift 2 | // 3 | // Copyright (c) 2014 Ruoyu Fu, Pinglin Tang 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import Foundation 24 | 25 | // MARK: - Error 26 | 27 | ///Error domain 28 | public let ErrorDomain: String! = "SwiftyJSONErrorDomain" 29 | 30 | ///Error code 31 | public let ErrorUnsupportedType: Int! = 999 32 | public let ErrorIndexOutOfBounds: Int! = 900 33 | public let ErrorWrongType: Int! = 901 34 | public let ErrorNotExist: Int! = 500 35 | public let ErrorInvalidJSON: Int! = 490 36 | 37 | // MARK: - JSON Type 38 | 39 | /** 40 | JSON's type definitions. 41 | 42 | See http://tools.ietf.org/html/rfc7231#section-4.3 43 | */ 44 | public enum Type :Int{ 45 | 46 | case Number 47 | case String 48 | case Bool 49 | case Array 50 | case Dictionary 51 | case Null 52 | case Unknown 53 | } 54 | 55 | // MARK: - JSON Base 56 | 57 | public struct JSON { 58 | 59 | /** 60 | Creates a JSON using the data. 61 | 62 | - parameter data: The NSData used to convert to json.Top level object in data is an NSArray or NSDictionary 63 | - parameter opt: The JSON serialization reading options. `.AllowFragments` by default. 64 | - parameter error: error The NSErrorPointer used to return the error. `nil` by default. 65 | 66 | - returns: The created JSON 67 | */ 68 | public init(data:NSData, options opt: NSJSONReadingOptions = .AllowFragments, error: NSErrorPointer = nil) { 69 | do { 70 | let object: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: opt) 71 | self.init(object) 72 | } catch let aError as NSError { 73 | if error != nil { 74 | error.memory = aError 75 | } 76 | self.init(NSNull()) 77 | } 78 | } 79 | 80 | /** 81 | Creates a JSON using the object. 82 | 83 | - parameter object: The object must have the following properties: All objects are NSString/String, NSNumber/Int/Float/Double/Bool, NSArray/Array, NSDictionary/Dictionary, or NSNull; All dictionary keys are NSStrings/String; NSNumbers are not NaN or infinity. 84 | 85 | - returns: The created JSON 86 | */ 87 | public init(_ object: AnyObject) { 88 | self.object = object 89 | } 90 | 91 | /** 92 | Creates a JSON from a [JSON] 93 | 94 | - parameter jsonArray: A Swift array of JSON objects 95 | 96 | - returns: The created JSON 97 | */ 98 | public init(_ jsonArray:[JSON]) { 99 | self.init(jsonArray.map { $0.object }) 100 | } 101 | 102 | /** 103 | Creates a JSON from a [String: JSON] 104 | 105 | - parameter jsonDictionary: A Swift dictionary of JSON objects 106 | 107 | - returns: The created JSON 108 | */ 109 | public init(_ jsonDictionary:[String: JSON]) { 110 | var dictionary = [String: AnyObject]() 111 | for (key, json) in jsonDictionary { 112 | dictionary[key] = json.object 113 | } 114 | self.init(dictionary) 115 | } 116 | 117 | /// Private object 118 | private var rawArray: [AnyObject] = [] 119 | private var rawDictionary: [String : AnyObject] = [:] 120 | private var rawString: String = "" 121 | private var rawNumber: NSNumber = 0 122 | private var rawNull: NSNull = NSNull() 123 | /// Private type 124 | private var _type: Type = .Null 125 | /// prviate error 126 | private var _error: NSError? = nil 127 | 128 | /// Object in JSON 129 | public var object: AnyObject { 130 | get { 131 | switch self.type { 132 | case .Array: 133 | return self.rawArray 134 | case .Dictionary: 135 | return self.rawDictionary 136 | case .String: 137 | return self.rawString 138 | case .Number: 139 | return self.rawNumber 140 | case .Bool: 141 | return self.rawNumber 142 | default: 143 | return self.rawNull 144 | } 145 | } 146 | set { 147 | _error = nil 148 | switch newValue { 149 | case let number as NSNumber: 150 | if number.isBool { 151 | _type = .Bool 152 | } else { 153 | _type = .Number 154 | } 155 | self.rawNumber = number 156 | case let string as String: 157 | _type = .String 158 | self.rawString = string 159 | case _ as NSNull: 160 | _type = .Null 161 | case let array as [AnyObject]: 162 | _type = .Array 163 | self.rawArray = array 164 | case let dictionary as [String : AnyObject]: 165 | _type = .Dictionary 166 | self.rawDictionary = dictionary 167 | default: 168 | _type = .Unknown 169 | _error = NSError(domain: ErrorDomain, code: ErrorUnsupportedType, userInfo: [NSLocalizedDescriptionKey: "It is a unsupported type"]) 170 | } 171 | } 172 | } 173 | 174 | /// json type 175 | public var type: Type { get { return _type } } 176 | 177 | /// Error in JSON 178 | public var error: NSError? { get { return self._error } } 179 | 180 | /// The static null json 181 | @available(*, unavailable, renamed="null") 182 | public static var nullJSON: JSON { get { return null } } 183 | public static var null: JSON { get { return JSON(NSNull()) } } 184 | } 185 | 186 | // MARK: - CollectionType, SequenceType, Indexable 187 | extension JSON : Swift.CollectionType, Swift.SequenceType, Swift.Indexable { 188 | 189 | public typealias Generator = JSONGenerator 190 | 191 | public typealias Index = JSONIndex 192 | 193 | public var startIndex: JSON.Index { 194 | switch self.type { 195 | case .Array: 196 | return JSONIndex(arrayIndex: self.rawArray.startIndex) 197 | case .Dictionary: 198 | return JSONIndex(dictionaryIndex: self.rawDictionary.startIndex) 199 | default: 200 | return JSONIndex() 201 | } 202 | } 203 | 204 | public var endIndex: JSON.Index { 205 | switch self.type { 206 | case .Array: 207 | return JSONIndex(arrayIndex: self.rawArray.endIndex) 208 | case .Dictionary: 209 | return JSONIndex(dictionaryIndex: self.rawDictionary.endIndex) 210 | default: 211 | return JSONIndex() 212 | } 213 | } 214 | 215 | public subscript (position: JSON.Index) -> JSON.Generator.Element { 216 | switch self.type { 217 | case .Array: 218 | return (String(position.arrayIndex), JSON(self.rawArray[position.arrayIndex!])) 219 | case .Dictionary: 220 | let (key, value) = self.rawDictionary[position.dictionaryIndex!] 221 | return (key, JSON(value)) 222 | default: 223 | return ("", JSON.null) 224 | } 225 | } 226 | 227 | /// If `type` is `.Array` or `.Dictionary`, return `array.empty` or `dictonary.empty` otherwise return `true`. 228 | public var isEmpty: Bool { 229 | get { 230 | switch self.type { 231 | case .Array: 232 | return self.rawArray.isEmpty 233 | case .Dictionary: 234 | return self.rawDictionary.isEmpty 235 | default: 236 | return true 237 | } 238 | } 239 | } 240 | 241 | /// If `type` is `.Array` or `.Dictionary`, return `array.count` or `dictonary.count` otherwise return `0`. 242 | public var count: Int { 243 | switch self.type { 244 | case .Array: 245 | return self.rawArray.count 246 | case .Dictionary: 247 | return self.rawDictionary.count 248 | default: 249 | return 0 250 | } 251 | } 252 | 253 | public func underestimateCount() -> Int { 254 | switch self.type { 255 | case .Array: 256 | return self.rawArray.underestimateCount() 257 | case .Dictionary: 258 | return self.rawDictionary.underestimateCount() 259 | default: 260 | return 0 261 | } 262 | } 263 | 264 | /** 265 | If `type` is `.Array` or `.Dictionary`, return a generator over the elements like `Array` or `Dictionary`, otherwise return a generator over empty. 266 | 267 | - returns: Return a *generator* over the elements of JSON. 268 | */ 269 | public func generate() -> JSON.Generator { 270 | return JSON.Generator(self) 271 | } 272 | } 273 | 274 | public struct JSONIndex: ForwardIndexType, _Incrementable, Equatable, Comparable { 275 | 276 | let arrayIndex: Int? 277 | let dictionaryIndex: DictionaryIndex? 278 | 279 | let type: Type 280 | 281 | init(){ 282 | self.arrayIndex = nil 283 | self.dictionaryIndex = nil 284 | self.type = .Unknown 285 | } 286 | 287 | init(arrayIndex: Int) { 288 | self.arrayIndex = arrayIndex 289 | self.dictionaryIndex = nil 290 | self.type = .Array 291 | } 292 | 293 | init(dictionaryIndex: DictionaryIndex) { 294 | self.arrayIndex = nil 295 | self.dictionaryIndex = dictionaryIndex 296 | self.type = .Dictionary 297 | } 298 | 299 | public func successor() -> JSONIndex { 300 | switch self.type { 301 | case .Array: 302 | return JSONIndex(arrayIndex: self.arrayIndex!.successor()) 303 | case .Dictionary: 304 | return JSONIndex(dictionaryIndex: self.dictionaryIndex!.successor()) 305 | default: 306 | return JSONIndex() 307 | } 308 | } 309 | } 310 | 311 | public func ==(lhs: JSONIndex, rhs: JSONIndex) -> Bool { 312 | switch (lhs.type, rhs.type) { 313 | case (.Array, .Array): 314 | return lhs.arrayIndex == rhs.arrayIndex 315 | case (.Dictionary, .Dictionary): 316 | return lhs.dictionaryIndex == rhs.dictionaryIndex 317 | default: 318 | return false 319 | } 320 | } 321 | 322 | public func <(lhs: JSONIndex, rhs: JSONIndex) -> Bool { 323 | switch (lhs.type, rhs.type) { 324 | case (.Array, .Array): 325 | return lhs.arrayIndex < rhs.arrayIndex 326 | case (.Dictionary, .Dictionary): 327 | return lhs.dictionaryIndex < rhs.dictionaryIndex 328 | default: 329 | return false 330 | } 331 | } 332 | 333 | public func <=(lhs: JSONIndex, rhs: JSONIndex) -> Bool { 334 | switch (lhs.type, rhs.type) { 335 | case (.Array, .Array): 336 | return lhs.arrayIndex <= rhs.arrayIndex 337 | case (.Dictionary, .Dictionary): 338 | return lhs.dictionaryIndex <= rhs.dictionaryIndex 339 | default: 340 | return false 341 | } 342 | } 343 | 344 | public func >=(lhs: JSONIndex, rhs: JSONIndex) -> Bool { 345 | switch (lhs.type, rhs.type) { 346 | case (.Array, .Array): 347 | return lhs.arrayIndex >= rhs.arrayIndex 348 | case (.Dictionary, .Dictionary): 349 | return lhs.dictionaryIndex >= rhs.dictionaryIndex 350 | default: 351 | return false 352 | } 353 | } 354 | 355 | public func >(lhs: JSONIndex, rhs: JSONIndex) -> Bool { 356 | switch (lhs.type, rhs.type) { 357 | case (.Array, .Array): 358 | return lhs.arrayIndex > rhs.arrayIndex 359 | case (.Dictionary, .Dictionary): 360 | return lhs.dictionaryIndex > rhs.dictionaryIndex 361 | default: 362 | return false 363 | } 364 | } 365 | 366 | public struct JSONGenerator : GeneratorType { 367 | 368 | public typealias Element = (String, JSON) 369 | 370 | private let type: Type 371 | private var dictionayGenerate: DictionaryGenerator? 372 | private var arrayGenerate: IndexingGenerator<[AnyObject]>? 373 | private var arrayIndex: Int = 0 374 | 375 | init(_ json: JSON) { 376 | self.type = json.type 377 | if type == .Array { 378 | self.arrayGenerate = json.rawArray.generate() 379 | }else { 380 | self.dictionayGenerate = json.rawDictionary.generate() 381 | } 382 | } 383 | 384 | public mutating func next() -> JSONGenerator.Element? { 385 | switch self.type { 386 | case .Array: 387 | if let o = self.arrayGenerate!.next() { 388 | return (String(self.arrayIndex++), JSON(o)) 389 | } else { 390 | return nil 391 | } 392 | case .Dictionary: 393 | if let (k, v): (String, AnyObject) = self.dictionayGenerate!.next() { 394 | return (k, JSON(v)) 395 | } else { 396 | return nil 397 | } 398 | default: 399 | return nil 400 | } 401 | } 402 | } 403 | 404 | // MARK: - Subscript 405 | 406 | /** 407 | * To mark both String and Int can be used in subscript. 408 | */ 409 | public enum JSONKey { 410 | case Index(Int) 411 | case Key(String) 412 | } 413 | 414 | public protocol JSONSubscriptType { 415 | var jsonKey:JSONKey { get } 416 | } 417 | 418 | extension Int: JSONSubscriptType { 419 | public var jsonKey:JSONKey { 420 | return JSONKey.Index(self) 421 | } 422 | } 423 | 424 | extension String: JSONSubscriptType { 425 | public var jsonKey:JSONKey { 426 | return JSONKey.Key(self) 427 | } 428 | } 429 | 430 | extension JSON { 431 | 432 | /// If `type` is `.Array`, return json which's object is `array[index]`, otherwise return null json with error. 433 | private subscript(index index: Int) -> JSON { 434 | get { 435 | if self.type != .Array { 436 | var r = JSON.null 437 | r._error = self._error ?? NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] failure, It is not an array"]) 438 | return r 439 | } else if index >= 0 && index < self.rawArray.count { 440 | return JSON(self.rawArray[index]) 441 | } else { 442 | var r = JSON.null 443 | r._error = NSError(domain: ErrorDomain, code:ErrorIndexOutOfBounds , userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] is out of bounds"]) 444 | return r 445 | } 446 | } 447 | set { 448 | if self.type == .Array { 449 | if self.rawArray.count > index && newValue.error == nil { 450 | self.rawArray[index] = newValue.object 451 | } 452 | } 453 | } 454 | } 455 | 456 | /// If `type` is `.Dictionary`, return json which's object is `dictionary[key]` , otherwise return null json with error. 457 | private subscript(key key: String) -> JSON { 458 | get { 459 | var r = JSON.null 460 | if self.type == .Dictionary { 461 | if let o = self.rawDictionary[key] { 462 | r = JSON(o) 463 | } else { 464 | r._error = NSError(domain: ErrorDomain, code: ErrorNotExist, userInfo: [NSLocalizedDescriptionKey: "Dictionary[\"\(key)\"] does not exist"]) 465 | } 466 | } else { 467 | r._error = self._error ?? NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Dictionary[\"\(key)\"] failure, It is not an dictionary"]) 468 | } 469 | return r 470 | } 471 | set { 472 | if self.type == .Dictionary && newValue.error == nil { 473 | self.rawDictionary[key] = newValue.object 474 | } 475 | } 476 | } 477 | 478 | /// If `sub` is `Int`, return `subscript(index:)`; If `sub` is `String`, return `subscript(key:)`. 479 | private subscript(sub sub: JSONSubscriptType) -> JSON { 480 | get { 481 | switch sub.jsonKey { 482 | case .Index(let index): return self[index: index] 483 | case .Key(let key): return self[key: key] 484 | } 485 | } 486 | set { 487 | switch sub.jsonKey { 488 | case .Index(let index): self[index: index] = newValue 489 | case .Key(let key): self[key: key] = newValue 490 | } 491 | } 492 | } 493 | 494 | /** 495 | Find a json in the complex data structuresby using the Int/String's array. 496 | 497 | - parameter path: The target json's path. Example: 498 | 499 | let json = JSON[data] 500 | let path = [9,"list","person","name"] 501 | let name = json[path] 502 | 503 | The same as: let name = json[9]["list"]["person"]["name"] 504 | 505 | - returns: Return a json found by the path or a null json with error 506 | */ 507 | public subscript(path: [JSONSubscriptType]) -> JSON { 508 | get { 509 | return path.reduce(self) { $0[sub: $1] } 510 | } 511 | set { 512 | switch path.count { 513 | case 0: 514 | return 515 | case 1: 516 | self[sub:path[0]].object = newValue.object 517 | default: 518 | var aPath = path; aPath.removeAtIndex(0) 519 | var nextJSON = self[sub: path[0]] 520 | nextJSON[aPath] = newValue 521 | self[sub: path[0]] = nextJSON 522 | } 523 | } 524 | } 525 | 526 | /** 527 | Find a json in the complex data structuresby using the Int/String's array. 528 | 529 | - parameter path: The target json's path. Example: 530 | 531 | let name = json[9,"list","person","name"] 532 | 533 | The same as: let name = json[9]["list"]["person"]["name"] 534 | 535 | - returns: Return a json found by the path or a null json with error 536 | */ 537 | public subscript(path: JSONSubscriptType...) -> JSON { 538 | get { 539 | return self[path] 540 | } 541 | set { 542 | self[path] = newValue 543 | } 544 | } 545 | } 546 | 547 | // MARK: - LiteralConvertible 548 | 549 | extension JSON: Swift.StringLiteralConvertible { 550 | 551 | public init(stringLiteral value: StringLiteralType) { 552 | self.init(value) 553 | } 554 | 555 | public init(extendedGraphemeClusterLiteral value: StringLiteralType) { 556 | self.init(value) 557 | } 558 | 559 | public init(unicodeScalarLiteral value: StringLiteralType) { 560 | self.init(value) 561 | } 562 | } 563 | 564 | extension JSON: Swift.IntegerLiteralConvertible { 565 | 566 | public init(integerLiteral value: IntegerLiteralType) { 567 | self.init(value) 568 | } 569 | } 570 | 571 | extension JSON: Swift.BooleanLiteralConvertible { 572 | 573 | public init(booleanLiteral value: BooleanLiteralType) { 574 | self.init(value) 575 | } 576 | } 577 | 578 | extension JSON: Swift.FloatLiteralConvertible { 579 | 580 | public init(floatLiteral value: FloatLiteralType) { 581 | self.init(value) 582 | } 583 | } 584 | 585 | extension JSON: Swift.DictionaryLiteralConvertible { 586 | 587 | public init(dictionaryLiteral elements: (String, AnyObject)...) { 588 | self.init(elements.reduce([String : AnyObject]()){(dictionary: [String : AnyObject], element:(String, AnyObject)) -> [String : AnyObject] in 589 | var d = dictionary 590 | d[element.0] = element.1 591 | return d 592 | }) 593 | } 594 | } 595 | 596 | extension JSON: Swift.ArrayLiteralConvertible { 597 | 598 | public init(arrayLiteral elements: AnyObject...) { 599 | self.init(elements) 600 | } 601 | } 602 | 603 | extension JSON: Swift.NilLiteralConvertible { 604 | 605 | public init(nilLiteral: ()) { 606 | self.init(NSNull()) 607 | } 608 | } 609 | 610 | // MARK: - Raw 611 | 612 | extension JSON: Swift.RawRepresentable { 613 | 614 | public init?(rawValue: AnyObject) { 615 | if JSON(rawValue).type == .Unknown { 616 | return nil 617 | } else { 618 | self.init(rawValue) 619 | } 620 | } 621 | 622 | public var rawValue: AnyObject { 623 | return self.object 624 | } 625 | 626 | public func rawData(options opt: NSJSONWritingOptions = NSJSONWritingOptions(rawValue: 0)) throws -> NSData { 627 | guard NSJSONSerialization.isValidJSONObject(self.object) else { 628 | throw NSError(domain: ErrorDomain, code: ErrorInvalidJSON, userInfo: [NSLocalizedDescriptionKey: "JSON is invalid"]) 629 | } 630 | 631 | return try NSJSONSerialization.dataWithJSONObject(self.object, options: opt) 632 | } 633 | 634 | public func rawString(encoding: UInt = NSUTF8StringEncoding, options opt: NSJSONWritingOptions = .PrettyPrinted) -> String? { 635 | switch self.type { 636 | case .Array, .Dictionary: 637 | do { 638 | let data = try self.rawData(options: opt) 639 | return NSString(data: data, encoding: encoding) as? String 640 | } catch _ { 641 | return nil 642 | } 643 | case .String: 644 | return self.rawString 645 | case .Number: 646 | return self.rawNumber.stringValue 647 | case .Bool: 648 | return self.rawNumber.boolValue.description 649 | case .Null: 650 | return "null" 651 | default: 652 | return nil 653 | } 654 | } 655 | } 656 | 657 | // MARK: - Printable, DebugPrintable 658 | 659 | extension JSON: Swift.Printable, Swift.DebugPrintable { 660 | 661 | public var description: String { 662 | if let string = self.rawString(options:.PrettyPrinted) { 663 | return string 664 | } else { 665 | return "unknown" 666 | } 667 | } 668 | 669 | public var debugDescription: String { 670 | return description 671 | } 672 | } 673 | 674 | // MARK: - Array 675 | 676 | extension JSON { 677 | 678 | //Optional [JSON] 679 | public var array: [JSON]? { 680 | get { 681 | if self.type == .Array { 682 | return self.rawArray.map{ JSON($0) } 683 | } else { 684 | return nil 685 | } 686 | } 687 | } 688 | 689 | //Non-optional [JSON] 690 | public var arrayValue: [JSON] { 691 | get { 692 | return self.array ?? [] 693 | } 694 | } 695 | 696 | //Optional [AnyObject] 697 | public var arrayObject: [AnyObject]? { 698 | get { 699 | switch self.type { 700 | case .Array: 701 | return self.rawArray 702 | default: 703 | return nil 704 | } 705 | } 706 | set { 707 | if let array = newValue { 708 | self.object = array 709 | } else { 710 | self.object = NSNull() 711 | } 712 | } 713 | } 714 | } 715 | 716 | // MARK: - Dictionary 717 | 718 | extension JSON { 719 | 720 | //Optional [String : JSON] 721 | public var dictionary: [String : JSON]? { 722 | if self.type == .Dictionary { 723 | return self.rawDictionary.reduce([String : JSON]()) { (dictionary: [String : JSON], element: (String, AnyObject)) -> [String : JSON] in 724 | var d = dictionary 725 | d[element.0] = JSON(element.1) 726 | return d 727 | } 728 | } else { 729 | return nil 730 | } 731 | } 732 | 733 | //Non-optional [String : JSON] 734 | public var dictionaryValue: [String : JSON] { 735 | return self.dictionary ?? [:] 736 | } 737 | 738 | //Optional [String : AnyObject] 739 | public var dictionaryObject: [String : AnyObject]? { 740 | get { 741 | switch self.type { 742 | case .Dictionary: 743 | return self.rawDictionary 744 | default: 745 | return nil 746 | } 747 | } 748 | set { 749 | if let v = newValue { 750 | self.object = v 751 | } else { 752 | self.object = NSNull() 753 | } 754 | } 755 | } 756 | } 757 | 758 | // MARK: - Bool 759 | 760 | extension JSON: Swift.BooleanType { 761 | 762 | //Optional bool 763 | public var bool: Bool? { 764 | get { 765 | switch self.type { 766 | case .Bool: 767 | return self.rawNumber.boolValue 768 | default: 769 | return nil 770 | } 771 | } 772 | set { 773 | if newValue != nil { 774 | self.object = NSNumber(bool: newValue!) 775 | } else { 776 | self.object = NSNull() 777 | } 778 | } 779 | } 780 | 781 | //Non-optional bool 782 | public var boolValue: Bool { 783 | get { 784 | switch self.type { 785 | case .Bool, .Number, .String: 786 | return self.object.boolValue 787 | default: 788 | return false 789 | } 790 | } 791 | set { 792 | self.object = NSNumber(bool: newValue) 793 | } 794 | } 795 | } 796 | 797 | // MARK: - String 798 | 799 | extension JSON { 800 | 801 | //Optional string 802 | public var string: String? { 803 | get { 804 | switch self.type { 805 | case .String: 806 | return self.object as? String 807 | default: 808 | return nil 809 | } 810 | } 811 | set { 812 | if newValue != nil { 813 | self.object = NSString(string:newValue!) 814 | } else { 815 | self.object = NSNull() 816 | } 817 | } 818 | } 819 | 820 | //Non-optional string 821 | public var stringValue: String { 822 | get { 823 | switch self.type { 824 | case .String: 825 | return self.object as! String 826 | case .Number: 827 | return self.object.stringValue 828 | case .Bool: 829 | return (self.object as! Bool).description 830 | default: 831 | return "" 832 | } 833 | } 834 | set { 835 | self.object = NSString(string:newValue) 836 | } 837 | } 838 | } 839 | 840 | // MARK: - Number 841 | extension JSON { 842 | 843 | //Optional number 844 | public var number: NSNumber? { 845 | get { 846 | switch self.type { 847 | case .Number, .Bool: 848 | return self.rawNumber 849 | default: 850 | return nil 851 | } 852 | } 853 | set { 854 | self.object = newValue ?? NSNull() 855 | } 856 | } 857 | 858 | //Non-optional number 859 | public var numberValue: NSNumber { 860 | get { 861 | switch self.type { 862 | case .String: 863 | let decimal = NSDecimalNumber(string: self.object as? String) 864 | if decimal == NSDecimalNumber.notANumber() { // indicates parse error 865 | return NSDecimalNumber.zero() 866 | } 867 | return decimal 868 | case .Number, .Bool: 869 | return self.object as! NSNumber 870 | default: 871 | return NSNumber(double: 0.0) 872 | } 873 | } 874 | set { 875 | self.object = newValue 876 | } 877 | } 878 | } 879 | 880 | //MARK: - Null 881 | extension JSON { 882 | 883 | public var null: NSNull? { 884 | get { 885 | switch self.type { 886 | case .Null: 887 | return self.rawNull 888 | default: 889 | return nil 890 | } 891 | } 892 | set { 893 | self.object = NSNull() 894 | } 895 | } 896 | public func isExists() -> Bool{ 897 | if let errorValue = error where errorValue.code == ErrorNotExist{ 898 | return false 899 | } 900 | return true 901 | } 902 | } 903 | 904 | //MARK: - URL 905 | extension JSON { 906 | 907 | //Optional URL 908 | public var URL: NSURL? { 909 | get { 910 | switch self.type { 911 | case .String: 912 | if let encodedString_ = self.rawString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()) { 913 | return NSURL(string: encodedString_) 914 | } else { 915 | return nil 916 | } 917 | default: 918 | return nil 919 | } 920 | } 921 | set { 922 | self.object = newValue?.absoluteString ?? NSNull() 923 | } 924 | } 925 | } 926 | 927 | // MARK: - Int, Double, Float, Int8, Int16, Int32, Int64 928 | 929 | extension JSON { 930 | 931 | public var double: Double? { 932 | get { 933 | return self.number?.doubleValue 934 | } 935 | set { 936 | if newValue != nil { 937 | self.object = NSNumber(double: newValue!) 938 | } else { 939 | self.object = NSNull() 940 | } 941 | } 942 | } 943 | 944 | public var doubleValue: Double { 945 | get { 946 | return self.numberValue.doubleValue 947 | } 948 | set { 949 | self.object = NSNumber(double: newValue) 950 | } 951 | } 952 | 953 | public var float: Float? { 954 | get { 955 | return self.number?.floatValue 956 | } 957 | set { 958 | if newValue != nil { 959 | self.object = NSNumber(float: newValue!) 960 | } else { 961 | self.object = NSNull() 962 | } 963 | } 964 | } 965 | 966 | public var floatValue: Float { 967 | get { 968 | return self.numberValue.floatValue 969 | } 970 | set { 971 | self.object = NSNumber(float: newValue) 972 | } 973 | } 974 | 975 | public var int: Int? { 976 | get { 977 | return self.number?.longValue 978 | } 979 | set { 980 | if newValue != nil { 981 | self.object = NSNumber(integer: newValue!) 982 | } else { 983 | self.object = NSNull() 984 | } 985 | } 986 | } 987 | 988 | public var intValue: Int { 989 | get { 990 | return self.numberValue.integerValue 991 | } 992 | set { 993 | self.object = NSNumber(integer: newValue) 994 | } 995 | } 996 | 997 | public var uInt: UInt? { 998 | get { 999 | return self.number?.unsignedLongValue 1000 | } 1001 | set { 1002 | if newValue != nil { 1003 | self.object = NSNumber(unsignedLong: newValue!) 1004 | } else { 1005 | self.object = NSNull() 1006 | } 1007 | } 1008 | } 1009 | 1010 | public var uIntValue: UInt { 1011 | get { 1012 | return self.numberValue.unsignedLongValue 1013 | } 1014 | set { 1015 | self.object = NSNumber(unsignedLong: newValue) 1016 | } 1017 | } 1018 | 1019 | public var int8: Int8? { 1020 | get { 1021 | return self.number?.charValue 1022 | } 1023 | set { 1024 | if newValue != nil { 1025 | self.object = NSNumber(char: newValue!) 1026 | } else { 1027 | self.object = NSNull() 1028 | } 1029 | } 1030 | } 1031 | 1032 | public var int8Value: Int8 { 1033 | get { 1034 | return self.numberValue.charValue 1035 | } 1036 | set { 1037 | self.object = NSNumber(char: newValue) 1038 | } 1039 | } 1040 | 1041 | public var uInt8: UInt8? { 1042 | get { 1043 | return self.number?.unsignedCharValue 1044 | } 1045 | set { 1046 | if newValue != nil { 1047 | self.object = NSNumber(unsignedChar: newValue!) 1048 | } else { 1049 | self.object = NSNull() 1050 | } 1051 | } 1052 | } 1053 | 1054 | public var uInt8Value: UInt8 { 1055 | get { 1056 | return self.numberValue.unsignedCharValue 1057 | } 1058 | set { 1059 | self.object = NSNumber(unsignedChar: newValue) 1060 | } 1061 | } 1062 | 1063 | public var int16: Int16? { 1064 | get { 1065 | return self.number?.shortValue 1066 | } 1067 | set { 1068 | if newValue != nil { 1069 | self.object = NSNumber(short: newValue!) 1070 | } else { 1071 | self.object = NSNull() 1072 | } 1073 | } 1074 | } 1075 | 1076 | public var int16Value: Int16 { 1077 | get { 1078 | return self.numberValue.shortValue 1079 | } 1080 | set { 1081 | self.object = NSNumber(short: newValue) 1082 | } 1083 | } 1084 | 1085 | public var uInt16: UInt16? { 1086 | get { 1087 | return self.number?.unsignedShortValue 1088 | } 1089 | set { 1090 | if newValue != nil { 1091 | self.object = NSNumber(unsignedShort: newValue!) 1092 | } else { 1093 | self.object = NSNull() 1094 | } 1095 | } 1096 | } 1097 | 1098 | public var uInt16Value: UInt16 { 1099 | get { 1100 | return self.numberValue.unsignedShortValue 1101 | } 1102 | set { 1103 | self.object = NSNumber(unsignedShort: newValue) 1104 | } 1105 | } 1106 | 1107 | public var int32: Int32? { 1108 | get { 1109 | return self.number?.intValue 1110 | } 1111 | set { 1112 | if newValue != nil { 1113 | self.object = NSNumber(int: newValue!) 1114 | } else { 1115 | self.object = NSNull() 1116 | } 1117 | } 1118 | } 1119 | 1120 | public var int32Value: Int32 { 1121 | get { 1122 | return self.numberValue.intValue 1123 | } 1124 | set { 1125 | self.object = NSNumber(int: newValue) 1126 | } 1127 | } 1128 | 1129 | public var uInt32: UInt32? { 1130 | get { 1131 | return self.number?.unsignedIntValue 1132 | } 1133 | set { 1134 | if newValue != nil { 1135 | self.object = NSNumber(unsignedInt: newValue!) 1136 | } else { 1137 | self.object = NSNull() 1138 | } 1139 | } 1140 | } 1141 | 1142 | public var uInt32Value: UInt32 { 1143 | get { 1144 | return self.numberValue.unsignedIntValue 1145 | } 1146 | set { 1147 | self.object = NSNumber(unsignedInt: newValue) 1148 | } 1149 | } 1150 | 1151 | public var int64: Int64? { 1152 | get { 1153 | return self.number?.longLongValue 1154 | } 1155 | set { 1156 | if newValue != nil { 1157 | self.object = NSNumber(longLong: newValue!) 1158 | } else { 1159 | self.object = NSNull() 1160 | } 1161 | } 1162 | } 1163 | 1164 | public var int64Value: Int64 { 1165 | get { 1166 | return self.numberValue.longLongValue 1167 | } 1168 | set { 1169 | self.object = NSNumber(longLong: newValue) 1170 | } 1171 | } 1172 | 1173 | public var uInt64: UInt64? { 1174 | get { 1175 | return self.number?.unsignedLongLongValue 1176 | } 1177 | set { 1178 | if newValue != nil { 1179 | self.object = NSNumber(unsignedLongLong: newValue!) 1180 | } else { 1181 | self.object = NSNull() 1182 | } 1183 | } 1184 | } 1185 | 1186 | public var uInt64Value: UInt64 { 1187 | get { 1188 | return self.numberValue.unsignedLongLongValue 1189 | } 1190 | set { 1191 | self.object = NSNumber(unsignedLongLong: newValue) 1192 | } 1193 | } 1194 | } 1195 | 1196 | //MARK: - Comparable 1197 | extension JSON : Swift.Comparable {} 1198 | 1199 | public func ==(lhs: JSON, rhs: JSON) -> Bool { 1200 | 1201 | switch (lhs.type, rhs.type) { 1202 | case (.Number, .Number): 1203 | return lhs.rawNumber == rhs.rawNumber 1204 | case (.String, .String): 1205 | return lhs.rawString == rhs.rawString 1206 | case (.Bool, .Bool): 1207 | return lhs.rawNumber.boolValue == rhs.rawNumber.boolValue 1208 | case (.Array, .Array): 1209 | return lhs.rawArray as NSArray == rhs.rawArray as NSArray 1210 | case (.Dictionary, .Dictionary): 1211 | return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary 1212 | case (.Null, .Null): 1213 | return true 1214 | default: 1215 | return false 1216 | } 1217 | } 1218 | 1219 | public func <=(lhs: JSON, rhs: JSON) -> Bool { 1220 | 1221 | switch (lhs.type, rhs.type) { 1222 | case (.Number, .Number): 1223 | return lhs.rawNumber <= rhs.rawNumber 1224 | case (.String, .String): 1225 | return lhs.rawString <= rhs.rawString 1226 | case (.Bool, .Bool): 1227 | return lhs.rawNumber.boolValue == rhs.rawNumber.boolValue 1228 | case (.Array, .Array): 1229 | return lhs.rawArray as NSArray == rhs.rawArray as NSArray 1230 | case (.Dictionary, .Dictionary): 1231 | return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary 1232 | case (.Null, .Null): 1233 | return true 1234 | default: 1235 | return false 1236 | } 1237 | } 1238 | 1239 | public func >=(lhs: JSON, rhs: JSON) -> Bool { 1240 | 1241 | switch (lhs.type, rhs.type) { 1242 | case (.Number, .Number): 1243 | return lhs.rawNumber >= rhs.rawNumber 1244 | case (.String, .String): 1245 | return lhs.rawString >= rhs.rawString 1246 | case (.Bool, .Bool): 1247 | return lhs.rawNumber.boolValue == rhs.rawNumber.boolValue 1248 | case (.Array, .Array): 1249 | return lhs.rawArray as NSArray == rhs.rawArray as NSArray 1250 | case (.Dictionary, .Dictionary): 1251 | return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary 1252 | case (.Null, .Null): 1253 | return true 1254 | default: 1255 | return false 1256 | } 1257 | } 1258 | 1259 | public func >(lhs: JSON, rhs: JSON) -> Bool { 1260 | 1261 | switch (lhs.type, rhs.type) { 1262 | case (.Number, .Number): 1263 | return lhs.rawNumber > rhs.rawNumber 1264 | case (.String, .String): 1265 | return lhs.rawString > rhs.rawString 1266 | default: 1267 | return false 1268 | } 1269 | } 1270 | 1271 | public func <(lhs: JSON, rhs: JSON) -> Bool { 1272 | 1273 | switch (lhs.type, rhs.type) { 1274 | case (.Number, .Number): 1275 | return lhs.rawNumber < rhs.rawNumber 1276 | case (.String, .String): 1277 | return lhs.rawString < rhs.rawString 1278 | default: 1279 | return false 1280 | } 1281 | } 1282 | 1283 | private let trueNumber = NSNumber(bool: true) 1284 | private let falseNumber = NSNumber(bool: false) 1285 | private let trueObjCType = String.fromCString(trueNumber.objCType) 1286 | private let falseObjCType = String.fromCString(falseNumber.objCType) 1287 | 1288 | // MARK: - NSNumber: Comparable 1289 | 1290 | extension NSNumber { 1291 | var isBool:Bool { 1292 | get { 1293 | let objCType = String.fromCString(self.objCType) 1294 | if (self.compare(trueNumber) == NSComparisonResult.OrderedSame && objCType == trueObjCType) 1295 | || (self.compare(falseNumber) == NSComparisonResult.OrderedSame && objCType == falseObjCType){ 1296 | return true 1297 | } else { 1298 | return false 1299 | } 1300 | } 1301 | } 1302 | } 1303 | 1304 | public func ==(lhs: NSNumber, rhs: NSNumber) -> Bool { 1305 | switch (lhs.isBool, rhs.isBool) { 1306 | case (false, true): 1307 | return false 1308 | case (true, false): 1309 | return false 1310 | default: 1311 | return lhs.compare(rhs) == NSComparisonResult.OrderedSame 1312 | } 1313 | } 1314 | 1315 | public func !=(lhs: NSNumber, rhs: NSNumber) -> Bool { 1316 | return !(lhs == rhs) 1317 | } 1318 | 1319 | public func <(lhs: NSNumber, rhs: NSNumber) -> Bool { 1320 | 1321 | switch (lhs.isBool, rhs.isBool) { 1322 | case (false, true): 1323 | return false 1324 | case (true, false): 1325 | return false 1326 | default: 1327 | return lhs.compare(rhs) == NSComparisonResult.OrderedAscending 1328 | } 1329 | } 1330 | 1331 | public func >(lhs: NSNumber, rhs: NSNumber) -> Bool { 1332 | 1333 | switch (lhs.isBool, rhs.isBool) { 1334 | case (false, true): 1335 | return false 1336 | case (true, false): 1337 | return false 1338 | default: 1339 | return lhs.compare(rhs) == NSComparisonResult.OrderedDescending 1340 | } 1341 | } 1342 | 1343 | public func <=(lhs: NSNumber, rhs: NSNumber) -> Bool { 1344 | 1345 | switch (lhs.isBool, rhs.isBool) { 1346 | case (false, true): 1347 | return false 1348 | case (true, false): 1349 | return false 1350 | default: 1351 | return lhs.compare(rhs) != NSComparisonResult.OrderedDescending 1352 | } 1353 | } 1354 | 1355 | public func >=(lhs: NSNumber, rhs: NSNumber) -> Bool { 1356 | 1357 | switch (lhs.isBool, rhs.isBool) { 1358 | case (false, true): 1359 | return false 1360 | case (true, false): 1361 | return false 1362 | default: 1363 | return lhs.compare(rhs) != NSComparisonResult.OrderedAscending 1364 | } 1365 | } 1366 | --------------------------------------------------------------------------------