├── .gitignore ├── .travis.yml ├── DGtech.jpg ├── LICENSE ├── README.md ├── iOSAppIconGenerator.py ├── py2app ├── iOSAppIconGenerator.py ├── macholib_patch.py └── setup.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | AppIcon.appiconset 2 | *.jpg 3 | *.png 4 | !DGtech.jpg 5 | .DS_Store 6 | build 7 | dist 8 | __pycache__ 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.6" 4 | # command to install dependencies 5 | install: "pip install -r requirements.txt" 6 | # command to run tests 7 | script: python iOSAppIconGenerator.py 8 | -------------------------------------------------------------------------------- /DGtech.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorukgezici/iOSAppIconGenerator/408eefd5d17c611fc33486956bcbe489319611e3/DGtech.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Doruk Gezici 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iOSAppIconGenerator 2 | [![Build Status](https://travis-ci.org/dorukgezici/iOSAppIconGenerator.svg?branch=master)](https://travis-ci.org/dorukgezici/iOSAppIconGenerator) [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE) 3 | 4 | iOS App Icon Generator for Xcode - written in Python by @dorukgezici. 5 | 6 | Check out the blog post: http://dorukgezici.com/ios-app-icon-generator-script/ 7 | 8 | ## Usage 9 | 10 | Put the image you want to use as your app icon in the same directory as the script and the script will generate all the resized images you need and put them in the Xcode format “AppIcon.appiconset” folder. The only thing left for you to do is to drag that folder in your Xcode project assests. 11 | 12 | How to use my script: 13 | 14 | 1) You have to have python 3.x installed. Look at python.org for instructions. 15 | 16 | 2) Install the Python Library “Pillow” by the command: `pip install Pillow` 17 | 18 | 3) Download the script "iOSAppIconGenerator.py". The image you want to use must be in the same folder as the script (*.png or *.jpg). 19 | 20 | 4) You have two options for running the script. You can run it with Python Launcher if you installed python from python.org or you can just run it from terminal. So 2 options are: 21 | - Just right click the script and open with Python Launcher. The script will automatically find the image in its current folder and create a new folder within with resized images and a contents.json file. 22 |  - Change directory (cd) to the directory you have the script and the picture from terminal. Then run this command: `python3 iOSAppIconGenerator.py` 23 | 24 | The asset folder is ready! Just drag the created folder in your Xcode project assets. Don’t forget to delete the old empty asset folder named “AppIcon” and rename the new folder to the same name. 25 | Please keep in mind that this is just a 2 hours project and the script has no error handling whatsoever. So if you come across any bugs leave a comment! 26 | -------------------------------------------------------------------------------- /iOSAppIconGenerator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os, sys 4 | import json 5 | from PIL import Image 6 | from PIL import ImageFilter 7 | 8 | valid_images = [".jpg",".png"] 9 | filename = None 10 | for f in os.listdir(): 11 | ext = os.path.splitext(f)[1] 12 | if ext.lower() in valid_images: 13 | filename = f 14 | break 15 | 16 | print("\n<- iOS App Icon Generator Script by DG ->\nPlease make sure the script is in the same folder as the image.") 17 | if filename is None: 18 | sys.exit("Couldn't find an image(.jpg/.png) in the folder. Please try again.") 19 | 20 | json_data = { 21 | "images":[ 22 | { 23 | "idiom":"iphone", 24 | "size":"20x20", 25 | "scale":"2x", 26 | "filename":"Icon-App-20x20@2x.png" 27 | }, 28 | { 29 | "idiom":"iphone", 30 | "size":"20x20", 31 | "scale":"3x", 32 | "filename":"Icon-App-20x20@3x.png" 33 | }, 34 | { 35 | "idiom":"iphone", 36 | "size":"29x29", 37 | "scale":"1x", 38 | "filename":"Icon-App-29x29@1x.png" 39 | }, 40 | { 41 | "idiom":"iphone", 42 | "size":"29x29", 43 | "scale":"2x", 44 | "filename":"Icon-App-29x29@2x.png" 45 | }, 46 | { 47 | "idiom":"iphone", 48 | "size":"29x29", 49 | "scale":"3x", 50 | "filename":"Icon-App-29x29@3x.png" 51 | }, 52 | { 53 | "idiom":"iphone", 54 | "size":"40x40", 55 | "scale":"2x", 56 | "filename":"Icon-App-40x40@2x.png" 57 | }, 58 | { 59 | "idiom":"iphone", 60 | "size":"40x40", 61 | "scale":"3x", 62 | "filename":"Icon-App-40x40@3x.png" 63 | }, 64 | { 65 | "idiom":"iphone", 66 | "size":"57x57", 67 | "scale":"1x", 68 | "filename":"Icon-App-57x57@1x.png" 69 | }, 70 | { 71 | "idiom":"iphone", 72 | "size":"57x57", 73 | "scale":"2x", 74 | "filename":"Icon-App-57x57@2x.png" 75 | }, 76 | { 77 | "idiom":"iphone", 78 | "size":"60x60", 79 | "scale":"2x", 80 | "filename":"Icon-App-60x60@2x.png" 81 | }, 82 | { 83 | "idiom":"iphone", 84 | "size":"60x60", 85 | "scale":"3x", 86 | "filename":"Icon-App-60x60@3x.png" 87 | }, 88 | { 89 | "idiom":"ipad", 90 | "size":"20x20", 91 | "scale":"1x", 92 | "filename":"Icon-App-20x20@1x.png" 93 | }, 94 | { 95 | "idiom":"ipad", 96 | "size":"20x20", 97 | "scale":"2x", 98 | "filename":"Icon-App-20x20@2x.png" 99 | }, 100 | { 101 | "idiom":"ipad", 102 | "size":"29x29", 103 | "scale":"1x", 104 | "filename":"Icon-App-29x29@1x.png" 105 | }, 106 | { 107 | "idiom":"ipad", 108 | "size":"29x29", 109 | "scale":"2x", 110 | "filename":"Icon-App-29x29@2x.png" 111 | }, 112 | { 113 | "idiom":"ipad", 114 | "size":"40x40", 115 | "scale":"1x", 116 | "filename":"Icon-App-40x40@1x.png" 117 | }, 118 | { 119 | "idiom":"ipad", 120 | "size":"40x40", 121 | "scale":"2x", 122 | "filename":"Icon-App-40x40@2x.png" 123 | }, 124 | { 125 | "size" : "50x50", 126 | "idiom" : "ipad", 127 | "filename" : "Icon-Small-50x50@1x.png", 128 | "scale" : "1x" 129 | }, 130 | { 131 | "size" : "50x50", 132 | "idiom" : "ipad", 133 | "filename" : "Icon-Small-50x50@2x.png", 134 | "scale" : "2x" 135 | }, 136 | { 137 | "idiom":"ipad", 138 | "size":"72x72", 139 | "scale":"1x", 140 | "filename":"Icon-App-72x72@1x.png" 141 | }, 142 | { 143 | "idiom":"ipad", 144 | "size":"72x72", 145 | "scale":"2x", 146 | "filename":"Icon-App-72x72@2x.png" 147 | }, 148 | { 149 | "idiom":"ipad", 150 | "size":"76x76", 151 | "scale":"1x", 152 | "filename":"Icon-App-76x76@1x.png" 153 | }, 154 | { 155 | "idiom":"ipad", 156 | "size":"76x76", 157 | "scale":"2x", 158 | "filename":"Icon-App-76x76@2x.png" 159 | }, 160 | { 161 | "idiom":"ipad", 162 | "size":"83.5x83.5", 163 | "scale":"2x", 164 | "filename":"Icon-App-83.5x83.5@2x.png" 165 | } 166 | ], 167 | "info":{ 168 | "version":1, 169 | "author":"Doruk Gezici" 170 | } 171 | } 172 | 173 | if os.access("AppIcon.appiconset", os.W_OK) == False: 174 | os.mkdir("AppIcon.appiconset") 175 | file = open("AppIcon.appiconset/Contents.json", mode="w") 176 | json.dump(json_data, file, indent=4, sort_keys=True, separators=(',', ':')) 177 | file.close() 178 | 179 | def get_size(image): 180 | size_str = image["size"] 181 | i = size_str.find("x") 182 | size = (float(size_str[:i]),float(size_str[i+1:])) 183 | scale_str = image["scale"] 184 | j = scale_str.find("x") 185 | scale = int(scale_str[:j]) 186 | return (int(scale * float(size[0])), int(scale * float(size[1]))) 187 | 188 | with open("AppIcon.appiconset/Contents.json", mode="r") as data_file: 189 | data = json.load(data_file) 190 | images = data["images"] 191 | try: 192 | im = Image.open(filename) 193 | print("Image found:", im.format, im.size, im.mode) 194 | print("-"*50) 195 | except IOError: 196 | print("Image not found") 197 | 198 | for image in images: 199 | size = get_size(image) 200 | out = im.resize(size, Image.ANTIALIAS) 201 | out.save("AppIcon.appiconset/"+image["filename"], format="PNG") 202 | print("Image generated: {0}".format(size)) 203 | 204 | print("-"*50, "\nGenerator completed. Copy the 'AppIcon.appiconset' folder to your Xcode project assets and your app icon is good to go!\nGitHub: @dorukgezici -> https://github.com/dorukgezici/iOSAppIconGenerator.git\n") 205 | -------------------------------------------------------------------------------- /py2app/iOSAppIconGenerator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os, sys, json, subprocess 4 | from PIL import Image, ImageFilter 5 | import tkinter as tk 6 | from tkinter.filedialog import askopenfilename, askdirectory 7 | 8 | class Data: 9 | supported_img_types = [".jpg", ".png"] 10 | image_path = "" 11 | save_path = os.path.dirname(__file__) 12 | if ".app" in save_path: 13 | while not save_path.endswith(".app"): 14 | save_path = os.path.dirname(save_path) 15 | 16 | json_data = { 17 | "images":[ 18 | { 19 | "idiom":"iphone", 20 | "size":"20x20", 21 | "scale":"2x", 22 | "filename":"Icon-App-20x20@2x.png" 23 | }, 24 | { 25 | "idiom":"iphone", 26 | "size":"20x20", 27 | "scale":"3x", 28 | "filename":"Icon-App-20x20@3x.png" 29 | }, 30 | { 31 | "idiom":"iphone", 32 | "size":"29x29", 33 | "scale":"1x", 34 | "filename":"Icon-App-29x29@1x.png" 35 | }, 36 | { 37 | "idiom":"iphone", 38 | "size":"29x29", 39 | "scale":"2x", 40 | "filename":"Icon-App-29x29@2x.png" 41 | }, 42 | { 43 | "idiom":"iphone", 44 | "size":"29x29", 45 | "scale":"3x", 46 | "filename":"Icon-App-29x29@3x.png" 47 | }, 48 | { 49 | "idiom":"iphone", 50 | "size":"40x40", 51 | "scale":"2x", 52 | "filename":"Icon-App-40x40@2x.png" 53 | }, 54 | { 55 | "idiom":"iphone", 56 | "size":"40x40", 57 | "scale":"3x", 58 | "filename":"Icon-App-40x40@3x.png" 59 | }, 60 | { 61 | "idiom":"iphone", 62 | "size":"57x57", 63 | "scale":"1x", 64 | "filename":"Icon-App-57x57@1x.png" 65 | }, 66 | { 67 | "idiom":"iphone", 68 | "size":"57x57", 69 | "scale":"2x", 70 | "filename":"Icon-App-57x57@2x.png" 71 | }, 72 | { 73 | "idiom":"iphone", 74 | "size":"60x60", 75 | "scale":"2x", 76 | "filename":"Icon-App-60x60@2x.png" 77 | }, 78 | { 79 | "idiom":"iphone", 80 | "size":"60x60", 81 | "scale":"3x", 82 | "filename":"Icon-App-60x60@3x.png" 83 | }, 84 | { 85 | "idiom":"ipad", 86 | "size":"20x20", 87 | "scale":"1x", 88 | "filename":"Icon-App-20x20@1x.png" 89 | }, 90 | { 91 | "idiom":"ipad", 92 | "size":"20x20", 93 | "scale":"2x", 94 | "filename":"Icon-App-20x20@2x.png" 95 | }, 96 | { 97 | "idiom":"ipad", 98 | "size":"29x29", 99 | "scale":"1x", 100 | "filename":"Icon-App-29x29@1x.png" 101 | }, 102 | { 103 | "idiom":"ipad", 104 | "size":"29x29", 105 | "scale":"2x", 106 | "filename":"Icon-App-29x29@2x.png" 107 | }, 108 | { 109 | "idiom":"ipad", 110 | "size":"40x40", 111 | "scale":"1x", 112 | "filename":"Icon-App-40x40@1x.png" 113 | }, 114 | { 115 | "idiom":"ipad", 116 | "size":"40x40", 117 | "scale":"2x", 118 | "filename":"Icon-App-40x40@2x.png" 119 | }, 120 | { 121 | "size" : "50x50", 122 | "idiom" : "ipad", 123 | "filename" : "Icon-Small-50x50@1x.png", 124 | "scale" : "1x" 125 | }, 126 | { 127 | "size" : "50x50", 128 | "idiom" : "ipad", 129 | "filename" : "Icon-Small-50x50@2x.png", 130 | "scale" : "2x" 131 | }, 132 | { 133 | "idiom":"ipad", 134 | "size":"72x72", 135 | "scale":"1x", 136 | "filename":"Icon-App-72x72@1x.png" 137 | }, 138 | { 139 | "idiom":"ipad", 140 | "size":"72x72", 141 | "scale":"2x", 142 | "filename":"Icon-App-72x72@2x.png" 143 | }, 144 | { 145 | "idiom":"ipad", 146 | "size":"76x76", 147 | "scale":"1x", 148 | "filename":"Icon-App-76x76@1x.png" 149 | }, 150 | { 151 | "idiom":"ipad", 152 | "size":"76x76", 153 | "scale":"2x", 154 | "filename":"Icon-App-76x76@2x.png" 155 | }, 156 | { 157 | "idiom":"ipad", 158 | "size":"83.5x83.5", 159 | "scale":"2x", 160 | "filename":"Icon-App-83.5x83.5@2x.png" 161 | } 162 | ], 163 | "info":{ 164 | "version":1, 165 | "author":"Doruk Gezici" 166 | } 167 | } 168 | 169 | def get_image_path(): 170 | Data.image_path = askopenfilename() 171 | chosen_file_path_label["text"] = Data.image_path 172 | return Data.image_path 173 | 174 | def get_save_path(): 175 | Data.save_path = askdirectory() 176 | chosen_save_path_label["text"] = Data.save_path 177 | return Data.save_path 178 | 179 | 180 | def get_size(image): 181 | size_str = image["size"] 182 | i = size_str.find("x") 183 | size = (float(size_str[:i]),float(size_str[i+1:])) 184 | scale_str = image["scale"] 185 | j = scale_str.find("x") 186 | scale = int(scale_str[:j]) 187 | return (int(scale * float(size[0])), int(scale * float(size[1]))) 188 | 189 | def open_saved(): 190 | subprocess.call(["open", Data.save_path + "/AppIcon.appiconset"]) 191 | 192 | 193 | def run(): 194 | if Data.image_path == "": 195 | output_label["text"] = "Choose the image file first." 196 | return 197 | if os.access("{}/AppIcon.appiconset".format(Data.save_path), os.W_OK) == False: 198 | os.mkdir("{}/AppIcon.appiconset".format(Data.save_path)) 199 | file = open("{}/AppIcon.appiconset/Contents.json".format(Data.save_path), mode="w") 200 | json.dump(json_data, file, indent=4, sort_keys=True, separators=(',', ':')) 201 | file.close() 202 | 203 | with open("{}/AppIcon.appiconset/Contents.json".format(Data.save_path), mode="r") as data_file: 204 | data = json.load(data_file) 205 | images = data["images"] 206 | try: 207 | im = Image.open(Data.image_path) 208 | output_label["text"] = "Image specs: {} {} {}\n{}\n".format(im.format, im.size, im.mode, "-"*35) 209 | for image in images: 210 | size = get_size(image) 211 | out = im.resize(size, Image.ANTIALIAS) 212 | out.save("{}/AppIcon.appiconset/{}".format(Data.save_path, image["filename"]), format="PNG") 213 | output_label["text"] += "Image generated: {}\n".format(size) 214 | open_saved_button.grid(row=1, column=0, columnspan=2, sticky=tk.W+tk.E) 215 | except IOError: 216 | output_label["text"] = "Please select a supported image file." 217 | 218 | 219 | def clear(): 220 | Data.image_path = "" 221 | Data.save_path = os.path.dirname(__file__) 222 | output_label["text"] = "" 223 | chosen_file_path_label["text"] = "" 224 | chosen_save_path_label["text"] = "" 225 | open_saved_button.grid_remove() 226 | if ".app" in Data.save_path: 227 | while not Data.save_path.endswith(".app"): 228 | Data.save_path = os.path.dirname(Data.save_path) 229 | 230 | root = tk.Tk() 231 | root.title("iOS App Icon Generator by @dorukgezici") 232 | root.geometry("640x480+350+150") 233 | 234 | choose_frame = tk.Frame(root) 235 | choose_frame.pack(padx=30, pady=30) 236 | 237 | choose_file_labelframe = tk.LabelFrame(choose_frame, text="Choose Image File") 238 | choose_file_labelframe.grid(row=0, column=0, padx=10) 239 | choose_file_button = tk.Button(choose_file_labelframe, text="Image", command=get_image_path) 240 | choose_file_button.pack() 241 | chosen_file_path_label = tk.Label(choose_file_labelframe) 242 | chosen_file_path_label.pack() 243 | 244 | choose_save_labelframe = tk.LabelFrame(choose_frame, text="Choose Path to Save") 245 | choose_save_labelframe.grid(row=0, column=1, padx=10) 246 | choose_save_button = tk.Button(choose_save_labelframe, text="Directory", command=get_save_path) 247 | choose_save_button.pack() 248 | chosen_save_path_label = tk.Label(choose_save_labelframe) 249 | chosen_save_path_label.pack() 250 | 251 | open_saved_button = tk.Button(choose_frame, text="Show in Finder", command=open_saved) 252 | open_saved_button.grid(row=1, column=0, columnspan=2, sticky=tk.W+tk.E) 253 | open_saved_button.grid_remove() 254 | 255 | output_label = tk.Label(root, text="") 256 | output_label.pack() 257 | 258 | control_frame = tk.Frame(root) 259 | control_frame.pack() 260 | 261 | run_button = tk.Button(control_frame, text="RUN", command=run) 262 | run_button.pack(side="left") 263 | 264 | clear_button = tk.Button(control_frame, text="CLEAR", command=clear) 265 | clear_button.pack(side="left") 266 | 267 | quit_button = tk.Button(control_frame, text="QUIT", command=root.destroy) 268 | quit_button.pack(side="right") 269 | 270 | root.lift() 271 | root.mainloop() 272 | -------------------------------------------------------------------------------- /py2app/macholib_patch.py: -------------------------------------------------------------------------------- 1 | """ 2 | Monkey-patch macholib to fix "dyld_find() got an unexpected keyword argument 'loader'". 3 | 4 | Add 'import macholib_patch' to the top of set_py2app.py 5 | """ 6 | 7 | import macholib 8 | #print("~"*60 + "macholib verion: "+macholib.__version__) 9 | if macholib.__version__ <= "1.7": 10 | print("Applying macholib patch...") 11 | import macholib.dyld 12 | import macholib.MachOGraph 13 | dyld_find_1_7 = macholib.dyld.dyld_find 14 | def dyld_find(name, loader=None, **kwargs): 15 | #print("~"*60 + "calling alternate dyld_find") 16 | if loader is not None: 17 | kwargs['loader_path'] = loader 18 | return dyld_find_1_7(name, **kwargs) 19 | macholib.MachOGraph.dyld_find = dyld_find 20 | -------------------------------------------------------------------------------- /py2app/setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from setuptools import setup 4 | import macholib_patch 5 | 6 | APP = ['iOSAppIconGenerator.py'] 7 | APP_NAME = "iOSAppIconGenerator" 8 | DATA_FILES = [] 9 | OPTIONS = { 10 | 'argv_emulation': False, 11 | 'packages': 'PIL', 12 | 'plist': { 13 | 'CFBundleName': APP_NAME, 14 | 'CFBundleDisplayName': APP_NAME, 15 | 'CFBundleGetInfoString': "Generates iOS App Icons.", 16 | 'CFBundleIdentifier': "com.dorukgezici.iOSAppIconGenerator", 17 | 'CFBundleVersion': "0.1.0", 18 | 'CFBundleShortVersionString': "0.1.0", 19 | 'NSHumanReadableCopyright': u"Copyright © 2017, Doruk Gezici, All Rights Reserved" 20 | } 21 | } 22 | 23 | setup( 24 | app=APP, 25 | data_files=DATA_FILES, 26 | options={'py2app': OPTIONS}, 27 | setup_requires=['py2app'], 28 | ) 29 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Pillow>=3.3.1 2 | --------------------------------------------------------------------------------