├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── RTImageAssets.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ └── RTImageAssets.xcscheme └── xcuserdata │ └── ricky.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── RTImageAssets ├── Appicon.png ├── Appicon@2x.png ├── Appicon@2x~.png ├── Appicon~.png ├── IAAppiconWindow.h ├── IAAppiconWindow.m ├── IAAppiconWindow.xib ├── IAGenerateOperation.h ├── IAGenerateOperation.m ├── IAImageSet.h ├── IAImageSet.m ├── IASettingsWindow.h ├── IASettingsWindow.m ├── IASettingsWindow.xib ├── IAWorkspace.h ├── IAWorkspace.m ├── Info.plist ├── RTImageAssets.h ├── RTImageAssets.m ├── XcodeIDE.h ├── en.lproj │ └── Localizable.strings └── zh-Hans.lproj │ └── Localizable.strings └── ScreenCap ├── iconset-gen.gif ├── p.png └── usage.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | # Pods/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | osx_image: xcode10 3 | language: objective-c 4 | script: xcodebuild 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.6.0 2 | - add App Iconset generation 3 | 4 | # 0.5.0 5 | - Fix #21 6 | - init set version number to `0.5.0` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Ricky Tan 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | RTImageAssets 3 | ============= 4 | [![Build Status](https://travis-ci.org/rickytan/RTImageAssets.svg)](https://travis-ci.org/rickytan/RTImageAssets) 5 | [![License](http://img.shields.io/badge/license-MIT-yellowgreen.svg)](./LICENSE) 6 | [![GitHub stars](https://img.shields.io/github/stars/rickytan/RTImageAssets.svg?style=social&label=Star)](https://github.com/rickytan/RTImageAssets) 7 | 8 | [![Donate](https://www.paypalobjects.com/webstatic/en_US/btn/btn_donate_pp_142x27.png "Donate me a cup of coffee")](http://rickytan.cn/donate/ "Donate me a cup of coffee") 9 | 10 | 11 | 12 | It seems ~~**Xcode 8**~~ not supported, here is a **[Solution](https://github.com/fpg1503/MakeXcodeGr8Again)**, and [Here](https://github.com/inket/update_xcode_plugins), use at your own risk! 13 | 14 | Brief 15 | --- 16 | A Xcode plugin to automatically generate @2x, @1x image from @3x image for you, or upscale to @3x from @2x. As easy as you press `Ctrl+Shift+A`, and **DONE**! 17 | 18 | 19 | _NOTICE_ 20 | --- 21 | You have to put your `.xcassets` in the folder the same name with the project, which means, if your project is `abc.xcodeproj`, and the assets must be in the `abc` folder 22 | 23 | 24 | Features 25 | --- 26 | - Only generate those missing assets, if you have already set your own @2x image, it does nothing 27 | - Automaticaly rename those image files under `N.imageset` to `N.png`, `N@2x.png`, `N@3x.png` 28 | - Easy to use, don't need ask for your designer's help any more! 29 | 30 | ### _New Feature!!!_ 31 | - **Auto generate all the App icons needed**。press `Ctrl+Shift+Option+A` to open **App Icon** window, choose the `xcasset`, then the `appiconset` you want to use, drag & drop **1024x1024** big icon, and click **generate**, DONE! 32 | 33 | ![Usage](./ScreenCap/usage.gif) 34 | 35 | ![Iconset](./ScreenCap/iconset-gen.gif) 36 | 37 | 38 | Settings 39 | --- 40 | ![Setting](./ScreenCap/p.png) 41 | 42 | 43 | Install 44 | --- 45 | 46 | ### From Source 47 | Clone this Repo, build it in `Xcode`, and restart your `Xcode`. 48 | 49 | ### From Plugin Manager 50 | Install [Package Manager](http://alcatraz.io/) for **Xcode**, search: `RTImageAssets`. 51 | 52 | 53 | Issues 54 | --- 55 | This plugin is **NOT** fully tested, if you have any problems, please let me know: 56 | 57 | 58 | Alternatives 59 | --- 60 | + [Prepo](http://wearemothership.com/work/prepo/) 61 | + [Asset Catalog Creator](https://itunes.apple.com/us/app/asset-catalog-creator-app/id809625456?mt=12) 62 | 63 | 64 | Maybe Interested 65 | --- 66 | + [sketch-measure-downloader](https://github.com/rickytan/sketch-measure-downloader): A Chrome extension to download sketch-measure exported asserts as a zip file 67 | + [XPaste](https://github.com/rickytan/XPaste): A Xcode extension to power up your code paste ability 68 | + [RTComponentTableView](https://github.com/rickytan/RTComponentTableView): A Componentized UITableView implementation 69 | + [RTPlaceholderTextView](https://github.com/rickytan/RTPlaceholderTextView): A UITextView with placeholder support 70 | + [RTIconButton](https://github.com/rickytan/RTIconButton): A drop-in replacement for UIButton 71 | + [RTViewAttachment](https://github.com/rickytan/RTViewAttachment): Attach a custom UIView to text view, based on `NSTextAttachment` 72 | + [RTRootNavigationController](https://github.com/rickytan/RTRootNavigationController): Implicitly make every view controller has its own navigation bar 73 | + [RTPagedCollectionViewLayout](https://github.com/rickytan/RTPagedCollectionViewLayout): A paged collection view layout 74 | + [RTTinyPNGWebAPIPlugin](https://github.com/rickytan/RTTinyPNGWebAPIPlugin): A Xcode plugin which calls api to minimize image assets 75 | + [RTDraggableBadge](https://github.com/rickytan/RTDraggableBadge): A drag to clear badge view 76 | 77 | 78 | License 79 | --- 80 | **MIT** 81 | 82 | 83 | 简介 84 | --- 85 | 本项目是一个 **Xcode** 插件,用来生成 @3x 的图片资源对应的 @2x 和 @1x 版本,只要拖拽高清图到 @3x 的位置上,然后按 `Ctrl+Shift+A` 即可自动生成两张低清的补全空位。当然你也可以从 @2x 的图生成 @3x 版本,如果你对图片质量要求不高的话。 86 | 87 | 88 | _注意_ 89 | --- 90 | 请将您的 .xcassets 放到与项目名同名的目录下,如 abcd.xcodeproj ,那么放到 abcd 目录下! 91 | 92 | 93 | 特性 94 | --- 95 | - 只会填补空位,如果你已经设置好了自己的 @2x 图,则不会生成; 96 | - 自动重命名,保持项目干净(把 N.imageset 下的图片名字改为 `N.png` `N@2x.png` `N@3x.png` 等); 97 | - 使用简单,不用再麻烦美术同学缩放了; 98 | 99 | ### _新特性!!!_ 100 | - **自动生成所有所需的应用程序图标**。按 `Ctrl+Shift+Option+A` 打开 **App Icon** 窗口,选择 `xcasset`,再选择 `appiconset`,拖拽 **1024x1024** 的大图标到窗口中,点击 **generate**,完成! 101 | 102 | ***注意:***本插件从 @3x 到 @2x 的缩放保证图片在屏幕上显示的物理尺寸一样,而不是与屏幕比例一样,缩放系数是 **1.5**,而不是 `1242 / 640 = 1.94`。 103 | 104 | ![Usage](./ScreenCap/usage.gif) 105 | 106 | ![Iconset](./ScreenCap/iconset-gen.gif) 107 | 108 | 109 | 设置 110 | --- 111 | ![Setting](./ScreenCap/p.png) 112 | 113 | 114 | 安装 115 | --- 116 | 117 | ### 编译安装 118 | 下载本项目,在 `Xcode` 中打开,构建、重启即可。 119 | 120 | ### `Plugin Manager` 安装 121 | 安装 [插件管理](http://alcatraz.io/) 插件,搜索:`RTImageAssets`。 122 | 123 | 124 | 问题 125 | --- 126 | 此插件还没有经过严格测试,如果你有什么问题,请提出: 127 | 128 | 129 | 其它选择 130 | --- 131 | + [Prepo](http://wearemothership.com/work/prepo/) 132 | + [Asset Catalog Creator](https://itunes.apple.com/us/app/asset-catalog-creator-app/id809625456?mt=12) 133 | 134 | 135 | 可能感兴趣 136 | --- 137 | + [sketch-measure-downloader](https://github.com/rickytan/sketch-measure-downloader): A Chrome extension to download sketch-measure exported asserts as a zip file 138 | + [XPaste](https://github.com/rickytan/XPaste): A Xcode extension to power up your code paste ability 139 | + [RTComponentTableView](https://github.com/rickytan/RTComponentTableView): A Componentized UITableView implementation 140 | + [RTPlaceholderTextView](https://github.com/rickytan/RTPlaceholderTextView): A UITextView with placeholder support 141 | + [RTIconButton](https://github.com/rickytan/RTIconButton): A drop-in replacement for UIButton 142 | + [RTViewAttachment](https://github.com/rickytan/RTViewAttachment): Attach a custom UIView to text view, based on `NSTextAttachment` 143 | + [RTRootNavigationController](https://github.com/rickytan/RTRootNavigationController): Implicitly make every view controller has its own navigation bar 144 | + [RTPagedCollectionViewLayout](https://github.com/rickytan/RTPagedCollectionViewLayout): A paged collection view layout 145 | + [RTTinyPNGWebAPIPlugin](https://github.com/rickytan/RTTinyPNGWebAPIPlugin): A Xcode plugin which calls api to minimize image assets 146 | + [RTDraggableBadge](https://github.com/rickytan/RTDraggableBadge): A drag to clear badge view 147 | 148 | 149 | 协议 150 | --- 151 | **MIT** 152 | -------------------------------------------------------------------------------- /RTImageAssets.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9C1218FF1A385DA6002EA7C2 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C1218FE1A385DA6002EA7C2 /* AppKit.framework */; }; 11 | 9C1219011A385DA6002EA7C2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C1219001A385DA6002EA7C2 /* Foundation.framework */; }; 12 | 9C1219061A385DA6002EA7C2 /* RTImageAssets.xcscheme in Resources */ = {isa = PBXBuildFile; fileRef = 9C1219051A385DA6002EA7C2 /* RTImageAssets.xcscheme */; }; 13 | 9C1219091A385DA6002EA7C2 /* RTImageAssets.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C1219081A385DA6002EA7C2 /* RTImageAssets.m */; }; 14 | 9C1219161A38657A002EA7C2 /* IASettingsWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C1219141A38657A002EA7C2 /* IASettingsWindow.m */; }; 15 | 9C1219171A38657A002EA7C2 /* IASettingsWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9C1219151A38657A002EA7C2 /* IASettingsWindow.xib */; }; 16 | 9C12191A1A386854002EA7C2 /* IAWorkspace.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C1219191A386854002EA7C2 /* IAWorkspace.m */; }; 17 | 9C12191D1A3869D3002EA7C2 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9C12191F1A3869D3002EA7C2 /* Localizable.strings */; }; 18 | 9CC798221A42116500353912 /* IAAppiconWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CC798201A42116500353912 /* IAAppiconWindow.m */; }; 19 | 9CC798231A42116500353912 /* IAAppiconWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9CC798211A42116500353912 /* IAAppiconWindow.xib */; }; 20 | 9CC798281A421C1F00353912 /* Appicon.png in Resources */ = {isa = PBXBuildFile; fileRef = 9CC798261A421C1F00353912 /* Appicon.png */; }; 21 | 9CC798291A421C1F00353912 /* Appicon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9CC798271A421C1F00353912 /* Appicon@2x.png */; }; 22 | 9CD435E91A387EC5002C8A99 /* IAImageSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CD435E81A387EC5002C8A99 /* IAImageSet.m */; }; 23 | 9CD435EC1A388F83002C8A99 /* IAGenerateOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CD435EB1A388F83002C8A99 /* IAGenerateOperation.m */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 9C1218FB1A385DA6002EA7C2 /* RTImageAssets.xcplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RTImageAssets.xcplugin; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 9C1218FE1A385DA6002EA7C2 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 29 | 9C1219001A385DA6002EA7C2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 30 | 9C1219041A385DA6002EA7C2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | 9C1219051A385DA6002EA7C2 /* RTImageAssets.xcscheme */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = RTImageAssets.xcscheme; path = RTImageAssets.xcodeproj/xcshareddata/xcschemes/RTImageAssets.xcscheme; sourceTree = SOURCE_ROOT; }; 32 | 9C1219071A385DA6002EA7C2 /* RTImageAssets.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RTImageAssets.h; sourceTree = ""; }; 33 | 9C1219081A385DA6002EA7C2 /* RTImageAssets.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RTImageAssets.m; sourceTree = ""; }; 34 | 9C1219131A38657A002EA7C2 /* IASettingsWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASettingsWindow.h; sourceTree = ""; }; 35 | 9C1219141A38657A002EA7C2 /* IASettingsWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASettingsWindow.m; sourceTree = ""; }; 36 | 9C1219151A38657A002EA7C2 /* IASettingsWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = IASettingsWindow.xib; sourceTree = ""; }; 37 | 9C1219181A386854002EA7C2 /* IAWorkspace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IAWorkspace.h; sourceTree = ""; }; 38 | 9C1219191A386854002EA7C2 /* IAWorkspace.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IAWorkspace.m; sourceTree = ""; }; 39 | 9C12191E1A3869D3002EA7C2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 40 | 9C1219201A386A01002EA7C2 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; 41 | 9CC7981F1A42116500353912 /* IAAppiconWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IAAppiconWindow.h; sourceTree = ""; }; 42 | 9CC798201A42116500353912 /* IAAppiconWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IAAppiconWindow.m; sourceTree = ""; }; 43 | 9CC798211A42116500353912 /* IAAppiconWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = IAAppiconWindow.xib; sourceTree = ""; }; 44 | 9CC798261A421C1F00353912 /* Appicon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Appicon.png; sourceTree = ""; }; 45 | 9CC798271A421C1F00353912 /* Appicon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Appicon@2x.png"; sourceTree = ""; }; 46 | 9CD435E71A387EC5002C8A99 /* IAImageSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IAImageSet.h; sourceTree = ""; }; 47 | 9CD435E81A387EC5002C8A99 /* IAImageSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IAImageSet.m; sourceTree = ""; }; 48 | 9CD435EA1A388F83002C8A99 /* IAGenerateOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IAGenerateOperation.h; sourceTree = ""; }; 49 | 9CD435EB1A388F83002C8A99 /* IAGenerateOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IAGenerateOperation.m; sourceTree = ""; }; 50 | 9CF448BC1A386E9000490BED /* XcodeIDE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XcodeIDE.h; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 9C1218F91A385DA6002EA7C2 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | 9C1218FF1A385DA6002EA7C2 /* AppKit.framework in Frameworks */, 59 | 9C1219011A385DA6002EA7C2 /* Foundation.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 9C1218F21A385DA6002EA7C2 = { 67 | isa = PBXGroup; 68 | children = ( 69 | 9C1219021A385DA6002EA7C2 /* RTImageAssets */, 70 | 9C1218FD1A385DA6002EA7C2 /* Frameworks */, 71 | 9C1218FC1A385DA6002EA7C2 /* Products */, 72 | ); 73 | sourceTree = ""; 74 | }; 75 | 9C1218FC1A385DA6002EA7C2 /* Products */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 9C1218FB1A385DA6002EA7C2 /* RTImageAssets.xcplugin */, 79 | ); 80 | name = Products; 81 | sourceTree = ""; 82 | }; 83 | 9C1218FD1A385DA6002EA7C2 /* Frameworks */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 9C1218FE1A385DA6002EA7C2 /* AppKit.framework */, 87 | 9C1219001A385DA6002EA7C2 /* Foundation.framework */, 88 | ); 89 | name = Frameworks; 90 | sourceTree = ""; 91 | }; 92 | 9C1219021A385DA6002EA7C2 /* RTImageAssets */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 9CC798261A421C1F00353912 /* Appicon.png */, 96 | 9CC798271A421C1F00353912 /* Appicon@2x.png */, 97 | 9CD435EA1A388F83002C8A99 /* IAGenerateOperation.h */, 98 | 9CD435EB1A388F83002C8A99 /* IAGenerateOperation.m */, 99 | 9CD435E71A387EC5002C8A99 /* IAImageSet.h */, 100 | 9CD435E81A387EC5002C8A99 /* IAImageSet.m */, 101 | 9C1219131A38657A002EA7C2 /* IASettingsWindow.h */, 102 | 9C1219141A38657A002EA7C2 /* IASettingsWindow.m */, 103 | 9C1219151A38657A002EA7C2 /* IASettingsWindow.xib */, 104 | 9C1219181A386854002EA7C2 /* IAWorkspace.h */, 105 | 9C1219191A386854002EA7C2 /* IAWorkspace.m */, 106 | 9C1219071A385DA6002EA7C2 /* RTImageAssets.h */, 107 | 9C1219081A385DA6002EA7C2 /* RTImageAssets.m */, 108 | 9C1219031A385DA6002EA7C2 /* Supporting Files */, 109 | 9CF448BC1A386E9000490BED /* XcodeIDE.h */, 110 | 9CC7981F1A42116500353912 /* IAAppiconWindow.h */, 111 | 9CC798201A42116500353912 /* IAAppiconWindow.m */, 112 | 9CC798211A42116500353912 /* IAAppiconWindow.xib */, 113 | ); 114 | path = RTImageAssets; 115 | sourceTree = ""; 116 | }; 117 | 9C1219031A385DA6002EA7C2 /* Supporting Files */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 9C1219041A385DA6002EA7C2 /* Info.plist */, 121 | 9C1219051A385DA6002EA7C2 /* RTImageAssets.xcscheme */, 122 | 9C12191F1A3869D3002EA7C2 /* Localizable.strings */, 123 | ); 124 | name = "Supporting Files"; 125 | sourceTree = ""; 126 | }; 127 | /* End PBXGroup section */ 128 | 129 | /* Begin PBXNativeTarget section */ 130 | 9C1218FA1A385DA6002EA7C2 /* RTImageAssets */ = { 131 | isa = PBXNativeTarget; 132 | buildConfigurationList = 9C12190C1A385DA6002EA7C2 /* Build configuration list for PBXNativeTarget "RTImageAssets" */; 133 | buildPhases = ( 134 | 9C1218F71A385DA6002EA7C2 /* Sources */, 135 | 9C1218F81A385DA6002EA7C2 /* Resources */, 136 | 9C1218F91A385DA6002EA7C2 /* Frameworks */, 137 | ); 138 | buildRules = ( 139 | ); 140 | dependencies = ( 141 | ); 142 | name = RTImageAssets; 143 | productName = RTImageAssets; 144 | productReference = 9C1218FB1A385DA6002EA7C2 /* RTImageAssets.xcplugin */; 145 | productType = "com.apple.product-type.bundle"; 146 | }; 147 | /* End PBXNativeTarget section */ 148 | 149 | /* Begin PBXProject section */ 150 | 9C1218F31A385DA6002EA7C2 /* Project object */ = { 151 | isa = PBXProject; 152 | attributes = { 153 | LastUpgradeCheck = 0700; 154 | ORGANIZATIONNAME = rickytan; 155 | TargetAttributes = { 156 | 9C1218FA1A385DA6002EA7C2 = { 157 | CreatedOnToolsVersion = 6.1.1; 158 | }; 159 | }; 160 | }; 161 | buildConfigurationList = 9C1218F61A385DA6002EA7C2 /* Build configuration list for PBXProject "RTImageAssets" */; 162 | compatibilityVersion = "Xcode 3.2"; 163 | developmentRegion = English; 164 | hasScannedForEncodings = 0; 165 | knownRegions = ( 166 | en, 167 | "zh-Hans", 168 | ); 169 | mainGroup = 9C1218F21A385DA6002EA7C2; 170 | productRefGroup = 9C1218FC1A385DA6002EA7C2 /* Products */; 171 | projectDirPath = ""; 172 | projectRoot = ""; 173 | targets = ( 174 | 9C1218FA1A385DA6002EA7C2 /* RTImageAssets */, 175 | ); 176 | }; 177 | /* End PBXProject section */ 178 | 179 | /* Begin PBXResourcesBuildPhase section */ 180 | 9C1218F81A385DA6002EA7C2 /* Resources */ = { 181 | isa = PBXResourcesBuildPhase; 182 | buildActionMask = 2147483647; 183 | files = ( 184 | 9CC798281A421C1F00353912 /* Appicon.png in Resources */, 185 | 9CC798231A42116500353912 /* IAAppiconWindow.xib in Resources */, 186 | 9C1219061A385DA6002EA7C2 /* RTImageAssets.xcscheme in Resources */, 187 | 9C12191D1A3869D3002EA7C2 /* Localizable.strings in Resources */, 188 | 9C1219171A38657A002EA7C2 /* IASettingsWindow.xib in Resources */, 189 | 9CC798291A421C1F00353912 /* Appicon@2x.png in Resources */, 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | }; 193 | /* End PBXResourcesBuildPhase section */ 194 | 195 | /* Begin PBXSourcesBuildPhase section */ 196 | 9C1218F71A385DA6002EA7C2 /* Sources */ = { 197 | isa = PBXSourcesBuildPhase; 198 | buildActionMask = 2147483647; 199 | files = ( 200 | 9CC798221A42116500353912 /* IAAppiconWindow.m in Sources */, 201 | 9CD435E91A387EC5002C8A99 /* IAImageSet.m in Sources */, 202 | 9CD435EC1A388F83002C8A99 /* IAGenerateOperation.m in Sources */, 203 | 9C12191A1A386854002EA7C2 /* IAWorkspace.m in Sources */, 204 | 9C1219161A38657A002EA7C2 /* IASettingsWindow.m in Sources */, 205 | 9C1219091A385DA6002EA7C2 /* RTImageAssets.m in Sources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXSourcesBuildPhase section */ 210 | 211 | /* Begin PBXVariantGroup section */ 212 | 9C12191F1A3869D3002EA7C2 /* Localizable.strings */ = { 213 | isa = PBXVariantGroup; 214 | children = ( 215 | 9C12191E1A3869D3002EA7C2 /* en */, 216 | 9C1219201A386A01002EA7C2 /* zh-Hans */, 217 | ); 218 | name = Localizable.strings; 219 | sourceTree = ""; 220 | }; 221 | /* End PBXVariantGroup section */ 222 | 223 | /* Begin XCBuildConfiguration section */ 224 | 9C12190A1A385DA6002EA7C2 /* Debug */ = { 225 | isa = XCBuildConfiguration; 226 | buildSettings = { 227 | ALWAYS_SEARCH_USER_PATHS = NO; 228 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 229 | CLANG_CXX_LIBRARY = "libc++"; 230 | CLANG_ENABLE_MODULES = YES; 231 | CLANG_ENABLE_OBJC_ARC = YES; 232 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 233 | CLANG_WARN_BOOL_CONVERSION = YES; 234 | CLANG_WARN_COMMA = YES; 235 | CLANG_WARN_CONSTANT_CONVERSION = YES; 236 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 237 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 238 | CLANG_WARN_EMPTY_BODY = YES; 239 | CLANG_WARN_ENUM_CONVERSION = YES; 240 | CLANG_WARN_INFINITE_RECURSION = YES; 241 | CLANG_WARN_INT_CONVERSION = YES; 242 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 243 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 244 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 245 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 246 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 247 | CLANG_WARN_STRICT_PROTOTYPES = YES; 248 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 249 | CLANG_WARN_UNREACHABLE_CODE = YES; 250 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 251 | COPY_PHASE_STRIP = NO; 252 | ENABLE_STRICT_OBJC_MSGSEND = YES; 253 | ENABLE_TESTABILITY = YES; 254 | GCC_C_LANGUAGE_STANDARD = gnu99; 255 | GCC_DYNAMIC_NO_PIC = NO; 256 | GCC_NO_COMMON_BLOCKS = YES; 257 | GCC_OPTIMIZATION_LEVEL = 0; 258 | GCC_PREPROCESSOR_DEFINITIONS = ( 259 | "DEBUG=1", 260 | "$(inherited)", 261 | ); 262 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 263 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 264 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 265 | GCC_WARN_UNDECLARED_SELECTOR = YES; 266 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 267 | GCC_WARN_UNUSED_FUNCTION = YES; 268 | GCC_WARN_UNUSED_VARIABLE = YES; 269 | MTL_ENABLE_DEBUG_INFO = YES; 270 | ONLY_ACTIVE_ARCH = YES; 271 | }; 272 | name = Debug; 273 | }; 274 | 9C12190B1A385DA6002EA7C2 /* Release */ = { 275 | isa = XCBuildConfiguration; 276 | buildSettings = { 277 | ALWAYS_SEARCH_USER_PATHS = NO; 278 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 279 | CLANG_CXX_LIBRARY = "libc++"; 280 | CLANG_ENABLE_MODULES = YES; 281 | CLANG_ENABLE_OBJC_ARC = YES; 282 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 283 | CLANG_WARN_BOOL_CONVERSION = YES; 284 | CLANG_WARN_COMMA = YES; 285 | CLANG_WARN_CONSTANT_CONVERSION = YES; 286 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 287 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 288 | CLANG_WARN_EMPTY_BODY = YES; 289 | CLANG_WARN_ENUM_CONVERSION = YES; 290 | CLANG_WARN_INFINITE_RECURSION = YES; 291 | CLANG_WARN_INT_CONVERSION = YES; 292 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 293 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 294 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 295 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 296 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 297 | CLANG_WARN_STRICT_PROTOTYPES = YES; 298 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 299 | CLANG_WARN_UNREACHABLE_CODE = YES; 300 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 301 | COPY_PHASE_STRIP = YES; 302 | ENABLE_NS_ASSERTIONS = NO; 303 | ENABLE_STRICT_OBJC_MSGSEND = YES; 304 | GCC_C_LANGUAGE_STANDARD = gnu99; 305 | GCC_NO_COMMON_BLOCKS = YES; 306 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 307 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 308 | GCC_WARN_UNDECLARED_SELECTOR = YES; 309 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 310 | GCC_WARN_UNUSED_FUNCTION = YES; 311 | GCC_WARN_UNUSED_VARIABLE = YES; 312 | MTL_ENABLE_DEBUG_INFO = NO; 313 | }; 314 | name = Release; 315 | }; 316 | 9C12190D1A385DA6002EA7C2 /* Debug */ = { 317 | isa = XCBuildConfiguration; 318 | buildSettings = { 319 | COMBINE_HIDPI_IMAGES = YES; 320 | DEPLOYMENT_LOCATION = YES; 321 | DSTROOT = "$(HOME)"; 322 | INFOPLIST_FILE = RTImageAssets/Info.plist; 323 | INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins"; 324 | MACOSX_DEPLOYMENT_TARGET = 10.9; 325 | PRODUCT_BUNDLE_IDENTIFIER = "cn.rickytan.$(PRODUCT_NAME:rfc1034identifier)"; 326 | PRODUCT_NAME = "$(TARGET_NAME)"; 327 | SDKROOT = macosx; 328 | WRAPPER_EXTENSION = xcplugin; 329 | }; 330 | name = Debug; 331 | }; 332 | 9C12190E1A385DA6002EA7C2 /* Release */ = { 333 | isa = XCBuildConfiguration; 334 | buildSettings = { 335 | COMBINE_HIDPI_IMAGES = YES; 336 | DEPLOYMENT_LOCATION = YES; 337 | DSTROOT = "$(HOME)"; 338 | INFOPLIST_FILE = RTImageAssets/Info.plist; 339 | INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins"; 340 | MACOSX_DEPLOYMENT_TARGET = 10.9; 341 | PRODUCT_BUNDLE_IDENTIFIER = "cn.rickytan.$(PRODUCT_NAME:rfc1034identifier)"; 342 | PRODUCT_NAME = "$(TARGET_NAME)"; 343 | SDKROOT = macosx; 344 | WRAPPER_EXTENSION = xcplugin; 345 | }; 346 | name = Release; 347 | }; 348 | /* End XCBuildConfiguration section */ 349 | 350 | /* Begin XCConfigurationList section */ 351 | 9C1218F61A385DA6002EA7C2 /* Build configuration list for PBXProject "RTImageAssets" */ = { 352 | isa = XCConfigurationList; 353 | buildConfigurations = ( 354 | 9C12190A1A385DA6002EA7C2 /* Debug */, 355 | 9C12190B1A385DA6002EA7C2 /* Release */, 356 | ); 357 | defaultConfigurationIsVisible = 0; 358 | defaultConfigurationName = Release; 359 | }; 360 | 9C12190C1A385DA6002EA7C2 /* Build configuration list for PBXNativeTarget "RTImageAssets" */ = { 361 | isa = XCConfigurationList; 362 | buildConfigurations = ( 363 | 9C12190D1A385DA6002EA7C2 /* Debug */, 364 | 9C12190E1A385DA6002EA7C2 /* Release */, 365 | ); 366 | defaultConfigurationIsVisible = 0; 367 | defaultConfigurationName = Release; 368 | }; 369 | /* End XCConfigurationList section */ 370 | }; 371 | rootObject = 9C1218F31A385DA6002EA7C2 /* Project object */; 372 | } 373 | -------------------------------------------------------------------------------- /RTImageAssets.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RTImageAssets.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RTImageAssets.xcodeproj/xcshareddata/xcschemes/RTImageAssets.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 48 | 49 | 61 | 65 | 66 | 67 | 73 | 74 | 75 | 76 | 77 | 78 | 84 | 85 | 87 | 88 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /RTImageAssets.xcodeproj/xcuserdata/ricky.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /RTImageAssets.xcodeproj/xcuserdata/ricky.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SuppressBuildableAutocreation 6 | 7 | 9C1218FA1A385DA6002EA7C2 8 | 9 | primary 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /RTImageAssets/Appicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickytan/RTImageAssets/4520ac67fd688d3641da0b0b864afbff3ad28c1b/RTImageAssets/Appicon.png -------------------------------------------------------------------------------- /RTImageAssets/Appicon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickytan/RTImageAssets/4520ac67fd688d3641da0b0b864afbff3ad28c1b/RTImageAssets/Appicon@2x.png -------------------------------------------------------------------------------- /RTImageAssets/Appicon@2x~.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickytan/RTImageAssets/4520ac67fd688d3641da0b0b864afbff3ad28c1b/RTImageAssets/Appicon@2x~.png -------------------------------------------------------------------------------- /RTImageAssets/Appicon~.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickytan/RTImageAssets/4520ac67fd688d3641da0b0b864afbff3ad28c1b/RTImageAssets/Appicon~.png -------------------------------------------------------------------------------- /RTImageAssets/IAAppiconWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // IAAppiconWindow.h 3 | // RTImageAssets 4 | // 5 | // Created by ricky on 14-12-18. 6 | // Copyright (c) 2014年 rickytan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class IAAppiconWindow; 12 | @class IAIconSet; 13 | 14 | @protocol IAAppiconWindowDelegate 15 | @optional 16 | - (void)appIconWindow:(IAAppiconWindow *)window 17 | generateIconsWithImage:(NSImage *)image; 18 | 19 | @end 20 | 21 | typedef NS_ENUM(NSInteger, IAIconGenerationOSType) { 22 | IAIconGenerationOSTypeAll = 0, 23 | IAIconGenerationOSTypeiOS7 24 | }; 25 | 26 | typedef NS_ENUM(NSInteger, IAIconGenerationDeviceType) { 27 | IAIconGenerationDeviceTypeUniversal = 0, 28 | IAIconGenerationDeviceTypeiPhone, 29 | IAIconGenerationDeviceTypeiPad 30 | }; 31 | 32 | @interface IAAppiconWindow : NSWindowController 33 | @property (nonatomic, assign) IBOutlet id delegate; 34 | @property (nonatomic, readonly) IAIconSet *selectedIconSet; 35 | 36 | @property (nonatomic, strong) NSArray *imageAssets; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /RTImageAssets/IAAppiconWindow.m: -------------------------------------------------------------------------------- 1 | // 2 | // IAAppiconWindow.m 3 | // RTImageAssets 4 | // 5 | // Created by ricky on 14-12-18. 6 | // Copyright (c) 2014年 rickytan. All rights reserved. 7 | // 8 | 9 | #import "RTImageAssets.h" 10 | #import "IAAppiconWindow.h" 11 | #import "IAImageSet.h" 12 | #import 13 | 14 | @interface IAImageViewInternal : NSImageView 15 | @end 16 | 17 | @implementation IAImageViewInternal 18 | 19 | - (void)awakeFromNib 20 | { 21 | self.layer.cornerRadius = 120.f; 22 | self.layer.masksToBounds = YES; 23 | self.wantsLayer = YES; 24 | 25 | [self registerForDraggedTypes:[NSImage imagePasteboardTypes]]; 26 | } 27 | 28 | - (BOOL)performDragOperation:(id)sender 29 | { 30 | NSPasteboard *board = [sender draggingPasteboard]; 31 | NSURL *fileURL = [NSURL URLFromPasteboard:board]; 32 | if (fileURL) { 33 | NSImage *image = [[NSImage alloc] initWithContentsOfURL:fileURL]; 34 | NSImageRep *rep = [[image representations] firstObject]; 35 | if (rep.pixelsHigh == 1024 && rep.pixelsWide == 1024) { 36 | return YES; 37 | } 38 | } 39 | 40 | NSBeginAlertSheet(LocalizedString(@"Not Supported!"), LocalizedString(@"OK"), nil, nil, self.window, nil, NULL, NULL, NULL, @"%@", LocalizedString(@"Please provide a 1024x1024 resolution image!")); 41 | 42 | return NO; 43 | } 44 | 45 | @end 46 | 47 | @interface IAAppiconWindow () 48 | @property (weak) IBOutlet NSPopUpButton *imageAssetButton; 49 | @property (weak) IBOutlet NSPopUpButton *iconSetButton; 50 | @property (weak) IBOutlet NSImageView *appIconImageView; 51 | @property (weak) IBOutlet NSButton *generateButton; 52 | 53 | @end 54 | 55 | @implementation IAAppiconWindow 56 | 57 | - (void)windowDidLoad { 58 | [super windowDidLoad]; 59 | self.generateButton.enabled = NO; 60 | 61 | [self reloadMenu]; 62 | } 63 | 64 | - (BOOL)windowShouldClose:(id)sender 65 | { 66 | self.appIconImageView.image = [[RTImageAssets sharedPlugin].bundle imageForResource:@"Appicon"]; 67 | self.generateButton.enabled = NO; 68 | return YES; 69 | } 70 | 71 | - (void)reloadSecondMenu 72 | { 73 | [self.iconSetButton.menu removeAllItems]; 74 | if (self.imageAssetButton.selectedItem) { 75 | IAImageAssets *asset = self.imageAssetButton.selectedItem.representedObject; 76 | 77 | for (IAIconSet *icon in asset.iconSets) { 78 | NSMenuItem *subitem = [self.iconSetButton.menu addItemWithTitle:icon.name 79 | action:@selector(onSelect:) 80 | keyEquivalent:@""]; 81 | subitem.representedObject = icon; 82 | } 83 | } 84 | } 85 | 86 | - (IAIconSet *)selectedIconSet 87 | { 88 | return self.iconSetButton.selectedItem.representedObject; 89 | } 90 | 91 | - (void)reloadMenu 92 | { 93 | [self.imageAssetButton.menu removeAllItems]; 94 | for (IAImageAssets *asset in self.imageAssets) { 95 | NSMenuItem *item = [self.imageAssetButton.menu addItemWithTitle:asset.name 96 | action:@selector(onSelectAsset:) 97 | keyEquivalent:@""]; 98 | item.representedObject = asset; 99 | if (!asset.iconSets.count) { 100 | item.enabled = NO; 101 | } 102 | } 103 | [self reloadSecondMenu]; 104 | } 105 | 106 | - (void)setImageAssets:(NSArray *)imageAssets 107 | { 108 | _imageAssets = imageAssets; 109 | if (self.isWindowLoaded) { 110 | [self reloadMenu]; 111 | } 112 | } 113 | 114 | #pragma mark - Actions 115 | 116 | - (void)onSelectAsset:(NSMenuItem *)item { 117 | [self reloadSecondMenu]; 118 | } 119 | 120 | - (void)onSelect:(NSMenuItem *)item { 121 | 122 | } 123 | 124 | - (IBAction)onDropImage:(id)sender { 125 | self.generateButton.enabled = self.appIconImageView.image != nil; 126 | if (!self.appIconImageView.image) { 127 | self.appIconImageView.image = [[RTImageAssets sharedPlugin].bundle imageForResource:@"Appicon"]; 128 | } 129 | } 130 | 131 | - (IBAction)onGenerate:(id)sender { 132 | if ([self.delegate respondsToSelector:@selector(appIconWindow:generateIconsWithImage:)]) { 133 | [self.delegate appIconWindow:self 134 | generateIconsWithImage:self.appIconImageView.image]; 135 | } 136 | } 137 | 138 | 139 | @end 140 | -------------------------------------------------------------------------------- /RTImageAssets/IAAppiconWindow.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 | 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 | -------------------------------------------------------------------------------- /RTImageAssets/IAGenerateOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // IAGenerateOperation.h 3 | // RTImageAssets 4 | // 5 | // Created by ricky on 14-12-10. 6 | // Copyright (c) 2014年 rickytan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class IAImageAssets; 12 | 13 | @interface IAGenerateOperation : NSOperation 14 | @property (nonatomic, readonly) IAImageAssets *imageAssets; 15 | 16 | + (instancetype)operationWithAssets:(IAImageAssets *)assets; 17 | @end 18 | -------------------------------------------------------------------------------- /RTImageAssets/IAGenerateOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // IAGenerateOperation.m 3 | // RTImageAssets 4 | // 5 | // Created by ricky on 14-12-10. 6 | // Copyright (c) 2014年 rickytan. All rights reserved. 7 | // 8 | 9 | #import "IAGenerateOperation.h" 10 | 11 | #import "IAImageSet.h" 12 | 13 | @interface IAImageSet (Generate) 14 | - (void)generate3xIfNeeded; 15 | - (void)generate2xIfNeeded; 16 | - (void)generate1xIfNeeded; 17 | - (void)generateMissing; 18 | @end 19 | 20 | @interface IAGenerateOperation () 21 | @property (nonatomic, strong) IAImageAssets *imageAssets; 22 | @end 23 | 24 | @implementation IAGenerateOperation 25 | 26 | + (instancetype)operationWithAssets:(IAImageAssets *)assets 27 | { 28 | IAGenerateOperation *op = [[IAGenerateOperation alloc] init]; 29 | op.imageAssets = assets; 30 | //op.name = assets.bundle.bundlePath.lastPathComponent; 31 | return op; 32 | } 33 | 34 | - (void)main 35 | { 36 | for (IAImageSet *set in self.imageAssets.imageSets) { 37 | [set generateMissing]; 38 | } 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /RTImageAssets/IAImageSet.h: -------------------------------------------------------------------------------- 1 | // 2 | // IAImageSet.h 3 | // RTImageAssets 4 | // 5 | // Created by ricky on 14-12-10. 6 | // Copyright (c) 2014年 rickytan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | extern NSString const *IAImageIdiom; 13 | extern NSString const *IAImageScale; 14 | extern NSString const *IAImageFilename; 15 | extern NSString const *IAImageSubtype; 16 | 17 | 18 | @interface IAImageSet : NSObject 19 | @property (nonatomic, readonly) NSString *name; 20 | @property (nonatomic, readonly) NSArray *images; 21 | 22 | + (instancetype)imageSetWithPath:(NSString *)path; 23 | - (id)initWithPath:(NSString *)path; 24 | 25 | @end 26 | 27 | @interface IAIconSet : IAImageSet 28 | 29 | - (void)generateAllIcons:(NSImage* )image; 30 | 31 | @end 32 | 33 | @interface IAImageAssets : NSObject 34 | @property (nonatomic, readonly) NSString *name; 35 | @property (nonatomic, readonly) NSArray *imageSets; 36 | @property (nonatomic, readonly) NSArray *iconSets; 37 | 38 | + (instancetype)assetsWithPath:(NSString *)path; 39 | 40 | - (id)initWithPath:(NSString *)path; 41 | - (void)addToProccesingQueue:(NSOperationQueue *)queue; 42 | @end 43 | 44 | @interface NSImage (Resizing) 45 | - (NSImage *)resizedImageWithScale:(CGFloat)scale; 46 | - (NSImage *)resizedImageWithSize:(NSSize)size; 47 | - (BOOL)saveToFile:(NSString *)filePath withType:(NSBitmapImageFileType)type; 48 | @end -------------------------------------------------------------------------------- /RTImageAssets/IAImageSet.m: -------------------------------------------------------------------------------- 1 | // 2 | // IAImageSet.m 3 | // RTImageAssets 4 | // 5 | // Created by ricky on 14-12-10. 6 | // Copyright (c) 2014年 rickytan. All rights reserved. 7 | // 8 | 9 | #import "IAImageSet.h" 10 | #import "IAGenerateOperation.h" 11 | #import "IASettingsWindow.h" 12 | 13 | NSString const *IAImageIdiom = @"idiom"; 14 | NSString const *IAImageScale = @"scale"; 15 | NSString const *IAImageSize = @"size"; 16 | NSString const *IAImageFilename = @"filename"; 17 | NSString const *IAImageSubtype = @"subtype"; 18 | NSString const *IAImageResizing = @"resizing"; 19 | 20 | @implementation NSImage (Resizing) 21 | 22 | - (NSImage *)resizedImageWithScale:(CGFloat)scale 23 | { 24 | NSBitmapImageRep *rep = (NSBitmapImageRep *)self.representations.firstObject; 25 | // issue #56: https://github.com/rickytan/RTImageAssets/issues/56 26 | if (![rep isKindOfClass:[NSBitmapImageRep class]]) { 27 | return nil; 28 | } 29 | 30 | NSSize pixelSize = NSMakeSize(rep.pixelsWide, rep.pixelsHigh); 31 | 32 | // issue #8: https://github.com/rickytan/RTImageAssets/issues/8 33 | if (pixelSize.width == 0.f || pixelSize.height == 0.f) { 34 | pixelSize = rep.size; 35 | } 36 | NSSize scaledSize = NSMakeSize(floorf(pixelSize.width * scale), floorf(pixelSize.height * scale)); 37 | 38 | return [self resizedImageWithSize:scaledSize]; 39 | } 40 | 41 | - (NSImage *)resizedImageWithSize:(NSSize)newSize 42 | { 43 | NSBitmapImageRep *rep = (NSBitmapImageRep *)self.representations.firstObject; 44 | 45 | 46 | // issue #21: https://github.com/rickytan/RTImageAssets/issues/21 47 | rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL 48 | pixelsWide:newSize.width 49 | pixelsHigh:newSize.height 50 | bitsPerSample:rep.bitsPerSample 51 | samplesPerPixel:rep.samplesPerPixel 52 | hasAlpha:rep.hasAlpha 53 | isPlanar:rep.isPlanar 54 | colorSpaceName:rep.colorSpaceName 55 | bytesPerRow:0 56 | bitsPerPixel:0]; 57 | 58 | rep.size = newSize; 59 | 60 | NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep:rep]; 61 | 62 | if (!context) { 63 | // issue #24: https://github.com/rickytan/RTImageAssets/issues/24 64 | rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL 65 | pixelsWide:newSize.width 66 | pixelsHigh:newSize.height 67 | bitsPerSample:8 68 | samplesPerPixel:4 69 | hasAlpha:YES 70 | isPlanar:NO 71 | colorSpaceName:NSCalibratedRGBColorSpace 72 | bytesPerRow:0 73 | bitsPerPixel:0]; 74 | rep.size = newSize; 75 | context = [NSGraphicsContext graphicsContextWithBitmapImageRep:rep]; 76 | } 77 | 78 | [NSGraphicsContext saveGraphicsState]; 79 | [NSGraphicsContext setCurrentContext:context]; 80 | [self drawInRect:NSMakeRect(0, 0, newSize.width, newSize.height)]; 81 | [NSGraphicsContext restoreGraphicsState]; 82 | 83 | return [[NSImage alloc] initWithData:[rep representationUsingType:NSPNGFileType 84 | properties:@{}]]; 85 | } 86 | 87 | - (BOOL)saveToFile:(NSString *)filePath withType:(NSBitmapImageFileType)type 88 | { 89 | NSData *data = nil; 90 | if (type == NSTIFFFileType) { 91 | data = self.TIFFRepresentation; 92 | } 93 | else { 94 | NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:self.TIFFRepresentation]; 95 | data = [rep representationUsingType:type 96 | properties:@{}]; 97 | } 98 | return [data writeToFile:filePath 99 | atomically:NO]; 100 | } 101 | 102 | @end 103 | 104 | @interface IAImageSet () 105 | @property (nonatomic, strong) NSString *name; 106 | @property (nonatomic, copy) NSString *path; 107 | @property (nonatomic, strong) NSMutableDictionary *contentInfo; 108 | @property (nonatomic, assign, getter=isChanged) BOOL changed; 109 | - (void)generate3xIfNeeded; 110 | - (void)generate2xIfNeeded; 111 | - (void)generate1xIfNeeded; 112 | - (void)generateMissing; 113 | @end 114 | 115 | @implementation IAImageSet 116 | 117 | + (instancetype)imageSetWithPath:(NSString *)path 118 | { 119 | return [[self alloc] initWithPath:path]; 120 | } 121 | 122 | - (id)initWithPath:(NSString *)path 123 | { 124 | self = [super init]; 125 | if (self) { 126 | self.path = path; 127 | self.name = path.stringByDeletingPathExtension.pathComponents.lastObject; 128 | 129 | NSString *contentFile = [self.path stringByAppendingPathComponent:@"Contents.json"]; 130 | NSData *data = [NSData dataWithContentsOfFile:contentFile]; 131 | if (data) { 132 | NSError *error = nil; 133 | self.contentInfo = [NSJSONSerialization JSONObjectWithData:data 134 | options:NSJSONReadingMutableContainers 135 | error:&error]; 136 | if (error) { 137 | NSLog(@"%@", error); 138 | } 139 | } 140 | } 141 | return self; 142 | } 143 | 144 | - (NSArray *)images 145 | { 146 | return self.contentInfo[@"images"]; 147 | } 148 | 149 | - (void)setFilename:(NSString *)filename 150 | forScale:(NSString *)scale 151 | { 152 | [self setFilename:filename 153 | forScale:scale 154 | resizing:nil]; 155 | } 156 | 157 | - (void)setFilename:(NSString *)filename 158 | forScale:(NSString *)scale 159 | resizing:(NSDictionary *)resizing 160 | { 161 | for (id obj in self.images) { 162 | if ([obj[IAImageScale] isEqualToString:scale] && 163 | ([@[@"iphone", @"universal"] containsObject:obj[IAImageIdiom]])) { 164 | obj[IAImageFilename] = filename; 165 | if (resizing) obj[IAImageResizing] = resizing; 166 | self.changed = YES; 167 | } 168 | } 169 | } 170 | 171 | - (NSDictionary *)resizingFromResizing:(NSDictionary *)resizingParam 172 | withScale:(CGFloat)scale 173 | imageSize:(NSSize)size 174 | { 175 | NSMutableDictionary *value = nil; 176 | if (resizingParam) { 177 | // Must deep copy !!! 178 | value = [[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:resizingParam]] mutableCopy]; 179 | 180 | CGFloat width = size.width, height = size.height; 181 | if (value[@"cap-insets"]) { 182 | if (value[@"cap-insets"][@"bottom"]) { 183 | CGFloat bottom = ceilf([value[@"cap-insets"][@"bottom"] floatValue] * scale); 184 | height -= bottom; 185 | value[@"cap-insets"][@"bottom"] = @(bottom); 186 | } 187 | if (value[@"cap-insets"][@"top"]) { 188 | CGFloat top = ceilf([value[@"cap-insets"][@"top"] floatValue] * scale); 189 | height -= top; 190 | value[@"cap-insets"][@"top"] = @(top); 191 | } 192 | if (value[@"cap-insets"][@"right"]) { 193 | CGFloat right = ceilf([value[@"cap-insets"][@"right"] floatValue] * scale); 194 | width -= right; 195 | value[@"cap-insets"][@"right"] = @(right); 196 | } 197 | if (value[@"cap-insets"][@"left"]) { 198 | CGFloat left = ceilf([value[@"cap-insets"][@"left"] floatValue] * scale); 199 | width -= left; 200 | value[@"cap-insets"][@"left"] = @(left); 201 | } 202 | } 203 | if (value[@"center"]) { 204 | if (value[@"center"][@"width"]) { 205 | value[@"center"][@"width"] = @(MAX(1, MIN(width, ceilf([value[@"center"][@"width"] floatValue] * scale)))); 206 | } 207 | if (value[@"center"][@"height"]) { 208 | value[@"center"][@"height"] = @(MAX(1, MIN(height, ceilf([value[@"center"][@"height"] floatValue] * scale)))); 209 | } 210 | } 211 | } 212 | return value; 213 | } 214 | 215 | - (NSString *)filenameForImageName:(NSString *)imageName ofScaleExtension:(NSString *)scaleExt 216 | { 217 | NSString *filename = imageName.stringByDeletingPathExtension; 218 | NSRange range = [imageName rangeOfString:@"@2x"]; 219 | if (range.location != NSNotFound) { 220 | filename = [imageName substringToIndex:range.location]; 221 | } 222 | range = [imageName rangeOfString:@"@3x"]; 223 | if (range.location != NSNotFound) { 224 | filename = [imageName substringToIndex:range.location]; 225 | } 226 | 227 | NSString *final = [NSString stringWithFormat:@"%@%@.png", filename, scaleExt]; 228 | NSInteger count = 0; 229 | while ([[NSFileManager defaultManager] fileExistsAtPath:[self.path stringByAppendingPathComponent:final]]) { 230 | final = [NSString stringWithFormat:@"%@%@~%ld.png", filename, scaleExt, ++count]; 231 | } 232 | return final; 233 | } 234 | 235 | - (NSInteger)get3xImageIndex 236 | { 237 | NSArray *images = self.images; 238 | for (NSUInteger i = 0; i < images.count; ++i) { 239 | if ([images[i][IAImageScale] isEqualToString:@"3x"] && [images[i][IAImageFilename] length]) 240 | return i; 241 | } 242 | return NSNotFound; 243 | } 244 | 245 | - (NSInteger)get2xImageIndex 246 | { 247 | NSArray *images = self.images; 248 | for (NSUInteger i = 0; i < images.count; ++i) { 249 | if ([images[i][IAImageScale] isEqualToString:@"2x"] && 250 | [images[i][IAImageFilename] length] && 251 | [@[@"iphone", @"universal"] containsObject:images[i][IAImageIdiom]]) 252 | return i; 253 | } 254 | return NSNotFound; 255 | } 256 | 257 | - (NSInteger)get1xImageIndex 258 | { 259 | NSArray *images = self.images; 260 | for (NSUInteger i = 0; i < images.count; ++i) { 261 | if ([images[i][IAImageScale] isEqualToString:@"1x"] && [images[i][IAImageFilename] length]) 262 | return i; 263 | } 264 | return NSNotFound; 265 | } 266 | 267 | 268 | 269 | - (void)generate1xIfNeeded 270 | { 271 | if ([[NSUserDefaults standardUserDefaults] boolForKey:IASettingsGenerateNonRetinaKey]) { 272 | NSInteger idx = [self get1xImageIndex]; 273 | if (idx != NSNotFound) 274 | return; 275 | 276 | CGFloat scale = 1.f / 3; 277 | idx = [self get3xImageIndex]; 278 | if (idx == NSNotFound) { 279 | scale = 1.f / 2; 280 | /* 281 | if ([[[NSUserDefaults standardUserDefaults] stringForKey:IASettingsDownscaleKey] isEqualToString:@"iphone6"]) { 282 | scale /= 750.f / 640.f; 283 | } 284 | */ 285 | idx = [self get2xImageIndex]; 286 | } 287 | if (idx != NSNotFound) { 288 | NSString *imgName = self.images[idx][IAImageFilename]; 289 | NSImage *img = [[NSImage alloc] initWithData:[NSData dataWithContentsOfFile:[self.path stringByAppendingPathComponent:imgName]]]; 290 | NSImage *scaledImage = [img resizedImageWithScale:scale]; 291 | NSString *fileName = [self filenameForImageName:imgName 292 | ofScaleExtension:@""]; 293 | if ([scaledImage saveToFile:[self.path stringByAppendingPathComponent:fileName] 294 | withType:NSPNGFileType]) { 295 | [self setFilename:fileName 296 | forScale:@"1x" 297 | resizing:[self resizingFromResizing:self.images[idx][IAImageResizing] 298 | withScale:scale 299 | imageSize:scaledImage.size]]; 300 | } 301 | } 302 | } 303 | } 304 | 305 | - (void)generate2xIfNeeded 306 | { 307 | NSInteger idx = [self get2xImageIndex]; 308 | if (idx != NSNotFound) 309 | return; 310 | 311 | idx = [self get3xImageIndex]; 312 | if (idx != NSNotFound) { 313 | NSString *imgName = self.images[idx][IAImageFilename]; 314 | NSImage *img = [[NSImage alloc] initWithData:[NSData dataWithContentsOfFile:[self.path stringByAppendingPathComponent:imgName]]]; 315 | CGFloat scale = 640.f/960;// [[[NSUserDefaults standardUserDefaults] stringForKey:IASettingsDownscaleKey] isEqualToString:@"iphone6"] ? 750.f/960 : 640.f/960; 316 | NSImage *scaledImage = [img resizedImageWithScale:scale]; 317 | NSString *fileName = [self filenameForImageName:imgName 318 | ofScaleExtension:@"@2x"]; 319 | if ([scaledImage saveToFile:[self.path stringByAppendingPathComponent:fileName] 320 | withType:NSPNGFileType]) { 321 | [self setFilename:fileName 322 | forScale:@"2x" 323 | resizing:[self resizingFromResizing:self.images[idx][IAImageResizing] 324 | withScale:scale 325 | imageSize:scaledImage.size]]; 326 | } 327 | } 328 | } 329 | 330 | - (void)generate3xIfNeeded 331 | { 332 | if ([[NSUserDefaults standardUserDefaults] boolForKey:IASettingsUpscaleKey]) { 333 | NSInteger idx = [self get3xImageIndex]; 334 | if (idx != NSNotFound) 335 | return; 336 | 337 | idx = [self get2xImageIndex]; 338 | if (idx != NSNotFound) { 339 | NSString *imgName = self.images[idx][IAImageFilename]; 340 | NSImage *img = [[NSImage alloc] initWithData:[NSData dataWithContentsOfFile:[self.path stringByAppendingPathComponent:imgName]]]; 341 | CGFloat scale = 960.f/640;// [[[NSUserDefaults standardUserDefaults] stringForKey:IASettingsDownscaleKey] isEqualToString:@"iphone6"] ? 960.f/750 : 960.f/640; 342 | NSImage *scaledImage = [img resizedImageWithScale:scale]; 343 | NSString *fileName = [self filenameForImageName:imgName 344 | ofScaleExtension:@"@3x"]; 345 | if ([scaledImage saveToFile:[self.path stringByAppendingPathComponent:fileName] 346 | withType:NSPNGFileType]) { 347 | [self setFilename:fileName 348 | forScale:@"3x" 349 | resizing:[self resizingFromResizing:self.images[idx][IAImageResizing] 350 | withScale:scale 351 | imageSize:scaledImage.size]]; 352 | } 353 | } 354 | } 355 | } 356 | 357 | - (void)generateMissing 358 | { 359 | [self generate1xIfNeeded]; 360 | [self generate2xIfNeeded]; 361 | [self generate3xIfNeeded]; 362 | 363 | [self postProcess]; 364 | } 365 | 366 | - (void)postProcess 367 | { 368 | if ([[NSUserDefaults standardUserDefaults] boolForKey:IASettingsRename]) { 369 | [self rename]; 370 | } 371 | 372 | if (self.isChanged) { 373 | self.changed = NO; 374 | [[NSJSONSerialization dataWithJSONObject:self.contentInfo 375 | options:NSJSONWritingPrettyPrinted 376 | error:NULL] 377 | writeToFile:[self.path stringByAppendingPathComponent:@"Contents.json"] 378 | atomically:NO]; 379 | } 380 | } 381 | 382 | - (void)rename 383 | { 384 | for (NSMutableDictionary *dic in self.images) { 385 | NSString *filename = dic[IAImageFilename]; 386 | if (filename.length) { 387 | NSString *filepath = [self.path stringByAppendingPathComponent:filename]; 388 | NSString *backfile = [[[self.path stringByAppendingPathComponent:filename.stringByDeletingPathExtension] 389 | stringByAppendingString:@"__backup~"] 390 | stringByAppendingPathExtension:filename.pathExtension]; 391 | if ([[NSFileManager defaultManager] moveItemAtPath:filepath 392 | toPath:backfile 393 | error:NULL]) { 394 | dic[IAImageFilename] = backfile.lastPathComponent; 395 | self.changed = YES; 396 | } 397 | } 398 | } 399 | 400 | NSString *renameFilename = self.path.lastPathComponent.stringByDeletingPathExtension; 401 | for (NSMutableDictionary *dic in self.images) { 402 | NSString *filename = dic[IAImageFilename]; 403 | NSString *ext = filename.pathExtension; 404 | if (!ext.length) { 405 | ext = @"png"; 406 | } 407 | if (filename.length) { 408 | NSString *filepath = [self.path stringByAppendingPathComponent:filename]; 409 | filename = renameFilename; 410 | if ([dic[IAImageIdiom] isEqualToString:@"ipad"]) { 411 | filename = [renameFilename stringByAppendingString:@"~ipad"]; 412 | } 413 | 414 | if ([dic[IAImageSubtype] isEqualToString:@"retina4"]) { 415 | filename = [renameFilename stringByAppendingString:@"-568h"]; 416 | } 417 | 418 | if (dic[IAImageScale] && ![dic[IAImageScale] isEqualToString:@"1x"]) { 419 | filename = [NSString stringWithFormat:@"%@@%@", filename, dic[IAImageScale]]; 420 | } 421 | filename = [filename stringByAppendingPathExtension:ext]; 422 | NSString *newPath = [self.path stringByAppendingPathComponent:filename]; 423 | if ([[NSFileManager defaultManager] moveItemAtPath:filepath 424 | toPath:newPath 425 | error:NULL]) { 426 | dic[IAImageFilename] = filename; 427 | self.changed = YES; 428 | } 429 | } 430 | } 431 | } 432 | 433 | @end 434 | 435 | @implementation IAIconSet 436 | 437 | - (NSString *)imageNameForSize:(NSString *)sizeStr 438 | { 439 | NSDictionary *sizeName = @{@"29x29": @"Icon-Small", 440 | @"40x40": @"Icon-Spotlight-40", 441 | @"50x50": @"Icon-Small-50", 442 | @"57x57": @"Icon", 443 | @"60x60": @"Icon-60", 444 | @"72x72": @"Icon-72", 445 | @"76x76": @"Icon-76"}; 446 | return sizeName[sizeStr] ?: [@"Icon-" stringByAppendingString:[sizeStr componentsSeparatedByString:@"x"].firstObject]; 447 | } 448 | 449 | - (void)generateAllIcons:(NSImage *)image 450 | { 451 | @synchronized(self) { 452 | NSString *contentFile = [self.path stringByAppendingPathComponent:@"Contents.json"]; 453 | NSData *data = [NSData dataWithContentsOfFile:contentFile]; 454 | if (data) { 455 | NSError *error = nil; 456 | self.contentInfo = [NSJSONSerialization JSONObjectWithData:data 457 | options:NSJSONReadingMutableContainers 458 | error:&error]; 459 | if (error) { 460 | NSLog(@"%@", error); 461 | } 462 | } 463 | 464 | for (NSMutableDictionary *img in self.images) { 465 | if (![img[IAImageFilename] length] || ! 466 | [[NSFileManager defaultManager] fileExistsAtPath:[self.path stringByAppendingPathComponent:img[IAImageFilename]]]) { 467 | NSString *filename = [self imageNameForSize:img[IAImageSize]]; 468 | NSInteger scale = [img[IAImageScale] integerValue]; 469 | CGFloat size = [img[IAImageSize] floatValue]; 470 | if (scale > 1) { 471 | filename = [NSString stringWithFormat:@"%@@%@", filename, img[IAImageScale]]; 472 | } 473 | filename = [filename stringByAppendingPathExtension:@"png"]; 474 | NSImage *iconImage = [image resizedImageWithSize:NSMakeSize(size * scale, size * scale)]; 475 | if ([iconImage saveToFile:[self.path stringByAppendingPathComponent:filename] 476 | withType:NSPNGFileType]) { 477 | img[IAImageFilename] = filename; 478 | } 479 | } 480 | } 481 | 482 | [[NSJSONSerialization dataWithJSONObject:self.contentInfo 483 | options:NSJSONWritingPrettyPrinted 484 | error:NULL] 485 | writeToFile:[self.path stringByAppendingPathComponent:@"Contents.json"] 486 | atomically:NO]; 487 | } 488 | } 489 | 490 | @end 491 | 492 | @interface IAImageAssets () 493 | @property (nonatomic, strong) NSString *name; 494 | @property (nonatomic, copy) NSString *path; 495 | @property (nonatomic, strong) NSArray *imageSets; 496 | @property (nonatomic, strong) NSArray *iconSets; 497 | @end 498 | 499 | @implementation IAImageAssets 500 | 501 | + (instancetype)assetsWithPath:(NSString *)path 502 | { 503 | return [[self alloc] initWithPath:path]; 504 | } 505 | 506 | - (id)initWithPath:(NSString *)path 507 | { 508 | self = [super init]; 509 | if (self) { 510 | self.path = path; 511 | self.name = path.stringByDeletingPathExtension.pathComponents.lastObject; 512 | 513 | NSArray *items = [self allFilesInDirectoryAtPath:path]; 514 | 515 | NSPredicate *filter = [NSPredicate predicateWithFormat:@"pathExtension = 'imageset'"]; 516 | NSArray *arr = [items filteredArrayUsingPredicate:filter]; 517 | NSMutableArray *images = [NSMutableArray arrayWithCapacity:items.count]; 518 | for (NSURL *p in arr) { 519 | IAImageSet *imageSet = [IAImageSet imageSetWithPath:p.path]; 520 | [images addObject:imageSet]; 521 | } 522 | self.imageSets = [NSArray arrayWithArray:images]; 523 | 524 | arr = [items filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"pathExtension = 'appiconset'"]]; 525 | [images removeAllObjects]; 526 | for (NSURL *p in arr) { 527 | IAIconSet *icon = [IAIconSet imageSetWithPath:p.path]; 528 | [images addObject:icon]; 529 | } 530 | self.iconSets = [NSArray arrayWithArray:images]; 531 | } 532 | 533 | return self; 534 | } 535 | 536 | - (NSArray *)allFilesInDirectoryAtPath:(NSString *)path { 537 | NSFileManager *fileManager = [NSFileManager defaultManager]; 538 | NSURL *bundleURL = [NSURL fileURLWithPath:path]; 539 | NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtURL:bundleURL includingPropertiesForKeys:@[NSURLIsDirectoryKey] options:NSDirectoryEnumerationSkipsHiddenFiles errorHandler:^BOOL(NSURL *url, NSError *error) { 540 | return YES; 541 | }]; 542 | 543 | NSMutableArray *mutableFileURLs = [NSMutableArray array]; 544 | for (NSURL *fileURL in enumerator) { 545 | NSNumber *isDirectory; 546 | [fileURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil]; 547 | 548 | if ([isDirectory boolValue]) { 549 | [mutableFileURLs addObject:fileURL]; 550 | } 551 | } 552 | return [mutableFileURLs copy]; 553 | } 554 | 555 | - (void)addToProccesingQueue:(NSOperationQueue *)queue 556 | { 557 | [queue addOperation:[IAGenerateOperation operationWithAssets:self]]; 558 | } 559 | 560 | @end 561 | -------------------------------------------------------------------------------- /RTImageAssets/IASettingsWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // IASettingsWindow.h 3 | // RTImageAssets 4 | // 5 | // Created by ricky on 14-12-10. 6 | // Copyright (c) 2014年 rickytan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSString *IASettingsGenerateNonRetinaKey; 12 | extern NSString *IASettingsUpscaleKey; 13 | extern NSString *IASettingsDownscaleKey; 14 | extern NSString *IASettingsRename; 15 | 16 | @interface IASettingsWindow : NSWindowController 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /RTImageAssets/IASettingsWindow.m: -------------------------------------------------------------------------------- 1 | // 2 | // IASettingsWindow.m 3 | // RTImageAssets 4 | // 5 | // Created by ricky on 14-12-10. 6 | // Copyright (c) 2014年 rickytan. All rights reserved. 7 | // 8 | 9 | #import "IASettingsWindow.h" 10 | 11 | NSString *IASettingsGenerateNonRetinaKey = @"IASettingsGenerateNonRetinaKey"; 12 | NSString *IASettingsUpscaleKey = @"IASettingsUpscaleKey"; 13 | NSString *IASettingsDownscaleKey = @"IASettingsDownscaleKey"; 14 | NSString *IASettingsRename = @"IASettingsRename"; 15 | 16 | @interface IASettingsWindow () 17 | @property (weak) IBOutlet NSButton *nonretinaButton; 18 | @property (weak) IBOutlet NSButton *upscaleButton; 19 | @property (weak) IBOutlet NSMatrix *downscaleRadio; 20 | @property (weak) IBOutlet NSButton *renameButton; 21 | 22 | @end 23 | 24 | @implementation IASettingsWindow 25 | 26 | - (void)windowDidLoad { 27 | [super windowDidLoad]; 28 | 29 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. 30 | self.nonretinaButton.state = [[NSUserDefaults standardUserDefaults] boolForKey:IASettingsGenerateNonRetinaKey] ? NSOnState : NSOffState; 31 | self.upscaleButton.state = [[NSUserDefaults standardUserDefaults] boolForKey:IASettingsUpscaleKey] ? NSOnState : NSOffState; 32 | self.renameButton.state = [[NSUserDefaults standardUserDefaults] boolForKey:IASettingsRename] ? NSOnState : NSOffState; 33 | [self.downscaleRadio selectCellAtRow:0 34 | column:[[[NSUserDefaults standardUserDefaults] stringForKey:IASettingsDownscaleKey] isEqualToString:@"iphone6"] ? 1 : 0]; 35 | } 36 | 37 | - (IBAction)onHelp:(id)sender { 38 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://github.com/rickytan/RTImageAssets"]]; 39 | } 40 | 41 | - (IBAction)onRadio:(id)sender { 42 | if (self.downscaleRadio.selectedColumn == 0) { 43 | [[NSUserDefaults standardUserDefaults] setObject:@"iphone5" 44 | forKey:IASettingsDownscaleKey]; 45 | } 46 | else { 47 | [[NSUserDefaults standardUserDefaults] setObject:@"iphone6" 48 | forKey:IASettingsDownscaleKey]; 49 | } 50 | [[NSUserDefaults standardUserDefaults] synchronize]; 51 | } 52 | 53 | - (IBAction)onNonretina:(id)sender { 54 | if (self.nonretinaButton.state == NSOnState) 55 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:IASettingsGenerateNonRetinaKey]; 56 | else 57 | [[NSUserDefaults standardUserDefaults] setObject:@NO forKey:IASettingsGenerateNonRetinaKey]; 58 | [[NSUserDefaults standardUserDefaults] synchronize]; 59 | } 60 | 61 | - (IBAction)onUpscale:(id)sender { 62 | if (self.upscaleButton.state == NSOnState) 63 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:IASettingsUpscaleKey]; 64 | else 65 | [[NSUserDefaults standardUserDefaults] setObject:@NO forKey:IASettingsUpscaleKey]; 66 | [[NSUserDefaults standardUserDefaults] synchronize]; 67 | } 68 | 69 | - (IBAction)onRename:(id)sender { 70 | if (self.renameButton.state == NSOnState) 71 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:IASettingsRename]; 72 | else 73 | [[NSUserDefaults standardUserDefaults] setObject:@NO forKey:IASettingsRename]; 74 | [[NSUserDefaults standardUserDefaults] synchronize]; 75 | } 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /RTImageAssets/IASettingsWindow.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 | 40 | 50 | 58 | 68 | 95 | 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 | -------------------------------------------------------------------------------- /RTImageAssets/IAWorkspace.h: -------------------------------------------------------------------------------- 1 | // 2 | // IAWorkspace.h 3 | // RTImageAssets 4 | // 5 | // Created by ricky on 14-12-10. 6 | // Copyright (c) 2014年 rickytan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface IAWorkspace : NSObject 12 | 13 | + (NSString *)currentWorkspacePath; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /RTImageAssets/IAWorkspace.m: -------------------------------------------------------------------------------- 1 | // 2 | // IAWorkspace.m 3 | // RTImageAssets 4 | // 5 | // Created by ricky on 14-12-10. 6 | // Copyright (c) 2014年 rickytan. All rights reserved. 7 | // 8 | 9 | #import "XcodeIDE.h" 10 | #import "IAWorkspace.h" 11 | 12 | @implementation IAWorkspace 13 | 14 | + (IDEWorkspaceWindowController *)keyWindowController 15 | { 16 | NSArray *workspaceWindowControllers = [NSClassFromString(@"IDEWorkspaceWindowController") valueForKey:@"workspaceWindowControllers"]; 17 | for (IDEWorkspaceWindowController *controller in workspaceWindowControllers) { 18 | if (controller.window.isKeyWindow) { 19 | return controller; 20 | } 21 | } 22 | return workspaceWindowControllers.firstObject; 23 | } 24 | 25 | + (IDEWorkspace *)workspaceForKeyWindow 26 | { 27 | return [[self keyWindowController] valueForKey:@"_workspace"]; 28 | } 29 | 30 | + (NSString *)currentWorkspacePath 31 | { 32 | return [self workspaceForKeyWindow].representingFilePath.pathString; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /RTImageAssets/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | zh_CN 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 | BNDL 19 | CFBundleShortVersionString 20 | 0.6.1 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | DVTPlugInCompatibilityUUIDs 26 | 27 | 7FDF5C7A-131F-4ABB-9EDC-8C5F8F0B8A90 28 | AABB7188-E14E-4433-AD3B-5CD791EAD9A3 29 | 992275C1-432A-4CF7-B659-D84ED6D42D3F 30 | 9F75337B-21B4-4ADC-B558-F9CADF7073A7 31 | A16FF353-8441-459E-A50C-B071F53F51B7 32 | C4A681B0-4A26-480E-93EC-1218098B9AA0 33 | AD68E85B-441B-4301-B564-A45E4919A6AD 34 | A2E4D43F-41F4-4FB9-BB94-7177011C9AED 35 | 63FC1C47-140D-42B0-BB4D-A10B2D225574 36 | 37B30044-3B14-46BA-ABAA-F01000C27B63 37 | 640F884E-CE55-4B40-87C0-8869546CAB7A 38 | 8DC44374-2B35-4C57-A6FE-2AD66A36AAD9 39 | E969541F-E6F9-4D25-8158-72DC3545A6C6 40 | AABB7188-E14E-4433-AD3B-5CD791EAD9A3 41 | 7FDF5C7A-131F-4ABB-9EDC-8C5F8F0B8A90 42 | 0420B86A-AA43-4792-9ED0-6FE0F2B16A13 43 | 7265231C-39B4-402C-89E1-16167C4CC990 44 | F41BD31E-2683-44B8-AE7F-5F09E919790E 45 | ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C 46 | 1637F4D5-0B27-416B-A78D-498965D64877 47 | 8A66E736-A720-4B3C-92F1-33D9962C69DF 48 | 65C57D32-1E9B-44B8-8C04-A27BA7AAE2C4 49 | C3998872-68CC-42C2-847C-B44D96AB2691 50 | DFFB3951-EB0A-4C09-9DAC-5F2D28CC839C 51 | 8B9F56A7-4D8B-41AA-A65D-D4906CDF1539 52 | 426A087B-D3AA-431A-AFDF-F135EC00DE1C 53 | D7881182-AD00-4C36-A94D-F45FC9B0CF85 54 | 55 | LSMinimumSystemVersion 56 | $(MACOSX_DEPLOYMENT_TARGET) 57 | NSPrincipalClass 58 | RTImageAssets 59 | XC4Compatible 60 | 61 | XC5Compatible 62 | 63 | XCPluginHasUI 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /RTImageAssets/RTImageAssets.h: -------------------------------------------------------------------------------- 1 | // 2 | // RTImageAssets.h 3 | // RTImageAssets 4 | // 5 | // Created by ricky on 14-12-10. 6 | // Copyright (c) 2014年 rickytan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define LocalizedString(key) [[RTImageAssets sharedPlugin].bundle localizedStringForKey:(key) value:(key) table:nil] 12 | 13 | @interface RTImageAssets : NSObject 14 | 15 | + (instancetype)sharedPlugin; 16 | 17 | @property (nonatomic, strong, readonly) NSBundle* bundle; 18 | @end -------------------------------------------------------------------------------- /RTImageAssets/RTImageAssets.m: -------------------------------------------------------------------------------- 1 | // 2 | // RTImageAssets.m 3 | // RTImageAssets 4 | // 5 | // Created by ricky on 14-12-10. 6 | // Copyright (c) 2014年 rickytan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RTImageAssets.h" 11 | #import "IASettingsWindow.h" 12 | #import "IAAppiconWindow.h" 13 | #import "IAWorkspace.h" 14 | #import "IAImageSet.h" 15 | #import "XcodeIDE.h" 16 | 17 | #include 18 | 19 | @interface DTAssetiLifeDelegate (Hook) 20 | @end 21 | 22 | @implementation DTAssetiLifeDelegate 23 | 24 | - (void)assetCategoryController:(id)arg1 25 | willDisplayAsset:(id)arg2 26 | { 27 | 28 | } 29 | 30 | @end 31 | 32 | static RTImageAssets *sharedPlugin; 33 | 34 | @interface RTImageAssets() 35 | @property (nonatomic, strong, readwrite) NSBundle *bundle; 36 | @property (nonatomic, strong) IASettingsWindow *settingsWindow; 37 | @property (nonatomic, strong) IAAppiconWindow *iconWindow; 38 | @property (nonatomic, strong) NSOperationQueue *queue; 39 | @property (nonatomic, strong) NSMenuItem *menuItem; 40 | @property (atomic, strong) NSMutableArray *workspaces; 41 | @end 42 | 43 | @implementation RTImageAssets 44 | 45 | + (void)pluginDidLoad:(NSBundle *)plugin 46 | { 47 | static dispatch_once_t onceToken; 48 | NSString *currentApplicationName = [[NSBundle mainBundle] infoDictionary][@"CFBundleName"]; 49 | if ([currentApplicationName isEqual:@"Xcode"]) { 50 | dispatch_once(&onceToken, ^{ 51 | sharedPlugin = [[self alloc] initWithBundle:plugin]; 52 | }); 53 | } 54 | } 55 | 56 | + (instancetype)sharedPlugin 57 | { 58 | return sharedPlugin; 59 | } 60 | 61 | - (id)initWithBundle:(NSBundle *)plugin 62 | { 63 | if (self = [super init]) { 64 | // reference to plugin's bundle, for resource access 65 | self.bundle = plugin; 66 | self.workspaces = [NSMutableArray array]; 67 | 68 | [[NSNotificationCenter defaultCenter] addObserver:self 69 | selector:@selector(onApplicationLaunch:) 70 | name:NSApplicationDidFinishLaunchingNotification 71 | object:nil]; 72 | 73 | [[NSNotificationCenter defaultCenter] addObserver:self 74 | selector:@selector(onProjectChanged:) 75 | name:@"PBXProjectDidChangeNotification" 76 | object:nil]; 77 | 78 | [[NSNotificationCenter defaultCenter] addObserver:self 79 | selector:@selector(onProjectOpen:) 80 | name:@"PBXProjectDidOpenNotification" 81 | object:nil]; 82 | 83 | [[NSNotificationCenter defaultCenter] addObserver:self 84 | selector:@selector(onProjectClose:) 85 | name:@"PBXProjectWillCloseNotification" 86 | object:nil]; 87 | 88 | [[NSNotificationCenter defaultCenter] addObserver:self 89 | selector:@selector(onProjectClose:) 90 | name:@"_IDEWorkspaceClosedNotification" 91 | object:nil]; 92 | 93 | [[NSNotificationCenter defaultCenter] addObserver:self 94 | selector:@selector(onContainerOpen:) 95 | name:@"IDEContainerDidOpenContainerNotification" 96 | object:nil]; 97 | 98 | [[NSUserDefaults standardUserDefaults] registerDefaults:@{IASettingsDownscaleKey: @"iphone5", 99 | IASettingsUpscaleKey: @NO, 100 | IASettingsGenerateNonRetinaKey: @NO, 101 | IASettingsRename: @YES}]; 102 | } 103 | return self; 104 | } 105 | 106 | - (void)dealloc 107 | { 108 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 109 | 110 | [_queue waitUntilAllOperationsAreFinished]; 111 | _queue = nil; 112 | _settingsWindow = nil; 113 | } 114 | 115 | #pragma mark - delegate 116 | 117 | #pragma mark - Actions 118 | 119 | - (NSMenuItem *)menuItem 120 | { 121 | if (!_menuItem) { 122 | // Create menu items, initialize UI, etc. 123 | 124 | NSMenuItem *menuItem = [[NSApp mainMenu] itemWithTitle:@"File"]; 125 | if (menuItem) { 126 | [[menuItem submenu] addItem:[NSMenuItem separatorItem]]; 127 | 128 | NSMenuItem *imageAssetsItem = [[menuItem submenu] addItemWithTitle:@"ImageAssets" 129 | action:nil 130 | keyEquivalent:@""]; 131 | imageAssetsItem.enabled = NO; 132 | imageAssetsItem.submenu = [[NSMenu alloc] init]; 133 | NSMenuItem *generateItem = [[imageAssetsItem submenu] addItemWithTitle:[self.bundle localizedStringForKey:@"Generate" 134 | value:nil 135 | table:nil] 136 | action:@selector(_generateAssets:) 137 | keyEquivalent:@"a"]; 138 | generateItem.keyEquivalentModifierMask = NSControlKeyMask | NSShiftKeyMask; 139 | generateItem.target = self; 140 | 141 | NSMenuItem *appiconItem = [[imageAssetsItem submenu] addItemWithTitle:[self.bundle localizedStringForKey:@"AppIcons" 142 | value:nil 143 | table:nil] 144 | action:@selector(_dropAppicon:) 145 | keyEquivalent:@"a"]; 146 | appiconItem.target = self; 147 | appiconItem.keyEquivalentModifierMask = NSControlKeyMask | NSShiftKeyMask | NSAlternateKeyMask; 148 | 149 | [[imageAssetsItem submenu] addItemWithTitle:[self.bundle localizedStringForKey:@"Settings" 150 | value:nil 151 | table:nil] 152 | action:@selector(_settings:) 153 | keyEquivalent:@""].target = self; 154 | 155 | self.menuItem = imageAssetsItem; 156 | } 157 | } 158 | return _menuItem; 159 | } 160 | 161 | - (void)_generateAssets:(id)sender 162 | { 163 | NSString *currentWorkspace = [IAWorkspace currentWorkspacePath]; 164 | if (currentWorkspace) { 165 | // issue #13 https://github.com/rickytan/RTImageAssets/issues/13 166 | if (self.queue.operationCount) 167 | return; 168 | 169 | NSString *currentWorkingDir = [currentWorkspace stringByDeletingPathExtension]; 170 | NSArray *bundlesToProcess = [self assetsBundlesInPath:currentWorkingDir]; 171 | 172 | if (bundlesToProcess.count == 0) { 173 | NSBeginAlertSheet(LocalizedString(@"Can't find any .xcassets bundle"), LocalizedString(@"OK"), nil, nil, [NSApp mainWindow], nil, NULL, NULL, NULL, @"%@", LocalizedString(@"use Asset Catalogs")); 174 | } 175 | else { 176 | for (NSString *bundlePath in bundlesToProcess) { 177 | IAImageAssets *assets = [IAImageAssets assetsWithPath:bundlePath]; 178 | [assets addToProccesingQueue:self.queue]; 179 | } 180 | } 181 | } 182 | } 183 | 184 | - (void)_dropAppicon:(id)sender 185 | { 186 | NSString *currentWorkspace = [IAWorkspace currentWorkspacePath]; 187 | if (currentWorkspace) { 188 | NSString *currentWorkingDir = [currentWorkspace stringByDeletingPathExtension]; 189 | NSArray *bundlesToProcess = [self assetsBundlesInPath:currentWorkingDir]; 190 | NSMutableArray *arr = [NSMutableArray arrayWithCapacity:bundlesToProcess.count]; 191 | for (NSString *bundlePath in bundlesToProcess) { 192 | IAImageAssets *assets = [IAImageAssets assetsWithPath:bundlePath]; 193 | [arr addObject:assets]; 194 | } 195 | self.iconWindow.imageAssets = arr; 196 | [self.iconWindow showWindow:sender]; 197 | } 198 | } 199 | 200 | - (void)_settings:(id)sender 201 | { 202 | if (_queue.operationCount) { 203 | NSAlert *alert = [NSAlert alertWithMessageText:LocalizedString(@"Processing") 204 | defaultButton:LocalizedString(@"OK") 205 | alternateButton:nil 206 | otherButton:nil 207 | informativeTextWithFormat:@"%@", LocalizedString(@"Please Wait")]; 208 | [alert runModal]; 209 | } 210 | else { 211 | [self.settingsWindow showWindow:sender]; 212 | } 213 | } 214 | 215 | #pragma mark - Methods 216 | 217 | - (void)onApplicationLaunch:(NSNotification *)notification 218 | { 219 | self.menuItem.enabled = NO; 220 | } 221 | 222 | - (void)onProjectOpen:(NSNotification *)notification 223 | { 224 | self.menuItem.enabled = YES; 225 | } 226 | 227 | - (void)onProjectClose:(NSNotification *)notification 228 | { 229 | if ([notification.object isKindOfClass:NSClassFromString(@"IDEWorkspace")]) { 230 | [self.workspaces removeObject:notification.object]; 231 | 232 | if (self.workspaces.count == 0) { 233 | self.menuItem.enabled = NO; 234 | } 235 | } 236 | else { 237 | self.menuItem.enabled = NO; 238 | } 239 | [self.iconWindow close]; 240 | } 241 | 242 | - (void)onProjectChanged:(NSNotification *)notification 243 | { 244 | self.menuItem.enabled = YES; 245 | [self.iconWindow close]; 246 | } 247 | 248 | - (void)onContainerOpen:(NSNotification *)notification 249 | { 250 | if ([notification.object isKindOfClass:NSClassFromString(@"IDEWorkspace")]) { 251 | [self.workspaces addObject:notification.object]; 252 | self.menuItem.enabled = YES; 253 | [self.iconWindow close]; 254 | } 255 | } 256 | 257 | - (NSArray *)assetsBundlesInPath:(NSString *)path 258 | { 259 | NSMutableArray *bundles = [NSMutableArray array]; 260 | 261 | NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtPath:path]; 262 | NSString *filePath = nil; 263 | while (filePath = [enumerator nextObject]) { 264 | if ([@"xcassets" isEqualToString:filePath.pathExtension]) { 265 | NSString *fullPath = [path stringByAppendingPathComponent:filePath]; 266 | NSError *error = nil; 267 | NSDictionary *attr = [[NSFileManager defaultManager] attributesOfItemAtPath:fullPath 268 | error:&error]; 269 | if ([attr[NSFileType] isEqualTo:NSFileTypeDirectory]) { 270 | [bundles addObject:fullPath]; 271 | } 272 | } 273 | } 274 | 275 | return [NSArray arrayWithArray:bundles]; 276 | } 277 | 278 | - (IASettingsWindow *)settingsWindow 279 | { 280 | if (!_settingsWindow) { 281 | _settingsWindow = [[IASettingsWindow alloc] initWithWindowNibName:NSStringFromClass([IASettingsWindow class])]; 282 | } 283 | return _settingsWindow; 284 | } 285 | 286 | - (IAAppiconWindow *)iconWindow 287 | { 288 | if (!_iconWindow) { 289 | _iconWindow = [[IAAppiconWindow alloc] initWithWindowNibName:NSStringFromClass([IAAppiconWindow class])]; 290 | _iconWindow.delegate = self; 291 | } 292 | return _iconWindow; 293 | } 294 | 295 | - (NSOperationQueue *)queue 296 | { 297 | if (!_queue) { 298 | _queue = [[NSOperationQueue alloc] init]; 299 | _queue.name = @"RTImageAssets Generation Queue"; 300 | _queue.maxConcurrentOperationCount = 5; 301 | } 302 | return _queue; 303 | } 304 | 305 | #pragma mark - IAAppicon delegate 306 | 307 | - (void)appIconWindow:(IAAppiconWindow *)window 308 | generateIconsWithImage:(NSImage *)image 309 | { 310 | IAIconSet *iconset = window.selectedIconSet; 311 | if (iconset) { 312 | dispatch_async(dispatch_get_main_queue(), ^{ 313 | [iconset generateAllIcons:image]; 314 | }); 315 | } 316 | } 317 | 318 | @end 319 | -------------------------------------------------------------------------------- /RTImageAssets/XcodeIDE.h: -------------------------------------------------------------------------------- 1 | // 2 | // XcodeIDE.h 3 | // RTImageAssets 4 | // 5 | // Created by ricky on 14-12-10. 6 | // Copyright (c) 2014年 rickytan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class IDEWorkspace; 12 | @class IDEWorkspaceSettings; 13 | @class IDEWorkspaceSharedSettings; 14 | @class IDEWorkspaceUserSettings; 15 | 16 | @interface DVTFilePath : NSObject 17 | @property(readonly) DVTFilePath *symbolicLinkDestinationFilePath; 18 | @property(readonly) NSURL *fileReferenceURL; 19 | @property(readonly) NSDictionary *fileSystemAttributes; 20 | @property(readonly) NSDictionary *fileAttributes; 21 | @property(readonly) NSString *fileTypeAttribute; 22 | @property(readonly) NSArray *sortedDirectoryContents; 23 | @property(readonly) NSArray *directoryContents; 24 | @property(readonly) NSDate *modificationDate; 25 | @property(readonly) BOOL isExcludedFromBackup; 26 | @property(readonly) BOOL isExecutable; 27 | @property(readonly) BOOL isDeletable; 28 | @property(readonly) BOOL isWritable; 29 | @property(readonly) BOOL isReadable; 30 | @property(readonly) BOOL existsInFileSystem; 31 | @property(readonly) NSString *fileName; 32 | @property(readonly) NSURL *fileURL; 33 | @property(readonly) NSString *pathString; 34 | @property(readonly) DVTFilePath *volumeFilePath; 35 | @property(readonly) DVTFilePath *parentFilePath; 36 | @end 37 | 38 | @interface IDEWorkspaceWindowController : NSWindowController 39 | @end 40 | 41 | @interface IDEWorkspaceArena : NSObject 42 | @property(readonly) IDEWorkspace *workspace; 43 | @property(readonly) DVTFilePath *testResultsFolderPath; 44 | @property(readonly) DVTFilePath *logFolderPath; 45 | @property(readonly) DVTFilePath *indexPrecompiledHeadersFolderPath; 46 | @property(readonly) DVTFilePath *indexFolderPath; 47 | @property(readonly) DVTFilePath *precompiledHeadersFolderPath; 48 | @property(readonly) DVTFilePath *installingBuildFolderPath; 49 | @property(readonly) DVTFilePath *archivingBuildFolderPath; 50 | @property(readonly) DVTFilePath *buildIntermediatesFolderPath; 51 | @end 52 | 53 | 54 | 55 | @interface IDEWorkspace : NSObject 56 | @property BOOL isCleaningBuildFolder; 57 | @property(nonatomic) BOOL finishedLoading; 58 | @property(nonatomic) BOOL pendingFileReferencesAndContainers; 59 | @property BOOL initialContainerScanComplete; 60 | @property(retain, nonatomic) IDEWorkspaceArena *workspaceArena; 61 | @property(readonly) DVTFilePath *wrappedXcode3ProjectPath; 62 | @property(readonly) NSString *representingTitle; 63 | @property(readonly) DVTFilePath *representingFilePath; 64 | @property(retain, nonatomic) IDEWorkspaceSharedSettings *sharedSettings; 65 | @property(retain, nonatomic) IDEWorkspaceUserSettings *userSettings; 66 | @end 67 | 68 | @interface DVTLayoutView_ML : NSView 69 | @end 70 | 71 | @protocol IBViewDragDelegate 72 | - (BOOL)view:(id)arg1 performDragOperation:(id)arg2; 73 | - (BOOL)view:(id)arg1 prepareForDragOperation:(id)arg2; 74 | - (unsigned long long)view:(id)arg1 draggingEntered:(id)arg2; 75 | - (id)dragTypesForView:(id)arg1; 76 | 77 | @optional 78 | - (void)view:(id)arg1 draggingEnded:(id)arg2; 79 | - (void)view:(id)arg1 draggingExited:(id)arg2; 80 | - (void)view:(id)arg1 concludeDragOperation:(id)arg2; 81 | - (unsigned long long)view:(id)arg1 draggingUpdated:(id)arg2; 82 | @end 83 | 84 | @protocol IBICMultipartImageViewDelegate 85 | - (id)multipartImageView:(id)arg1 imageForImageRepIdentifier:(id)arg2; 86 | - (id)multipartImageView:(id)arg1 titleForImageRepIdentifier:(id)arg2; 87 | - (void)multipartImageViewWillLayout:(id)arg1; 88 | - (BOOL)multipartImageView:(id)arg1 interceptMouseUp:(id)arg2; 89 | - (BOOL)multipartImageView:(id)arg1 interceptMouseDragged:(id)arg2 withOriginalMouseDown:(id)arg3; 90 | - (BOOL)multipartImageView:(id)arg1 interceptMouseDown:(id)arg2; 91 | - (void)multipartImageView:(id)arg1 userDidEditTitle:(id)arg2; 92 | - (void)multipartImageView:(id)arg1 performDelete:(id)arg2; 93 | @end 94 | 95 | @interface IBICMultipartImageView : DVTLayoutView_ML 96 | @property(nonatomic) __weak id delegate; 97 | - (id)effectiveOuterBorderColor; 98 | - (id)effectiveTitleColor; 99 | @end 100 | 101 | @protocol IDEApplicationEventDelegate 102 | @optional 103 | - (BOOL)application:(id)arg1 shouldSendEvent:(id)arg2; 104 | @end 105 | 106 | 107 | @interface DVTApplication : NSApplication 108 | - (id)addActionMonitorWithHandlerBlock:(id)arg1; 109 | @end 110 | 111 | 112 | @interface IDEApplication : DVTApplication 113 | @property(retain) id eventDelegate; // @synthesize 114 | @end 115 | 116 | 117 | @interface IBICAbstractCatalogItem : NSObject 118 | @property(readonly, nonatomic) NSSet *children; // @synthesize children=_children; 119 | @property(readonly, nonatomic) IBICAbstractCatalogItem *parent; // @synthesize parent=_parent; 120 | @property(readonly, nonatomic) NSDate *manifestModificationDate; // @synthesize manifestModificationDate=_manifestModificationDate; 121 | @property(readonly, nonatomic) NSDate *modificatoinDate; // @synthesize modificatoinDate=_modificatoinDate; 122 | @property(copy, nonatomic) NSURL *absoluteFileURL; // @synthesize absoluteFileURL=_absoluteFileURL; 123 | @property(copy, nonatomic) NSString *explicitContainingDirectory; // @synthesize explicitContainingDirectory=_explicitContainingDirectory; 124 | @property(copy, nonatomic) NSString *fileName; // @synthesize fileName=_fileName; 125 | @property long long changeCount; // @synthesize changeCount=_changeCount; 126 | @property(readonly, nonatomic) NSArray *displayOrderedChildren; 127 | @property(readonly) NSString *absoluteManifestFilePath; 128 | @property(readonly) NSData *manifestFileData; 129 | @property(readonly) NSString *manifestFileName; 130 | @property(readonly, nonatomic) NSString *relativeFilePathFromRoot; 131 | @property(readonly, nonatomic) NSString *relativeIdentifierPath; 132 | @property(copy, nonatomic) NSString *absoluteFilePath; 133 | @property(readonly, nonatomic) BOOL canBeEmbeddedInFolder; 134 | @property(readonly, nonatomic) BOOL canHaveChildren; 135 | @property(readonly, nonatomic) NSString *identifier; 136 | @property(readonly, nonatomic) NSString *displayName; 137 | @end 138 | 139 | @interface IBICFolder : IBICAbstractCatalogItem 140 | - (BOOL)fileStructureSnapshotChildWouldMapToModelChild:(id)arg1; 141 | - (void)replaceChildrenFromFileSystemSnapshot:(id)arg1 results:(id)arg2; 142 | - (Class)itemClassForDirectoryExtension:(id)arg1; 143 | - (id)imageSetWithFileName:(id)arg1; 144 | - (id)imageSetWithName:(id)arg1; 145 | - (id)folderForFileName:(id)arg1; 146 | - (id)childWithFileName:(id)arg1; 147 | - (id)validatedFileNameForProposedDisplayName:(id)arg1; 148 | - (id)enclosingFolderIncludingReceiver; 149 | - (BOOL)canBeEmbeddedInFolder; 150 | - (BOOL)canHaveChildren; 151 | - (id)identifier; 152 | - (id)displayName; 153 | - (id)descriptionShortClassName; 154 | 155 | @end 156 | 157 | @interface IBICCatalog : IBICFolder 158 | - (void)replaceChildrenWithDiskContent:(id)arg1; 159 | - (BOOL)canBeEmbeddedInFolder; 160 | - (BOOL)canHaveChildren; 161 | - (id)displayName; 162 | - (id)allIconSets; 163 | - (id)allImageSets; 164 | - (id)catalog; 165 | - (id)descriptionShortClassName; 166 | 167 | @end 168 | 169 | 170 | @interface IBICMultipartImage : IBICAbstractCatalogItem 171 | { 172 | } 173 | 174 | + (Class)repIdentifierClass; 175 | + (Class)slotClass; 176 | + (id)pluralTypeNameForIssues; 177 | + (id)typeNameForIssues; 178 | + (id)keysThatImpactImageName; 179 | + (id)fileNameForImageSetName:(id)arg1; 180 | + (Class)imageRepClass; 181 | + (id)fileExtension; 182 | + (id)defaultInstanceForPlatforms:(id)arg1; 183 | + (id)defaultImageName; 184 | + (Class)classForDirectoryExtension:(id)arg1; 185 | + (id)multipartImageClassesInImportOrder; 186 | + (id)multipartImageClasses; 187 | + (id)allocWithZone:(struct _NSZone *)arg1; 188 | - (void)populateMutatorsToAddRequiredChildCounterparts:(id)arg1; 189 | - (id)suggestedFileNameForImageRepInSlot:(id)arg1; 190 | - (void)populateIssues:(id)arg1 context:(id)arg2; 191 | - (void)assertChildIsLegalToAdd:(id)arg1; 192 | - (id)pluralTypeNameForIssues; 193 | - (id)typeNameForIssues; 194 | - (id)descriptionShortClassName; 195 | - (id)imageRepForImageRepIdentifier:(id)arg1; 196 | - (id)imageRepForSlot:(id)arg1; 197 | - (id)imageRepForIdentifier:(id)arg1; 198 | - (id)childForIdentifier:(id)arg1; 199 | - (BOOL)canBeEmbeddedInFolder; 200 | - (BOOL)canHaveChildren; 201 | - (id)identifier; 202 | - (id)displayName; 203 | @property(copy, nonatomic) NSString *imageName; 204 | - (id)validatedFileNameForProposedImageName:(id)arg1; 205 | - (id)enclosingMultipartImageIncludingReceiver; 206 | 207 | @end 208 | 209 | 210 | @interface IBICMappedMultipartImage : IBICMultipartImage 211 | { 212 | } 213 | 214 | + (id)orderedSlotComponentClasses; 215 | + (double)currentContentsJSONVersionNumber; 216 | + (double)latestUnderstoodContentsJSONVersionNumber; 217 | + (double)earliestUnderstoodContentsJSONVersionNumber; 218 | - (id)contentsDictionary; 219 | - (void)populateContentsJSONImageEntry:(id)arg1 forImageRep:(id)arg2; 220 | - (BOOL)shouldIncludeImageRepInContentsJSON:(id)arg1; 221 | - (void)replaceChildrenFromFileSystemSnapshot:(id)arg1 results:(id)arg2; 222 | - (id)imageRepsByMergingLooseFilesContentFromSnapshot:(id)arg1 withJSONReferencedContent:(id)arg2 results:(id)arg3; 223 | - (id)imageRepsFromContentsJSONImageEntries:(id)arg1 results:(id)arg2; 224 | - (id)imageRepFromImageEntry:(id)arg1 results:(id)arg2; 225 | - (id)validatedContentsJSONImageEntriesFromSnapshot:(id)arg1 results:(id)arg2; 226 | - (id)readContentsJSONFromSnapshot:(id)arg1 results:(id)arg2; 227 | - (BOOL)fileStructureSnapshotChildWouldMapToModelChild:(id)arg1; 228 | - (id)manifestFileData; 229 | - (id)manifestFileName; 230 | - (id)orderedSlotComponentClasses; 231 | 232 | @end 233 | 234 | 235 | 236 | @interface IBICAppIconSet : IBICMappedMultipartImage 237 | { 238 | BOOL _preRendered; 239 | } 240 | 241 | + (id)pluralTypeNameForIssues; 242 | + (id)typeNameForIssues; 243 | + (id)fileExtension; 244 | + (id)defaultInstanceForPlatforms:(id)arg1; 245 | + (id)defaultImageName; 246 | + (double)currentContentsJSONVersionNumber; 247 | + (double)latestUnderstoodContentsJSONVersionNumber; 248 | + (double)earliestUnderstoodContentsJSONVersionNumber; 249 | + (Class)imageRepClass; 250 | @property(nonatomic, getter=isPreRendered) BOOL preRendered; // @synthesize preRendered=_preRendered; 251 | - (id)descriptionShortClassName; 252 | - (id)contentsDictionary; 253 | - (id)readContentsJSONFromSnapshot:(id)arg1 results:(id)arg2; 254 | - (id)suggestedFileNameForImageRepInSlot:(id)arg1; 255 | - (id)childForIdentifier:(id)arg1; 256 | - (id)imageRepForIdentifier:(id)arg1; 257 | - (id)imageRepForImageRepIdentifier:(id)arg1; 258 | - (id)imageRepForSlot:(id)arg1; 259 | 260 | @end 261 | 262 | @interface IBICLaunchImageSet : IBICMappedMultipartImage 263 | { 264 | } 265 | 266 | + (id)pluralTypeNameForIssues; 267 | + (id)typeNameForIssues; 268 | + (id)fileExtension; 269 | + (id)defaultInstanceForPlatforms:(id)arg1; 270 | + (id)defaultImageName; 271 | + (double)currentContentsJSONVersionNumber; 272 | + (double)latestUnderstoodContentsJSONVersionNumber; 273 | + (double)earliestUnderstoodContentsJSONVersionNumber; 274 | + (Class)imageRepClass; 275 | - (id)descriptionShortClassName; 276 | - (id)validatedContentsJSONImageEntriesFromSnapshot:(id)arg1 results:(id)arg2; 277 | - (id)suggestedFileNameForImageRepInSlot:(id)arg1; 278 | - (id)childForIdentifier:(id)arg1; 279 | - (id)imageRepForIdentifier:(id)arg1; 280 | - (id)imageRepForImageRepIdentifier:(id)arg1; 281 | - (id)imageRepForSlot:(id)arg1; 282 | 283 | @end 284 | 285 | 286 | @interface IBICMultipartImageRepSlot : NSObject 287 | { 288 | NSDictionary *_componentsByClass; 289 | } 290 | 291 | + (id)orderedComponentClasses; 292 | + (id)defaultSlot; 293 | + (id)emptySlot; 294 | + (id)allocWithZone:(struct _NSZone *)arg1; 295 | + (id)slotWithComponents:(id)arg1; 296 | + (id)slotWithComponents:(id *)arg1 count:(unsigned long long)arg2; 297 | - (id)requiredPointSize; 298 | - (id)requiredPixelSize; 299 | - (id)suggestedRepNameForMultipartImageSetName:(id)arg1; 300 | - (id)detailAreaKey; 301 | - (id)requiredFileName; 302 | - (id)displayName; 303 | - (id)description; 304 | - (id)stringRepresentation; 305 | - (id)shortDisplayNameDefiningItem; 306 | - (unsigned long long)hash; 307 | - (BOOL)isEqual:(id)arg1; 308 | - (BOOL)isEqualToMultipartImageRepSlot:(id)arg1; 309 | - (long long)compareDisplayOrder:(id)arg1; 310 | - (id)slotComponentsForClasses:(id)arg1; 311 | - (id)slotComponentForClass:(Class)arg1; 312 | - (void)enumerateOrderedSlotComponentsAndValues:(id)arg1; 313 | - (id)initWithComponents:(id)arg1; 314 | - (void)captureComponents; 315 | 316 | @end 317 | 318 | 319 | 320 | @interface IBICMultipartImageRep : IBICAbstractCatalogItem 321 | + (id)keysThatImpactDisplayOrder; 322 | + (id)validSourceImageExtensions; 323 | + (id)imageRepWithRepIdentifier:(id)arg1; 324 | + (id)imageRepWithSlot:(id)arg1 fileName:(id)arg2 andUnassigned:(BOOL)arg3; 325 | + (Class)slotClass; 326 | + (Class)multiplartImageClass; 327 | + (Class)repIdentifierClass; 328 | + (id)allocWithZone:(struct _NSZone *)arg1; 329 | @property(copy, nonatomic) NSData *imageData; // @synthesize imageData=_imageData; 330 | @property(copy, nonatomic) IBICMultipartImageRepSlot *slot; // @synthesize slot=_slot; 331 | @property(nonatomic, getter=isUnassigned) BOOL unassigned; // @synthesize unassigned=_unassigned; 332 | - (id)suggestedFileName; 333 | @property(readonly) NSValue *imageDataPixelSize; 334 | @property(readonly) NSValue *requiredPointSize; 335 | @property(readonly) NSValue *requiredPixelSize; 336 | - (void)populateIssues:(id)arg1 context:(id)arg2; 337 | - (BOOL)updateModificationDatesWithMutationResult:(id)arg1; 338 | - (void)setImageDataFromPath:(id)arg1; 339 | - (BOOL)fileStructureSnapshotChildWouldMapToModelChild:(id)arg1; 340 | - (long long)compareDisplayOrder:(id)arg1; 341 | - (void)replaceChildrenFromFileSystemSnapshot:(id)arg1 results:(id)arg2; 342 | - (id)fileWrapperRepresentationWithOptions:(unsigned long long)arg1; 343 | - (id)parent; 344 | - (void)enumerateDescriptionAttributeComponents:(id)arg1; 345 | - (BOOL)isBrokenFileReference; 346 | - (id)identifier; 347 | - (id)structuredIdentifier; 348 | - (BOOL)canBeEmbeddedInFolder; 349 | - (BOOL)canHaveChildren; 350 | - (id)descriptionShortClassName; 351 | - (BOOL)isMinimallyFitForCompiling; 352 | @property(readonly) NSString *shortDisplayName; 353 | - (id)displayName; 354 | - (id)initWithSlot:(id)arg1; 355 | 356 | @end 357 | 358 | 359 | @interface IBICIconSetRep : IBICMultipartImageRep 360 | { 361 | } 362 | 363 | + (id)outputImageExtension; 364 | + (Class)multiplartImageClass; 365 | + (Class)repIdentifierClass; 366 | + (id)imageRepWithSlot:(id)arg1 fileName:(id)arg2 andUnassigned:(BOOL)arg3; 367 | + (id)imageRepWithRepIdentifier:(id)arg1; 368 | - (BOOL)isMinimallyFitForCompiling; 369 | - (BOOL)isImageDataSizedProperly; 370 | - (id)descriptionShortClassName; 371 | - (void)setSlot:(id)arg1; 372 | - (id)slot; 373 | - (id)structuredIdentifier; 374 | - (id)parent; 375 | - (id)initWithSlot:(id)arg1; 376 | 377 | @end 378 | 379 | 380 | @interface IBICCatalogSynchronizer : NSObject 381 | + (id)synchronizerForCatalogAtPath:(NSString *)path; 382 | - (void)preventSynchronizationDuring:(id)arg1; 383 | - (BOOL)isSynchronizationEnabled; 384 | - (void)enableSynchronization; 385 | - (void)disableSynchronization; 386 | - (void)applyMutationToModelAndScheduleForDisk:(id)arg1; 387 | - (id)replaceCatalogWithContentsOfPathWhileItIsKnowThatSyncOperationsAreNotInflightAndAreDisabled:(id)arg1; 388 | - (id)replaceCatalogWithContentsOfPath:(id)arg1; 389 | - (void)validateBatchedChanges:(id)arg1; 390 | - (void)validateChangesToDiskIfNeeded; 391 | - (void)validateChangesFromDiskIfNeeded; 392 | - (void)resetContentFromDisk; 393 | @property(readonly) IBICCatalog *catalog; 394 | - (void)primitiveInvalidate; 395 | - (id)init; 396 | - (id)initByTakingOwnershipsOfCatalog:(id)arg1; 397 | 398 | // Remaining properties 399 | @property(readonly, nonatomic, getter=isValid) BOOL valid; 400 | @end 401 | 402 | @interface DTAssetiLifeDelegate : NSObject 403 | @end 404 | -------------------------------------------------------------------------------- /RTImageAssets/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | RTImageAssets 4 | 5 | Created by ricky on 14-12-10. 6 | Copyright (c) 2014年 rickytan. All rights reserved. 7 | */ 8 | "Generate" = "Generate Missing Assets"; 9 | "Settings" = "Settings"; 10 | "Processing" = "Processing"; 11 | "OK" = "OK"; 12 | "Please Wait" = "Please wait until process finished"; 13 | "Appicons" = "App Icons"; 14 | "Not Supported!" = "Not Supported!"; 15 | "Please provide a 1024X1024 resolution image!" = "Please provide a 1024X1024 resolution image!"; 16 | "Can't find any .xcassets bundle" = "Can't find any .xcassets bundle"; 17 | "use Asset Catalogs" = "This plugin doesn't support origin images in Project resources, please use Asset Catalogs"; -------------------------------------------------------------------------------- /RTImageAssets/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | RTImageAssets 4 | 5 | Created by ricky on 14-12-10. 6 | Copyright (c) 2014年 rickytan. All rights reserved. 7 | */ 8 | "Generate" = "生成缺失资源"; 9 | "Settings" = "设置"; 10 | "Processing" = "正在处理资源"; 11 | "OK" = "好"; 12 | "Please Wait" = "请等待处理完成"; 13 | "Appicons" = "应用图标"; 14 | "Not Supported!" = "不支持!"; 15 | "Please provide a 1024X1024 resolution image!" = "请提供一个 1024X1024 分辨率的图片!"; 16 | "Can't find any .xcassets bundle" = "找不到 .xcassets 文件夹"; 17 | "use Asset Catalogs" = "此插件不支持项目中的原始图片文件,请使用 Asset Catalogs"; -------------------------------------------------------------------------------- /ScreenCap/iconset-gen.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickytan/RTImageAssets/4520ac67fd688d3641da0b0b864afbff3ad28c1b/ScreenCap/iconset-gen.gif -------------------------------------------------------------------------------- /ScreenCap/p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickytan/RTImageAssets/4520ac67fd688d3641da0b0b864afbff3ad28c1b/ScreenCap/p.png -------------------------------------------------------------------------------- /ScreenCap/usage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rickytan/RTImageAssets/4520ac67fd688d3641da0b0b864afbff3ad28c1b/ScreenCap/usage.gif --------------------------------------------------------------------------------