├── .gitignore ├── Demo Project ├── MOOMaskedIconView Demo.xcodeproj │ └── project.pbxproj └── MOOMaskedIconView Demo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Images │ ├── Default.png │ ├── Default@2x.png │ ├── Gray-Bar.png │ ├── Gray-Bar@2x.png │ ├── Icon.png │ ├── Icon@2x.png │ └── Icons │ │ ├── Arrow.pdf │ │ ├── Beer.pdf │ │ ├── Beer.png │ │ ├── Beer@2x.png │ │ ├── Chat Bubble.pdf │ │ ├── Eye.pdf │ │ ├── Icons to Cache.plist │ │ ├── Location.pdf │ │ ├── Overlay.pdf │ │ ├── PSDs │ │ ├── Arrow.psd │ │ ├── Beer.psd │ │ ├── Chat Bubble.psd │ │ ├── Eye.psd │ │ ├── Location.psd │ │ ├── Overlay.psd │ │ ├── Rounded Rect.psd │ │ └── Search.psd │ │ ├── Rounded Rect.pdf │ │ └── Search.pdf │ ├── MOOMaskedIconView Demo-Info.plist │ ├── MOOMaskedIconView Demo-Prefix.pch │ ├── ViewController.h │ ├── ViewController.m │ ├── en.lproj │ └── InfoPlist.strings │ └── main.m ├── Editor Support ├── Photoshop │ ├── CGPDFIcon.joboptions │ └── Exporting Icons.atn └── README.md ├── MOOMaskedIconView.podspec ├── MOOMaskedIconView ├── AHHelper.h ├── MOOCGImageWrapper.h ├── MOOCGImageWrapper.m ├── MOOMaskedIconView.h ├── MOOMaskedIconView.m ├── MOOResourceList.h ├── MOOResourceList.m ├── MOOStyleTrait.h └── MOOStyleTrait.m ├── README.md └── UNLICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X Finder and whatnot 2 | .DS_Store 3 | 4 | 5 | # Sparkle distribution Private Key (Don't check me in!) 6 | dsa_priv.pem 7 | 8 | 9 | # XCode (and ancestors) per-user config (very noisy, and not relevant) 10 | xcuserdata 11 | *.mode1 12 | *.mode1v3 13 | *.mode2v3 14 | *.perspective 15 | *.perspectivev3 16 | *.pbxuser 17 | *.xcuserdata 18 | *.xcuserstate 19 | *.xcworkspace 20 | 21 | # Moved aside 22 | *.moved-aside 23 | 24 | # Generated files 25 | VersionX-revision.h 26 | 27 | 28 | # build products 29 | build/ 30 | 31 | 32 | # Other source repository archive directories (protects when importing) 33 | .hg 34 | .svn 35 | CVS 36 | 37 | 38 | # automatic backup files 39 | *~.nib 40 | *.swp 41 | *~ 42 | *(Autosaved).rtfd/ 43 | Backup[ ]of[ ]*.pages/ 44 | Backup[ ]of[ ]*.key/ 45 | Backup[ ]of[ ]*.numbers/ 46 | [#]*[#] 47 | -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9C36411414F28E33005DE008 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C36411314F28E33005DE008 /* UIKit.framework */; }; 11 | 9C36411614F28E33005DE008 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C36411514F28E33005DE008 /* Foundation.framework */; }; 12 | 9C36411814F28E33005DE008 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C36411714F28E33005DE008 /* CoreGraphics.framework */; }; 13 | 9C36411E14F28E33005DE008 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9C36411C14F28E33005DE008 /* InfoPlist.strings */; }; 14 | 9C36412014F28E33005DE008 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C36411F14F28E33005DE008 /* main.m */; }; 15 | 9C36412414F28E33005DE008 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C36412314F28E33005DE008 /* AppDelegate.m */; }; 16 | 9C36412714F28E33005DE008 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C36412614F28E33005DE008 /* ViewController.m */; }; 17 | 9C777F3B14FB095700C1565A /* MOOStyleTrait.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C777F3A14FB095700C1565A /* MOOStyleTrait.m */; }; 18 | 9C78D8C914F8490D0059D260 /* Arrow.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 9C78D8C014F8490D0059D260 /* Arrow.pdf */; }; 19 | 9C78D8CA14F8490D0059D260 /* Beer.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 9C78D8C114F8490D0059D260 /* Beer.pdf */; }; 20 | 9C78D8CB14F8490D0059D260 /* Beer.png in Resources */ = {isa = PBXBuildFile; fileRef = 9C78D8C214F8490D0059D260 /* Beer.png */; }; 21 | 9C78D8CC14F8490D0059D260 /* Beer@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9C78D8C314F8490D0059D260 /* Beer@2x.png */; }; 22 | 9C78D8CD14F8490D0059D260 /* Chat Bubble.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 9C78D8C414F8490D0059D260 /* Chat Bubble.pdf */; }; 23 | 9C78D8CE14F8490D0059D260 /* Eye.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 9C78D8C514F8490D0059D260 /* Eye.pdf */; }; 24 | 9C78D8CF14F8490D0059D260 /* Location.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 9C78D8C614F8490D0059D260 /* Location.pdf */; }; 25 | 9C78D8D014F8490D0059D260 /* Overlay.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 9C78D8C714F8490D0059D260 /* Overlay.pdf */; }; 26 | 9C78D8D114F8490D0059D260 /* Search.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 9C78D8C814F8490D0059D260 /* Search.pdf */; }; 27 | 9C78D8D414F849240059D260 /* Gray-Bar.png in Resources */ = {isa = PBXBuildFile; fileRef = 9C78D8D214F849240059D260 /* Gray-Bar.png */; }; 28 | 9C78D8D514F849240059D260 /* Gray-Bar@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9C78D8D314F849240059D260 /* Gray-Bar@2x.png */; }; 29 | 9C7BC2EA14F9385E0052D1E4 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 9C7BC2E614F9385E0052D1E4 /* Default.png */; }; 30 | 9C7BC2EB14F9385E0052D1E4 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9C7BC2E714F9385E0052D1E4 /* Default@2x.png */; }; 31 | 9C7BC2EC14F9385E0052D1E4 /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 9C7BC2E814F9385E0052D1E4 /* Icon.png */; }; 32 | 9C7BC2ED14F9385E0052D1E4 /* Icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9C7BC2E914F9385E0052D1E4 /* Icon@2x.png */; }; 33 | 9C941C7614FBD253002DA298 /* MOOMaskedIconView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C941C7514FBD0CA002DA298 /* MOOMaskedIconView.m */; }; 34 | 9C941C8014FBEAFC002DA298 /* Icons to Cache.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9C941C7F14FBEAFC002DA298 /* Icons to Cache.plist */; }; 35 | 9C941C8414FBF56C002DA298 /* MOOResourceList.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C941C8314FBF56C002DA298 /* MOOResourceList.m */; }; 36 | 9C941C8714FC0CB1002DA298 /* MOOCGImageWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C941C8614FC0CB1002DA298 /* MOOCGImageWrapper.m */; }; 37 | 9CFA4C5114F85C2F00956B67 /* Rounded Rect.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 9CFA4C5014F85C2F00956B67 /* Rounded Rect.pdf */; }; 38 | 9CFA4C5814F87B2C00956B67 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9CFA4C5714F87B2C00956B67 /* Accelerate.framework */; settings = {ATTRIBUTES = (Required, ); }; }; 39 | /* End PBXBuildFile section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | 9C36410F14F28E33005DE008 /* MOOMaskedIconView Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MOOMaskedIconView Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 9C36411314F28E33005DE008 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 44 | 9C36411514F28E33005DE008 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 45 | 9C36411714F28E33005DE008 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 46 | 9C36411B14F28E33005DE008 /* MOOMaskedIconView Demo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MOOMaskedIconView Demo-Info.plist"; sourceTree = ""; }; 47 | 9C36411D14F28E33005DE008 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 48 | 9C36411F14F28E33005DE008 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 49 | 9C36412114F28E33005DE008 /* MOOMaskedIconView Demo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MOOMaskedIconView Demo-Prefix.pch"; sourceTree = ""; }; 50 | 9C36412214F28E33005DE008 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 51 | 9C36412314F28E33005DE008 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 52 | 9C36412514F28E33005DE008 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 53 | 9C36412614F28E33005DE008 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 54 | 9C54E2FB14FDC171001EA2DB /* AHHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AHHelper.h; path = ../MOOMaskedIconView/AHHelper.h; sourceTree = ""; }; 55 | 9C777F3914FB095700C1565A /* MOOStyleTrait.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MOOStyleTrait.h; path = ../MOOMaskedIconView/MOOStyleTrait.h; sourceTree = ""; }; 56 | 9C777F3A14FB095700C1565A /* MOOStyleTrait.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MOOStyleTrait.m; path = ../MOOMaskedIconView/MOOStyleTrait.m; sourceTree = ""; }; 57 | 9C78D8C014F8490D0059D260 /* Arrow.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = Arrow.pdf; path = "MOOMaskedIconView Demo/Images/Icons/Arrow.pdf"; sourceTree = ""; }; 58 | 9C78D8C114F8490D0059D260 /* Beer.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = Beer.pdf; path = "MOOMaskedIconView Demo/Images/Icons/Beer.pdf"; sourceTree = ""; }; 59 | 9C78D8C214F8490D0059D260 /* Beer.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Beer.png; path = "MOOMaskedIconView Demo/Images/Icons/Beer.png"; sourceTree = ""; }; 60 | 9C78D8C314F8490D0059D260 /* Beer@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Beer@2x.png"; path = "MOOMaskedIconView Demo/Images/Icons/Beer@2x.png"; sourceTree = ""; }; 61 | 9C78D8C414F8490D0059D260 /* Chat Bubble.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = "Chat Bubble.pdf"; path = "MOOMaskedIconView Demo/Images/Icons/Chat Bubble.pdf"; sourceTree = ""; }; 62 | 9C78D8C514F8490D0059D260 /* Eye.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = Eye.pdf; path = "MOOMaskedIconView Demo/Images/Icons/Eye.pdf"; sourceTree = ""; }; 63 | 9C78D8C614F8490D0059D260 /* Location.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = Location.pdf; path = "MOOMaskedIconView Demo/Images/Icons/Location.pdf"; sourceTree = ""; }; 64 | 9C78D8C714F8490D0059D260 /* Overlay.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = Overlay.pdf; path = "MOOMaskedIconView Demo/Images/Icons/Overlay.pdf"; sourceTree = ""; }; 65 | 9C78D8C814F8490D0059D260 /* Search.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = Search.pdf; path = "MOOMaskedIconView Demo/Images/Icons/Search.pdf"; sourceTree = ""; }; 66 | 9C78D8D214F849240059D260 /* Gray-Bar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Gray-Bar.png"; path = "Images/Gray-Bar.png"; sourceTree = ""; }; 67 | 9C78D8D314F849240059D260 /* Gray-Bar@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Gray-Bar@2x.png"; path = "Images/Gray-Bar@2x.png"; sourceTree = ""; }; 68 | 9C7BC2E614F9385E0052D1E4 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Default.png; path = Images/Default.png; sourceTree = ""; }; 69 | 9C7BC2E714F9385E0052D1E4 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default@2x.png"; path = "Images/Default@2x.png"; sourceTree = ""; }; 70 | 9C7BC2E814F9385E0052D1E4 /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Icon.png; path = Images/Icon.png; sourceTree = ""; }; 71 | 9C7BC2E914F9385E0052D1E4 /* Icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Icon@2x.png"; path = "Images/Icon@2x.png"; sourceTree = ""; }; 72 | 9C941C7414FBD0CA002DA298 /* MOOMaskedIconView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MOOMaskedIconView.h; path = ../MOOMaskedIconView/MOOMaskedIconView.h; sourceTree = ""; }; 73 | 9C941C7514FBD0CA002DA298 /* MOOMaskedIconView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MOOMaskedIconView.m; path = ../MOOMaskedIconView/MOOMaskedIconView.m; sourceTree = ""; }; 74 | 9C941C7F14FBEAFC002DA298 /* Icons to Cache.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "Icons to Cache.plist"; path = "MOOMaskedIconView Demo/Images/Icons/Icons to Cache.plist"; sourceTree = ""; }; 75 | 9C941C8214FBF56C002DA298 /* MOOResourceList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MOOResourceList.h; path = ../MOOMaskedIconView/MOOResourceList.h; sourceTree = ""; }; 76 | 9C941C8314FBF56C002DA298 /* MOOResourceList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MOOResourceList.m; path = ../MOOMaskedIconView/MOOResourceList.m; sourceTree = ""; }; 77 | 9C941C8514FC0CB1002DA298 /* MOOCGImageWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MOOCGImageWrapper.h; path = ../MOOMaskedIconView/MOOCGImageWrapper.h; sourceTree = ""; }; 78 | 9C941C8614FC0CB1002DA298 /* MOOCGImageWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MOOCGImageWrapper.m; path = ../MOOMaskedIconView/MOOCGImageWrapper.m; sourceTree = ""; }; 79 | 9CFA4C5014F85C2F00956B67 /* Rounded Rect.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = "Rounded Rect.pdf"; path = "MOOMaskedIconView Demo/Images/Icons/Rounded Rect.pdf"; sourceTree = ""; }; 80 | 9CFA4C5714F87B2C00956B67 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; 81 | /* End PBXFileReference section */ 82 | 83 | /* Begin PBXFrameworksBuildPhase section */ 84 | 9C36410C14F28E33005DE008 /* Frameworks */ = { 85 | isa = PBXFrameworksBuildPhase; 86 | buildActionMask = 2147483647; 87 | files = ( 88 | 9CFA4C5814F87B2C00956B67 /* Accelerate.framework in Frameworks */, 89 | 9C36411414F28E33005DE008 /* UIKit.framework in Frameworks */, 90 | 9C36411614F28E33005DE008 /* Foundation.framework in Frameworks */, 91 | 9C36411814F28E33005DE008 /* CoreGraphics.framework in Frameworks */, 92 | ); 93 | runOnlyForDeploymentPostprocessing = 0; 94 | }; 95 | /* End PBXFrameworksBuildPhase section */ 96 | 97 | /* Begin PBXGroup section */ 98 | 9C36410414F28E33005DE008 = { 99 | isa = PBXGroup; 100 | children = ( 101 | 9C36411914F28E33005DE008 /* MOOMaskedIconView Demo */, 102 | 9C36413014F28E78005DE008 /* External */, 103 | 9C36411214F28E33005DE008 /* Frameworks */, 104 | 9C36411014F28E33005DE008 /* Products */, 105 | ); 106 | sourceTree = ""; 107 | }; 108 | 9C36411014F28E33005DE008 /* Products */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 9C36410F14F28E33005DE008 /* MOOMaskedIconView Demo.app */, 112 | ); 113 | name = Products; 114 | sourceTree = ""; 115 | }; 116 | 9C36411214F28E33005DE008 /* Frameworks */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 9CFA4C5714F87B2C00956B67 /* Accelerate.framework */, 120 | 9C36411314F28E33005DE008 /* UIKit.framework */, 121 | 9C36411514F28E33005DE008 /* Foundation.framework */, 122 | 9C36411714F28E33005DE008 /* CoreGraphics.framework */, 123 | ); 124 | name = Frameworks; 125 | sourceTree = ""; 126 | }; 127 | 9C36411914F28E33005DE008 /* MOOMaskedIconView Demo */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 9C36412214F28E33005DE008 /* AppDelegate.h */, 131 | 9C36412314F28E33005DE008 /* AppDelegate.m */, 132 | 9C36412514F28E33005DE008 /* ViewController.h */, 133 | 9C36412614F28E33005DE008 /* ViewController.m */, 134 | 9C78D8BF14F848F90059D260 /* Images */, 135 | 9C36411A14F28E33005DE008 /* Supporting Files */, 136 | ); 137 | path = "MOOMaskedIconView Demo"; 138 | sourceTree = ""; 139 | }; 140 | 9C36411A14F28E33005DE008 /* Supporting Files */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 9C36411B14F28E33005DE008 /* MOOMaskedIconView Demo-Info.plist */, 144 | 9C36411C14F28E33005DE008 /* InfoPlist.strings */, 145 | 9C36411F14F28E33005DE008 /* main.m */, 146 | 9C36412114F28E33005DE008 /* MOOMaskedIconView Demo-Prefix.pch */, 147 | ); 148 | name = "Supporting Files"; 149 | sourceTree = ""; 150 | }; 151 | 9C36413014F28E78005DE008 /* External */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 9C36413114F28E7F005DE008 /* MOOMaskedIconView */, 155 | ); 156 | name = External; 157 | sourceTree = ""; 158 | }; 159 | 9C36413114F28E7F005DE008 /* MOOMaskedIconView */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 9C54E2FB14FDC171001EA2DB /* AHHelper.h */, 163 | 9C941C8514FC0CB1002DA298 /* MOOCGImageWrapper.h */, 164 | 9C941C8614FC0CB1002DA298 /* MOOCGImageWrapper.m */, 165 | 9C941C8214FBF56C002DA298 /* MOOResourceList.h */, 166 | 9C941C8314FBF56C002DA298 /* MOOResourceList.m */, 167 | 9C941C7414FBD0CA002DA298 /* MOOMaskedIconView.h */, 168 | 9C941C7514FBD0CA002DA298 /* MOOMaskedIconView.m */, 169 | 9C777F3914FB095700C1565A /* MOOStyleTrait.h */, 170 | 9C777F3A14FB095700C1565A /* MOOStyleTrait.m */, 171 | ); 172 | name = MOOMaskedIconView; 173 | sourceTree = ""; 174 | }; 175 | 9C36414014F2A2BF005DE008 /* Icons */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | 9C941C7F14FBEAFC002DA298 /* Icons to Cache.plist */, 179 | 9C78D8C014F8490D0059D260 /* Arrow.pdf */, 180 | 9C78D8C114F8490D0059D260 /* Beer.pdf */, 181 | 9C78D8C214F8490D0059D260 /* Beer.png */, 182 | 9C78D8C314F8490D0059D260 /* Beer@2x.png */, 183 | 9C78D8C414F8490D0059D260 /* Chat Bubble.pdf */, 184 | 9C78D8C514F8490D0059D260 /* Eye.pdf */, 185 | 9C78D8C614F8490D0059D260 /* Location.pdf */, 186 | 9C78D8C714F8490D0059D260 /* Overlay.pdf */, 187 | 9CFA4C5014F85C2F00956B67 /* Rounded Rect.pdf */, 188 | 9C78D8C814F8490D0059D260 /* Search.pdf */, 189 | ); 190 | name = Icons; 191 | path = ..; 192 | sourceTree = ""; 193 | }; 194 | 9C78D8BF14F848F90059D260 /* Images */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | 9C7BC2E614F9385E0052D1E4 /* Default.png */, 198 | 9C7BC2E714F9385E0052D1E4 /* Default@2x.png */, 199 | 9C78D8D214F849240059D260 /* Gray-Bar.png */, 200 | 9C78D8D314F849240059D260 /* Gray-Bar@2x.png */, 201 | 9C7BC2E814F9385E0052D1E4 /* Icon.png */, 202 | 9C7BC2E914F9385E0052D1E4 /* Icon@2x.png */, 203 | 9C36414014F2A2BF005DE008 /* Icons */, 204 | ); 205 | name = Images; 206 | sourceTree = ""; 207 | }; 208 | /* End PBXGroup section */ 209 | 210 | /* Begin PBXNativeTarget section */ 211 | 9C36410E14F28E33005DE008 /* MOOMaskedIconView Demo */ = { 212 | isa = PBXNativeTarget; 213 | buildConfigurationList = 9C36412D14F28E33005DE008 /* Build configuration list for PBXNativeTarget "MOOMaskedIconView Demo" */; 214 | buildPhases = ( 215 | 9C36410B14F28E33005DE008 /* Sources */, 216 | 9C36410C14F28E33005DE008 /* Frameworks */, 217 | 9C36410D14F28E33005DE008 /* Resources */, 218 | ); 219 | buildRules = ( 220 | ); 221 | dependencies = ( 222 | ); 223 | name = "MOOMaskedIconView Demo"; 224 | productName = "MOOMaskedIconView Demo"; 225 | productReference = 9C36410F14F28E33005DE008 /* MOOMaskedIconView Demo.app */; 226 | productType = "com.apple.product-type.application"; 227 | }; 228 | /* End PBXNativeTarget section */ 229 | 230 | /* Begin PBXProject section */ 231 | 9C36410614F28E33005DE008 /* Project object */ = { 232 | isa = PBXProject; 233 | attributes = { 234 | LastUpgradeCheck = 0430; 235 | ORGANIZATIONNAME = pandolph; 236 | }; 237 | buildConfigurationList = 9C36410914F28E33005DE008 /* Build configuration list for PBXProject "MOOMaskedIconView Demo" */; 238 | compatibilityVersion = "Xcode 3.2"; 239 | developmentRegion = English; 240 | hasScannedForEncodings = 0; 241 | knownRegions = ( 242 | en, 243 | ); 244 | mainGroup = 9C36410414F28E33005DE008; 245 | productRefGroup = 9C36411014F28E33005DE008 /* Products */; 246 | projectDirPath = ""; 247 | projectRoot = ""; 248 | targets = ( 249 | 9C36410E14F28E33005DE008 /* MOOMaskedIconView Demo */, 250 | ); 251 | }; 252 | /* End PBXProject section */ 253 | 254 | /* Begin PBXResourcesBuildPhase section */ 255 | 9C36410D14F28E33005DE008 /* Resources */ = { 256 | isa = PBXResourcesBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | 9C36411E14F28E33005DE008 /* InfoPlist.strings in Resources */, 260 | 9C78D8C914F8490D0059D260 /* Arrow.pdf in Resources */, 261 | 9C78D8CA14F8490D0059D260 /* Beer.pdf in Resources */, 262 | 9C78D8CB14F8490D0059D260 /* Beer.png in Resources */, 263 | 9C78D8CC14F8490D0059D260 /* Beer@2x.png in Resources */, 264 | 9C78D8CD14F8490D0059D260 /* Chat Bubble.pdf in Resources */, 265 | 9C78D8CE14F8490D0059D260 /* Eye.pdf in Resources */, 266 | 9C78D8CF14F8490D0059D260 /* Location.pdf in Resources */, 267 | 9C78D8D014F8490D0059D260 /* Overlay.pdf in Resources */, 268 | 9C78D8D114F8490D0059D260 /* Search.pdf in Resources */, 269 | 9C78D8D414F849240059D260 /* Gray-Bar.png in Resources */, 270 | 9C78D8D514F849240059D260 /* Gray-Bar@2x.png in Resources */, 271 | 9CFA4C5114F85C2F00956B67 /* Rounded Rect.pdf in Resources */, 272 | 9C7BC2EA14F9385E0052D1E4 /* Default.png in Resources */, 273 | 9C7BC2EB14F9385E0052D1E4 /* Default@2x.png in Resources */, 274 | 9C7BC2EC14F9385E0052D1E4 /* Icon.png in Resources */, 275 | 9C7BC2ED14F9385E0052D1E4 /* Icon@2x.png in Resources */, 276 | 9C941C8014FBEAFC002DA298 /* Icons to Cache.plist in Resources */, 277 | ); 278 | runOnlyForDeploymentPostprocessing = 0; 279 | }; 280 | /* End PBXResourcesBuildPhase section */ 281 | 282 | /* Begin PBXSourcesBuildPhase section */ 283 | 9C36410B14F28E33005DE008 /* Sources */ = { 284 | isa = PBXSourcesBuildPhase; 285 | buildActionMask = 2147483647; 286 | files = ( 287 | 9C941C7614FBD253002DA298 /* MOOMaskedIconView.m in Sources */, 288 | 9C36412014F28E33005DE008 /* main.m in Sources */, 289 | 9C36412414F28E33005DE008 /* AppDelegate.m in Sources */, 290 | 9C36412714F28E33005DE008 /* ViewController.m in Sources */, 291 | 9C777F3B14FB095700C1565A /* MOOStyleTrait.m in Sources */, 292 | 9C941C8414FBF56C002DA298 /* MOOResourceList.m in Sources */, 293 | 9C941C8714FC0CB1002DA298 /* MOOCGImageWrapper.m in Sources */, 294 | ); 295 | runOnlyForDeploymentPostprocessing = 0; 296 | }; 297 | /* End PBXSourcesBuildPhase section */ 298 | 299 | /* Begin PBXVariantGroup section */ 300 | 9C36411C14F28E33005DE008 /* InfoPlist.strings */ = { 301 | isa = PBXVariantGroup; 302 | children = ( 303 | 9C36411D14F28E33005DE008 /* en */, 304 | ); 305 | name = InfoPlist.strings; 306 | sourceTree = ""; 307 | }; 308 | /* End PBXVariantGroup section */ 309 | 310 | /* Begin XCBuildConfiguration section */ 311 | 9C36412B14F28E33005DE008 /* Debug */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | ALWAYS_SEARCH_USER_PATHS = NO; 315 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 316 | CLANG_ENABLE_OBJC_ARC = YES; 317 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 318 | COPY_PHASE_STRIP = NO; 319 | GCC_C_LANGUAGE_STANDARD = gnu99; 320 | GCC_DYNAMIC_NO_PIC = NO; 321 | GCC_OPTIMIZATION_LEVEL = 0; 322 | GCC_PREPROCESSOR_DEFINITIONS = ( 323 | "DEBUG=1", 324 | "$(inherited)", 325 | ); 326 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 327 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 328 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 329 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 330 | GCC_WARN_UNUSED_VARIABLE = YES; 331 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 332 | SDKROOT = iphoneos; 333 | }; 334 | name = Debug; 335 | }; 336 | 9C36412C14F28E33005DE008 /* Release */ = { 337 | isa = XCBuildConfiguration; 338 | buildSettings = { 339 | ALWAYS_SEARCH_USER_PATHS = NO; 340 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 341 | CLANG_ENABLE_OBJC_ARC = YES; 342 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 343 | COPY_PHASE_STRIP = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 346 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 347 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 350 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 351 | SDKROOT = iphoneos; 352 | VALIDATE_PRODUCT = YES; 353 | }; 354 | name = Release; 355 | }; 356 | 9C36412E14F28E33005DE008 /* Debug */ = { 357 | isa = XCBuildConfiguration; 358 | buildSettings = { 359 | GCC_OPTIMIZATION_LEVEL = s; 360 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 361 | GCC_PREFIX_HEADER = "MOOMaskedIconView Demo/MOOMaskedIconView Demo-Prefix.pch"; 362 | INFOPLIST_FILE = "MOOMaskedIconView Demo/MOOMaskedIconView Demo-Info.plist"; 363 | IPHONEOS_DEPLOYMENT_TARGET = 4.0; 364 | PRODUCT_NAME = "$(TARGET_NAME)"; 365 | WRAPPER_EXTENSION = app; 366 | }; 367 | name = Debug; 368 | }; 369 | 9C36412F14F28E33005DE008 /* Release */ = { 370 | isa = XCBuildConfiguration; 371 | buildSettings = { 372 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 373 | GCC_PREFIX_HEADER = "MOOMaskedIconView Demo/MOOMaskedIconView Demo-Prefix.pch"; 374 | INFOPLIST_FILE = "MOOMaskedIconView Demo/MOOMaskedIconView Demo-Info.plist"; 375 | IPHONEOS_DEPLOYMENT_TARGET = 4.0; 376 | PRODUCT_NAME = "$(TARGET_NAME)"; 377 | WRAPPER_EXTENSION = app; 378 | }; 379 | name = Release; 380 | }; 381 | /* End XCBuildConfiguration section */ 382 | 383 | /* Begin XCConfigurationList section */ 384 | 9C36410914F28E33005DE008 /* Build configuration list for PBXProject "MOOMaskedIconView Demo" */ = { 385 | isa = XCConfigurationList; 386 | buildConfigurations = ( 387 | 9C36412B14F28E33005DE008 /* Debug */, 388 | 9C36412C14F28E33005DE008 /* Release */, 389 | ); 390 | defaultConfigurationIsVisible = 0; 391 | defaultConfigurationName = Release; 392 | }; 393 | 9C36412D14F28E33005DE008 /* Build configuration list for PBXNativeTarget "MOOMaskedIconView Demo" */ = { 394 | isa = XCConfigurationList; 395 | buildConfigurations = ( 396 | 9C36412E14F28E33005DE008 /* Debug */, 397 | 9C36412F14F28E33005DE008 /* Release */, 398 | ); 399 | defaultConfigurationIsVisible = 0; 400 | defaultConfigurationName = Release; 401 | }; 402 | /* End XCConfigurationList section */ 403 | }; 404 | rootObject = 9C36410614F28E33005DE008 /* Project object */; 405 | } 406 | -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MOOMaskedIconView Demo 4 | // 5 | // Created by Peyton Randolph on 2/20/12. 6 | // 7 | 8 | #import 9 | 10 | @class ViewController; 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (strong, nonatomic) UIWindow *window; 15 | 16 | @property (strong, nonatomic) ViewController *viewController; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MOOMaskedIconView Demo 4 | // 5 | // Created by Peyton Randolph on 2/20/12. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | 10 | #import "ViewController.h" 11 | 12 | #import "MOOResourceList.h" 13 | 14 | @implementation AppDelegate 15 | 16 | @synthesize window = _window; 17 | @synthesize viewController = _viewController; 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 20 | { 21 | // Cache masks listed in "Icons to Cache.plist" in the background 22 | MOOResourceList *iconsToCache = [MOOResourceList listWithPlistNamed:@"Icons to Cache.plist"]; 23 | [[MOOResourceRegistry sharedRegistry] registerList:iconsToCache]; 24 | 25 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 26 | 27 | // Override point for customization after application launch. 28 | self.viewController = [[ViewController alloc] init]; 29 | self.window.rootViewController = self.viewController; 30 | [self.window makeKeyAndVisible]; 31 | 32 | [iconsToCache renderMasksInBackground]; 33 | 34 | return YES; 35 | } 36 | 37 | - (void)applicationWillResignActive:(UIApplication *)application 38 | { 39 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 40 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 41 | } 42 | 43 | - (void)applicationDidEnterBackground:(UIApplication *)application 44 | { 45 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 46 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 47 | } 48 | 49 | - (void)applicationWillEnterForeground:(UIApplication *)application 50 | { 51 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 52 | } 53 | 54 | - (void)applicationDidBecomeActive:(UIApplication *)application 55 | { 56 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 57 | } 58 | 59 | - (void)applicationWillTerminate:(UIApplication *)application 60 | { 61 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Default.png -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Default@2x.png -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Gray-Bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Gray-Bar.png -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Gray-Bar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Gray-Bar@2x.png -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icon.png -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icon@2x.png -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/Arrow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/Arrow.pdf -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/Beer.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/Beer.pdf -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/Beer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/Beer.png -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/Beer@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/Beer@2x.png -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/Chat Bubble.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/Chat Bubble.pdf -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/Eye.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/Eye.pdf -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/Icons to Cache.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Icons 6 | 7 | Search.pdf 8 | Rounded Rect.pdf 9 | Chat Bubble.pdf 10 | Arrow.pdf 11 | Eye.pdf 12 | Beer.pdf 13 | Location.pdf 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/Location.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/Location.pdf -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/Overlay.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/Overlay.pdf -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Arrow.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Arrow.psd -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Beer.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Beer.psd -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Chat Bubble.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Chat Bubble.psd -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Eye.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Eye.psd -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Location.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Location.psd -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Overlay.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Overlay.psd -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Rounded Rect.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Rounded Rect.psd -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Search.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/PSDs/Search.psd -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/Rounded Rect.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/Rounded Rect.pdf -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/Images/Icons/Search.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Demo Project/MOOMaskedIconView Demo/Images/Icons/Search.pdf -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/MOOMaskedIconView Demo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Icon Demo 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFiles 12 | 13 | Icon.png 14 | Icon@2x.png 15 | 16 | CFBundleIdentifier 17 | com.peytn.${PRODUCT_NAME:rfc1034identifier} 18 | CFBundleInfoDictionaryVersion 19 | 6.0 20 | CFBundleName 21 | ${PRODUCT_NAME} 22 | CFBundlePackageType 23 | APPL 24 | CFBundleShortVersionString 25 | 1.0 26 | CFBundleSignature 27 | ???? 28 | CFBundleVersion 29 | 1.0 30 | LSRequiresIPhoneOS 31 | 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/MOOMaskedIconView Demo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'MOOMaskedIconView Demo' target in the 'MOOMaskedIconView Demo' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_4_0 8 | #warning "This project uses features only available in iOS SDK 4.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // MOOMaskedIconView Demo 4 | // 5 | // Created by Peyton Randolph on 2/20/12. 6 | // 7 | 8 | #import 9 | 10 | @class MOOMaskedIconView; 11 | 12 | @interface ViewController : UIViewController 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // MOOMaskedIconView Demo 4 | // 5 | // Created by Peyton Randolph on 2/20/12. 6 | // 7 | 8 | #import "ViewController.h" 9 | 10 | #import 11 | 12 | #import "MOOMaskedIconView.h" 13 | #import "MOOStyleTrait.h" 14 | 15 | #define kBarHeight 64.0f 16 | 17 | @implementation ViewController 18 | 19 | #pragma mark - View lifecycle 20 | 21 | - (void)loadView; 22 | { 23 | [super loadView]; 24 | 25 | self.view.backgroundColor = [UIColor blackColor]; 26 | 27 | /* 28 | * Create toolbar 29 | */ 30 | 31 | UIView *grayBar = [[UIView alloc] initWithFrame:CGRectMake(0.0f, CGRectGetHeight(self.view.frame) - kBarHeight, CGRectGetWidth(self.view.bounds), kBarHeight)]; 32 | grayBar.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Gray-Bar.png"]]; 33 | [self.view addSubview:grayBar]; 34 | 35 | // Appearance trait for small, gray icons 36 | MOOStyleTrait *grayIconTrait = [MOOStyleTrait trait]; 37 | 38 | grayIconTrait.gradientColors = [NSArray arrayWithObjects: 39 | [UIColor colorWithHue:0.0f saturation:0.05f brightness:0.34f alpha:1.0f], 40 | [UIColor colorWithHue:0.0f saturation:0.05f brightness:0.57f alpha:1.0f], nil]; 41 | grayIconTrait.shadowColor = [UIColor colorWithWhite:0.0f alpha:1.0f]; 42 | grayIconTrait.shadowOffset = CGSizeMake(0.0f, -1.0f); 43 | 44 | grayIconTrait.innerShadowColor = [UIColor colorWithWhite:1.0f alpha:0.6f]; 45 | grayIconTrait.innerShadowOffset = CGSizeMake(0.0f, -1.0f); 46 | 47 | // Appearance trait for small, blue icons 48 | MOOStyleTrait *blueIconTrait = [MOOStyleTrait trait]; 49 | blueIconTrait.shadowColor = [UIColor colorWithHue:234.f/360.f saturation:0.91f brightness:1.0f alpha:0.70f]; 50 | blueIconTrait.shadowOffset = CGSizeMake(0.0f, -1.0f); 51 | blueIconTrait.innerShadowColor = [UIColor colorWithHue:212.f/360.f saturation:0.87f brightness:1.0f alpha:0.93]; 52 | blueIconTrait.innerShadowOffset = CGSizeMake(0.0f, -1.0f); 53 | 54 | blueIconTrait.gradientColors = [NSArray arrayWithObjects: 55 | [UIColor colorWithHue:237.0f/360.0f saturation:.83f brightness:.74f alpha:1.0f], 56 | [UIColor colorWithHue:205.0f/360.0f saturation:.71f brightness:.96f alpha:1.0f], nil]; 57 | blueIconTrait.outerGlowColor = [UIColor colorWithHue:210.0f/360.0f saturation:.95f brightness:.43f alpha:0.9f]; 58 | blueIconTrait.outerGlowRadius = 15.0f; 59 | blueIconTrait.innerGlowColor = [UIColor colorWithRed:0.8f green:0.9f blue:1.0f alpha:0.8f]; 60 | blueIconTrait.innerGlowRadius = 1.0f; 61 | 62 | // Quick closure to position icons 63 | void (^positionIconAtIndex)(MOOMaskedIconView *icon, NSUInteger index) = ^(MOOMaskedIconView *icon, NSUInteger index) { 64 | CGFloat stepSize = CGRectGetWidth(self.view.bounds) / 5.0f; 65 | 66 | icon.layer.position = CGPointMake(stepSize * (index + 0.5f), CGRectGetHeight(grayBar.bounds) / 2.0f); 67 | icon.frame = CGRectIntegral(icon.frame); 68 | }; 69 | 70 | MOOMaskedIconView *eye = [MOOMaskedIconView iconWithResourceNamed:@"Eye.pdf"]; 71 | [eye mixInTrait:grayIconTrait]; 72 | positionIconAtIndex(eye, 0); 73 | [grayBar addSubview:eye]; 74 | 75 | MOOMaskedIconView *chatBubble = [MOOMaskedIconView iconWithResourceNamed:@"Chat Bubble.pdf"]; 76 | [chatBubble mixInTrait:grayIconTrait]; 77 | positionIconAtIndex(chatBubble, 1); 78 | [grayBar addSubview:chatBubble]; 79 | 80 | MOOMaskedIconView *roundedRect = [MOOMaskedIconView iconWithResourceNamed:@"Rounded Rect.pdf"]; 81 | [roundedRect mixInTrait:grayIconTrait]; 82 | positionIconAtIndex(roundedRect, 2); 83 | [grayBar addSubview:roundedRect]; 84 | 85 | MOOMaskedIconView *location = [MOOMaskedIconView iconWithResourceNamed:@"Location.pdf"]; 86 | [location mixInTrait:blueIconTrait]; 87 | positionIconAtIndex(location, 2); 88 | 89 | MOOMaskedIconView *locationOverlay = [MOOMaskedIconView iconWithResourceNamed:@"Overlay.pdf" size:location.frame.size]; 90 | locationOverlay.color = [UIColor colorWithWhite:1.0f alpha:0.7f]; 91 | location.overlay = [locationOverlay renderImage]; 92 | 93 | [grayBar addSubview:location]; 94 | 95 | MOOMaskedIconView *search = [MOOMaskedIconView iconWithResourceNamed:@"Search.pdf"]; 96 | [search mixInTrait:grayIconTrait]; 97 | positionIconAtIndex(search, 3); 98 | [grayBar addSubview:search]; 99 | 100 | MOOMaskedIconView *arrow = [MOOMaskedIconView iconWithResourceNamed:@"Arrow.pdf"]; 101 | [arrow mixInTrait:grayIconTrait]; 102 | positionIconAtIndex(arrow, 4); 103 | [grayBar addSubview:arrow]; 104 | 105 | // Large gray gradient mirrored PDF with overlay 106 | CGSize beer5Size = CGSizeMake(CGRectGetWidth(self.view.bounds) - 20.0f, CGRectGetHeight(self.view.bounds) - kBarHeight - 20.0f); 107 | MOOMaskedIconView *icon5OverlayView = [[MOOMaskedIconView alloc] initWithResourceNamed:@"Overlay.pdf" size:beer5Size]; 108 | icon5OverlayView.color = [UIColor whiteColor]; 109 | icon5OverlayView.transform = CGAffineTransformMakeScale(-1.0f, 1.0f); 110 | UIImage *icon5Overlay = [icon5OverlayView renderImage]; 111 | 112 | MOOMaskedIconView *beer = [MOOMaskedIconView iconWithResourceNamed:@"Beer.pdf" size:beer5Size]; 113 | beer.backgroundColor = self.view.backgroundColor; 114 | beer.gradientColors = [NSArray arrayWithObjects: 115 | [UIColor colorWithWhite:0.8f alpha:1.0f], 116 | [UIColor colorWithWhite:0.9f alpha:1.0f], 117 | [UIColor colorWithWhite:0.9f alpha:1.0f], 118 | [UIColor colorWithWhite:0.5f alpha:1.0f], nil]; 119 | beer.gradientLocations = [NSArray arrayWithObjects: 120 | [NSNumber numberWithFloat:0.0f], 121 | [NSNumber numberWithFloat:0.1f], 122 | [NSNumber numberWithFloat:0.9f], 123 | [NSNumber numberWithFloat:1.0f], nil]; 124 | beer.overlay = icon5Overlay; 125 | 126 | CGRect beerFrame = beer.frame; 127 | beerFrame.origin = CGPointMake(10.0f, 10.0f); 128 | beer.frame = beerFrame; 129 | [self.view addSubview:beer]; 130 | } 131 | 132 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 133 | { 134 | return interfaceOrientation == UIInterfaceOrientationPortrait; 135 | } 136 | 137 | @end 138 | -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Demo Project/MOOMaskedIconView Demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MOOMaskedIconView Demo 4 | // 5 | // Created by Peyton Randolph on 2/20/12. 6 | // 7 | 8 | #import 9 | 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Editor Support/Photoshop/CGPDFIcon.joboptions: -------------------------------------------------------------------------------- 1 | << 2 | /ASCII85EncodePages false 3 | /AllowPSXObjects true 4 | /AllowTransparency false 5 | /AlwaysEmbed [ 6 | true 7 | ] 8 | /AntiAliasColorImages false 9 | /AntiAliasGrayImages false 10 | /AntiAliasMonoImages false 11 | /AutoFilterColorImages false 12 | /AutoFilterGrayImages true 13 | /AutoPositionEPSFiles true 14 | /AutoRotatePages /All 15 | /Binding /Left 16 | /CalCMYKProfile (U.S. Web Coated \050SWOP\051 v2) 17 | /CalGrayProfile (Gray Gamma 2.2) 18 | /CalRGBProfile (Monitor RGB - Display) 19 | /CannotEmbedFontPolicy /Warning 20 | /CheckCompliance [ 21 | /None 22 | ] 23 | /ColorACSImageDict << 24 | /HSamples [ 25 | 2 26 | 1 27 | 1 28 | 2 29 | ] 30 | /QFactor 1.30000 31 | /VSamples [ 32 | 2 33 | 1 34 | 1 35 | 2 36 | ] 37 | >> 38 | /ColorConversionStrategy /LeaveColorUnchanged 39 | /ColorImageAutoFilterStrategy /JPEG 40 | /ColorImageDepth -1 41 | /ColorImageDict << 42 | /HSamples [ 43 | 2 44 | 1 45 | 1 46 | 2 47 | ] 48 | /QFactor 1.30000 49 | /VSamples [ 50 | 2 51 | 1 52 | 1 53 | 2 54 | ] 55 | >> 56 | /ColorImageDownsampleThreshold 1.50000 57 | /ColorImageDownsampleType /None 58 | /ColorImageFilter /DCTEncode 59 | /ColorImageMinDownsampleDepth 1 60 | /ColorImageMinResolution 100 61 | /ColorImageMinResolutionPolicy /OK 62 | /ColorImageResolution 100 63 | /ColorSettingsFile () 64 | /CompatibilityLevel 1.5 65 | /CompressObjects /All 66 | /CompressPages true 67 | /ConvertImagesToIndexed true 68 | /CreateJDFFile false 69 | /CreateJobTicket false 70 | /CropColorImages true 71 | /CropGrayImages true 72 | /CropMonoImages true 73 | /DSCReportingLevel 0 74 | /DefaultRenderingIntent /Default 75 | /Description << 76 | /ENU (This barebones setting creates PDFs suitable for icon use.) 77 | >> 78 | /DetectBlends true 79 | /DetectCurves 0.10000 80 | /DoThumbnails false 81 | /DownsampleColorImages false 82 | /DownsampleGrayImages true 83 | /DownsampleMonoImages true 84 | /EmbedAllFonts false 85 | /EmbedJobOptions false 86 | /EmbedOpenType false 87 | /EmitDSCWarnings false 88 | /EncodeColorImages false 89 | /EncodeGrayImages true 90 | /EncodeMonoImages true 91 | /EndPage -1 92 | /GrayACSImageDict << 93 | /HSamples [ 94 | 2 95 | 1 96 | 1 97 | 2 98 | ] 99 | /QFactor 1.30000 100 | /VSamples [ 101 | 2 102 | 1 103 | 1 104 | 2 105 | ] 106 | >> 107 | /GrayImageAutoFilterStrategy /JPEG 108 | /GrayImageDepth -1 109 | /GrayImageDict << 110 | /HSamples [ 111 | 2 112 | 1 113 | 1 114 | 2 115 | ] 116 | /QFactor 1.30000 117 | /VSamples [ 118 | 2 119 | 1 120 | 1 121 | 2 122 | ] 123 | >> 124 | /GrayImageDownsampleThreshold 1.50000 125 | /GrayImageDownsampleType /Bicubic 126 | /GrayImageFilter /DCTEncode 127 | /GrayImageMinDownsampleDepth 2 128 | /GrayImageMinResolution 150 129 | /GrayImageMinResolutionPolicy /OK 130 | /GrayImageResolution 150 131 | /ImageMemory 1048576 132 | /JPEG2000ColorACSImageDict << 133 | /Quality 10 134 | /TileHeight 256 135 | /TileWidth 256 136 | >> 137 | /JPEG2000ColorImageDict << 138 | /Quality 10 139 | /TileHeight 256 140 | /TileWidth 256 141 | >> 142 | /JPEG2000GrayACSImageDict << 143 | /Quality 10 144 | /TileHeight 256 145 | /TileWidth 256 146 | >> 147 | /JPEG2000GrayImageDict << 148 | /Quality 10 149 | /TileHeight 256 150 | /TileWidth 256 151 | >> 152 | /LockDistillerParams false 153 | /MaxSubsetPct 100 154 | /MonoImageDepth -1 155 | /MonoImageDict << 156 | /K -1 157 | >> 158 | /MonoImageDownsampleThreshold 1.50000 159 | /MonoImageDownsampleType /Bicubic 160 | /MonoImageFilter /CCITTFaxEncode 161 | /MonoImageMinResolution 300 162 | /MonoImageMinResolutionPolicy /OK 163 | /MonoImageResolution 300 164 | /Namespace [ 165 | (Adobe) 166 | (Common) 167 | (1.0) 168 | ] 169 | /NeverEmbed [ 170 | true 171 | ] 172 | /OPM 1 173 | /Optimize false 174 | /OtherNamespaces [ 175 | << 176 | /AsReaderSpreads false 177 | /CropImagesToFrames true 178 | /ErrorControl /WarnAndContinue 179 | /FlattenerIgnoreSpreadOverrides false 180 | /IncludeGuidesGrids false 181 | /IncludeNonPrinting false 182 | /IncludeSlug false 183 | /Namespace [ 184 | (Adobe) 185 | (InDesign) 186 | (4.0) 187 | ] 188 | /OmitPlacedBitmaps false 189 | /OmitPlacedEPS false 190 | /OmitPlacedPDF false 191 | /SimulateOverprint /Legacy 192 | >> 193 | << 194 | /AddBleedMarks false 195 | /AddColorBars false 196 | /AddCropMarks false 197 | /AddPageInfo false 198 | /AddRegMarks false 199 | /BleedOffset [ 200 | 0 201 | 0 202 | 0 203 | 0 204 | ] 205 | /ConvertColors /NoConversion 206 | /DestinationProfileName (Display) 207 | /DestinationProfileSelector /NA 208 | /Downsample16BitImages true 209 | /FlattenerPreset << 210 | /PresetSelector /MediumResolution 211 | >> 212 | /FormElements false 213 | /GenerateStructure false 214 | /IncludeBookmarks false 215 | /IncludeHyperlinks false 216 | /IncludeInteractive false 217 | /IncludeLayers false 218 | /IncludeProfiles false 219 | /MarksOffset 6 220 | /MarksWeight 0.25000 221 | /MultimediaHandling /UseObjectSettings 222 | /Namespace [ 223 | (Adobe) 224 | (CreativeSuite) 225 | (2.0) 226 | ] 227 | /PDFXOutputIntentProfileSelector /NA 228 | /PageMarksFile /RomanDefault 229 | /PreserveEditing false 230 | /UntaggedCMYKHandling /UseDocumentProfile 231 | /UntaggedRGBHandling /UseDocumentProfile 232 | /UseDocumentBleed false 233 | >> 234 | << 235 | /AllowImageBreaks true 236 | /AllowTableBreaks true 237 | /ExpandPage false 238 | /HonorBaseURL true 239 | /HonorRolloverEffect false 240 | /IgnoreHTMLPageBreaks false 241 | /IncludeHeaderFooter false 242 | /MarginOffset [ 243 | 0 244 | 0 245 | 0 246 | 0 247 | ] 248 | /MetadataAuthor () 249 | /MetadataKeywords () 250 | /MetadataSubject () 251 | /MetadataTitle () 252 | /MetricPageSize [ 253 | 0 254 | 0 255 | ] 256 | /MetricUnit /inch 257 | /MobileCompatible 0 258 | /Namespace [ 259 | (Adobe) 260 | (GoLive) 261 | (8.0) 262 | ] 263 | /OpenZoomToHTMLFontSize false 264 | /PageOrientation /Portrait 265 | /RemoveBackground false 266 | /ShrinkContent true 267 | /TreatColorsAs /MainMonitorColors 268 | /UseEmbeddedProfiles false 269 | /UseHTMLTitleAsMetadata true 270 | >> 271 | ] 272 | /PDFX1aCheck false 273 | /PDFX3Check false 274 | /PDFXBleedBoxToTrimBoxOffset [ 275 | 0 276 | 0 277 | 0 278 | 0 279 | ] 280 | /PDFXCompliantPDFOnly false 281 | /PDFXNoTrimBoxError true 282 | /PDFXOutputCondition () 283 | /PDFXOutputConditionIdentifier () 284 | /PDFXOutputIntentProfile () 285 | /PDFXRegistryName () 286 | /PDFXSetBleedBoxToMediaBox true 287 | /PDFXTrapped /False 288 | /PDFXTrimBoxToMediaBoxOffset [ 289 | 0 290 | 0 291 | 0 292 | 0 293 | ] 294 | /ParseDSCComments true 295 | /ParseDSCCommentsForDocInfo false 296 | /ParseICCProfilesInComments true 297 | /PassThroughJPEGImages false 298 | /PreserveCopyPage true 299 | /PreserveDICMYKValues true 300 | /PreserveEPSInfo false 301 | /PreserveFlatness true 302 | /PreserveHalftoneInfo false 303 | /PreserveOPIComments false 304 | /PreserveOverprintSettings true 305 | /StartPage 1 306 | /SubsetFonts true 307 | /TransferFunctionInfo /Apply 308 | /UCRandBGInfo /Remove 309 | /UsePrologue false 310 | /sRGBProfile (sRGB IEC61966-2.1) 311 | >> setdistillerparams 312 | << 313 | /HWResolution [600 600] 314 | /PageSize [612.000 792.000] 315 | >> setpagedevice 316 | -------------------------------------------------------------------------------- /Editor Support/Photoshop/Exporting Icons.atn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peyton/MOOMaskedIconView/d7d0c099c5cafa29f9df42dea848b703f57236bc/Editor Support/Photoshop/Exporting Icons.atn -------------------------------------------------------------------------------- /Editor Support/README.md: -------------------------------------------------------------------------------- 1 | https://github.com/peyton/MOOMaskedIconView/wiki/Exporting-PDFs 2 | -------------------------------------------------------------------------------- /MOOMaskedIconView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'MOOMaskedIconView' 3 | s.version = '0.1.0' 4 | s.license = 'Public domain / MIT' 5 | s.summary = 'UIView subclass that uses black-and-white masks to draw icons.' 6 | s.homepage = 'https://github.com/peyton/MOOMaskedIconView' 7 | s.author = { 'Peyton Randolph' => '_@peytn.com' } 8 | s.source = { :git => 'https://github.com/peyton/MOOMaskedIconView.git', :tag => 'v0.1.0' } 9 | s.description = 'MOOMaskedIconView is a UIView subclass that uses ' \ 10 | 'black-and-white masks to draw icons of different ' \ 11 | 'styles at any size or resolution. It\'s like Photoshop ' \ 12 | 'layer styles, using the same technique as UITabBar to ' \ 13 | 'generate various effects from a single icon mask. ' \ 14 | 'MOOMaskedIconView displays common image formats and PDFs.' 15 | s.platform = :ios 16 | s.source_files = 'MOOMaskedIconView' 17 | s.clean_paths = "Demo Project", "Editor Support" 18 | s.frameworks = 'QuartzCore', 'Accelerate' 19 | s.requires_arc = false 20 | end 21 | -------------------------------------------------------------------------------- /MOOMaskedIconView/AHHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // ARC Helper 3 | // 4 | // Version 1.2.1 5 | // 6 | // Created by Nick Lockwood on 05/01/2012. 7 | // Copyright 2012 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib license 10 | // Get the latest version from here: 11 | // 12 | // https://gist.github.com/1563325 13 | // 14 | 15 | #ifndef AH_RETAIN 16 | #if __has_feature(objc_arc) 17 | #define AH_RETAIN(x) (x) 18 | #define AH_RELEASE(x) 19 | #define AH_AUTORELEASE(x) (x) 20 | #define AH_SUPER_DEALLOC 21 | #else 22 | #warning "Compiling MOOMaskedIconView without ARC is beta and may not work. Use at your own risk" 23 | #define __AH_WEAK 24 | #define AH_WEAK assign 25 | #define AH_RETAIN(x) [(x) retain] 26 | #define AH_RELEASE(x) [(x) release] 27 | #define AH_AUTORELEASE(x) [(x) autorelease] 28 | #define AH_SUPER_DEALLOC [super dealloc] 29 | #endif 30 | #endif 31 | 32 | // Weak reference support 33 | 34 | #ifndef AH_WEAK 35 | #if defined __IPHONE_OS_VERSION_MIN_REQUIRED 36 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_5_0 37 | #define __AH_WEAK __weak 38 | #define AH_WEAK weak 39 | #else 40 | #define __AH_WEAK __unsafe_unretained 41 | #define AH_WEAK unsafe_unretained 42 | #endif 43 | #elif defined __MAC_OS_X_VERSION_MIN_REQUIRED 44 | #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_7 45 | #define __AH_WEAK __weak 46 | #define AH_WEAK weak 47 | #else 48 | #define __AH_WEAK __unsafe_unretained 49 | #define AH_WEAK unsafe_unretained 50 | #endif 51 | #endif 52 | #endif 53 | 54 | // ARC Helper ends -------------------------------------------------------------------------------- /MOOMaskedIconView/MOOCGImageWrapper.h: -------------------------------------------------------------------------------- 1 | // 2 | // MOOCGImageWrapper.h 3 | // MOOMaskedIconView 4 | // 5 | // Created by Peyton Randolph on 2/27/12. 6 | // 7 | 8 | #import 9 | #if TARGET_OS_IPHONE 10 | #import 11 | #else 12 | #import 13 | #endif 14 | 15 | @interface MOOCGImageWrapper : NSObject 16 | { 17 | CGImageRef _CGImage; 18 | } 19 | 20 | @property (assign) CGImageRef CGImage; 21 | @property (assign, readonly) NSUInteger cost; 22 | 23 | - (id)initWithCGImage:(CGImageRef)image; 24 | + (MOOCGImageWrapper *)wrapperWithCGImage:(CGImageRef)image; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /MOOMaskedIconView/MOOCGImageWrapper.m: -------------------------------------------------------------------------------- 1 | // 2 | // MOOCGImageWrapper.m 3 | // MOOMaskedIconView 4 | // 5 | // Created by Peyton Randolph on 2/27/12. 6 | // 7 | 8 | #import "MOOCGImageWrapper.h" 9 | 10 | #import "AHHelper.h" 11 | 12 | @implementation MOOCGImageWrapper 13 | @dynamic CGImage; 14 | @dynamic cost; 15 | 16 | - (id)initWithCGImage:(CGImageRef)image; 17 | { 18 | if (!(self = [super init])) 19 | return nil; 20 | 21 | self.CGImage = image; 22 | 23 | return self; 24 | } 25 | 26 | + (MOOCGImageWrapper *)wrapperWithCGImage:(CGImageRef)image; 27 | { 28 | return AH_AUTORELEASE([[self alloc] initWithCGImage:image]); 29 | } 30 | 31 | - (void)dealloc; 32 | { 33 | self.CGImage = NULL; 34 | 35 | AH_SUPER_DEALLOC; 36 | } 37 | 38 | #pragma mark - Getters and setters 39 | 40 | - (CGImageRef)CGImage; 41 | { 42 | @synchronized(self) 43 | { 44 | return _CGImage; 45 | } 46 | } 47 | 48 | - (void)setCGImage:(CGImageRef)CGImage; 49 | { 50 | @synchronized(self) 51 | { 52 | if (CGImage == _CGImage) 53 | return; 54 | 55 | if (_CGImage != NULL) { 56 | CGImageRelease(_CGImage); 57 | } 58 | _CGImage = CGImageRetain(CGImage); 59 | } 60 | } 61 | 62 | - (NSUInteger)cost; 63 | { 64 | return CGImageGetBytesPerRow(self.CGImage) * CGImageGetHeight(self.CGImage); 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /MOOMaskedIconView/MOOMaskedIconView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MOOMaskedIconView.h 3 | // MOOMaskedIconView 4 | // 5 | // Created by Peyton Randolph on 2/6/12. 6 | // 7 | 8 | #import 9 | 10 | 11 | typedef void (^DrawingBlock)(CGContextRef context); 12 | 13 | typedef enum { 14 | MOOGradientTypeLinear = 0, 15 | } MOOGradientType; 16 | 17 | 18 | /** 19 | MOOMaskedIconViewStyles is a protocol that abstracts the configurable styles from MOOMaskedIconView, allowing others to reuse the same set of styles. 20 | 21 | @see MOOMaskedIconView 22 | @see MOOStyleTrait 23 | */ 24 | @protocol MOOMaskedIconViewStyles 25 | 26 | 27 | /** @name Style Properties */ 28 | 29 | /** 30 | * Fill color painted when the icon view is unhighlighted. 31 | * 32 | * When gradientStartColor and gradientEndColor are both non-nil, 33 | * the view ignores fill color and draws a gradient. 34 | * 35 | * @see highlightedColor 36 | */ 37 | @property (nonatomic, strong) UIColor *color; 38 | 39 | /** 40 | * Fill color painted when the icon view is highlighted. 41 | * 42 | * When gradientStartColor and gradientEndColor are both non-nil, 43 | * the view ignores fill color and draws a gradient. 44 | * 45 | * @see color 46 | */ 47 | @property (nonatomic, strong) UIColor *highlightedColor; 48 | 49 | /** 50 | * A pattern composited over the icon. Created with UIColor's colorWithPatternImage:. 51 | */ 52 | @property (nonatomic, strong) UIColor *pattern; 53 | 54 | /** 55 | * The blend mode under which the pattern is drawn. Defaults to kCGBlendModeNormal. For a list of options see "CGBlendMode". 56 | */ 57 | @property (nonatomic, assign) CGBlendMode patternBlendMode; 58 | 59 | /** 60 | * An image composited over the icon after drawing's done. 61 | * 62 | * @see overlayBlendMode 63 | */ 64 | @property (nonatomic, strong) UIImage *overlay; 65 | 66 | /** 67 | * The blend mode under which the overlay is drawn. Defaults to kCGBlendModeNormal. For a list of options see "CGBlendMode". 68 | * 69 | * @see overlay 70 | */ 71 | @property (nonatomic, assign) CGBlendMode overlayBlendMode; 72 | 73 | /** 74 | * A block called with the current context at the end of every drawing. Has signature `void (^DrawingBlock)(CGContextRef context)`. 75 | * 76 | * Useful for custom drawing. 77 | */ 78 | @property (nonatomic, strong) DrawingBlock drawingBlock; 79 | 80 | 81 | /** @name Gradient Properties */ 82 | 83 | /** 84 | * The color filled at the gradient's start location. Cleared by gradientColors if that's set. 85 | * 86 | * @deprecated Use gradientColors instead 87 | * 88 | * @see gradientEndColor 89 | * @see gradientColors 90 | */ 91 | @property (nonatomic, strong) UIColor *gradientStartColor; 92 | 93 | /** 94 | * The color filled at the gradient's end location. Cleared by gradientColors if that's set. 95 | * 96 | * @deprecated Use gradientColors instead 97 | * 98 | * @see gradientStartColor 99 | * @see gradientColors 100 | */ 101 | @property (nonatomic, strong) UIColor *gradientEndColor; 102 | 103 | /** 104 | * An optional array of UIColors defining the color of the gradient at each stop. Setting gradientColors clears gradientStartColor and gradientEndColor. 105 | * 106 | * @see gradientLocations 107 | */ 108 | @property (nonatomic, strong) NSArray *gradientColors; 109 | 110 | /** 111 | * An optional array of NSNumber objects defining the location of each gradient stop. 112 | * 113 | * Must have the same number of components as gradientColors. The gradient stops are specified as values between 0 and 1. The values must be monotonically increasing. If nil, the stops are spread uniformly across the range. Defaults to nil. 114 | * 115 | * @see gradientColors 116 | */ 117 | @property (nonatomic, strong) NSArray *gradientLocations; 118 | 119 | /** 120 | * Style of gradient drawn. MOOGradientTypeLinear is the sole option right now. 121 | */ 122 | @property (nonatomic, assign) MOOGradientType gradientType; 123 | 124 | 125 | /** @name Shadow Properties */ 126 | 127 | /** 128 | * Specifies shadow color. 129 | * 130 | * @see shadowOffset 131 | */ 132 | @property (nonatomic, strong) UIColor *shadowColor; 133 | 134 | /** 135 | * Specifies shadow offset. 136 | * 137 | * @see shadowColor 138 | */ 139 | @property (nonatomic, assign) CGSize shadowOffset; 140 | 141 | /** 142 | * Set to YES if your icon is translucent and you don't want the shadow showing through. 143 | * 144 | * @see shadowColor 145 | * @see shadowOffset 146 | */ 147 | @property (nonatomic, assign) BOOL clipsShadow; 148 | 149 | /** 150 | * Specifies inner shadow color. 151 | * 152 | * @see innerShadowOffset 153 | */ 154 | @property (nonatomic, strong) UIColor *innerShadowColor; 155 | 156 | /** 157 | * Specifies inner shadow offset. 158 | * 159 | * @see innerShadowColor 160 | */ 161 | @property (nonatomic, assign) CGSize innerShadowOffset; 162 | 163 | /** @name Glow Properties */ 164 | 165 | /** 166 | * Specifies outer glow color. 167 | * 168 | * Note: Generally requires a higher opacity or greater radius than its Photoshop counterpart to achieve the same effect. 169 | * 170 | * @see outerGlowRadius. 171 | */ 172 | @property (nonatomic, strong) UIColor *outerGlowColor; 173 | 174 | /** 175 | * Specifies the total displacement of the outer glow in points. 176 | * 177 | * @see outerGlowColor 178 | */ 179 | @property (nonatomic, assign) CGFloat outerGlowRadius; 180 | 181 | /** 182 | * Specifies inner glow color. 183 | * 184 | * @see innerGlowRadius 185 | */ 186 | @property (nonatomic, strong) UIColor *innerGlowColor; 187 | 188 | /** 189 | * Specifies the total displacement of the inner glow in points. 190 | * 191 | * @see innerGlowColor 192 | */ 193 | @property (nonatomic, assign) CGFloat innerGlowRadius; 194 | 195 | @end 196 | 197 | @protocol MOOStyleTrait; 198 | @class MOOStyleTrait; 199 | 200 | /** 201 | MOOMaskedIconView is a UIView subclass that uses black-and-white masks to draw icons of different styles at any size or resolution. It's like Photoshop layer styles, using the same technique as UITabBar to generate various effects from a single icon mask. 202 | 203 | @see MOOMaskedIconViewStyles 204 | @see MOOStyleTrait 205 | */ 206 | 207 | @interface MOOMaskedIconView : UIView 208 | { 209 | BOOL _highlighted; 210 | 211 | UIColor *_color; 212 | UIColor *_highlightedColor; 213 | UIColor *_pattern; 214 | CGBlendMode _patternBlendMode; 215 | UIImage *_overlay; 216 | CGBlendMode _overlayBlendMode; 217 | 218 | NSArray *_gradientColors; 219 | NSArray *_gradientLocations; 220 | MOOGradientType _gradientType; 221 | 222 | UIColor *_shadowColor; 223 | CGSize _shadowOffset; 224 | BOOL _clipsShadow; 225 | UIColor *_innerShadowColor; 226 | CGSize _innerShadowOffset; 227 | 228 | UIColor *_outerGlowColor; 229 | CGFloat _outerGlowRadius; 230 | UIColor *_innerGlowColor; 231 | CGFloat _innerGlowRadius; 232 | 233 | DrawingBlock _drawingBlock; 234 | CGImageRef _mask; 235 | CGGradientRef _gradient; 236 | 237 | struct { 238 | BOOL hasGradientStartColor: 1; 239 | BOOL hasGradientEndColor: 1; 240 | BOOL needsGradient: 1; 241 | } _iconViewFlags; 242 | } 243 | 244 | /** @name State Properties */ 245 | 246 | /** 247 | * Whether the icon view is in its highlighted state. 248 | */ 249 | @property (nonatomic, assign, getter = isHighlighted) BOOL highlighted; 250 | 251 | /** 252 | * The current CGImage mask held by the view. Read-only. 253 | */ 254 | @property (nonatomic, assign, readonly) CGImageRef mask; 255 | 256 | 257 | /** @name Other Properties */ 258 | 259 | @property (nonatomic, strong, readonly) Protocol *styleProtocol; 260 | 261 | /** @name Initialization methods */ 262 | - (id)initWithImage:(UIImage *)image; 263 | - (id)initWithImage:(UIImage *)image size:(CGSize)size; 264 | - (id)initWithImageNamed:(NSString *)imageName; 265 | - (id)initWithImageNamed:(NSString *)imageName size:(CGSize)size; 266 | - (id)initWithPDFNamed:(NSString *)pdfName; 267 | - (id)initWithPDFNamed:(NSString *)pdfName size:(CGSize)size; 268 | - (id)initWithPDFData:(NSData *)pdfData; 269 | - (id)initWithPDFData:(NSData *)pdfData size:(CGSize)size; 270 | - (id)initWithResourceNamed:(NSString *)resourceName; 271 | - (id)initWithResourceNamed:(NSString *)resourceName size:(CGSize)size; 272 | 273 | /** @name Creation methods */ 274 | + (MOOMaskedIconView *)iconWithImage:(UIImage *)image; 275 | + (MOOMaskedIconView *)iconWithImage:(UIImage *)image size:(CGSize)size; 276 | + (MOOMaskedIconView *)iconWithImageNamed:(NSString *)imageName; 277 | + (MOOMaskedIconView *)iconWithImageNamed:(NSString *)imageName size:(CGSize)size; 278 | + (MOOMaskedIconView *)iconWithPDFNamed:(NSString *)pdfName; 279 | + (MOOMaskedIconView *)iconWithPDFNamed:(NSString *)pdfName size:(CGSize)size; 280 | + (MOOMaskedIconView *)iconWithPDFData:(NSData *)pdfData; 281 | + (MOOMaskedIconView *)iconWithPDFData:(NSData *)pdfData size:(CGSize)size; 282 | + (MOOMaskedIconView *)iconWithResourceNamed:(NSString *)resourceName; 283 | + (MOOMaskedIconView *)iconWithResourceNamed:(NSString *)resourceName size:(CGSize)size; 284 | 285 | /** @name Configuration methods */ 286 | - (void)configureWithImage:(UIImage *)image; 287 | - (void)configureWithImage:(UIImage *)image size:(CGSize)size; 288 | - (void)configureWithImageNamed:(NSString *)imageName; 289 | - (void)configureWithImageNamed:(NSString *)imageName size:(CGSize)size; 290 | - (void)configureWithPDFNamed:(NSString *)pdfName; 291 | - (void)configureWithPDFNamed:(NSString *)pdfName size:(CGSize)size; 292 | - (void)configureWithPDFData:(NSData *)pdfData; 293 | - (void)configureWithPDFData:(NSData *)pdfData size:(CGSize)size; 294 | - (void)configureWithResourceNamed:(NSString *)resourceName; 295 | - (void)configureWithResourceNamed:(NSString *)resourceName size:(CGSize)size; 296 | 297 | 298 | /** @name Traits */ 299 | 300 | /** 301 | * Returns a trait composed of the icon's current style properties. 302 | * 303 | * Setting overwrites all properties of the icon with those of the passed-in trait. 304 | * 305 | * @see mixInTrait: 306 | */ 307 | @property (nonatomic, strong) id trait; 308 | 309 | /*** 310 | * Apply the properties of the passed-in trait in addition to the icon's current properties. 311 | * 312 | * @param trait The trait to mix in. 313 | * @see trait 314 | */ 315 | - (void)mixInTrait:(id)trait; 316 | 317 | 318 | /** @name Rendering */ 319 | 320 | /** 321 | * Render the icon unhighlighted to a UIImage. 322 | * 323 | * Useful for passing the icon to other views, e.g. UIButton. 324 | * 325 | * @see renderHighlightedImage 326 | */ 327 | - (UIImage *)renderImage; 328 | 329 | /** 330 | * Render the icon highlighted to a UIImage. 331 | * 332 | * Useful for passing the icon to other views, e.g. UIButton. 333 | * 334 | * @see renderImage 335 | */ 336 | - (UIImage *)renderHighlightedImage; 337 | 338 | /** @name Caching */ 339 | 340 | + (NSCache *)defaultMaskCache; 341 | 342 | + (BOOL)shouldCacheMaskForKey:(NSString *)key; 343 | 344 | @end 345 | 346 | /** @name Helper functions */ 347 | 348 | /** 349 | * Create a mask CGImage from a given image for a given size. 350 | */ 351 | CGImageRef CGImageCreateMaskFromCGImage(CGImageRef source, CGSize size); 352 | 353 | /** 354 | * Create a mask CGImage from a given image name for a given size. 355 | */ 356 | CGImageRef CGImageCreateMaskFromImageNamed(NSString *imageName, CGSize size); 357 | 358 | /** 359 | * Create a mask CGImage from a given pdf name for a given size. 360 | */ 361 | CGImageRef CGImageCreateMaskFromPDFNamed(NSString *pdfName, CGSize size); 362 | 363 | /** 364 | * Create a mask CGImage from a given pdf data for a given size. 365 | */ 366 | CGImageRef CGImageCreateMaskFromPDFData(NSData *pdfData, CGSize size); 367 | 368 | /** 369 | * Create a mask CGImage from a given pdf page for a given size. 370 | */ 371 | CGImageRef CGImageCreateMaskFromPDFPage(CGPDFPageRef page, CGSize size); 372 | 373 | /** 374 | * Create a mask CGImage from a given resource name for a given size 375 | */ 376 | CGImageRef CGImageCreateMaskFromResourceNamed(NSString *resourceName, CGSize size); 377 | -------------------------------------------------------------------------------- /MOOMaskedIconView/MOOMaskedIconView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MOOMaskedIconView.m 3 | // MOOMaskedIconView 4 | // 5 | // Created by Peyton Randolph on 2/6/12. 6 | // 7 | 8 | #import "MOOMaskedIconView.h" 9 | 10 | #import 11 | #import 12 | 13 | #import "AHHelper.h" 14 | #import "MOOCGImageWrapper.h" 15 | #import "MOOResourceList.h" 16 | #import "MOOStyleTrait.h" 17 | 18 | // Keys for KVO 19 | static NSString * const MOOHighlightedKeyPath = @"highlighted"; 20 | static NSString * const MOOMaskKeyPath = @"mask"; 21 | static NSString * const MOOOverlayKeyPath = @"overlay"; 22 | 23 | static NSString * const MOOGradientStartColorKeyPath = @"gradientStartColor"; 24 | static NSString * const MOOGradientEndColorKeyPath = @"gradientEndColor"; 25 | static NSString * const MOOGradientColorsKeyPath = @"gradientColors"; 26 | static NSString * const MOOGradientLocationsKeyPath = @"gradientLocations"; 27 | static NSString * const MOOGradientTypeKeyPath = @"gradientType"; 28 | 29 | static NSString * const MOOShadowColorKeyPath = @"shadowColor"; 30 | static NSString * const MOOShadowOffsetKeyPath = @"shadowOffset"; 31 | 32 | static NSString * const MOOOuterGlowRadiusKeyPath = @"outerGlowRadius"; 33 | 34 | // Helper functions 35 | static NSURL *NSURLWithResourceNamed(NSString *resourceName, NSBundle *bundle); 36 | static CGImageRef CGImageCreateInvertedMaskWithMask(CGImageRef sourceImage); 37 | 38 | // Caches 39 | NSCache *_defaultMaskCache; 40 | 41 | @interface MOOMaskedIconView () 42 | 43 | @property (nonatomic, assign) CGImageRef mask; 44 | @property (nonatomic, assign) CGGradientRef gradient; 45 | 46 | - (id)_initWithMask:(CGImageRef)mask; 47 | 48 | 49 | - (void)_addKVO; 50 | - (void)_configureViewWithDefaults; 51 | - (UIImage *)_renderImageHighlighted:(BOOL)shouldBeHighlighted; 52 | - (void)_setNeedsGradient; 53 | - (void)_updateGradientWithColors:(NSArray *)colors locations:(NSArray *)locations forType:(MOOGradientType)type; 54 | 55 | @end 56 | 57 | @implementation MOOMaskedIconView 58 | @synthesize highlighted = _highlighted; 59 | @dynamic trait; 60 | 61 | @synthesize color = _color; 62 | @synthesize highlightedColor = _highlightedColor; 63 | @synthesize pattern = _pattern; 64 | @synthesize patternBlendMode = _patternBlendMode; 65 | @synthesize overlay = _overlay; 66 | @synthesize overlayBlendMode = _overlayBlendMode; 67 | 68 | @dynamic gradientStartColor; 69 | @dynamic gradientEndColor; 70 | @synthesize gradientColors = _gradientColors; 71 | @synthesize gradientLocations = _gradientLocations; 72 | @synthesize gradientType = _gradientType; 73 | 74 | @synthesize shadowColor = _shadowColor; 75 | @synthesize shadowOffset = _shadowOffset; 76 | @synthesize clipsShadow = _clipsShadow; 77 | @synthesize innerShadowColor = _innerShadowColor; 78 | @synthesize innerShadowOffset = _innerShadowOffset; 79 | 80 | @synthesize outerGlowColor = _outerGlowColor; 81 | @synthesize outerGlowRadius = _outerGlowRadius; 82 | @synthesize innerGlowColor = _innerGlowColor; 83 | @synthesize innerGlowRadius = _innerGlowRadius; 84 | 85 | @synthesize drawingBlock = _drawingBlock; 86 | @synthesize mask = _mask; 87 | @synthesize gradient = _gradient; 88 | 89 | - (id)initWithFrame:(CGRect)frame; 90 | { 91 | if (!(self = [super initWithFrame:frame])) 92 | return nil; 93 | 94 | // Configure view with defaults 95 | [self _configureViewWithDefaults]; 96 | 97 | // Set up observing 98 | [self _addKVO]; 99 | 100 | return self; 101 | } 102 | 103 | - (id)initWithCoder:(NSCoder *)aDecoder; 104 | { 105 | if (!(self = [super initWithCoder:aDecoder])) 106 | return nil; 107 | 108 | // Configure view with defaults 109 | [self _configureViewWithDefaults]; 110 | 111 | // Set up observing 112 | [self _addKVO]; 113 | 114 | return self; 115 | } 116 | 117 | - (id)initWithImage:(UIImage *)image; 118 | { 119 | return [self initWithImage:image size:CGSizeZero]; 120 | } 121 | 122 | - (id)initWithImage:(UIImage *)image size:(CGSize)size; 123 | { 124 | if (!(self = [self initWithFrame:CGRectZero])) 125 | return nil; 126 | 127 | // Configure with image 128 | [self configureWithImage:image size:size]; 129 | 130 | return self; 131 | } 132 | 133 | - (id)initWithImageNamed:(NSString *)imageName; 134 | { 135 | return [self initWithImageNamed:imageName size:CGSizeZero]; 136 | } 137 | 138 | - (id)initWithImageNamed:(NSString *)imageName size:(CGSize)size; 139 | { 140 | if (!(self = [self initWithFrame:CGRectZero])) 141 | return nil; 142 | 143 | [self configureWithImageNamed:imageName size:size]; 144 | 145 | return self; 146 | } 147 | 148 | - (id)initWithPDFNamed:(NSString *)pdfName; 149 | { 150 | return [self initWithPDFNamed:pdfName size:CGSizeZero]; 151 | } 152 | 153 | - (id)initWithPDFNamed:(NSString *)pdfName size:(CGSize)size; 154 | { 155 | if (!(self = [self initWithFrame:CGRectZero])) 156 | return nil; 157 | 158 | [self configureWithPDFNamed:pdfName size:size]; 159 | 160 | return self; 161 | } 162 | 163 | - (id)initWithPDFData:(NSData *)pdfData 164 | { 165 | return [self initWithPDFData:pdfData size:CGSizeZero]; 166 | } 167 | 168 | - (id)initWithPDFData:(NSData *)pdfData size:(CGSize)size 169 | { 170 | if (!(self = [self initWithFrame:CGRectZero])) 171 | return nil; 172 | 173 | [self configureWithPDFData:pdfData size:size]; 174 | 175 | return self; 176 | } 177 | 178 | - (id)initWithResourceNamed:(NSString *)resourceName; 179 | { 180 | return [self initWithResourceNamed:resourceName size:CGSizeZero]; 181 | } 182 | 183 | - (id)initWithResourceNamed:(NSString *)resourceName size:(CGSize)size; 184 | { 185 | if (!(self = [self initWithFrame:CGRectZero])) 186 | return nil; 187 | 188 | [self configureWithResourceNamed:resourceName size:size]; 189 | 190 | return self; 191 | } 192 | 193 | - (id)_initWithMask:(CGImageRef)mask; 194 | { 195 | if (!(self = [self initWithFrame:CGRectZero])) 196 | return nil; 197 | 198 | self.mask = mask; 199 | 200 | return self; 201 | } 202 | 203 | - (void)dealloc; 204 | { 205 | [self removeObserver:self forKeyPath:MOOHighlightedKeyPath]; 206 | [self removeObserver:self forKeyPath:MOOMaskKeyPath]; 207 | [self removeObserver:self forKeyPath:MOOOverlayKeyPath]; 208 | [self removeObserver:self forKeyPath:MOOGradientStartColorKeyPath]; 209 | [self removeObserver:self forKeyPath:MOOGradientEndColorKeyPath]; 210 | [self removeObserver:self forKeyPath:MOOGradientColorsKeyPath]; 211 | [self removeObserver:self forKeyPath:MOOGradientLocationsKeyPath]; 212 | [self removeObserver:self forKeyPath:MOOGradientTypeKeyPath]; 213 | [self removeObserver:self forKeyPath:MOOShadowColorKeyPath]; 214 | [self removeObserver:self forKeyPath:MOOShadowOffsetKeyPath]; 215 | [self removeObserver:self forKeyPath:MOOOuterGlowRadiusKeyPath]; 216 | 217 | self.color = nil; 218 | self.highlightedColor = nil; 219 | self.pattern = nil; 220 | self.overlay = nil; 221 | self.drawingBlock = NULL; 222 | self.mask = NULL; 223 | self.gradient = NULL; 224 | self.gradientColors = nil; 225 | self.gradientLocations = nil; 226 | self.shadowColor = nil; 227 | self.innerShadowColor = nil; 228 | self.outerGlowColor = nil; 229 | self.innerGlowColor = nil; 230 | 231 | AH_SUPER_DEALLOC; 232 | } 233 | 234 | #pragma mark - Creation methods 235 | 236 | + (MOOMaskedIconView *)iconWithImage:(UIImage *)image; 237 | { 238 | return AH_AUTORELEASE([[self alloc] initWithImage:image]); 239 | } 240 | 241 | + (MOOMaskedIconView *)iconWithImage:(UIImage *)image size:(CGSize)size; 242 | { 243 | return AH_AUTORELEASE([[self alloc] initWithImage:image size:size]); 244 | } 245 | 246 | + (MOOMaskedIconView *)iconWithImageNamed:(NSString *)imageName; 247 | { 248 | return AH_AUTORELEASE([[self alloc] initWithImageNamed:imageName]); 249 | } 250 | 251 | + (MOOMaskedIconView *)iconWithImageNamed:(NSString *)imageName size:(CGSize)size; 252 | { 253 | return AH_AUTORELEASE([[self alloc] initWithImageNamed:imageName size:size]); 254 | } 255 | 256 | + (MOOMaskedIconView *)iconWithPDFNamed:(NSString *)pdfName; 257 | { 258 | return AH_AUTORELEASE([[self alloc] initWithPDFNamed:pdfName]); 259 | } 260 | 261 | + (MOOMaskedIconView *)iconWithPDFNamed:(NSString *)pdfName size:(CGSize)size; 262 | { 263 | return AH_AUTORELEASE([[self alloc] initWithPDFNamed:pdfName size:size]); 264 | } 265 | 266 | + (MOOMaskedIconView *)iconWithPDFData:(NSData *)pdfData 267 | { 268 | return AH_AUTORELEASE([[self alloc] initWithPDFData:pdfData]); 269 | } 270 | 271 | + (MOOMaskedIconView *)iconWithPDFData:(NSData *)pdfData size:(CGSize)size 272 | { 273 | return AH_AUTORELEASE([[self alloc] initWithPDFData:pdfData size:size]); 274 | } 275 | 276 | + (MOOMaskedIconView *)iconWithResourceNamed:(NSString *)resourceName; 277 | { 278 | return AH_AUTORELEASE([[self alloc] initWithResourceNamed:resourceName]); 279 | } 280 | 281 | + (MOOMaskedIconView *)iconWithResourceNamed:(NSString *)resourceName size:(CGSize)size; 282 | { 283 | return AH_AUTORELEASE([[self alloc] initWithResourceNamed:resourceName size:size]); 284 | } 285 | 286 | #pragma mark - Drawing and layout methods 287 | 288 | - (void)drawRect:(CGRect)rect 289 | { 290 | // Generate gradient if needed 291 | if (_iconViewFlags.needsGradient) 292 | { 293 | [self _updateGradientWithColors:self.gradientColors locations:self.gradientLocations forType:self.gradientType]; 294 | _iconViewFlags.needsGradient = NO; 295 | } 296 | 297 | CGContextRef context = UIGraphicsGetCurrentContext(); 298 | CGImageRef invertedMask = NULL; 299 | 300 | // Flip coordinates so images don't draw upside down 301 | CGContextTranslateCTM(context, 0.0f, CGRectGetHeight(rect)); 302 | CGContextScaleCTM(context, 1.0f, -1.0f); 303 | 304 | CGRect imageRect = CGRectMake(0.0f, 0.0f, CGImageGetWidth(self.mask) / [UIScreen mainScreen].scale, CGImageGetHeight(self.mask) / [UIScreen mainScreen].scale); 305 | CGRect shadowRect = imageRect; 306 | shadowRect.origin = CGPointMake(self.shadowOffset.width, -self.shadowOffset.height); 307 | 308 | CGFloat dOuterGlow = (self.outerGlowRadius > 0.0f) ? -self.outerGlowRadius : 0.0f; 309 | 310 | CGRect unionRect = CGRectUnion(CGRectInset(imageRect, dOuterGlow, dOuterGlow), shadowRect); 311 | CGAffineTransform zeroOriginTransform = CGAffineTransformMakeTranslation(-CGRectGetMinX(unionRect), -CGRectGetMinY(unionRect)); 312 | 313 | imageRect = CGRectApplyAffineTransform(imageRect, zeroOriginTransform); 314 | shadowRect = CGRectApplyAffineTransform(shadowRect, zeroOriginTransform); 315 | 316 | // Draw outer glow 317 | if (self.outerGlowRadius > 0.0f) 318 | { 319 | CGContextSaveGState(context); 320 | 321 | CGContextSetShadowWithColor(context, CGSizeZero, self.outerGlowRadius, (self.outerGlowColor) ? self.outerGlowColor.CGColor : [UIColor blackColor].CGColor); 322 | 323 | CGContextBeginTransparencyLayer(context, NULL); 324 | CGContextClipToMask(context, imageRect, self.mask); 325 | 326 | UIColor *fillColor = [UIColor blackColor]; 327 | if (self.outerGlowColor) 328 | { 329 | CGColorRef outerGlowColorFullOpacity = CGColorCreateCopyWithAlpha(self.outerGlowColor.CGColor, 1.0f); 330 | fillColor = [UIColor colorWithCGColor:outerGlowColorFullOpacity]; 331 | CGColorRelease(outerGlowColorFullOpacity); 332 | } 333 | 334 | [fillColor set]; 335 | 336 | CGContextFillRect(context, imageRect); 337 | CGContextEndTransparencyLayer(context); 338 | 339 | CGContextRestoreGState(context); 340 | } 341 | 342 | // Draw shadow 343 | if (!CGSizeEqualToSize(self.shadowOffset, CGSizeZero)) 344 | { 345 | CGContextSaveGState(context); 346 | [((self.shadowColor) ? self.shadowColor : [UIColor blackColor]) set]; 347 | 348 | CGContextClipToMask(context, shadowRect, self.mask); 349 | 350 | // Clip to inverted mask to prevent icon from being filled 351 | if (self.clipsShadow) 352 | { 353 | if (!invertedMask) 354 | invertedMask = CGImageCreateInvertedMaskWithMask(self.mask); 355 | CGContextClipToMask(context, imageRect, invertedMask); 356 | } 357 | 358 | CGContextFillRect(context, shadowRect); 359 | CGContextRestoreGState(context); 360 | } 361 | 362 | CGContextSaveGState(context); // Push state before clipping to icon 363 | // Clip drawing to icon image 364 | CGContextClipToMask(context, imageRect, self.mask); 365 | 366 | // Fill icon 367 | CGContextSaveGState(context); // Save state before filling 368 | 369 | if (self.gradient && !(self.highlighted && self.highlightedColor)) 370 | { 371 | // Draw gradient 372 | 373 | // Because the context is flipped, the start and end points must be swapped 374 | CGPoint startPoint = CGPointMake(CGRectGetMinX(imageRect), CGRectGetMinY(imageRect) + CGRectGetHeight(imageRect)); 375 | CGPoint endPoint = CGPointMake(CGRectGetMinX(imageRect), CGRectGetMinY(imageRect)); 376 | CGContextDrawLinearGradient(context, self.gradient, startPoint, endPoint, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); 377 | } else { 378 | // Draw solid color 379 | if (self.highlighted && self.highlightedColor) 380 | [self.highlightedColor set]; 381 | else 382 | [self.color set]; 383 | 384 | CGContextFillRect(context, imageRect); 385 | } 386 | CGContextRestoreGState(context); // Restore state after filling 387 | 388 | if (self.pattern) 389 | { 390 | CGContextSaveGState(context); 391 | CGContextSetBlendMode(context, self.patternBlendMode); 392 | [self.pattern set]; 393 | CGContextFillRect(context, imageRect); 394 | CGContextRestoreGState(context); 395 | } 396 | 397 | CGContextRestoreGState(context); // Pop state clipping to icon 398 | 399 | // Draw inner glow 400 | if (self.innerGlowRadius > 0.0f) 401 | { 402 | CGContextSaveGState(context); 403 | 404 | // Clip to inverted mask 405 | if (!invertedMask) 406 | invertedMask = CGImageCreateInvertedMaskWithMask(self.mask); 407 | 408 | CGContextClipToRect(context, imageRect); 409 | // Transparency layers create a drawing-context-within-a-drawing-context, allowing clearing without affecting what's been previously drawn. 410 | CGContextBeginTransparencyLayer(context, NULL); 411 | CGContextSetShadowWithColor(context, CGSizeZero, self.innerGlowRadius, (self.innerGlowColor) ? self.innerGlowColor.CGColor : [UIColor blackColor].CGColor); 412 | 413 | // Begin another transparency layer for the actual glow. 414 | CGContextBeginTransparencyLayer(context, NULL); 415 | CGContextClipToMask(context, imageRect, invertedMask); 416 | 417 | UIColor *fillColor = [UIColor blackColor]; 418 | if (self.innerGlowColor) 419 | { 420 | CGColorRef outerGlowColorFullOpacity = CGColorCreateCopyWithAlpha(self.innerGlowColor.CGColor, 1.0f); 421 | fillColor = [UIColor colorWithCGColor:outerGlowColorFullOpacity]; 422 | CGColorRelease(outerGlowColorFullOpacity); 423 | } 424 | 425 | [fillColor set]; 426 | 427 | CGContextFillRect(context, self.bounds); 428 | CGContextEndTransparencyLayer(context); // End glow layer 429 | 430 | CGContextClipToMask(context, imageRect, invertedMask); // Reclip before clearing 431 | CGContextClearRect(context, imageRect); // Clear color drawn 432 | CGContextEndTransparencyLayer(context); // End makeshift context-within-a-context. 433 | 434 | CGContextRestoreGState(context); 435 | } 436 | 437 | CGContextClipToMask(context, imageRect, self.mask); 438 | // Draw inner shadow 439 | if (!CGSizeEqualToSize(self.innerShadowOffset, CGSizeZero)) 440 | { 441 | CGContextSaveGState(context); 442 | 443 | // Clip to inverted mask to prevent main area from being filled 444 | if (!invertedMask) 445 | invertedMask = CGImageCreateInvertedMaskWithMask(self.mask); 446 | 447 | // Clip to inverted mask translated by innerShadowOffset 448 | CGAffineTransform innerShadowOffsetTransform = CGAffineTransformMakeTranslation(self.innerShadowOffset.width, -self.innerShadowOffset.height); 449 | CGContextClipToMask(context, CGRectApplyAffineTransform(imageRect, innerShadowOffsetTransform), invertedMask); 450 | // Fill inner shadow color 451 | [self.innerShadowColor set]; 452 | CGContextFillRect(context, imageRect); 453 | CGContextRestoreGState(context); 454 | } 455 | CGImageRelease(invertedMask); // Done with invertedMask 456 | 457 | // Draw overlay 458 | if (self.overlay) 459 | { 460 | CGContextSaveGState(context); 461 | CGContextSetBlendMode(context, self.overlayBlendMode); 462 | CGContextDrawImage(context, self.bounds, self.overlay.CGImage); 463 | CGContextRestoreGState(context); 464 | } 465 | } 466 | 467 | - (CGSize)sizeThatFits:(CGSize)size; 468 | { 469 | const CGFloat scale = [UIScreen mainScreen].scale; 470 | CGSize newSize = CGSizeMake(CGImageGetWidth(self.mask) / scale + MAX(fabsf(self.shadowOffset.width), 2.0f * self.outerGlowRadius), CGImageGetHeight(self.mask) / scale + MAX(fabsf(self.shadowOffset.height), 2.0f * self.outerGlowRadius)); 471 | return newSize; 472 | } 473 | 474 | #pragma mark - Configuration methods 475 | 476 | - (void)configureWithImage:(UIImage *)image; 477 | { 478 | [self configureWithImage:image size:CGSizeZero]; 479 | } 480 | 481 | - (void)configureWithImage:(UIImage *)image size:(CGSize)size; 482 | { 483 | CGImageRef mask = CGImageCreateMaskFromCGImage(image.CGImage, size); 484 | self.mask = mask; 485 | CGImageRelease(mask); 486 | } 487 | 488 | - (void)configureWithImageNamed:(NSString *)imageName; 489 | { 490 | [self configureWithImageNamed:imageName size:CGSizeZero]; 491 | } 492 | 493 | - (void)configureWithImageNamed:(NSString *)imageName size:(CGSize)size; 494 | { 495 | // Fetch mask if it exists 496 | NSString *key = [imageName stringByAppendingString:NSStringFromCGSize(size)]; 497 | CGImageRef mask = CGImageRetain(((MOOCGImageWrapper *)[[[self class] defaultMaskCache] objectForKey:key]).CGImage); 498 | if (!mask) { 499 | mask = CGImageCreateMaskFromImageNamed(imageName, size); 500 | } 501 | self.mask = mask; 502 | 503 | // Cache mask 504 | if ([[self class] shouldCacheMaskForKey:key]) { 505 | [[[self class] defaultMaskCache] setObject:[MOOCGImageWrapper wrapperWithCGImage:mask] forKey:key]; 506 | } 507 | 508 | CGImageRelease(mask); 509 | } 510 | 511 | - (void)configureWithPDFNamed:(NSString *)pdfName; 512 | { 513 | [self configureWithPDFNamed:pdfName size:CGSizeZero]; 514 | } 515 | 516 | - (void)configureWithPDFNamed:(NSString *)pdfName size:(CGSize)size; 517 | { 518 | // Fetch mask if it exists 519 | NSString *key = [pdfName stringByAppendingString:NSStringFromCGSize(size)]; 520 | CGImageRef mask = CGImageRetain(((MOOCGImageWrapper *)[[[self class] defaultMaskCache] objectForKey:key]).CGImage); 521 | if (!mask) 522 | mask = CGImageCreateMaskFromPDFNamed(pdfName, size); 523 | 524 | self.mask = mask; 525 | 526 | // Cache mask 527 | if ([[self class] shouldCacheMaskForKey:key]) 528 | [[[self class] defaultMaskCache] setObject:[MOOCGImageWrapper wrapperWithCGImage:mask] forKey:key]; 529 | CGImageRelease(mask); 530 | } 531 | 532 | - (void)configureWithPDFData:(NSData *)pdfData 533 | { 534 | [self configureWithPDFData:pdfData size:CGSizeZero]; 535 | } 536 | 537 | - (void)configureWithPDFData:(NSData *)pdfData size:(CGSize)size 538 | { 539 | CGImageRef mask = CGImageCreateMaskFromPDFData(pdfData, size); 540 | self.mask = mask; 541 | CGImageRelease(mask); 542 | } 543 | 544 | - (void)configureWithResourceNamed:(NSString *)resourceName; 545 | { 546 | [self configureWithResourceNamed:resourceName size:CGSizeZero]; 547 | } 548 | 549 | - (void)configureWithResourceNamed:(NSString *)resourceName size:(CGSize)size; 550 | { 551 | NSString *key = [resourceName stringByAppendingString:NSStringFromCGSize(size)]; 552 | CGImageRef mask = CGImageRetain(((MOOCGImageWrapper *)[[[self class] defaultMaskCache] objectForKey:key]).CGImage); 553 | if (!mask) 554 | mask = CGImageCreateMaskFromResourceNamed(resourceName, size); 555 | self.mask = mask; 556 | 557 | // Cache mask 558 | if ([[self class] shouldCacheMaskForKey:key]) 559 | [[[self class] defaultMaskCache] setObject:[MOOCGImageWrapper wrapperWithCGImage:mask] forKey:key]; 560 | CGImageRelease(mask); 561 | } 562 | 563 | #pragma mark - Trait methods 564 | 565 | - (void)mixInTrait:(id)trait; 566 | { 567 | // Duplicated from MOOStyleTrait.m. TODO: share implementations 568 | if (![[self class] conformsToProtocol:trait.styleProtocol]) 569 | { 570 | NSLog(@"Attempting to mix object %@ of incompatible protocol %@ into object %@ of protocol %@.", trait, NSStringFromProtocol(trait.styleProtocol), self, NSStringFromProtocol(self.styleProtocol)); 571 | return; 572 | } 573 | 574 | id propertyValue; 575 | for (NSString *propertyName in propertyNamesForStyleProtocol(trait.styleProtocol)) 576 | if ((propertyValue = [(NSObject *)trait valueForKey:propertyName])) 577 | [self setValue:propertyValue forKey:propertyName]; 578 | } 579 | 580 | #pragma mark - Getters and setters 581 | 582 | - (void)setGradient:(CGGradientRef)gradient; 583 | { 584 | if (gradient == self.gradient) 585 | return; 586 | 587 | CGGradientRelease(_gradient); 588 | _gradient = CGGradientRetain(gradient); 589 | 590 | [self setNeedsDisplay]; 591 | } 592 | 593 | - (void)setGradientColors:(NSArray *)gradientColors; 594 | { 595 | if ([gradientColors isEqualToArray:self.gradientColors]) 596 | return; 597 | 598 | _gradientColors = gradientColors; 599 | 600 | // Clear gradient start color and gradient end color 601 | _iconViewFlags.hasGradientStartColor = NO; 602 | _iconViewFlags.hasGradientEndColor = NO; 603 | } 604 | 605 | - (UIColor *)gradientStartColor; 606 | { 607 | // Deprecated. Use gradientColors instead 608 | if (!_iconViewFlags.hasGradientStartColor) 609 | return nil; 610 | 611 | return [self.gradientColors objectAtIndex:0]; 612 | } 613 | 614 | - (void)setGradientStartColor:(UIColor *)gradientStartColor; 615 | { 616 | // Deprecated. Setting gradientStartColor is overly complicated. Use gradientColors instead 617 | if (gradientStartColor == self.gradientStartColor) 618 | return; 619 | 620 | if (gradientStartColor == nil) 621 | { 622 | [self willChangeValueForKey:@"gradientColors"]; 623 | _gradientColors = (_iconViewFlags.hasGradientEndColor) ? [NSArray arrayWithObject:self.gradientEndColor] : nil; 624 | [self didChangeValueForKey:@"gradientColors"]; 625 | _iconViewFlags.hasGradientStartColor = NO; 626 | return; 627 | } 628 | 629 | [self willChangeValueForKey:@"gradientColors"]; 630 | _gradientColors = (_iconViewFlags.hasGradientEndColor) ? [NSArray arrayWithObjects:gradientStartColor, self.gradientEndColor, nil] : [NSArray arrayWithObject:gradientStartColor]; 631 | [self didChangeValueForKey:@"gradientColors"]; 632 | 633 | _iconViewFlags.hasGradientStartColor = YES; 634 | } 635 | 636 | - (UIColor *)gradientEndColor; 637 | { 638 | // Deprecated. Use gradientColors instead 639 | if (!_iconViewFlags.hasGradientEndColor) 640 | return nil; 641 | 642 | return [self.gradientColors lastObject]; 643 | } 644 | 645 | - (void)setGradientEndColor:(UIColor *)gradientEndColor; 646 | { 647 | // Deprecated. Setting gradientEndColor is overly complicated. Use gradientColors instead 648 | if (gradientEndColor == self.gradientEndColor) 649 | return; 650 | 651 | if (gradientEndColor == nil) 652 | { 653 | [self willChangeValueForKey:@"gradientColors"]; 654 | _gradientColors = (_iconViewFlags.hasGradientStartColor) ? [NSArray arrayWithObject:self.gradientStartColor] : nil; 655 | [self didChangeValueForKey:@"gradientColors"]; 656 | _iconViewFlags.hasGradientEndColor = NO; 657 | return; 658 | } 659 | 660 | 661 | [self willChangeValueForKey:@"gradientColors"]; 662 | _gradientColors = (_iconViewFlags.hasGradientStartColor) ? [NSArray arrayWithObjects:self.gradientStartColor, gradientEndColor, nil] : [NSArray arrayWithObject:gradientEndColor]; 663 | [self didChangeValueForKey:@"gradientColors"]; 664 | 665 | _iconViewFlags.hasGradientEndColor = YES; 666 | } 667 | 668 | - (void)setMask:(CGImageRef)mask; 669 | { 670 | if (mask == self.mask) 671 | return; 672 | 673 | CGImageRelease(_mask); 674 | _mask = CGImageRetain(mask); 675 | 676 | // Resize view when mask changes 677 | [self sizeToFit]; 678 | [self setNeedsDisplay]; 679 | } 680 | 681 | - (Protocol *)styleProtocol; 682 | { 683 | return @protocol(MOOMaskedIconViewStyles); 684 | } 685 | 686 | - (id)trait; 687 | { 688 | MOOStyleTrait *trait = [MOOStyleTrait trait]; 689 | 690 | for (NSString *propertyName in propertyNamesForStyleProtocol(self.styleProtocol)) 691 | [trait setValue:[(NSObject *)self valueForKey:propertyName] forKey:propertyName]; 692 | 693 | return trait; 694 | } 695 | 696 | - (void)setTrait:(id)trait; 697 | { 698 | if (![[self class] conformsToProtocol:trait.styleProtocol]) 699 | { 700 | NSLog(@"Attempting to mix object %@ of incompatible protocol %@ into object %@ of protocol %@.", trait, NSStringFromProtocol(trait.styleProtocol), self, NSStringFromProtocol(self.styleProtocol)); 701 | return; 702 | } 703 | 704 | for (NSString *propertyName in propertyNamesForStyleProtocol(self.styleProtocol)) 705 | [self setValue:[(NSObject *)trait valueForKey:propertyName] forKey:propertyName]; 706 | } 707 | 708 | #pragma mark - Caching methods 709 | 710 | + (NSCache *)defaultMaskCache; 711 | { 712 | @synchronized(self) 713 | { 714 | if (!_defaultMaskCache) 715 | { 716 | _defaultMaskCache = [[NSCache alloc] init]; 717 | _defaultMaskCache.totalCostLimit = 1024 * 1024 * 2; // Default mask cache size of 2mb; 718 | } 719 | 720 | return _defaultMaskCache; 721 | } 722 | } 723 | 724 | + (BOOL)shouldCacheMaskForKey:(NSString *)key; 725 | { 726 | return [[MOOResourceRegistry sharedRegistry] shouldCacheResourceWithKey:key]; 727 | } 728 | 729 | #pragma mark - KVO methods 730 | 731 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; 732 | { 733 | if ([keyPath isEqualToString:MOOHighlightedKeyPath] || 734 | [keyPath isEqualToString:MOOMaskKeyPath] || 735 | [keyPath isEqualToString:MOOOverlayKeyPath]) 736 | { 737 | [self setNeedsDisplay]; 738 | return; 739 | } 740 | 741 | if ([keyPath isEqualToString:MOOShadowColorKeyPath] || 742 | [keyPath isEqualToString:MOOShadowOffsetKeyPath] || 743 | [keyPath isEqualToString:MOOOuterGlowRadiusKeyPath]) 744 | { 745 | [self sizeToFit]; 746 | [self setNeedsDisplay]; 747 | return; 748 | } 749 | 750 | if ([keyPath isEqualToString:MOOGradientStartColorKeyPath] || 751 | [keyPath isEqualToString:MOOGradientEndColorKeyPath] || 752 | [keyPath isEqualToString:MOOGradientColorsKeyPath] || 753 | [keyPath isEqualToString:MOOGradientLocationsKeyPath] || 754 | [keyPath isEqualToString:MOOGradientTypeKeyPath]) 755 | { 756 | [self _setNeedsGradient]; 757 | [self setNeedsDisplay]; 758 | return; 759 | } 760 | } 761 | 762 | #pragma mark - NSCopying methods 763 | 764 | - (id)copyWithZone:(NSZone *)zone; 765 | { 766 | // Todo: implement NSCopying 767 | return nil; 768 | } 769 | 770 | #pragma mark - Image rendering 771 | 772 | - (UIImage *)renderImage; 773 | { 774 | return [self _renderImageHighlighted:NO]; 775 | } 776 | 777 | - (UIImage *)renderHighlightedImage; 778 | { 779 | return [self _renderImageHighlighted:YES]; 780 | } 781 | 782 | #pragma mark - FOR PRIVATE EYES ONLY 783 | 784 | - (void)_addKVO; 785 | { 786 | 787 | // Set up observing 788 | [self addObserver:self forKeyPath:MOOHighlightedKeyPath options:0 context:NULL]; 789 | [self addObserver:self forKeyPath:MOOMaskKeyPath options:0 context:NULL]; 790 | [self addObserver:self forKeyPath:MOOOverlayKeyPath options:0 context:NULL]; 791 | [self addObserver:self forKeyPath:MOOGradientStartColorKeyPath options:0 context:NULL]; 792 | [self addObserver:self forKeyPath:MOOGradientEndColorKeyPath options:0 context:NULL]; 793 | [self addObserver:self forKeyPath:MOOGradientColorsKeyPath options:0 context:NULL]; 794 | [self addObserver:self forKeyPath:MOOGradientLocationsKeyPath options:0 context:NULL]; 795 | [self addObserver:self forKeyPath:MOOGradientTypeKeyPath options:0 context:NULL]; 796 | [self addObserver:self forKeyPath:MOOShadowColorKeyPath options:0 context:NULL]; 797 | [self addObserver:self forKeyPath:MOOShadowOffsetKeyPath options:0 context:NULL]; 798 | [self addObserver:self forKeyPath:MOOOuterGlowRadiusKeyPath options:0 context:NULL]; 799 | } 800 | 801 | - (void)_configureViewWithDefaults; 802 | { 803 | self.backgroundColor = [UIColor clearColor]; 804 | } 805 | 806 | - (UIImage *)_renderImageHighlighted:(BOOL)shouldBeHighlighted; 807 | { 808 | // Save state 809 | BOOL wasHighlighted = self.isHighlighted; 810 | 811 | // Render image 812 | self.highlighted = shouldBeHighlighted; 813 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0f); 814 | CGContextRef context = UIGraphicsGetCurrentContext(); 815 | [self.layer renderInContext:context]; 816 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 817 | UIGraphicsEndImageContext(); 818 | 819 | // Restore state 820 | self.highlighted = wasHighlighted; 821 | 822 | return image; 823 | } 824 | 825 | - (void)_setNeedsGradient; 826 | { 827 | _iconViewFlags.needsGradient = YES; 828 | } 829 | 830 | - (void)_updateGradientWithColors:(NSArray *)colors locations:(NSArray *)locations forType:(MOOGradientType)type; 831 | { 832 | if (!colors) 833 | { 834 | self.gradient = NULL; 835 | return; 836 | } 837 | 838 | if (!locations || [locations count] != [colors count]) 839 | { 840 | NSMutableArray *defaultLocations = [NSMutableArray arrayWithCapacity:[colors count]]; 841 | CGFloat step = 1.0f / ([colors count] - 1); 842 | CGFloat location = 0.0f; 843 | for (NSUInteger i = 0; i < [colors count]; i++) 844 | { 845 | [defaultLocations addObject:[NSNumber numberWithFloat:location]]; 846 | location += step; 847 | } 848 | 849 | locations = defaultLocations; 850 | } 851 | 852 | // Create colors and colorspace 853 | CGColorRef colorCArray[[colors count]]; 854 | 855 | // Get gradient locations 856 | CGFloat locationsCArray[[locations count]]; 857 | for (NSUInteger i = 0; i < [colors count]; i++) 858 | { 859 | colorCArray[i] = ((UIColor *)[colors objectAtIndex:i]).CGColor; 860 | locationsCArray[i] = [[locations objectAtIndex:i] floatValue]; 861 | } 862 | 863 | CFArrayRef colorsCFArray = CFArrayCreate(NULL, (const void **)&colorCArray, [colors count], &kCFTypeArrayCallBacks); 864 | CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); 865 | 866 | // Create and set gradient 867 | CGGradientRef gradient = CGGradientCreateWithColors(colorspace, colorsCFArray, locationsCArray); 868 | CGColorSpaceRelease(colorspace); 869 | CFRelease(colorsCFArray); 870 | self.gradient = gradient; 871 | CGGradientRelease(gradient); 872 | } 873 | 874 | @end 875 | 876 | // Helper functions 877 | 878 | static NSURL *NSURLWithResourceNamed(NSString *resourceName, NSBundle *bundle) 879 | { 880 | if (!resourceName) 881 | return nil; 882 | 883 | if (!bundle) 884 | bundle = [NSBundle mainBundle]; 885 | 886 | NSString *path = [bundle pathForResource:resourceName ofType:nil]; 887 | if (!path) 888 | { 889 | NSLog(@"File named %@ not found. Check spelling or capitalization?", resourceName); 890 | return nil; 891 | } 892 | 893 | return [NSURL fileURLWithPath:path]; 894 | } 895 | 896 | CGImageRef CGImageCreateMaskFromCGImage(CGImageRef source, CGSize size) 897 | { 898 | // If no image is passed, return nothing 899 | if (source == nil) { 900 | return NULL; 901 | } 902 | // Variables for image creation 903 | CGSize imageSize = CGSizeZero; 904 | size_t bytesPerRow = 0; 905 | const CGFloat scale = [UIScreen mainScreen].scale; 906 | 907 | CGImageRef maskSource; // The source to work create the mask from. Differs from `source` in that it may be resized 908 | 909 | if (size.width > 0.0f && size.height > 0.0f) 910 | { 911 | // Custom size 912 | imageSize = size; 913 | imageSize.width *= scale; 914 | imageSize.height *= scale; 915 | CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray(); 916 | bytesPerRow = imageSize.width * CGColorSpaceGetNumberOfComponents(colorspace); 917 | 918 | // Create bitmap context 919 | CGContextRef context = CGBitmapContextCreate(NULL, imageSize.width, imageSize.height, CGImageGetBitsPerComponent(source), bytesPerRow, colorspace, kCGBitmapAlphaInfoMask & kCGImageAlphaNone); 920 | CGColorSpaceRelease(colorspace); 921 | 922 | CGContextSetInterpolationQuality(context, kCGInterpolationLow); 923 | CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, imageSize.width, imageSize.height), source); 924 | maskSource = CGBitmapContextCreateImage(context); 925 | CGContextRelease(context); 926 | } 927 | else 928 | { 929 | // Default size 930 | imageSize = CGSizeMake(CGImageGetWidth(source), CGImageGetHeight(source)); 931 | bytesPerRow = CGImageGetBytesPerRow(source); 932 | 933 | // Retain maskSource to match the created image's retain count above 934 | maskSource = CGImageRetain(source); 935 | } 936 | 937 | // Create mask 938 | CGImageRef maskRef = CGImageMaskCreate(imageSize.width, imageSize.height, CGImageGetBitsPerComponent(maskSource), CGImageGetBitsPerPixel(maskSource), bytesPerRow, CGImageGetDataProvider(maskSource), NULL, NO); 939 | 940 | // release the source as it has been retained locally 941 | CGImageRelease(maskSource); 942 | return maskRef; 943 | } 944 | 945 | CGImageRef CGImageCreateMaskFromImageNamed(NSString *imageName, CGSize size) 946 | { 947 | NSURL *imageURL = NSURLWithResourceNamed(imageName, nil); 948 | UIImage *image = [UIImage imageWithContentsOfFile:[imageURL relativePath]]; 949 | return CGImageCreateMaskFromCGImage(image.CGImage, size); 950 | } 951 | 952 | CGImageRef CGImageCreateMaskFromPDFNamed(NSString *pdfName, CGSize size) 953 | { 954 | if (!pdfName) 955 | return NULL; 956 | 957 | // Grab pdf 958 | NSURL *pdfURL = NSURLWithResourceNamed(pdfName, nil); 959 | CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((__bridge CFURLRef)pdfURL); 960 | CGPDFPageRef firstPage = CGPDFDocumentGetPage(pdf, 1); 961 | 962 | if (firstPage == NULL) 963 | { 964 | CGPDFDocumentRelease(pdf); 965 | return NULL; 966 | } 967 | 968 | CGImageRef maskRef = CGImageCreateMaskFromPDFPage(firstPage, size); 969 | CGPDFDocumentRelease(pdf); 970 | 971 | return maskRef; 972 | } 973 | 974 | CGImageRef CGImageCreateMaskFromPDFData(NSData *pdfData, CGSize size) 975 | { 976 | if (!pdfData) 977 | return NULL; 978 | 979 | // Grab pdf 980 | CGDataProviderRef pdfDataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef) pdfData); 981 | CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(pdfDataProvider); 982 | CGPDFPageRef firstPage = CGPDFDocumentGetPage(pdf, 1); 983 | CGDataProviderRelease(pdfDataProvider); 984 | 985 | if (firstPage == NULL) 986 | { 987 | CGPDFDocumentRelease(pdf); 988 | return NULL; 989 | } 990 | 991 | CGImageRef maskRef = CGImageCreateMaskFromPDFPage(firstPage, size); 992 | CGPDFDocumentRelease(pdf); 993 | 994 | return maskRef; 995 | } 996 | 997 | CGImageRef CGImageCreateMaskFromPDFPage(CGPDFPageRef page, CGSize size) 998 | { 999 | // Calculate metrics 1000 | const CGRect mediaRect = CGPDFPageGetBoxRect(page, kCGPDFCropBox); 1001 | const CGSize pdfSize = (size.width > 0.0f && size.height > 0.0f) ? size : mediaRect.size; 1002 | 1003 | // Set up context 1004 | UIGraphicsBeginImageContextWithOptions(pdfSize, YES, 0.0f); 1005 | CGContextRef context = UIGraphicsGetCurrentContext(); 1006 | 1007 | // Draw background 1008 | [[UIColor whiteColor] set]; 1009 | CGContextFillRect(context, CGRectMake(0.0f, 0.0f, pdfSize.width, pdfSize.height)); 1010 | 1011 | // Scale and flip context right-side-up 1012 | CGContextScaleCTM(context, pdfSize.width / mediaRect.size.width, -pdfSize.height / mediaRect.size.height); 1013 | CGContextTranslateCTM(context, 0.0f, -mediaRect.size.height); 1014 | 1015 | // Draw pdf 1016 | CGContextDrawPDFPage(context, page); 1017 | 1018 | // Create image to mask 1019 | CGImageRef imageToMask = CGBitmapContextCreateImage(context); 1020 | UIGraphicsEndImageContext(); 1021 | 1022 | // Create image mask 1023 | CGImageRef maskRef = CGImageMaskCreate(CGImageGetWidth(imageToMask), CGImageGetHeight(imageToMask), CGImageGetBitsPerComponent(imageToMask), CGImageGetBitsPerPixel(imageToMask), CGImageGetBytesPerRow(imageToMask), CGImageGetDataProvider(imageToMask), NULL, NO); 1024 | CGImageRelease(imageToMask); 1025 | 1026 | return maskRef; 1027 | } 1028 | 1029 | CGImageRef CGImageCreateMaskFromResourceNamed(NSString *resourceName, CGSize size) 1030 | { 1031 | NSString *extension = [resourceName pathExtension]; 1032 | if ([extension isEqualToString:@"pdf"]) 1033 | return CGImageCreateMaskFromPDFNamed(resourceName, size); 1034 | 1035 | return CGImageCreateMaskFromImageNamed(resourceName, size); 1036 | }; 1037 | 1038 | /* 1039 | * CGImageCreateInvertedMaskWithMask. 1040 | * 1041 | * Adapted from Benjamin Godard's excellent NYXImagesKit: https://github.com/Nyx0uf/NYXImagesKit/blob/master/Categories/UIImage%2BFiltering.m 1042 | */ 1043 | /* Negative multiplier to invert a number */ 1044 | static float __negativeMultiplier = -1.0f; 1045 | static CGImageRef CGImageCreateInvertedMaskWithMask(CGImageRef sourceMask) 1046 | { 1047 | if (!sourceMask) 1048 | return NULL; 1049 | 1050 | if (!CGImageIsMask(sourceMask)) 1051 | { 1052 | NSLog(@"Attempting to invert non-mask: %@", sourceMask); 1053 | } 1054 | 1055 | /// Create an ARGB bitmap context 1056 | const size_t width = CGImageGetWidth(sourceMask); 1057 | const size_t height = CGImageGetHeight(sourceMask); 1058 | 1059 | /// Grab the image raw data 1060 | CFDataRef dataRef = CGDataProviderCopyData(CGImageGetDataProvider(sourceMask)); 1061 | if (!dataRef) 1062 | { 1063 | NSLog(@"Image to be inverted contains no data"); 1064 | return NULL; 1065 | } 1066 | UInt8* data = (UInt8*)CFDataGetBytePtr(dataRef); 1067 | 1068 | const size_t pixelsCount = width * height; 1069 | float* dataAsFloat = (float*)malloc(sizeof(float) * pixelsCount); 1070 | float min = 0.0f, max = 255.0f; 1071 | UInt8* dataGray = data + 1; 1072 | 1073 | /// vDSP_vsmsa() = multiply then add 1074 | /// slightly faster than the couple vDSP_vneg() & vDSP_vsadd() 1075 | /// Probably because there are 3 function calls less 1076 | 1077 | /// Calculate gray components 1078 | vDSP_vfltu8(dataGray, 2, dataAsFloat, 1, pixelsCount); 1079 | vDSP_vsmsa(dataAsFloat, 1, &__negativeMultiplier, &max, dataAsFloat, 1, pixelsCount); 1080 | vDSP_vclip(dataAsFloat, 1, &min, &max, dataAsFloat, 1, pixelsCount); 1081 | // The following line generates memory access errors on iOS 6+ and doesn't appear to be necessary. 1082 | // todo: figure out why float->uint conversion fails 1083 | // vDSP_vfixu8(dataAsFloat, 1, dataGray, 2, pixelsCount); 1084 | 1085 | // Create new image in the gray color space, since RGB images aren't valid masks 1086 | CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray(); 1087 | CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(dataRef); 1088 | CGImageRef invertedImage = CGImageCreate(width, height, CGImageGetBitsPerComponent(sourceMask), CGImageGetBitsPerPixel(sourceMask), CGImageGetBytesPerRow(sourceMask), colorspace, CGImageGetBitmapInfo(sourceMask), dataProvider, NULL, NO, kCGRenderingIntentDefault); 1089 | CGColorSpaceRelease(colorspace); 1090 | CGDataProviderRelease(dataProvider); 1091 | free(dataAsFloat); 1092 | CFRelease(dataRef); 1093 | 1094 | return invertedImage; 1095 | } 1096 | -------------------------------------------------------------------------------- /MOOMaskedIconView/MOOResourceList.h: -------------------------------------------------------------------------------- 1 | // 2 | // MOOResourceList.h 3 | // MOOMaskedIconView 4 | // 5 | // Created by Peyton Randolph on 2/27/12. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | /** 12 | MOOResourceList lists resources which should be cached and provides background rendering of itself. 13 | 14 | Resource lists should be placed inside the shared resource registry. 15 | 16 | @see MOOResourceRegistry 17 | */ 18 | @interface MOOResourceList : NSObject 19 | { 20 | NSArray *_names; 21 | } 22 | 23 | @property (strong, readonly) NSArray *names; 24 | @property (strong, readonly) NSArray *keys; 25 | 26 | /** 27 | Initializes a resource list with an array of resource names, e.g. {"Icon1.pdf", "Icon2.pdf", "Icon3.pdf"} 28 | 29 | @param resourceNames An array of resource names. *Note*: Names are not paths. 30 | 31 | @see initWithPlistNamed: 32 | @see listWithResourceNames: 33 | */ 34 | - (id)initWithResourceNames:(NSArray *)resourceNames; 35 | 36 | /** 37 | Initializes a resource list with an array taken from a given property list 38 | 39 | @param plistName The name of the plist of resource names to load 40 | 41 | @see initWithResourceNames: 42 | @see listWithPlistNamed: 43 | */ 44 | - (id)initWithPlistNamed:(NSString *)plistName; 45 | 46 | /** 47 | Creates a resource list with an array of resource names, e.g. {"Icon1.pdf", "Icon2.pdf", "Icon3.pdf"} 48 | 49 | @param resourceNames An array of resource names. *Note*: Names are not paths. 50 | 51 | @see initWithResourceNames: 52 | */ 53 | + (MOOResourceList *)listWithResourceNames:(NSArray *)resourceNames; 54 | 55 | /** 56 | Creates a resource list with an array taken from a given property list 57 | 58 | @param plistName The name of the plist of resource names to load 59 | 60 | @see initWithPlistNamed: 61 | */ 62 | + (MOOResourceList *)listWithPlistNamed:(NSString *)plistName; 63 | 64 | /** 65 | Renders and caches masks of every resource in the list in a background queue. 66 | */ 67 | - (void)renderMasksInBackground; 68 | 69 | /** 70 | The default background render queue. 71 | 72 | defaultRenderQueue is a serial GCD queue with background priority on iOS 5.0+ and low priority on iOS 4. 73 | */ 74 | + (dispatch_queue_t)defaultRenderQueue; 75 | 76 | @end 77 | 78 | /** 79 | MOOResourceRegistry registers resource lists that should be cached. After a MOOMaskedIconView renders its mask, it checks with the sharedRegistry to see whether the newly-created mask should go in the cache. 80 | */ 81 | @interface MOOResourceRegistry : NSObject 82 | { 83 | NSArray *_resourceLists; 84 | } 85 | 86 | /** 87 | The array of resourceLists tracked by the registry 88 | */ 89 | @property (nonatomic, strong, readonly) NSArray *resourceLists; 90 | 91 | /** 92 | Registers a resource list for querying by MOOMaskedIconView instances 93 | 94 | @param resourceList The resource list to register 95 | 96 | @see deregisterList: 97 | */ 98 | - (void)registerList:(MOOResourceList *)resourceList; 99 | 100 | /** 101 | Deregisters a resource list from querying by MOOMaskedIconView instances 102 | 103 | @param resourceList The resource list to deregister 104 | 105 | @see registerList: 106 | */ 107 | - (void)deregisterList:(MOOResourceList *)resourceList; 108 | 109 | /** 110 | Iterates through the resource list and returns whether the resource with key `key` should be cached. 111 | 112 | @param key The cache key of the queried resource 113 | 114 | @return Whether the resource with key `key` should go into the cache 115 | */ 116 | - (BOOL)shouldCacheResourceWithKey:(NSString *)key; 117 | 118 | /** 119 | @return A shared registry. 120 | */ 121 | + (MOOResourceRegistry *)sharedRegistry; 122 | 123 | @end -------------------------------------------------------------------------------- /MOOMaskedIconView/MOOResourceList.m: -------------------------------------------------------------------------------- 1 | // 2 | // MOOResourceList.m 3 | // MOOMaskedIconView 4 | // 5 | // Created by Peyton Randolph on 2/27/12. 6 | // 7 | 8 | #import "MOOResourceList.h" 9 | 10 | #import "AHHelper.h" 11 | #import "MOOCGImageWrapper.h" 12 | #import "MOOMaskedIconView.h" 13 | 14 | // Queues 15 | static dispatch_queue_t _defaultRenderQueue; 16 | 17 | // Shared instances 18 | static MOOResourceRegistry *_sharedRegistry; 19 | 20 | @interface MOOResourceList () 21 | 22 | @property (strong) NSArray *names; 23 | 24 | @end; 25 | 26 | @implementation MOOResourceList 27 | @synthesize names = _names; 28 | @dynamic keys; 29 | 30 | - (id)initWithResourceNames:(NSArray *)resourceNames; 31 | { 32 | if (!(self = [super init])) 33 | return nil; 34 | 35 | self.names = resourceNames; 36 | 37 | return self; 38 | } 39 | 40 | - (id)initWithPlistNamed:(NSString *)plistName; 41 | { 42 | NSString *plistPath = [[NSBundle mainBundle] pathForResource:plistName ofType:nil]; 43 | // Try loading as array 44 | NSArray *resourceNames = [NSArray arrayWithContentsOfFile:plistPath]; 45 | 46 | // Try loading as dictionary 47 | if (!resourceNames) 48 | resourceNames = [[NSDictionary dictionaryWithContentsOfFile:plistPath] objectForKey:@"Icons"]; 49 | 50 | if (!resourceNames) 51 | return nil; 52 | 53 | if (!(self = [self initWithResourceNames:resourceNames])) 54 | return nil; 55 | 56 | return self; 57 | } 58 | 59 | + (MOOResourceList *)listWithResourceNames:(NSArray *)resourceNames; 60 | { 61 | return AH_AUTORELEASE([[self alloc] initWithResourceNames:resourceNames]); 62 | } 63 | 64 | + (MOOResourceList *)listWithPlistNamed:(NSString *)plistName; 65 | { 66 | return AH_AUTORELEASE([[self alloc] initWithPlistNamed:plistName]); 67 | } 68 | 69 | - (void)dealloc; 70 | { 71 | self.names = nil; 72 | 73 | AH_SUPER_DEALLOC; 74 | } 75 | 76 | #pragma mark - Rendering methods 77 | 78 | - (void)renderMasksInBackground; 79 | { 80 | // Punt to the next cycle of the runLoop 81 | dispatch_async(dispatch_get_main_queue(), ^{ 82 | for (NSString *resourceName in self.names) 83 | dispatch_async([MOOResourceList defaultRenderQueue], ^{ 84 | NSCache *maskCache = [MOOMaskedIconView defaultMaskCache]; 85 | NSString *key = [resourceName stringByAppendingString:NSStringFromCGSize(CGSizeZero)]; 86 | if (![maskCache objectForKey:key]) 87 | { 88 | CGImageRef mask = CGImageCreateMaskFromResourceNamed(resourceName, CGSizeZero); 89 | MOOCGImageWrapper *imageWrapper = [MOOCGImageWrapper wrapperWithCGImage:mask]; 90 | CGImageRelease(mask); 91 | 92 | if (![maskCache objectForKey:key]) 93 | [maskCache setObject:imageWrapper forKey:key cost:imageWrapper.cost]; 94 | } 95 | }); 96 | }); 97 | 98 | } 99 | 100 | #pragma mark - Getters and setters 101 | 102 | - (NSArray *)keys; 103 | { 104 | NSMutableArray *keys = [NSMutableArray arrayWithCapacity:[self.names count]]; 105 | 106 | for (NSString *name in self.names) 107 | [keys addObject:[name stringByAppendingString:NSStringFromCGSize(CGSizeZero)]]; 108 | 109 | return keys; 110 | } 111 | 112 | #pragma mark - Queues 113 | 114 | + (dispatch_queue_t)defaultRenderQueue; 115 | { 116 | @synchronized (self) 117 | { 118 | if (!_defaultRenderQueue) 119 | { 120 | _defaultRenderQueue = dispatch_queue_create([NSStringFromSelector(_cmd) UTF8String], DISPATCH_QUEUE_SERIAL); 121 | 122 | // Background queue priority requires iOS 5.0+ 123 | NSString *reqSysVer = @"5.0"; 124 | NSString *currSysVer = [UIDevice currentDevice].systemVersion; 125 | dispatch_set_target_queue(_defaultRenderQueue, dispatch_get_global_queue(([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending) ? DISPATCH_QUEUE_PRIORITY_BACKGROUND : DISPATCH_QUEUE_PRIORITY_LOW, 0)); 126 | } 127 | return _defaultRenderQueue; 128 | } 129 | } 130 | 131 | @end 132 | 133 | @interface MOOResourceRegistry () 134 | 135 | @property (nonatomic, strong) NSArray *resourceLists; 136 | 137 | @end 138 | 139 | @implementation MOOResourceRegistry 140 | @synthesize resourceLists = _resourceLists; 141 | 142 | - (id)init; 143 | { 144 | if (!(self = [super init])) 145 | return nil; 146 | 147 | // Initialize resource lists 148 | self.resourceLists = [NSArray array]; 149 | 150 | return self; 151 | } 152 | 153 | - (void)dealloc; 154 | { 155 | self.resourceLists = nil; 156 | 157 | AH_SUPER_DEALLOC; 158 | } 159 | 160 | #pragma mark - List marshalling 161 | 162 | - (void)registerList:(MOOResourceList *)resourceList; 163 | { 164 | if (![self.resourceLists containsObject:resourceList]) 165 | self.resourceLists = [self.resourceLists arrayByAddingObject:resourceList]; 166 | } 167 | 168 | - (void)deregisterList:(MOOResourceList *)resourceList; 169 | { 170 | self.resourceLists = [self.resourceLists filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { 171 | return evaluatedObject != resourceList; 172 | }]]; 173 | } 174 | 175 | #pragma mark - Resource querying 176 | 177 | - (BOOL)shouldCacheResourceWithKey:(NSString *)key; 178 | { 179 | for (MOOResourceList *list in self.resourceLists) 180 | for (NSString *keyToCache in list.keys) 181 | if ([keyToCache isEqualToString:key]) 182 | return YES; 183 | return NO; 184 | } 185 | 186 | #pragma mark - Shared instances 187 | 188 | + (MOOResourceRegistry *)sharedRegistry; 189 | { 190 | if (!_sharedRegistry) 191 | _sharedRegistry = [[MOOResourceRegistry alloc] init]; 192 | 193 | return _sharedRegistry; 194 | } 195 | 196 | @end 197 | -------------------------------------------------------------------------------- /MOOMaskedIconView/MOOStyleTrait.h: -------------------------------------------------------------------------------- 1 | // 2 | // MOOStyleTrait.h 3 | // MOOMaskedIconView 4 | // 5 | // Created by Peyton Randolph on 2/26/12. 6 | // 7 | 8 | #import 9 | 10 | #import "MOOMaskedIconView.h" 11 | 12 | /** 13 | MOOStyleTrait is a protocol defining the basic operations of a style trait. 14 | */ 15 | @protocol MOOStyleTrait 16 | 17 | @property (nonatomic, strong, readonly) Protocol *styleProtocol; 18 | 19 | /** 20 | * Creates and returns a new, autoreleased trait 21 | */ 22 | + (id)trait; 23 | 24 | /** 25 | * Overwrites properties of the current trait with values from another trait as long as those values are set on the other trait. 26 | */ 27 | - (void)mixInTrait:(id)otherTrait; 28 | 29 | /** 30 | * Calls mixInTrait: on every element in an array of traits. 31 | * 32 | * @see mixInTrait: 33 | */ 34 | - (void)mixInTraits:(NSArray *)traits; 35 | 36 | /** 37 | * Creates a new trait composed of the current trait and a passed-in trait. Does not overwrite properties on the callee, unlike mixInTrait:. 38 | * 39 | * @see mixInTrait: 40 | */ 41 | - (id)traitMixedWithTrait:(id)otherTrait; 42 | 43 | /** 44 | * Creates a trait composed of the current trait and an array of other traits. Does not overwrite properties on the callee, unlike mixInTraits:. 45 | * 46 | * @see traitMixedWithTrait: 47 | * @see mixInTrait: 48 | */ 49 | - (id)traitMixedWithTraits:(NSArray *)otherTraits; 50 | 51 | @end 52 | 53 | /** 54 | MOOStyleTrait is an implementation of that conforms to 55 | */ 56 | @interface MOOStyleTrait : NSObject 57 | 58 | + (MOOStyleTrait *)trait; 59 | 60 | @end 61 | 62 | /** @name Helper functions */ 63 | 64 | /** 65 | * Returns the set of all property names in a protocol excluding properties. 66 | */ 67 | NSSet *propertyNamesForStyleProtocol(Protocol *proto); 68 | 69 | /** 70 | * Returns the set of all property names in a protocol. 71 | */ 72 | NSSet *propertyNamesForProtocol(Protocol *proto); 73 | -------------------------------------------------------------------------------- /MOOMaskedIconView/MOOStyleTrait.m: -------------------------------------------------------------------------------- 1 | // 2 | // MOOStyleTrait.m 3 | // MOOMaskedIconView 4 | // 5 | // Created by Peyton Randolph on 2/26/12. 6 | // 7 | 8 | #import "MOOStyleTrait.h" 9 | 10 | #import "AHHelper.h" 11 | #import 12 | 13 | @implementation MOOStyleTrait 14 | @dynamic styleProtocol; 15 | 16 | @synthesize color; 17 | @synthesize highlightedColor; 18 | @synthesize pattern; 19 | @synthesize patternBlendMode; 20 | @synthesize overlay; 21 | @synthesize overlayBlendMode; 22 | @synthesize drawingBlock; 23 | @synthesize gradientStartColor; 24 | @synthesize gradientEndColor; 25 | @synthesize gradientColors; 26 | @synthesize gradientLocations; 27 | @synthesize gradientType; 28 | @synthesize shadowColor; 29 | @synthesize shadowOffset; 30 | @synthesize clipsShadow; 31 | @synthesize innerShadowColor; 32 | @synthesize innerShadowOffset; 33 | @synthesize outerGlowColor; 34 | @synthesize outerGlowRadius; 35 | @synthesize innerGlowColor; 36 | @synthesize innerGlowRadius; 37 | 38 | + (MOOStyleTrait *)trait; 39 | { 40 | return AH_AUTORELEASE([[MOOStyleTrait alloc] init]); 41 | } 42 | 43 | - (void)dealloc; 44 | { 45 | self.color = nil; 46 | self.highlightedColor = nil; 47 | self.pattern = nil; 48 | self.overlay = nil; 49 | self.drawingBlock = NULL; 50 | self.gradientStartColor = nil; 51 | self.gradientEndColor = nil; 52 | self.gradientColors = nil; 53 | self.gradientLocations = nil; 54 | self.shadowColor = nil; 55 | self.innerShadowColor = nil; 56 | self.outerGlowColor = nil; 57 | self.innerGlowColor = nil; 58 | 59 | AH_SUPER_DEALLOC; 60 | } 61 | 62 | #pragma mark - MOOStyleTrait methods 63 | 64 | - (Protocol *)styleProtocol; 65 | { 66 | return @protocol(MOOMaskedIconViewStyles); 67 | } 68 | 69 | - (void)mixInTrait:(id)otherTrait; 70 | { 71 | if (![[self class] conformsToProtocol:otherTrait.styleProtocol]) 72 | { 73 | NSLog(@"Attempting to mix object %@ of incompatible protocol %@ into object %@ of protocol %@.", otherTrait, NSStringFromProtocol(otherTrait.styleProtocol), self, NSStringFromProtocol(self.styleProtocol)); 74 | return; 75 | } 76 | 77 | id propertyValue; 78 | for (NSString *propertyName in propertyNamesForStyleProtocol(otherTrait.styleProtocol)) 79 | if ((propertyValue = [(NSObject *)otherTrait valueForKey:propertyName])) 80 | [self setValue:propertyValue forKey:propertyName]; 81 | } 82 | 83 | - (void)mixInTraits:(NSArray *)traits; 84 | { 85 | if ([traits count] == 0) 86 | return; 87 | 88 | [self mixInTrait:[traits objectAtIndex:0]]; 89 | [self mixInTraits:[traits subarrayWithRange:NSMakeRange(1, [traits count] - 1)]]; 90 | } 91 | 92 | - (MOOStyleTrait *)traitMixedWithTrait:(MOOStyleTrait *)otherTrait; 93 | { 94 | MOOStyleTrait *newTrait = AH_AUTORELEASE([[MOOStyleTrait alloc] init]); 95 | [newTrait mixInTraits:[NSArray arrayWithObjects:self, otherTrait, nil]]; 96 | 97 | return newTrait; 98 | } 99 | 100 | - (MOOStyleTrait *)traitMixedWithTraits:(NSArray *)otherTraits; 101 | { 102 | MOOStyleTrait *newTrait = AH_AUTORELEASE([[MOOStyleTrait alloc] init]); 103 | NSMutableArray *otherTraitsMutable = [NSMutableArray arrayWithArray:otherTraits]; 104 | [otherTraitsMutable insertObject:self atIndex:0]; 105 | [newTrait mixInTraits:otherTraitsMutable]; 106 | 107 | return newTrait; 108 | } 109 | 110 | @end 111 | 112 | #pragma mark - Helper functions 113 | 114 | // Returns the set of all property names in a protocol excluding properties. 115 | NSSet *propertyNamesForStyleProtocol(Protocol *proto) 116 | { 117 | NSSet *propertyList = propertyNamesForProtocol(proto); 118 | 119 | // Strip properties. They're irrelevent. 120 | if (protocol_conformsToProtocol(proto, @protocol(NSObject))) 121 | { 122 | static NSSet *nsObjectProperties; 123 | if (!nsObjectProperties) 124 | nsObjectProperties = propertyNamesForProtocol(@protocol(NSObject)); 125 | 126 | propertyList = [propertyList objectsPassingTest:^BOOL(id obj, BOOL *stop) { 127 | return ![nsObjectProperties containsObject:obj]; 128 | }]; 129 | } 130 | 131 | return propertyList; 132 | } 133 | 134 | // Returns the set of all property names in a protocol. 135 | NSSet *propertyNamesForProtocol(Protocol *proto) 136 | { 137 | unsigned int propertyListCount; 138 | objc_property_t *propertyList = protocol_copyPropertyList(proto, &propertyListCount); 139 | objc_property_t *incPropertyList = propertyList; 140 | NSMutableSet *propertyNames = [NSMutableSet setWithCapacity:propertyListCount]; 141 | 142 | while (propertyListCount) 143 | { 144 | NSString *propertyName = [NSString stringWithUTF8String:property_getName(*incPropertyList)]; 145 | [propertyNames addObject:propertyName]; 146 | 147 | ++incPropertyList; 148 | --propertyListCount; 149 | } 150 | 151 | free(propertyList); 152 | return propertyNames; 153 | } 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Masked Icons](https://s3.amazonaws.com/peyton.github.com/MOOMaskedIconView/Why.png) 2 | 3 | #Introduction 4 | ---------- 5 | 6 | MOOMaskedIconView is a UIView subclass that uses black-and-white masks to draw icons of different 7 | styles at any size or resolution. It's like Photoshop layer styles, using the same technique as 8 | UITabBar to generate various effects from a single icon mask. 9 | 10 | ![Tab bar icons](https://s3.amazonaws.com/peyton.github.com/MOOMaskedIconView/Sample-Bar-Cutaway.png) 11 | 12 | MOOMaskedIconView displays common image formats and PDFs, the native vector file format of iOS and 13 | OS X. PDFs are best—they're easy to maintain and resolution independent. 14 | 15 | #Examples 16 | --------- 17 | 18 | ###Create a green icon from a PNG 19 | 20 | MOOMaskedIconView *iconView = [MOOMaskedIconView iconWithResourceNamed:@"icon.png"] 21 | iconView.color = [UIColor greenColor]; 22 | [self.view addSubview:iconView]; 23 | 24 | ###Resize a PDF icon and add a reusable gradient trait 25 | 26 | MOOStyleTrait *grayGradientTrait = [MOOStyleTrait trait]; 27 | grayGradientTrait.gradientColors = [NSArray arrayWithObjects:[UIColor colorWithWhite:0.7f alpha:1.0f], 28 | [UIColor colorWithWhite:0.5f alpha:1.0f], nil]; 29 | 30 | MOOMaskedIconView *iconView = [MOOMaskedIconView iconWithPDFNamed:@"icon.pdf" size:CGSizeMake(32.0f, 26.0f)]; 31 | [iconView mixInTrait:grayGradientTrait]; 32 | [self.view addSubview:iconView]; 33 | 34 | ###Add an overlay and a shadow to a red icon 35 | 36 | UIImage *overlay = [UIImage imageNamed:@"overlay.png"]; 37 | MOOMaskedIconView *iconView = [MOOMaskedIconView iconWithImageNamed:@"icon.png"]; 38 | iconView.color = [UIColor redColor]; 39 | iconView.overlay = overlay; 40 | iconView.shadowColor = [UIColor darkGrayColor]; 41 | iconView.shadowOffset = CGSizeMake(3.0f, 3.0f); 42 | [self.view addSubview:iconView]; 43 | 44 | ###Render a PDF icon into a UIButton 45 | 46 | MOOMaskedIconView *iconView = [MOOMaskedIconView iconWithResourceNamed:@"icon.pdf"]; 47 | iconView.color = [UIColor magentaColor]; 48 | iconView.highlightedColor = [UIColor orangeColor]; 49 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 50 | [button setImage:[iconView renderImage] forState:UIControlStateNormal] 51 | [button setImage:[iconView renderHighlightedImage] forState:UIControlStateHighlighted]; 52 | [self.view addSubview:button]; 53 | 54 | For more examples, check out the [Demo Project](https://github.com/peyton/MOOMaskedIconView/tree/master/Demo%20Project). 55 | 56 | For the full list of styleable properties, see [MOOMaskedIconViewStyles](http://peytn.com/MOOMaskedIconView/html/Protocols/MOOMaskedIconViewStyles.html). 57 | 58 | For information on reusable styles, caching, and other advanced topics, see [*Advanced Use*](https://github.com/peyton/MOOMaskedIconView/wiki/Advanced-Use). 59 | 60 | #How to install 61 | ----------- 62 | 63 | ###First: Clone into a submodule 64 | 65 | In your project's folder, type: 66 | 67 | git submodule add git://github.com/peyton/MOOMaskedIconView.git 68 | 69 | A submodule allows your repository to contain a clone of an external 70 | project. If you don't want a submodule, use: 71 | 72 | git clone git://github.com/peyton/MOOMaskedIconView.git 73 | 74 | ###Next: Add classes 75 | 76 | Drag `MOOMaskedIconView/` into your Xcode project's file browser. 77 | 78 | *Note:* An options dialog will pop up. If you're using MOOMaskedIconView as a submodule, 79 | you should uncheck "Copy items into destination group's folder (if needed)." 80 | 81 | ###Then: Add Accelerate.framework 82 | 83 | ![Accelerate.framework process](https://s3.amazonaws.com/peyton.github.com/MOOMaskedIconView/AddAccelerateFramework.png) 84 | 85 | With your application's target selected in the navigator, click on the "Build Phases" tab. Under "Link Binary With Libraries," click the "+" button and add `Accelerate.framework`. 86 | 87 | ###Finally: Import the header 88 | 89 | #import "MOOMaskedIconView.h" 90 | 91 | ###Later: Update to the latest version 92 | 93 | `cd` into the MOOMaskedIconView directory and run: 94 | 95 | git pull 96 | 97 | #Creating image masks 98 | --------- 99 | 100 | ![Image mask process](https://s3.amazonaws.com/peyton.github.com/MOOMaskedIconView/Mask.png) 101 | 102 | An image mask is a black-and-white image that clips drawing. Quartz clipping follows three simple rules: 103 | 104 | * Black pixels render opaquely. 105 | * White pixels render transparently. 106 | * Gray pixels render with an alpha value of 1 - *source pixel's gray value*. 107 | 108 | Mask images may not use an alpha channel, so icons with transparency must be set on a white background. For more information about Quartz image masking, see 109 | the [Quartz 2D Programming Guide](https://developer.apple.com/library/ios/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_images/dq_images.html%23//apple_ref/doc/uid/TP30001066-CH212-CJBHDDBE). 110 | 111 | #Scalable icons with PDFs 112 | --------- 113 | 114 | MOOMaskedIconView makes it easy to use PDFs as icons, eliminating "\*@2x.\*" files. 115 | Many Apple applications on OS X use PDF icons for resolution independence. 116 | 117 | Next to the network, the biggest source of latency on an iPhone is the disk. For small, simple icons the PDF format adds a few KB of overhead over PNG. 118 | Because the iPhone loads data in chunks, in practice the difference in loading time is nothing. For larger icons a PDF of vectors can *save* space. 119 | 120 | Some editors need a little massaging to export PDFs suitable for 121 | iOS icons. How-to instructions are [here](https://github.com/peyton/MOOMaskedIconView/wiki/Exporting-PDFs). 122 | 123 | #Contributing 124 | -------- 125 | 126 | Forks, patches, and other suggestions are always welcome and addressed as quickly as possible. Here's a [quick guide](https://github.com/peyton/MOOMaskedIconView/wiki/Contributing) to the process. 127 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | In jurisdictions where a dedication to public domain is not possible, 27 | the following applies: 28 | 29 | Copyright (C) 2012 Peyton Randolph 30 | 31 | Permission is hereby granted, free of charge, to any person obtaining 32 | a copy of this software and associated documentation files (the 33 | "Software"), to deal in the Software without restriction, including 34 | without limitation the rights to use, copy, modify, merge, publish, 35 | distribute, sublicense, and/or sell copies of the Software, and to 36 | permit persons to whom the Software is furnished to do so, subject 37 | to the following conditions: 38 | 39 | The above copyright notice and this permission notice shall be 40 | included in all copies or substantial portions of the Software. 41 | 42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 43 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 44 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 45 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 46 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 47 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 48 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 49 | --------------------------------------------------------------------------------