├── LayerX ├── Assets.xcassets │ ├── Contents.json │ ├── lock.imageset │ │ ├── lock.png │ │ ├── Lock-16.png │ │ ├── lock-32.png │ │ └── Contents.json │ └── AppIcon.appiconset │ │ ├── appIcon-1.png │ │ ├── appIcon-2.png │ │ ├── appIcon.png │ │ ├── appIcon-128.png │ │ ├── appIcon-16.png │ │ ├── appIcon-256.png │ │ ├── appIcon-257.png │ │ ├── appIcon-32.png │ │ ├── appIcon-34.png │ │ ├── appIcon-64.png │ │ └── Contents.json ├── Info.plist ├── MCWindow.swift ├── MCDragAndDropImageView.swift ├── ViewController.swift ├── AppDelegate.swift └── Base.lproj │ └── Main.storyboard ├── LayerX.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcuserdata │ └── yuhua.xcuserdatad │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── LayerX.xcscheme └── project.pbxproj ├── .gitignore └── README.md /LayerX/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/lock.imageset/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuhua-chen/LayerX/HEAD/LayerX/Assets.xcassets/lock.imageset/lock.png -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/lock.imageset/Lock-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuhua-chen/LayerX/HEAD/LayerX/Assets.xcassets/lock.imageset/Lock-16.png -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/lock.imageset/lock-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuhua-chen/LayerX/HEAD/LayerX/Assets.xcassets/lock.imageset/lock-32.png -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuhua-chen/LayerX/HEAD/LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-1.png -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuhua-chen/LayerX/HEAD/LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-2.png -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/AppIcon.appiconset/appIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuhua-chen/LayerX/HEAD/LayerX/Assets.xcassets/AppIcon.appiconset/appIcon.png -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuhua-chen/LayerX/HEAD/LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-128.png -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuhua-chen/LayerX/HEAD/LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-16.png -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuhua-chen/LayerX/HEAD/LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-256.png -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-257.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuhua-chen/LayerX/HEAD/LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-257.png -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuhua-chen/LayerX/HEAD/LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-32.png -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuhua-chen/LayerX/HEAD/LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-34.png -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuhua-chen/LayerX/HEAD/LayerX/Assets.xcassets/AppIcon.appiconset/appIcon-64.png -------------------------------------------------------------------------------- /LayerX.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/lock.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Lock-16.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "lock-32.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "lock.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /LayerX.xcodeproj/xcuserdata/yuhua.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | LayerX.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 8C09F9F01BDE2E5C00DD457E 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/xcode,osx 2 | 3 | ### Xcode ### 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata 22 | 23 | ## Other 24 | *.xccheckout 25 | *.moved-aside 26 | *.xcuserstate 27 | 28 | 29 | ### OSX ### 30 | .DS_Store 31 | .AppleDouble 32 | .LSOverride 33 | 34 | # Icon must end with two \r 35 | Icon 36 | 37 | 38 | # Thumbnails 39 | ._* 40 | 41 | # Files that might appear in the root of a volume 42 | .DocumentRevisions-V100 43 | .fseventsd 44 | .Spotlight-V100 45 | .TemporaryItems 46 | .Trashes 47 | .VolumeIcon.icns 48 | 49 | # Directories potentially created on remote AFP share 50 | .AppleDB 51 | .AppleDesktop 52 | Network Trash Folder 53 | Temporary Items 54 | .apdisk 55 | 56 | -------------------------------------------------------------------------------- /LayerX/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.2.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2015年 Michael Chen. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /LayerX/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "appIcon-16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "appIcon-32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "appIcon-34.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "appIcon-64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "appIcon-128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "appIcon-257.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "appIcon-256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "appIcon.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "appIcon-1.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "appIcon-2.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /LayerX/MCWindow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MCWindow.swift 3 | // LayerX 4 | // 5 | // Created by Michael Chen on 2015/10/27. 6 | // Copyright © 2015年 Michael Chen. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class MCWIndow: NSWindow { 12 | override func awakeFromNib() { 13 | styleMask = [.borderless, .resizable] 14 | isOpaque = false 15 | backgroundColor = NSColor.clear 16 | isMovableByWindowBackground = true 17 | hasShadow = false 18 | } 19 | 20 | func moveBy(_ offset: CGPoint) { 21 | var frame = self.frame 22 | frame.origin.x += offset.x 23 | frame.origin.y += offset.y 24 | 25 | setFrame(frame, display: true) 26 | } 27 | 28 | func fitsWithSize(_ size: NSSize) { 29 | var frame = self.frame 30 | if frame.size.width < size.width || frame.size.height < size.height { 31 | frame.size = size 32 | setFrame(frame, display: true) 33 | } 34 | } 35 | 36 | func resizeTo(_ size: NSSize, animated: Bool) { 37 | var frame = self.frame 38 | frame.size = size 39 | 40 | if !animated { 41 | setFrame(frame, display: true) 42 | return 43 | } 44 | 45 | let resizeAnimation = [NSViewAnimation.Key.target: self, NSViewAnimation.Key.endFrame: NSValue(rect: frame)] 46 | let animations = NSViewAnimation(viewAnimations: [resizeAnimation]) 47 | animations.animationBlockingMode = .blocking 48 | animations.animationCurve = .easeInOut 49 | animations.duration = 0.15 50 | animations.start() 51 | } 52 | 53 | override func constrainFrameRect(_ frameRect: NSRect, to screen: NSScreen?) -> NSRect { 54 | return frameRect 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LayerX 2 | 3 | An intuitive app to display transparent images on screen. 4 | 5 | [![Demo](http://img.youtube.com/vi/35KixjZBDjY/0.jpg)](http://www.youtube.com/watch?v=35KixjZBDjY) 6 | 7 | # System requirements 8 | 9 | Support Mac OS X 10.10 or later. 10 | 11 | # Features 12 | 13 | - [x] Drag and drop images 14 | - [x] Keyboard Shortcuts 15 | - [x] Adjust transparency 16 | - [x] Lock images 17 | - [X] Aspect ratio scaling 18 | 19 | # Keyboard Shortcuts 20 | 21 | ### Set image 22 | 23 | | Key | Action | 24 | |:--- |:--- | 25 | |`⌘ V`| Paste image or file from clipboard. | 26 | 27 | ### Scale 28 | 29 | | Key | Action | 30 | |:---|:---| 31 | |`⌘ 0`| Actual image size.| 32 | |`⌘ +`| Scale up 10%.| 33 | |`⌘ -`| Scale down 10%.| 34 | |`^ ⌘ +`| Scale up 1px.| 35 | |`^ ⌘ -`| Scale down 1px.| 36 | 37 | ### Move (Arrow keys) 38 | 39 | | Key | Action | 40 | |:---|:---| 41 | |`↑`| Move up 1px.| 42 | |`←`| Move left 1px.| 43 | |`→`| Move right 1px.| 44 | |`↓`| Move down 1px.| 45 | 46 | ### Alpha 47 | 48 | |Key|Action| 49 | |:---|:---| 50 | |`⌘ J`| Increase transparency.| 51 | |`⌘ K`| Reduce transparency.| 52 | 53 | ### Window 54 | 55 | |Key|Action| 56 | |:---|:---| 57 | |`⌘ L`| Lock images and make window always on top.| 58 | |`⌘ T`| Make window always on top.| 59 | |`⌘ A`| Keep window on all spaces.| 60 | 61 | # Mouse events 62 | 63 | | Event | Action | 64 | |:---|:---| 65 | | Scroll up on image | Reduce transparency. | 66 | | Scroll down on image | Increase transparency. | 67 | 68 | # Contact 69 | 70 | Any suggestions are welcome. Find me at [@yuhua_twit](https://twitter.com/yuhua_twit) 71 | 72 | # License 73 | 74 | Copyright (c) 2017 Michael Chen (a.k.a. Yuhua Chen) 75 | 76 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 77 | 78 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 79 | 80 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 81 | -------------------------------------------------------------------------------- /LayerX/MCDragAndDropImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MCDragAndDropImageView.swift 3 | // LayerX 4 | // 5 | // Created by Michael Chen on 2015/10/26. 6 | // Copyright © 2015年 Michael Chen. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | protocol MCDragAndDropImageViewDelegate: class { 12 | func dragAndDropImageViewDidDrop(_ imageView: MCDragAndDropImageView) 13 | } 14 | 15 | class MCDragAndDropImageView: NSImageView { 16 | 17 | weak var delegate: MCDragAndDropImageViewDelegate? 18 | 19 | required init?(coder: NSCoder) { 20 | super.init(coder: coder) 21 | 22 | registerForDraggedTypes(NSImage.imageTypes.map(NSPasteboard.PasteboardType.init(rawValue:))) 23 | 24 | wantsLayer = true 25 | } 26 | 27 | override func setNeedsDisplay() { 28 | alphaValue = image == nil ? 1.0 : 0.6 29 | super.setNeedsDisplay() 30 | } 31 | 32 | override func draw(_ dirtyRect: NSRect) { 33 | super.draw(dirtyRect) 34 | 35 | if let _ = image { 36 | layer?.backgroundColor = NSColor.clear.cgColor 37 | return 38 | } 39 | 40 | layer?.backgroundColor = NSColor(white: isHighlighted ? 0.5 : 0.8, alpha: 1.0).cgColor 41 | } 42 | 43 | override var mouseDownCanMoveWindow:Bool { 44 | return true 45 | } 46 | } 47 | 48 | // MARK: - NSDraggingSource 49 | 50 | extension MCDragAndDropImageView: NSDraggingSource { 51 | 52 | override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { 53 | 54 | if (NSImage.canInit(with: sender.draggingPasteboard)) { 55 | isHighlighted = true 56 | 57 | setNeedsDisplay() 58 | 59 | let sourceDragMask = sender.draggingSourceOperationMask 60 | let pboard = sender.draggingPasteboard 61 | 62 | if pboard.availableType(from: [.fileURL]) == .fileURL { 63 | if sourceDragMask.rawValue & NSDragOperation.copy.rawValue != 0 { 64 | return NSDragOperation.copy 65 | } 66 | } 67 | } 68 | 69 | return NSDragOperation() 70 | } 71 | 72 | override func draggingExited(_ sender: NSDraggingInfo?) { 73 | isHighlighted = false 74 | setNeedsDisplay() 75 | } 76 | 77 | override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool { 78 | isHighlighted = false 79 | setNeedsDisplay() 80 | 81 | return NSImage.canInit(with: sender.draggingPasteboard) 82 | } 83 | 84 | override func performDragOperation(_ sender: NSDraggingInfo) -> Bool { 85 | if (NSImage.canInit(with: sender.draggingPasteboard)) { 86 | image = NSImage(pasteboard: sender.draggingPasteboard) 87 | delegate?.dragAndDropImageViewDidDrop(self) 88 | setNeedsDisplay() 89 | } 90 | 91 | return true 92 | } 93 | 94 | func draggingSession(_ session: NSDraggingSession, sourceOperationMaskFor context: NSDraggingContext) -> NSDragOperation { 95 | switch context { 96 | case .outsideApplication: return NSDragOperation() 97 | case .withinApplication: return .copy 98 | @unknown default: return NSDragOperation() 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /LayerX/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // LayerX 4 | // 5 | // Created by Michael Chen on 2015/10/26. 6 | // Copyright © 2015年 Michael Chen. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ViewController: NSViewController { 12 | 13 | @IBOutlet weak var imageView: MCDragAndDropImageView! 14 | @IBOutlet weak var sizeTextField: NSTextField! 15 | @IBOutlet weak var placeholderTextField: NSTextField! 16 | @IBOutlet weak var lockIconImageView: NSImageView! 17 | 18 | override var acceptsFirstResponder: Bool { 19 | return true 20 | } 21 | 22 | lazy var trackingArea: NSTrackingArea = { 23 | let options: NSTrackingArea.Options = [.activeAlways, .mouseEnteredAndExited] 24 | return NSTrackingArea(rect: self.view.bounds, options: options, owner: self, userInfo: nil) 25 | }() 26 | 27 | deinit { 28 | NotificationCenter.default.removeObserver(self) 29 | } 30 | 31 | required init?(coder: NSCoder) { 32 | super.init(coder: coder) 33 | appDelegate().viewController = self 34 | } 35 | 36 | override func viewDidLoad() { 37 | super.viewDidLoad() 38 | 39 | imageView.delegate = self 40 | 41 | sizeTextField.layer?.cornerRadius = 3 42 | sizeTextField.layer?.masksToBounds = true 43 | 44 | lockIconImageView.wantsLayer = true 45 | lockIconImageView.layer?.backgroundColor = NSColor(white: 0.0, alpha: 0.5).cgColor 46 | lockIconImageView.layer?.cornerRadius = 5 47 | lockIconImageView.layer?.masksToBounds = true 48 | 49 | NotificationCenter.default.addObserver(self, selector: #selector(windowDidResize(_:)), name: NSWindow.didResizeNotification, object: appDelegate().window) 50 | 51 | view.addTrackingArea(trackingArea) 52 | } 53 | 54 | @objc func fadeOutSizeTextField() { 55 | let transition = CATransition() 56 | sizeTextField.layer?.add(transition, forKey: "fadeOut") 57 | sizeTextField.layer?.opacity = 0 58 | } 59 | 60 | @objc func windowDidResize(_ notification: Notification) { 61 | let window = notification.object as! NSWindow 62 | let size = window.frame.size 63 | sizeTextField.stringValue = "\(Int(size.width))x\(Int(size.height))" 64 | sizeTextField.layer?.opacity = 1 65 | 66 | NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(ViewController.fadeOutSizeTextField), object: nil) 67 | perform(#selector(ViewController.fadeOutSizeTextField), with: nil, afterDelay: 2) 68 | } 69 | 70 | // MARK: Mouse events 71 | 72 | override func scrollWheel(with theEvent: NSEvent) { 73 | guard let _ = imageView.image else { return } 74 | 75 | let delta = theEvent.deltaY * 0.005; 76 | var alpha = imageView.alphaValue - delta 77 | alpha = min(alpha, 1) 78 | alpha = max(alpha, 0.05) 79 | imageView.alphaValue = alpha 80 | } 81 | 82 | override func mouseEntered(with theEvent: NSEvent) { 83 | sizeTextField.layer?.opacity = 1 84 | } 85 | 86 | override func mouseExited(with theEvent: NSEvent) { 87 | fadeOutSizeTextField() 88 | } 89 | } 90 | 91 | // MARK: - MCDragAndDropImageViewDelegate 92 | 93 | extension ViewController: MCDragAndDropImageViewDelegate { 94 | func dragAndDropImageViewDidDrop(_ imageView: MCDragAndDropImageView) { 95 | 96 | sizeTextField.isHidden = false 97 | placeholderTextField.isHidden = true 98 | 99 | appDelegate().actualSize(nil) 100 | } 101 | } 102 | 103 | // MARK: - Movable NSView 104 | 105 | class MCMovableView: NSView{ 106 | override var mouseDownCanMoveWindow:Bool { 107 | return true 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /LayerX.xcodeproj/xcuserdata/yuhua.xcuserdatad/xcschemes/LayerX.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 | -------------------------------------------------------------------------------- /LayerX/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // LayerX 4 | // 5 | // Created by Michael Chen on 2015/10/26. 6 | // Copyright © 2015年 Michael Chen. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | private let defaultSize = NSMakeSize(480, 320) 15 | private let resizeStep: CGFloat = 0.1 16 | 17 | var allSpaces = false 18 | var locked = false 19 | var onTop = false 20 | 21 | weak var window: MCWIndow! 22 | weak var viewController: ViewController! 23 | var isLockIconHiddenWhileLocked = false { 24 | didSet { viewController.lockIconImageView.isHidden = window.isMovable || isLockIconHiddenWhileLocked } 25 | } 26 | var isSizeHidden = false { 27 | didSet { viewController.sizeTextField.isHidden = isSizeHidden } 28 | } 29 | 30 | func applicationDidFinishLaunching(_ aNotification: Notification) { 31 | if let window = NSApp.windows.first as? MCWIndow { 32 | window.fitsWithSize(defaultSize) 33 | window.collectionBehavior = [.managed, .moveToActiveSpace] 34 | self.window = window 35 | } 36 | } 37 | } 38 | 39 | fileprivate enum ArrowTag: Int { 40 | case up = 20 41 | case left = 21 42 | case right = 22 43 | case down = 23 44 | } 45 | 46 | // MARK: - Hotkeys 47 | 48 | extension AppDelegate { 49 | 50 | private var originalSize: NSSize { 51 | viewController.imageView.image?.size ?? defaultSize 52 | } 53 | 54 | private func resizeAspectFit(calculator: (_ original: CGFloat, _ current: CGFloat) -> CGFloat) { 55 | let originalSize = self.originalSize 56 | let width = calculator(originalSize.width, window.frame.size.width) 57 | let height = width / originalSize.width * originalSize.height 58 | 59 | if width > 0 { 60 | window.resizeTo(NSSize(width: width, height: height), animated: true) 61 | } 62 | } 63 | 64 | @IBAction func actualSize(_ sender: AnyObject?) { 65 | window.resizeTo(originalSize, animated: true) 66 | } 67 | 68 | @IBAction func makeLarger(_ sender: AnyObject) { 69 | resizeAspectFit { $0 * ($1 / $0 + resizeStep) } 70 | } 71 | 72 | @IBAction func makeSmaller(_ sender: AnyObject) { 73 | resizeAspectFit { $0 * ($1 / $0 - resizeStep) } 74 | } 75 | 76 | @IBAction func makeLargerOnePixel(_ sender: AnyObject) { 77 | resizeAspectFit { $1 + 1 } 78 | } 79 | 80 | @IBAction func makeSmallerOnePixel(_ sender: AnyObject) { 81 | resizeAspectFit { $1 - 1 } 82 | } 83 | 84 | @IBAction func increaseTransparency(_ sender: AnyObject) { 85 | var alpha = viewController.imageView.alphaValue 86 | alpha -= 0.1 87 | viewController.imageView.alphaValue = max(alpha, 0.05) 88 | } 89 | 90 | @IBAction func reduceTransparency(_ sender: AnyObject) { 91 | var alpha = viewController.imageView.alphaValue 92 | alpha += 0.1 93 | viewController.imageView.alphaValue = min(alpha, 1.0) 94 | } 95 | 96 | func getPasteboardImage() -> NSImage? { 97 | let pasteboard = NSPasteboard.general; 98 | if let file = pasteboard.data(forType: NSPasteboard.PasteboardType.fileURL), 99 | let str = String(data: file, encoding: .utf8), 100 | let url = URL(string: str) 101 | { 102 | return NSImage(contentsOf: url) 103 | } 104 | 105 | if let tiff = pasteboard.data(forType: NSPasteboard.PasteboardType.tiff) { 106 | return NSImage(data: tiff) 107 | } 108 | 109 | if let png = pasteboard.data(forType: NSPasteboard.PasteboardType.png) { 110 | return NSImage(data: png) 111 | } 112 | 113 | return nil 114 | } 115 | 116 | @IBAction func paste(_ sender: AnyObject) { 117 | guard let image = getPasteboardImage() else { return } 118 | let rep = image.representations[0] 119 | viewController.imageView.image = image 120 | let size = NSMakeSize(CGFloat(rep.pixelsWide), CGFloat(rep.pixelsHigh)) 121 | window.resizeTo(size, animated: true) 122 | viewController.sizeTextField.isHidden = false 123 | viewController.placeholderTextField.isHidden = true 124 | 125 | } 126 | 127 | @IBAction func toggleLockWindow(_ sender: AnyObject) { 128 | let menuItem = sender as! NSMenuItem 129 | locked = !locked 130 | onTop = locked 131 | if locked { 132 | menuItem.title = "Unlock" 133 | window.isMovable = false 134 | window.ignoresMouseEvents = true 135 | window.level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.maximumWindow))) 136 | } else { 137 | menuItem.title = "Lock" 138 | window.isMovable = true 139 | window.ignoresMouseEvents = false 140 | window.level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.normalWindow))) 141 | } 142 | 143 | viewController.lockIconImageView.isHidden = window.isMovable || isLockIconHiddenWhileLocked 144 | } 145 | 146 | @IBAction func toggleOnTop(_ sender: AnyObject) { 147 | let menuItem = sender as! NSMenuItem 148 | onTop = !onTop 149 | if onTop { 150 | menuItem.title = "Don't keep on top" 151 | window.level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.maximumWindow))) 152 | } else if !locked { 153 | menuItem.title = "Keep on top" 154 | window.level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.normalWindow))) 155 | } 156 | } 157 | 158 | @IBAction func toggleLockIconVisibility(_ sender: AnyObject) { 159 | let menuItem = sender as! NSMenuItem 160 | menuItem.state = menuItem.state == .on ? .off : .on 161 | isLockIconHiddenWhileLocked = menuItem.state == .on 162 | } 163 | 164 | @IBAction func toggleSizeVisibility(_ sender: AnyObject) { 165 | let menuItem = sender as! NSMenuItem 166 | menuItem.state = menuItem.state == .on ? .off : .on 167 | isSizeHidden = menuItem.state == .on 168 | } 169 | 170 | @IBAction func moveAround(_ sender: AnyObject) { 171 | let menuItem = sender as! NSMenuItem 172 | 173 | guard let arrow = ArrowTag(rawValue: menuItem.tag) else { 174 | return 175 | } 176 | 177 | switch arrow { 178 | case .up: 179 | window.moveBy(CGPoint(x: 0, y: 1)) 180 | case .left: 181 | window.moveBy(CGPoint(x: -1, y: 0)) 182 | case .right: 183 | window.moveBy(CGPoint(x: 1, y: 0)) 184 | case .down: 185 | window.moveBy(CGPoint(x: 0, y: -1)) 186 | } 187 | } 188 | 189 | @IBAction func toggleAllSpaces(_ sender: AnyObject) { 190 | let menuItem = sender as! NSMenuItem 191 | allSpaces = !allSpaces 192 | if allSpaces { 193 | menuItem.title = "Keep on this space" 194 | window.collectionBehavior = [.canJoinAllSpaces] 195 | } else { 196 | menuItem.title = "Keep on all spaces" 197 | window.collectionBehavior = [.managed, .moveToActiveSpace] 198 | } 199 | } 200 | 201 | func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { 202 | return viewController.imageView.image != nil 203 | } 204 | } 205 | 206 | // MARK: - Helper 207 | 208 | func appDelegate() -> AppDelegate { 209 | return NSApp.delegate as! AppDelegate 210 | } 211 | -------------------------------------------------------------------------------- /LayerX.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8C09F9F51BDE2E5C00DD457E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C09F9F41BDE2E5C00DD457E /* AppDelegate.swift */; }; 11 | 8C09F9F71BDE2E5C00DD457E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C09F9F61BDE2E5C00DD457E /* ViewController.swift */; }; 12 | 8C09F9F91BDE2E5C00DD457E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8C09F9F81BDE2E5C00DD457E /* Assets.xcassets */; }; 13 | 8C09F9FC1BDE2E5C00DD457E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8C09F9FA1BDE2E5C00DD457E /* Main.storyboard */; }; 14 | 8C09FA041BDE324500DD457E /* MCDragAndDropImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C09FA031BDE324500DD457E /* MCDragAndDropImageView.swift */; }; 15 | 8C09FA0C1BDF5F7300DD457E /* MCWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C09FA0B1BDF5F7300DD457E /* MCWindow.swift */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 8C09F9F11BDE2E5C00DD457E /* LayerX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LayerX.app; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | 8C09F9F41BDE2E5C00DD457E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 21 | 8C09F9F61BDE2E5C00DD457E /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 22 | 8C09F9F81BDE2E5C00DD457E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 23 | 8C09F9FB1BDE2E5C00DD457E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 24 | 8C09F9FD1BDE2E5C00DD457E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 25 | 8C09FA031BDE324500DD457E /* MCDragAndDropImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MCDragAndDropImageView.swift; sourceTree = ""; }; 26 | 8C09FA0B1BDF5F7300DD457E /* MCWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MCWindow.swift; sourceTree = ""; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | 8C09F9EE1BDE2E5C00DD457E /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | ); 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXFrameworksBuildPhase section */ 38 | 39 | /* Begin PBXGroup section */ 40 | 8C09F9E81BDE2E5C00DD457E = { 41 | isa = PBXGroup; 42 | children = ( 43 | 8C09F9F31BDE2E5C00DD457E /* LayerX */, 44 | 8C09F9F21BDE2E5C00DD457E /* Products */, 45 | ); 46 | sourceTree = ""; 47 | }; 48 | 8C09F9F21BDE2E5C00DD457E /* Products */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | 8C09F9F11BDE2E5C00DD457E /* LayerX.app */, 52 | ); 53 | name = Products; 54 | sourceTree = ""; 55 | }; 56 | 8C09F9F31BDE2E5C00DD457E /* LayerX */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | 8C09F9F41BDE2E5C00DD457E /* AppDelegate.swift */, 60 | 8C09FA0B1BDF5F7300DD457E /* MCWindow.swift */, 61 | 8C09F9F61BDE2E5C00DD457E /* ViewController.swift */, 62 | 8C09FA031BDE324500DD457E /* MCDragAndDropImageView.swift */, 63 | 8C09F9F81BDE2E5C00DD457E /* Assets.xcassets */, 64 | 8C09F9FA1BDE2E5C00DD457E /* Main.storyboard */, 65 | 8C09F9FD1BDE2E5C00DD457E /* Info.plist */, 66 | ); 67 | path = LayerX; 68 | sourceTree = ""; 69 | }; 70 | /* End PBXGroup section */ 71 | 72 | /* Begin PBXNativeTarget section */ 73 | 8C09F9F01BDE2E5C00DD457E /* LayerX */ = { 74 | isa = PBXNativeTarget; 75 | buildConfigurationList = 8C09FA001BDE2E5C00DD457E /* Build configuration list for PBXNativeTarget "LayerX" */; 76 | buildPhases = ( 77 | 8C09F9ED1BDE2E5C00DD457E /* Sources */, 78 | 8C09F9EE1BDE2E5C00DD457E /* Frameworks */, 79 | 8C09F9EF1BDE2E5C00DD457E /* Resources */, 80 | ); 81 | buildRules = ( 82 | ); 83 | dependencies = ( 84 | ); 85 | name = LayerX; 86 | productName = LayerX; 87 | productReference = 8C09F9F11BDE2E5C00DD457E /* LayerX.app */; 88 | productType = "com.apple.product-type.application"; 89 | }; 90 | /* End PBXNativeTarget section */ 91 | 92 | /* Begin PBXProject section */ 93 | 8C09F9E91BDE2E5C00DD457E /* Project object */ = { 94 | isa = PBXProject; 95 | attributes = { 96 | LastUpgradeCheck = 0700; 97 | ORGANIZATIONNAME = "Michael Chen"; 98 | TargetAttributes = { 99 | 8C09F9F01BDE2E5C00DD457E = { 100 | CreatedOnToolsVersion = 7.0; 101 | LastSwiftMigration = 0800; 102 | }; 103 | }; 104 | }; 105 | buildConfigurationList = 8C09F9EC1BDE2E5C00DD457E /* Build configuration list for PBXProject "LayerX" */; 106 | compatibilityVersion = "Xcode 3.2"; 107 | developmentRegion = English; 108 | hasScannedForEncodings = 0; 109 | knownRegions = ( 110 | English, 111 | en, 112 | Base, 113 | ); 114 | mainGroup = 8C09F9E81BDE2E5C00DD457E; 115 | productRefGroup = 8C09F9F21BDE2E5C00DD457E /* Products */; 116 | projectDirPath = ""; 117 | projectRoot = ""; 118 | targets = ( 119 | 8C09F9F01BDE2E5C00DD457E /* LayerX */, 120 | ); 121 | }; 122 | /* End PBXProject section */ 123 | 124 | /* Begin PBXResourcesBuildPhase section */ 125 | 8C09F9EF1BDE2E5C00DD457E /* Resources */ = { 126 | isa = PBXResourcesBuildPhase; 127 | buildActionMask = 2147483647; 128 | files = ( 129 | 8C09F9F91BDE2E5C00DD457E /* Assets.xcassets in Resources */, 130 | 8C09F9FC1BDE2E5C00DD457E /* Main.storyboard in Resources */, 131 | ); 132 | runOnlyForDeploymentPostprocessing = 0; 133 | }; 134 | /* End PBXResourcesBuildPhase section */ 135 | 136 | /* Begin PBXSourcesBuildPhase section */ 137 | 8C09F9ED1BDE2E5C00DD457E /* Sources */ = { 138 | isa = PBXSourcesBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | 8C09F9F71BDE2E5C00DD457E /* ViewController.swift in Sources */, 142 | 8C09FA0C1BDF5F7300DD457E /* MCWindow.swift in Sources */, 143 | 8C09F9F51BDE2E5C00DD457E /* AppDelegate.swift in Sources */, 144 | 8C09FA041BDE324500DD457E /* MCDragAndDropImageView.swift in Sources */, 145 | ); 146 | runOnlyForDeploymentPostprocessing = 0; 147 | }; 148 | /* End PBXSourcesBuildPhase section */ 149 | 150 | /* Begin PBXVariantGroup section */ 151 | 8C09F9FA1BDE2E5C00DD457E /* Main.storyboard */ = { 152 | isa = PBXVariantGroup; 153 | children = ( 154 | 8C09F9FB1BDE2E5C00DD457E /* Base */, 155 | ); 156 | name = Main.storyboard; 157 | sourceTree = ""; 158 | }; 159 | /* End PBXVariantGroup section */ 160 | 161 | /* Begin XCBuildConfiguration section */ 162 | 8C09F9FE1BDE2E5C00DD457E /* Debug */ = { 163 | isa = XCBuildConfiguration; 164 | buildSettings = { 165 | ALWAYS_SEARCH_USER_PATHS = NO; 166 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 167 | CLANG_CXX_LIBRARY = "libc++"; 168 | CLANG_ENABLE_MODULES = YES; 169 | CLANG_ENABLE_OBJC_ARC = YES; 170 | CLANG_WARN_BOOL_CONVERSION = YES; 171 | CLANG_WARN_CONSTANT_CONVERSION = YES; 172 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 173 | CLANG_WARN_EMPTY_BODY = YES; 174 | CLANG_WARN_ENUM_CONVERSION = YES; 175 | CLANG_WARN_INT_CONVERSION = YES; 176 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 177 | CLANG_WARN_UNREACHABLE_CODE = YES; 178 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 179 | CODE_SIGN_IDENTITY = "-"; 180 | COPY_PHASE_STRIP = NO; 181 | DEBUG_INFORMATION_FORMAT = dwarf; 182 | ENABLE_STRICT_OBJC_MSGSEND = YES; 183 | ENABLE_TESTABILITY = YES; 184 | GCC_C_LANGUAGE_STANDARD = gnu99; 185 | GCC_DYNAMIC_NO_PIC = NO; 186 | GCC_NO_COMMON_BLOCKS = YES; 187 | GCC_OPTIMIZATION_LEVEL = 0; 188 | GCC_PREPROCESSOR_DEFINITIONS = ( 189 | "DEBUG=1", 190 | "$(inherited)", 191 | ); 192 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 193 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 194 | GCC_WARN_UNDECLARED_SELECTOR = YES; 195 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 196 | GCC_WARN_UNUSED_FUNCTION = YES; 197 | GCC_WARN_UNUSED_VARIABLE = YES; 198 | MACOSX_DEPLOYMENT_TARGET = 10.13; 199 | MTL_ENABLE_DEBUG_INFO = YES; 200 | ONLY_ACTIVE_ARCH = YES; 201 | SDKROOT = macosx; 202 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 203 | SWIFT_VERSION = 5.0; 204 | }; 205 | name = Debug; 206 | }; 207 | 8C09F9FF1BDE2E5C00DD457E /* Release */ = { 208 | isa = XCBuildConfiguration; 209 | buildSettings = { 210 | ALWAYS_SEARCH_USER_PATHS = NO; 211 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 212 | CLANG_CXX_LIBRARY = "libc++"; 213 | CLANG_ENABLE_MODULES = YES; 214 | CLANG_ENABLE_OBJC_ARC = YES; 215 | CLANG_WARN_BOOL_CONVERSION = YES; 216 | CLANG_WARN_CONSTANT_CONVERSION = YES; 217 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 218 | CLANG_WARN_EMPTY_BODY = YES; 219 | CLANG_WARN_ENUM_CONVERSION = YES; 220 | CLANG_WARN_INT_CONVERSION = YES; 221 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 222 | CLANG_WARN_UNREACHABLE_CODE = YES; 223 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 224 | CODE_SIGN_IDENTITY = "-"; 225 | COPY_PHASE_STRIP = NO; 226 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 227 | ENABLE_NS_ASSERTIONS = NO; 228 | ENABLE_STRICT_OBJC_MSGSEND = YES; 229 | GCC_C_LANGUAGE_STANDARD = gnu99; 230 | GCC_NO_COMMON_BLOCKS = YES; 231 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 232 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 233 | GCC_WARN_UNDECLARED_SELECTOR = YES; 234 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 235 | GCC_WARN_UNUSED_FUNCTION = YES; 236 | GCC_WARN_UNUSED_VARIABLE = YES; 237 | MACOSX_DEPLOYMENT_TARGET = 10.13; 238 | MTL_ENABLE_DEBUG_INFO = NO; 239 | SDKROOT = macosx; 240 | SWIFT_VERSION = 5.0; 241 | }; 242 | name = Release; 243 | }; 244 | 8C09FA011BDE2E5C00DD457E /* Debug */ = { 245 | isa = XCBuildConfiguration; 246 | buildSettings = { 247 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 248 | COMBINE_HIDPI_IMAGES = YES; 249 | INFOPLIST_FILE = LayerX/Info.plist; 250 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 251 | PRODUCT_BUNDLE_IDENTIFIER = io.michaelchen.LayerX; 252 | PRODUCT_NAME = "$(TARGET_NAME)"; 253 | }; 254 | name = Debug; 255 | }; 256 | 8C09FA021BDE2E5C00DD457E /* Release */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 260 | COMBINE_HIDPI_IMAGES = YES; 261 | INFOPLIST_FILE = LayerX/Info.plist; 262 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 263 | PRODUCT_BUNDLE_IDENTIFIER = io.michaelchen.LayerX; 264 | PRODUCT_NAME = "$(TARGET_NAME)"; 265 | }; 266 | name = Release; 267 | }; 268 | /* End XCBuildConfiguration section */ 269 | 270 | /* Begin XCConfigurationList section */ 271 | 8C09F9EC1BDE2E5C00DD457E /* Build configuration list for PBXProject "LayerX" */ = { 272 | isa = XCConfigurationList; 273 | buildConfigurations = ( 274 | 8C09F9FE1BDE2E5C00DD457E /* Debug */, 275 | 8C09F9FF1BDE2E5C00DD457E /* Release */, 276 | ); 277 | defaultConfigurationIsVisible = 0; 278 | defaultConfigurationName = Release; 279 | }; 280 | 8C09FA001BDE2E5C00DD457E /* Build configuration list for PBXNativeTarget "LayerX" */ = { 281 | isa = XCConfigurationList; 282 | buildConfigurations = ( 283 | 8C09FA011BDE2E5C00DD457E /* Debug */, 284 | 8C09FA021BDE2E5C00DD457E /* Release */, 285 | ); 286 | defaultConfigurationIsVisible = 0; 287 | defaultConfigurationName = Release; 288 | }; 289 | /* End XCConfigurationList section */ 290 | }; 291 | rootObject = 8C09F9E91BDE2E5C00DD457E /* Project object */; 292 | } 293 | -------------------------------------------------------------------------------- /LayerX/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | --------------------------------------------------------------------------------