├── .gitignore ├── JMTabView.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── JMTabView ├── Classes │ ├── Categories │ │ ├── UIColor+Hex.h │ │ ├── UIColor+Hex.m │ │ ├── UIView+InnerShadow.h │ │ ├── UIView+InnerShadow.m │ │ ├── UIView+Positioning.h │ │ ├── UIView+Positioning.m │ │ ├── UIView+Size.h │ │ └── UIView+Size.m │ └── Subviews │ │ ├── JMSelectionView.h │ │ ├── JMSelectionView.m │ │ ├── JMTabConstants.h │ │ ├── JMTabContainer.h │ │ ├── JMTabContainer.m │ │ ├── JMTabItem.h │ │ ├── JMTabItem.m │ │ ├── JMTabView.h │ │ ├── JMTabView.m │ │ └── Layers │ │ ├── BarBackgroundLayer.h │ │ └── BarBackgroundLayer.m └── JMTabView-Prefix.pch ├── LICENSE ├── TabDemo ├── Classes │ ├── Custom │ │ ├── CustomBackgroundLayer.h │ │ ├── CustomBackgroundLayer.m │ │ ├── CustomNoiseBackgroundView.h │ │ ├── CustomNoiseBackgroundView.m │ │ ├── CustomSelectionView.h │ │ ├── CustomSelectionView.m │ │ ├── CustomTabItem.h │ │ └── CustomTabItem.m │ ├── TabDemoAppDelegate.h │ ├── TabDemoAppDelegate.m │ ├── TabDemoViewController.h │ └── TabDemoViewController.m ├── Resources │ ├── icon1.png │ ├── icon1@2x.png │ ├── icon2.png │ ├── icon2@2x.png │ ├── icon3.png │ ├── icon3@2x.png │ ├── noise-tile.png │ └── noise-tile@2x.png ├── TabDemo-Info.plist ├── TabDemo-Prefix.pch ├── en.lproj │ └── InfoPlist.strings └── main.m └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | # xcode noise 2 | *.mode1v3 3 | *.pbxuser 4 | *.perspective 5 | *.perspectivev3 6 | *.pyc 7 | *~.nib/ 8 | build/* 9 | 10 | *.xcuserdatad 11 | 12 | 13 | # Textmate - if you build your xcode projects with it 14 | *.tm_build_errors 15 | 16 | # old skool 17 | .svn 18 | 19 | # osx noise 20 | .DS_Store 21 | profile 22 | 23 | -------------------------------------------------------------------------------- /JMTabView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4719172713A4875400567E9E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4719172513A4875400567E9E /* InfoPlist.strings */; }; 11 | 4719172A13A4875400567E9E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4719172913A4875400567E9E /* main.m */; }; 12 | 4719174D13A48F4300567E9E /* libJMTabView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 471916E913A4833A00567E9E /* libJMTabView.a */; }; 13 | 4719175F13A4909500567E9E /* JMTabView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4719175C13A4909500567E9E /* JMTabView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 14 | 4719176013A4909500567E9E /* JMTabView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4719175D13A4909500567E9E /* JMTabView.m */; }; 15 | 4719176313A490CF00567E9E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4719176213A490CF00567E9E /* UIKit.framework */; }; 16 | 4719176513A490D500567E9E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4719176413A490D500567E9E /* CoreGraphics.framework */; }; 17 | 4719176713A490DC00567E9E /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4719176613A490DC00567E9E /* QuartzCore.framework */; }; 18 | 4719176913A490E200567E9E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4719176813A490E200567E9E /* Foundation.framework */; }; 19 | 4719176B13A490F500567E9E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4719176A13A490F500567E9E /* UIKit.framework */; }; 20 | 4719176D13A490FB00567E9E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4719176C13A490FB00567E9E /* Foundation.framework */; }; 21 | 4719176F13A4910000567E9E /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4719176E13A4910000567E9E /* QuartzCore.framework */; }; 22 | 4719177113A4910600567E9E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4719177013A4910600567E9E /* CoreGraphics.framework */; }; 23 | 47ABA8E813A9BCA900B056C3 /* CustomTabItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 47ABA8E713A9BCA900B056C3 /* CustomTabItem.m */; }; 24 | 47ABA90713A9F5B300B056C3 /* noise-tile.png in Resources */ = {isa = PBXBuildFile; fileRef = 47ABA90513A9F5B300B056C3 /* noise-tile.png */; }; 25 | 47ABA90813A9F5B300B056C3 /* noise-tile@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 47ABA90613A9F5B300B056C3 /* noise-tile@2x.png */; }; 26 | 47ABA90913A9F5CC00B056C3 /* CustomSelectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 47ABA8EC13A9C0E300B056C3 /* CustomSelectionView.m */; }; 27 | 47ABA90A13A9F5DA00B056C3 /* CustomBackgroundLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 47ABA8F613A9D3B700B056C3 /* CustomBackgroundLayer.m */; }; 28 | 47ABA90D13A9F75600B056C3 /* CustomNoiseBackgroundView.m in Sources */ = {isa = PBXBuildFile; fileRef = 47ABA90C13A9F75500B056C3 /* CustomNoiseBackgroundView.m */; }; 29 | 47F343C213A49AD700EC49ED /* UIColor+Hex.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F343C013A49AD700EC49ED /* UIColor+Hex.h */; settings = {ATTRIBUTES = (); }; }; 30 | 47F343C313A49AD700EC49ED /* UIColor+Hex.m in Sources */ = {isa = PBXBuildFile; fileRef = 47F343C113A49AD700EC49ED /* UIColor+Hex.m */; }; 31 | 47F343C713A49BA900EC49ED /* BarBackgroundLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F343C513A49BA900EC49ED /* BarBackgroundLayer.h */; settings = {ATTRIBUTES = (); }; }; 32 | 47F343C813A49BA900EC49ED /* BarBackgroundLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 47F343C613A49BA900EC49ED /* BarBackgroundLayer.m */; }; 33 | 47F343D213A4A23400EC49ED /* JMTabContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F343D013A4A23400EC49ED /* JMTabContainer.h */; }; 34 | 47F343D313A4A23400EC49ED /* JMTabContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 47F343D113A4A23400EC49ED /* JMTabContainer.m */; }; 35 | 47F343D613A4A3BE00EC49ED /* JMTabItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F343D413A4A3BE00EC49ED /* JMTabItem.h */; }; 36 | 47F343D713A4A3BE00EC49ED /* JMTabItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 47F343D513A4A3BE00EC49ED /* JMTabItem.m */; }; 37 | 47F343D913A4A78500EC49ED /* JMTabConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F343D813A4A78500EC49ED /* JMTabConstants.h */; }; 38 | 47F343E013A4AF6F00EC49ED /* TabDemoAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 47F343DC13A4AF6F00EC49ED /* TabDemoAppDelegate.m */; }; 39 | 47F343E113A4AF6F00EC49ED /* TabDemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 47F343DE13A4AF6F00EC49ED /* TabDemoViewController.m */; }; 40 | 47F343E813A4B52300EC49ED /* icon1.png in Resources */ = {isa = PBXBuildFile; fileRef = 47F343E213A4B15500EC49ED /* icon1.png */; }; 41 | 47F343E913A4B52500EC49ED /* icon1@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 47F343E313A4B15500EC49ED /* icon1@2x.png */; }; 42 | 47F343EA13A4B52700EC49ED /* icon2.png in Resources */ = {isa = PBXBuildFile; fileRef = 47F343E413A4B15500EC49ED /* icon2.png */; }; 43 | 47F343EB13A4B52A00EC49ED /* icon2@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 47F343E513A4B15500EC49ED /* icon2@2x.png */; }; 44 | 47F343EC13A4B52C00EC49ED /* icon3.png in Resources */ = {isa = PBXBuildFile; fileRef = 47F343E613A4B15500EC49ED /* icon3.png */; }; 45 | 47F343ED13A4B52E00EC49ED /* icon3@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 47F343E713A4B15500EC49ED /* icon3@2x.png */; }; 46 | 47F343F013A4B6ED00EC49ED /* JMSelectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F343EE13A4B6ED00EC49ED /* JMSelectionView.h */; }; 47 | 47F343F113A4B6ED00EC49ED /* JMSelectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 47F343EF13A4B6ED00EC49ED /* JMSelectionView.m */; }; 48 | 47F3441C13A4E27100EC49ED /* UIView+InnerShadow.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F3441A13A4E27100EC49ED /* UIView+InnerShadow.h */; }; 49 | 47F3441D13A4E27100EC49ED /* UIView+InnerShadow.m in Sources */ = {isa = PBXBuildFile; fileRef = 47F3441B13A4E27100EC49ED /* UIView+InnerShadow.m */; }; 50 | 47F3442213A4E95400EC49ED /* UIView+Positioning.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F3441E13A4E95400EC49ED /* UIView+Positioning.h */; }; 51 | 47F3442313A4E95400EC49ED /* UIView+Positioning.m in Sources */ = {isa = PBXBuildFile; fileRef = 47F3441F13A4E95400EC49ED /* UIView+Positioning.m */; }; 52 | 47F3442413A4E95400EC49ED /* UIView+Size.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F3442013A4E95400EC49ED /* UIView+Size.h */; }; 53 | 47F3442513A4E95400EC49ED /* UIView+Size.m in Sources */ = {isa = PBXBuildFile; fileRef = 47F3442113A4E95400EC49ED /* UIView+Size.m */; }; 54 | /* End PBXBuildFile section */ 55 | 56 | /* Begin PBXContainerItemProxy section */ 57 | 4719174B13A48F3C00567E9E /* PBXContainerItemProxy */ = { 58 | isa = PBXContainerItemProxy; 59 | containerPortal = 471916E013A4833A00567E9E /* Project object */; 60 | proxyType = 1; 61 | remoteGlobalIDString = 471916E813A4833A00567E9E; 62 | remoteInfo = JMTabView; 63 | }; 64 | /* End PBXContainerItemProxy section */ 65 | 66 | /* Begin PBXFileReference section */ 67 | 471916E913A4833A00567E9E /* libJMTabView.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libJMTabView.a; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | 471916F013A4833A00567E9E /* JMTabView-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JMTabView-Prefix.pch"; sourceTree = ""; }; 69 | 4719171D13A4875400567E9E /* TabDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TabDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 70 | 4719172413A4875400567E9E /* TabDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TabDemo-Info.plist"; sourceTree = ""; }; 71 | 4719172613A4875400567E9E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 72 | 4719172813A4875400567E9E /* TabDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TabDemo-Prefix.pch"; sourceTree = ""; }; 73 | 4719172913A4875400567E9E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 74 | 4719175C13A4909500567E9E /* JMTabView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMTabView.h; sourceTree = ""; }; 75 | 4719175D13A4909500567E9E /* JMTabView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMTabView.m; sourceTree = ""; }; 76 | 4719176213A490CF00567E9E /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 77 | 4719176413A490D500567E9E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 78 | 4719176613A490DC00567E9E /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 79 | 4719176813A490E200567E9E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 80 | 4719176A13A490F500567E9E /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 81 | 4719176C13A490FB00567E9E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 82 | 4719176E13A4910000567E9E /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 83 | 4719177013A4910600567E9E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 84 | 47ABA8E613A9BCA900B056C3 /* CustomTabItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomTabItem.h; sourceTree = ""; }; 85 | 47ABA8E713A9BCA900B056C3 /* CustomTabItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomTabItem.m; sourceTree = ""; }; 86 | 47ABA8EB13A9C0E300B056C3 /* CustomSelectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomSelectionView.h; sourceTree = ""; }; 87 | 47ABA8EC13A9C0E300B056C3 /* CustomSelectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomSelectionView.m; sourceTree = ""; }; 88 | 47ABA8F513A9D3B700B056C3 /* CustomBackgroundLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomBackgroundLayer.h; sourceTree = ""; }; 89 | 47ABA8F613A9D3B700B056C3 /* CustomBackgroundLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomBackgroundLayer.m; sourceTree = ""; }; 90 | 47ABA90513A9F5B300B056C3 /* noise-tile.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "noise-tile.png"; sourceTree = ""; }; 91 | 47ABA90613A9F5B300B056C3 /* noise-tile@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "noise-tile@2x.png"; sourceTree = ""; }; 92 | 47ABA90B13A9F75500B056C3 /* CustomNoiseBackgroundView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomNoiseBackgroundView.h; sourceTree = ""; }; 93 | 47ABA90C13A9F75500B056C3 /* CustomNoiseBackgroundView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomNoiseBackgroundView.m; sourceTree = ""; }; 94 | 47F343C013A49AD700EC49ED /* UIColor+Hex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+Hex.h"; sourceTree = ""; }; 95 | 47F343C113A49AD700EC49ED /* UIColor+Hex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+Hex.m"; sourceTree = ""; }; 96 | 47F343C513A49BA900EC49ED /* BarBackgroundLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BarBackgroundLayer.h; sourceTree = ""; }; 97 | 47F343C613A49BA900EC49ED /* BarBackgroundLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BarBackgroundLayer.m; sourceTree = ""; }; 98 | 47F343D013A4A23400EC49ED /* JMTabContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMTabContainer.h; sourceTree = ""; }; 99 | 47F343D113A4A23400EC49ED /* JMTabContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMTabContainer.m; sourceTree = ""; }; 100 | 47F343D413A4A3BE00EC49ED /* JMTabItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMTabItem.h; sourceTree = ""; }; 101 | 47F343D513A4A3BE00EC49ED /* JMTabItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMTabItem.m; sourceTree = ""; }; 102 | 47F343D813A4A78500EC49ED /* JMTabConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMTabConstants.h; sourceTree = ""; }; 103 | 47F343DB13A4AF6F00EC49ED /* TabDemoAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TabDemoAppDelegate.h; sourceTree = ""; }; 104 | 47F343DC13A4AF6F00EC49ED /* TabDemoAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TabDemoAppDelegate.m; sourceTree = ""; }; 105 | 47F343DD13A4AF6F00EC49ED /* TabDemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TabDemoViewController.h; sourceTree = ""; }; 106 | 47F343DE13A4AF6F00EC49ED /* TabDemoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TabDemoViewController.m; sourceTree = ""; }; 107 | 47F343E213A4B15500EC49ED /* icon1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon1.png; sourceTree = ""; }; 108 | 47F343E313A4B15500EC49ED /* icon1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon1@2x.png"; sourceTree = ""; }; 109 | 47F343E413A4B15500EC49ED /* icon2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon2.png; sourceTree = ""; }; 110 | 47F343E513A4B15500EC49ED /* icon2@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon2@2x.png"; sourceTree = ""; }; 111 | 47F343E613A4B15500EC49ED /* icon3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon3.png; sourceTree = ""; }; 112 | 47F343E713A4B15500EC49ED /* icon3@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon3@2x.png"; sourceTree = ""; }; 113 | 47F343EE13A4B6ED00EC49ED /* JMSelectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMSelectionView.h; sourceTree = ""; }; 114 | 47F343EF13A4B6ED00EC49ED /* JMSelectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMSelectionView.m; sourceTree = ""; }; 115 | 47F3441A13A4E27100EC49ED /* UIView+InnerShadow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+InnerShadow.h"; sourceTree = ""; }; 116 | 47F3441B13A4E27100EC49ED /* UIView+InnerShadow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+InnerShadow.m"; sourceTree = ""; }; 117 | 47F3441E13A4E95400EC49ED /* UIView+Positioning.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Positioning.h"; sourceTree = ""; }; 118 | 47F3441F13A4E95400EC49ED /* UIView+Positioning.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Positioning.m"; sourceTree = ""; }; 119 | 47F3442013A4E95400EC49ED /* UIView+Size.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Size.h"; sourceTree = ""; }; 120 | 47F3442113A4E95400EC49ED /* UIView+Size.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Size.m"; sourceTree = ""; }; 121 | /* End PBXFileReference section */ 122 | 123 | /* Begin PBXFrameworksBuildPhase section */ 124 | 471916E613A4833A00567E9E /* Frameworks */ = { 125 | isa = PBXFrameworksBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | 4719176913A490E200567E9E /* Foundation.framework in Frameworks */, 129 | 4719176713A490DC00567E9E /* QuartzCore.framework in Frameworks */, 130 | 4719176513A490D500567E9E /* CoreGraphics.framework in Frameworks */, 131 | 4719176313A490CF00567E9E /* UIKit.framework in Frameworks */, 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | 4719171A13A4875400567E9E /* Frameworks */ = { 136 | isa = PBXFrameworksBuildPhase; 137 | buildActionMask = 2147483647; 138 | files = ( 139 | 4719177113A4910600567E9E /* CoreGraphics.framework in Frameworks */, 140 | 4719176F13A4910000567E9E /* QuartzCore.framework in Frameworks */, 141 | 4719176D13A490FB00567E9E /* Foundation.framework in Frameworks */, 142 | 4719176B13A490F500567E9E /* UIKit.framework in Frameworks */, 143 | 4719174D13A48F4300567E9E /* libJMTabView.a in Frameworks */, 144 | ); 145 | runOnlyForDeploymentPostprocessing = 0; 146 | }; 147 | /* End PBXFrameworksBuildPhase section */ 148 | 149 | /* Begin PBXGroup section */ 150 | 471916DE13A4833A00567E9E = { 151 | isa = PBXGroup; 152 | children = ( 153 | 471916EE13A4833A00567E9E /* JMTabView */, 154 | 4719172213A4875400567E9E /* TabDemo */, 155 | 471916EA13A4833A00567E9E /* Products */, 156 | ); 157 | sourceTree = ""; 158 | }; 159 | 471916EA13A4833A00567E9E /* Products */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 4719171D13A4875400567E9E /* TabDemo.app */, 163 | ); 164 | name = Products; 165 | sourceTree = ""; 166 | }; 167 | 471916EE13A4833A00567E9E /* JMTabView */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | 4719175713A4909500567E9E /* Classes */, 171 | 471916EF13A4833A00567E9E /* Supporting Files */, 172 | 4719176113A4909B00567E9E /* Frameworks */, 173 | ); 174 | path = JMTabView; 175 | sourceTree = ""; 176 | }; 177 | 471916EF13A4833A00567E9E /* Supporting Files */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 471916F013A4833A00567E9E /* JMTabView-Prefix.pch */, 181 | ); 182 | name = "Supporting Files"; 183 | sourceTree = ""; 184 | }; 185 | 4719172213A4875400567E9E /* TabDemo */ = { 186 | isa = PBXGroup; 187 | children = ( 188 | 47F343DA13A4AF6F00EC49ED /* Classes */, 189 | 47F343DF13A4AF6F00EC49ED /* Resources */, 190 | 4719175413A4902900567E9E /* Frameworks */, 191 | 4719172313A4875400567E9E /* Supporting Files */, 192 | ); 193 | path = TabDemo; 194 | sourceTree = ""; 195 | }; 196 | 4719172313A4875400567E9E /* Supporting Files */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | 4719172413A4875400567E9E /* TabDemo-Info.plist */, 200 | 4719172513A4875400567E9E /* InfoPlist.strings */, 201 | 4719172813A4875400567E9E /* TabDemo-Prefix.pch */, 202 | 4719172913A4875400567E9E /* main.m */, 203 | ); 204 | name = "Supporting Files"; 205 | sourceTree = ""; 206 | }; 207 | 4719175413A4902900567E9E /* Frameworks */ = { 208 | isa = PBXGroup; 209 | children = ( 210 | 471916E913A4833A00567E9E /* libJMTabView.a */, 211 | 4719177013A4910600567E9E /* CoreGraphics.framework */, 212 | 4719176E13A4910000567E9E /* QuartzCore.framework */, 213 | 4719176C13A490FB00567E9E /* Foundation.framework */, 214 | 4719176A13A490F500567E9E /* UIKit.framework */, 215 | ); 216 | name = Frameworks; 217 | sourceTree = ""; 218 | }; 219 | 4719175713A4909500567E9E /* Classes */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | 47F343BF13A49AC700EC49ED /* Categories */, 223 | 4719175B13A4909500567E9E /* Subviews */, 224 | ); 225 | path = Classes; 226 | sourceTree = ""; 227 | }; 228 | 4719175B13A4909500567E9E /* Subviews */ = { 229 | isa = PBXGroup; 230 | children = ( 231 | 47F343C413A49B8600EC49ED /* Layers */, 232 | 4719175C13A4909500567E9E /* JMTabView.h */, 233 | 4719175D13A4909500567E9E /* JMTabView.m */, 234 | 47F343D013A4A23400EC49ED /* JMTabContainer.h */, 235 | 47F343D113A4A23400EC49ED /* JMTabContainer.m */, 236 | 47F343D413A4A3BE00EC49ED /* JMTabItem.h */, 237 | 47F343D513A4A3BE00EC49ED /* JMTabItem.m */, 238 | 47F343D813A4A78500EC49ED /* JMTabConstants.h */, 239 | 47F343EE13A4B6ED00EC49ED /* JMSelectionView.h */, 240 | 47F343EF13A4B6ED00EC49ED /* JMSelectionView.m */, 241 | ); 242 | path = Subviews; 243 | sourceTree = ""; 244 | }; 245 | 4719176113A4909B00567E9E /* Frameworks */ = { 246 | isa = PBXGroup; 247 | children = ( 248 | 4719176813A490E200567E9E /* Foundation.framework */, 249 | 4719176613A490DC00567E9E /* QuartzCore.framework */, 250 | 4719176413A490D500567E9E /* CoreGraphics.framework */, 251 | 4719176213A490CF00567E9E /* UIKit.framework */, 252 | ); 253 | name = Frameworks; 254 | sourceTree = ""; 255 | }; 256 | 47ABA8EA13A9C0B700B056C3 /* Custom */ = { 257 | isa = PBXGroup; 258 | children = ( 259 | 47ABA8E613A9BCA900B056C3 /* CustomTabItem.h */, 260 | 47ABA8E713A9BCA900B056C3 /* CustomTabItem.m */, 261 | 47ABA8EB13A9C0E300B056C3 /* CustomSelectionView.h */, 262 | 47ABA8EC13A9C0E300B056C3 /* CustomSelectionView.m */, 263 | 47ABA8F513A9D3B700B056C3 /* CustomBackgroundLayer.h */, 264 | 47ABA8F613A9D3B700B056C3 /* CustomBackgroundLayer.m */, 265 | 47ABA90B13A9F75500B056C3 /* CustomNoiseBackgroundView.h */, 266 | 47ABA90C13A9F75500B056C3 /* CustomNoiseBackgroundView.m */, 267 | ); 268 | path = Custom; 269 | sourceTree = ""; 270 | }; 271 | 47F343BF13A49AC700EC49ED /* Categories */ = { 272 | isa = PBXGroup; 273 | children = ( 274 | 47F3441E13A4E95400EC49ED /* UIView+Positioning.h */, 275 | 47F3441F13A4E95400EC49ED /* UIView+Positioning.m */, 276 | 47F3442013A4E95400EC49ED /* UIView+Size.h */, 277 | 47F3442113A4E95400EC49ED /* UIView+Size.m */, 278 | 47F343C013A49AD700EC49ED /* UIColor+Hex.h */, 279 | 47F343C113A49AD700EC49ED /* UIColor+Hex.m */, 280 | 47F3441A13A4E27100EC49ED /* UIView+InnerShadow.h */, 281 | 47F3441B13A4E27100EC49ED /* UIView+InnerShadow.m */, 282 | ); 283 | path = Categories; 284 | sourceTree = ""; 285 | }; 286 | 47F343C413A49B8600EC49ED /* Layers */ = { 287 | isa = PBXGroup; 288 | children = ( 289 | 47F343C513A49BA900EC49ED /* BarBackgroundLayer.h */, 290 | 47F343C613A49BA900EC49ED /* BarBackgroundLayer.m */, 291 | ); 292 | path = Layers; 293 | sourceTree = ""; 294 | }; 295 | 47F343DA13A4AF6F00EC49ED /* Classes */ = { 296 | isa = PBXGroup; 297 | children = ( 298 | 47ABA8EA13A9C0B700B056C3 /* Custom */, 299 | 47F343DB13A4AF6F00EC49ED /* TabDemoAppDelegate.h */, 300 | 47F343DC13A4AF6F00EC49ED /* TabDemoAppDelegate.m */, 301 | 47F343DD13A4AF6F00EC49ED /* TabDemoViewController.h */, 302 | 47F343DE13A4AF6F00EC49ED /* TabDemoViewController.m */, 303 | ); 304 | path = Classes; 305 | sourceTree = ""; 306 | }; 307 | 47F343DF13A4AF6F00EC49ED /* Resources */ = { 308 | isa = PBXGroup; 309 | children = ( 310 | 47ABA90513A9F5B300B056C3 /* noise-tile.png */, 311 | 47ABA90613A9F5B300B056C3 /* noise-tile@2x.png */, 312 | 47F343E213A4B15500EC49ED /* icon1.png */, 313 | 47F343E313A4B15500EC49ED /* icon1@2x.png */, 314 | 47F343E413A4B15500EC49ED /* icon2.png */, 315 | 47F343E513A4B15500EC49ED /* icon2@2x.png */, 316 | 47F343E613A4B15500EC49ED /* icon3.png */, 317 | 47F343E713A4B15500EC49ED /* icon3@2x.png */, 318 | ); 319 | path = Resources; 320 | sourceTree = ""; 321 | }; 322 | /* End PBXGroup section */ 323 | 324 | /* Begin PBXHeadersBuildPhase section */ 325 | 471916E713A4833A00567E9E /* Headers */ = { 326 | isa = PBXHeadersBuildPhase; 327 | buildActionMask = 2147483647; 328 | files = ( 329 | 4719175F13A4909500567E9E /* JMTabView.h in Headers */, 330 | 47F343C713A49BA900EC49ED /* BarBackgroundLayer.h in Headers */, 331 | 47F343C213A49AD700EC49ED /* UIColor+Hex.h in Headers */, 332 | 47F343D213A4A23400EC49ED /* JMTabContainer.h in Headers */, 333 | 47F343D613A4A3BE00EC49ED /* JMTabItem.h in Headers */, 334 | 47F343D913A4A78500EC49ED /* JMTabConstants.h in Headers */, 335 | 47F343F013A4B6ED00EC49ED /* JMSelectionView.h in Headers */, 336 | 47F3441C13A4E27100EC49ED /* UIView+InnerShadow.h in Headers */, 337 | 47F3442213A4E95400EC49ED /* UIView+Positioning.h in Headers */, 338 | 47F3442413A4E95400EC49ED /* UIView+Size.h in Headers */, 339 | ); 340 | runOnlyForDeploymentPostprocessing = 0; 341 | }; 342 | /* End PBXHeadersBuildPhase section */ 343 | 344 | /* Begin PBXNativeTarget section */ 345 | 471916E813A4833A00567E9E /* JMTabView */ = { 346 | isa = PBXNativeTarget; 347 | buildConfigurationList = 471916F313A4833A00567E9E /* Build configuration list for PBXNativeTarget "JMTabView" */; 348 | buildPhases = ( 349 | 471916E513A4833A00567E9E /* Sources */, 350 | 471916E613A4833A00567E9E /* Frameworks */, 351 | 471916E713A4833A00567E9E /* Headers */, 352 | ); 353 | buildRules = ( 354 | ); 355 | dependencies = ( 356 | ); 357 | name = JMTabView; 358 | productName = JMTabView; 359 | productReference = 471916E913A4833A00567E9E /* libJMTabView.a */; 360 | productType = "com.apple.product-type.library.static"; 361 | }; 362 | 4719171C13A4875400567E9E /* TabDemo */ = { 363 | isa = PBXNativeTarget; 364 | buildConfigurationList = 4719173713A4875400567E9E /* Build configuration list for PBXNativeTarget "TabDemo" */; 365 | buildPhases = ( 366 | 4719171913A4875400567E9E /* Sources */, 367 | 4719171A13A4875400567E9E /* Frameworks */, 368 | 4719171B13A4875400567E9E /* Resources */, 369 | ); 370 | buildRules = ( 371 | ); 372 | dependencies = ( 373 | 4719174C13A48F3C00567E9E /* PBXTargetDependency */, 374 | ); 375 | name = TabDemo; 376 | productName = TabDemo; 377 | productReference = 4719171D13A4875400567E9E /* TabDemo.app */; 378 | productType = "com.apple.product-type.application"; 379 | }; 380 | /* End PBXNativeTarget section */ 381 | 382 | /* Begin PBXProject section */ 383 | 471916E013A4833A00567E9E /* Project object */ = { 384 | isa = PBXProject; 385 | attributes = { 386 | ORGANIZATIONNAME = "The Design Shed"; 387 | }; 388 | buildConfigurationList = 471916E313A4833A00567E9E /* Build configuration list for PBXProject "JMTabView" */; 389 | compatibilityVersion = "Xcode 3.2"; 390 | developmentRegion = English; 391 | hasScannedForEncodings = 0; 392 | knownRegions = ( 393 | en, 394 | ); 395 | mainGroup = 471916DE13A4833A00567E9E; 396 | productRefGroup = 471916EA13A4833A00567E9E /* Products */; 397 | projectDirPath = ""; 398 | projectRoot = ""; 399 | targets = ( 400 | 471916E813A4833A00567E9E /* JMTabView */, 401 | 4719171C13A4875400567E9E /* TabDemo */, 402 | ); 403 | }; 404 | /* End PBXProject section */ 405 | 406 | /* Begin PBXResourcesBuildPhase section */ 407 | 4719171B13A4875400567E9E /* Resources */ = { 408 | isa = PBXResourcesBuildPhase; 409 | buildActionMask = 2147483647; 410 | files = ( 411 | 4719172713A4875400567E9E /* InfoPlist.strings in Resources */, 412 | 47F343E813A4B52300EC49ED /* icon1.png in Resources */, 413 | 47F343E913A4B52500EC49ED /* icon1@2x.png in Resources */, 414 | 47F343EA13A4B52700EC49ED /* icon2.png in Resources */, 415 | 47F343EB13A4B52A00EC49ED /* icon2@2x.png in Resources */, 416 | 47F343EC13A4B52C00EC49ED /* icon3.png in Resources */, 417 | 47F343ED13A4B52E00EC49ED /* icon3@2x.png in Resources */, 418 | 47ABA90713A9F5B300B056C3 /* noise-tile.png in Resources */, 419 | 47ABA90813A9F5B300B056C3 /* noise-tile@2x.png in Resources */, 420 | ); 421 | runOnlyForDeploymentPostprocessing = 0; 422 | }; 423 | /* End PBXResourcesBuildPhase section */ 424 | 425 | /* Begin PBXSourcesBuildPhase section */ 426 | 471916E513A4833A00567E9E /* Sources */ = { 427 | isa = PBXSourcesBuildPhase; 428 | buildActionMask = 2147483647; 429 | files = ( 430 | 4719176013A4909500567E9E /* JMTabView.m in Sources */, 431 | 47F343C313A49AD700EC49ED /* UIColor+Hex.m in Sources */, 432 | 47F343C813A49BA900EC49ED /* BarBackgroundLayer.m in Sources */, 433 | 47F343D313A4A23400EC49ED /* JMTabContainer.m in Sources */, 434 | 47F343D713A4A3BE00EC49ED /* JMTabItem.m in Sources */, 435 | 47F343F113A4B6ED00EC49ED /* JMSelectionView.m in Sources */, 436 | 47F3441D13A4E27100EC49ED /* UIView+InnerShadow.m in Sources */, 437 | 47F3442313A4E95400EC49ED /* UIView+Positioning.m in Sources */, 438 | 47F3442513A4E95400EC49ED /* UIView+Size.m in Sources */, 439 | ); 440 | runOnlyForDeploymentPostprocessing = 0; 441 | }; 442 | 4719171913A4875400567E9E /* Sources */ = { 443 | isa = PBXSourcesBuildPhase; 444 | buildActionMask = 2147483647; 445 | files = ( 446 | 4719172A13A4875400567E9E /* main.m in Sources */, 447 | 47F343E013A4AF6F00EC49ED /* TabDemoAppDelegate.m in Sources */, 448 | 47F343E113A4AF6F00EC49ED /* TabDemoViewController.m in Sources */, 449 | 47ABA8E813A9BCA900B056C3 /* CustomTabItem.m in Sources */, 450 | 47ABA90913A9F5CC00B056C3 /* CustomSelectionView.m in Sources */, 451 | 47ABA90A13A9F5DA00B056C3 /* CustomBackgroundLayer.m in Sources */, 452 | 47ABA90D13A9F75600B056C3 /* CustomNoiseBackgroundView.m in Sources */, 453 | ); 454 | runOnlyForDeploymentPostprocessing = 0; 455 | }; 456 | /* End PBXSourcesBuildPhase section */ 457 | 458 | /* Begin PBXTargetDependency section */ 459 | 4719174C13A48F3C00567E9E /* PBXTargetDependency */ = { 460 | isa = PBXTargetDependency; 461 | target = 471916E813A4833A00567E9E /* JMTabView */; 462 | targetProxy = 4719174B13A48F3C00567E9E /* PBXContainerItemProxy */; 463 | }; 464 | /* End PBXTargetDependency section */ 465 | 466 | /* Begin PBXVariantGroup section */ 467 | 4719172513A4875400567E9E /* InfoPlist.strings */ = { 468 | isa = PBXVariantGroup; 469 | children = ( 470 | 4719172613A4875400567E9E /* en */, 471 | ); 472 | name = InfoPlist.strings; 473 | sourceTree = ""; 474 | }; 475 | /* End PBXVariantGroup section */ 476 | 477 | /* Begin XCBuildConfiguration section */ 478 | 471916F113A4833A00567E9E /* Debug */ = { 479 | isa = XCBuildConfiguration; 480 | buildSettings = { 481 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 482 | GCC_C_LANGUAGE_STANDARD = gnu99; 483 | GCC_OPTIMIZATION_LEVEL = 0; 484 | GCC_PREPROCESSOR_DEFINITIONS = DEBUG; 485 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 486 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 487 | GCC_VERSION = com.apple.compilers.llvmgcc42; 488 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 489 | GCC_WARN_UNUSED_VARIABLE = YES; 490 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 491 | SDKROOT = iphoneos; 492 | }; 493 | name = Debug; 494 | }; 495 | 471916F213A4833A00567E9E /* Release */ = { 496 | isa = XCBuildConfiguration; 497 | buildSettings = { 498 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 499 | GCC_C_LANGUAGE_STANDARD = gnu99; 500 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 501 | GCC_VERSION = com.apple.compilers.llvmgcc42; 502 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 503 | GCC_WARN_UNUSED_VARIABLE = YES; 504 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 505 | SDKROOT = iphoneos; 506 | }; 507 | name = Release; 508 | }; 509 | 471916F413A4833A00567E9E /* Debug */ = { 510 | isa = XCBuildConfiguration; 511 | buildSettings = { 512 | ALWAYS_SEARCH_USER_PATHS = NO; 513 | DSTROOT = /tmp/JMTabView.dst; 514 | FRAMEWORK_SEARCH_PATHS = ( 515 | "$(inherited)", 516 | "\"$(SRCROOT)\"", 517 | ); 518 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 519 | GCC_PREFIX_HEADER = "JMTabView/JMTabView-Prefix.pch"; 520 | OTHER_LDFLAGS = ( 521 | "-ObjC", 522 | "-all_load", 523 | ); 524 | PRODUCT_NAME = "$(TARGET_NAME)"; 525 | }; 526 | name = Debug; 527 | }; 528 | 471916F513A4833A00567E9E /* Release */ = { 529 | isa = XCBuildConfiguration; 530 | buildSettings = { 531 | ALWAYS_SEARCH_USER_PATHS = NO; 532 | DSTROOT = /tmp/JMTabView.dst; 533 | FRAMEWORK_SEARCH_PATHS = ( 534 | "$(inherited)", 535 | "\"$(SRCROOT)\"", 536 | ); 537 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 538 | GCC_PREFIX_HEADER = "JMTabView/JMTabView-Prefix.pch"; 539 | OTHER_LDFLAGS = ( 540 | "-ObjC", 541 | "-all_load", 542 | ); 543 | PRODUCT_NAME = "$(TARGET_NAME)"; 544 | }; 545 | name = Release; 546 | }; 547 | 4719173813A4875400567E9E /* Debug */ = { 548 | isa = XCBuildConfiguration; 549 | buildSettings = { 550 | ALWAYS_SEARCH_USER_PATHS = NO; 551 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 552 | COPY_PHASE_STRIP = NO; 553 | FRAMEWORK_SEARCH_PATHS = ( 554 | "$(inherited)", 555 | "\"$(SRCROOT)\"", 556 | ); 557 | GCC_DYNAMIC_NO_PIC = NO; 558 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 559 | GCC_PREFIX_HEADER = "TabDemo/TabDemo-Prefix.pch"; 560 | INFOPLIST_FILE = "TabDemo/TabDemo-Info.plist"; 561 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 562 | OTHER_LDFLAGS = ( 563 | "-ObjC", 564 | "-all_load", 565 | ); 566 | PRODUCT_NAME = "$(TARGET_NAME)"; 567 | TARGETED_DEVICE_FAMILY = "1,2"; 568 | WRAPPER_EXTENSION = app; 569 | }; 570 | name = Debug; 571 | }; 572 | 4719173913A4875400567E9E /* Release */ = { 573 | isa = XCBuildConfiguration; 574 | buildSettings = { 575 | ALWAYS_SEARCH_USER_PATHS = NO; 576 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 577 | COPY_PHASE_STRIP = YES; 578 | FRAMEWORK_SEARCH_PATHS = ( 579 | "$(inherited)", 580 | "\"$(SRCROOT)\"", 581 | ); 582 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 583 | GCC_PREFIX_HEADER = "TabDemo/TabDemo-Prefix.pch"; 584 | INFOPLIST_FILE = "TabDemo/TabDemo-Info.plist"; 585 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 586 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 587 | OTHER_LDFLAGS = ( 588 | "-ObjC", 589 | "-all_load", 590 | ); 591 | PRODUCT_NAME = "$(TARGET_NAME)"; 592 | TARGETED_DEVICE_FAMILY = "1,2"; 593 | VALIDATE_PRODUCT = YES; 594 | WRAPPER_EXTENSION = app; 595 | }; 596 | name = Release; 597 | }; 598 | /* End XCBuildConfiguration section */ 599 | 600 | /* Begin XCConfigurationList section */ 601 | 471916E313A4833A00567E9E /* Build configuration list for PBXProject "JMTabView" */ = { 602 | isa = XCConfigurationList; 603 | buildConfigurations = ( 604 | 471916F113A4833A00567E9E /* Debug */, 605 | 471916F213A4833A00567E9E /* Release */, 606 | ); 607 | defaultConfigurationIsVisible = 0; 608 | defaultConfigurationName = Release; 609 | }; 610 | 471916F313A4833A00567E9E /* Build configuration list for PBXNativeTarget "JMTabView" */ = { 611 | isa = XCConfigurationList; 612 | buildConfigurations = ( 613 | 471916F413A4833A00567E9E /* Debug */, 614 | 471916F513A4833A00567E9E /* Release */, 615 | ); 616 | defaultConfigurationIsVisible = 0; 617 | defaultConfigurationName = Release; 618 | }; 619 | 4719173713A4875400567E9E /* Build configuration list for PBXNativeTarget "TabDemo" */ = { 620 | isa = XCConfigurationList; 621 | buildConfigurations = ( 622 | 4719173813A4875400567E9E /* Debug */, 623 | 4719173913A4875400567E9E /* Release */, 624 | ); 625 | defaultConfigurationIsVisible = 0; 626 | defaultConfigurationName = Release; 627 | }; 628 | /* End XCConfigurationList section */ 629 | }; 630 | rootObject = 471916E013A4833A00567E9E /* Project object */; 631 | } 632 | -------------------------------------------------------------------------------- /JMTabView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /JMTabView/Classes/Categories/UIColor+Hex.h: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import 4 | 5 | @interface UIColor (Hex) 6 | 7 | + (UIColor *)colorWithHex:(long)hexColor; 8 | + (UIColor *)colorWithHex:(long)hexColor alpha:(float)opacity; 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /JMTabView/Classes/Categories/UIColor+Hex.m: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import "UIColor+Hex.h" 4 | 5 | @implementation UIColor (Hex) 6 | 7 | + (UIColor*) colorWithHex:(long)hexColor; 8 | { 9 | return [UIColor colorWithHex:hexColor alpha:1.]; 10 | } 11 | 12 | + (UIColor *)colorWithHex:(long)hexColor alpha:(float)opacity 13 | { 14 | float red = ((float)((hexColor & 0xFF0000) >> 16))/255.0; 15 | float green = ((float)((hexColor & 0xFF00) >> 8))/255.0; 16 | float blue = ((float)(hexColor & 0xFF))/255.0; 17 | return [UIColor colorWithRed:red green:green blue:blue alpha:opacity]; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /JMTabView/Classes/Categories/UIView+InnerShadow.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface UIView (InnerShadow) 4 | 5 | - (void)drawInnerShadowInRect:(CGRect)rect fillColor:(UIColor *)fillColor; 6 | - (void)drawInnerShadowInRect:(CGRect)rect radius:(CGFloat)radius fillColor:(UIColor *)fillColor; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /JMTabView/Classes/Categories/UIView+InnerShadow.m: -------------------------------------------------------------------------------- 1 | #import "UIView+InnerShadow.h" 2 | 3 | @implementation UIView (InnerShadow) 4 | 5 | // sourced from http://stackoverflow.com/questions/4431292/inner-shadow-effect-on-uiview-layer 6 | 7 | - (void)drawInnerShadowInRect:(CGRect)rect radius:(CGFloat)radius fillColor:(UIColor *)fillColor; 8 | { 9 | CGRect bounds = [self bounds]; 10 | CGContextRef context = UIGraphicsGetCurrentContext(); 11 | CGFloat outsideOffset = 20.f; 12 | 13 | CGMutablePathRef visiblePath = CGPathCreateMutable(); 14 | CGPathMoveToPoint(visiblePath, NULL, bounds.size.width-radius, bounds.size.height); 15 | CGPathAddArc(visiblePath, NULL, bounds.size.width-radius, radius, radius, 0.5f*M_PI, 1.5f*M_PI, YES); 16 | CGPathAddLineToPoint(visiblePath, NULL, radius, 0.f); 17 | CGPathAddArc(visiblePath, NULL, radius, radius, radius, 1.5f*M_PI, 0.5f*M_PI, YES); 18 | CGPathAddLineToPoint(visiblePath, NULL, bounds.size.width-radius, bounds.size.height); 19 | CGPathCloseSubpath(visiblePath); 20 | 21 | [fillColor setFill]; 22 | CGContextAddPath(context, visiblePath); 23 | CGContextFillPath(context); 24 | 25 | CGMutablePathRef path = CGPathCreateMutable(); 26 | CGPathMoveToPoint(path, NULL, -outsideOffset, -outsideOffset); 27 | CGPathAddLineToPoint(path, NULL, bounds.size.width+outsideOffset, -outsideOffset); 28 | CGPathAddLineToPoint(path, NULL, bounds.size.width+outsideOffset, bounds.size.height+outsideOffset); 29 | CGPathAddLineToPoint(path, NULL, -outsideOffset, bounds.size.height+outsideOffset); 30 | 31 | CGPathAddPath(path, NULL, visiblePath); 32 | CGPathCloseSubpath(path); 33 | 34 | CGContextAddPath(context, visiblePath); 35 | CGContextClip(context); 36 | 37 | UIColor * shadowColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.6f]; 38 | CGContextSaveGState(context); 39 | CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 4.0f), 8.0f, [shadowColor CGColor]); 40 | [shadowColor setFill]; 41 | 42 | CGContextSaveGState(context); 43 | CGContextAddPath(context, path); 44 | CGContextFillPath(context); 45 | 46 | CGPathRelease(path); 47 | CGPathRelease(visiblePath); 48 | CGContextRestoreGState(context); 49 | } 50 | 51 | - (void)drawInnerShadowInRect:(CGRect)rect fillColor:(UIColor *)fillColor 52 | { 53 | [self drawInnerShadowInRect:rect radius:(0.5f * CGRectGetHeight(rect)) fillColor:fillColor]; 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /JMTabView/Classes/Categories/UIView+Positioning.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Kevin O'Neill 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // * Neither the name UsefulBits nor the names of its contributors may be used 15 | // to endorse or promote products derived from this software without specific 16 | // prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | // DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 22 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #import 30 | 31 | @interface UIView (Positioning) 32 | 33 | - (void)centerInRect:(CGRect)rect; 34 | - (void)centerVerticallyInRect:(CGRect)rect; 35 | - (void)centerHorizontallyInRect:(CGRect)rect; 36 | 37 | - (void)centerInSuperView; 38 | - (void)centerVerticallyInSuperView; 39 | - (void)centerHorizontallyInSuperView; 40 | 41 | - (void)centerHorizontallyBelow:(UIView *)view padding:(CGFloat)padding; 42 | - (void)centerHorizontallyBelow:(UIView *)view; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /JMTabView/Classes/Categories/UIView+Positioning.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Kevin O'Neill 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // * Neither the name UsefulBits nor the names of its contributors may be used 15 | // to endorse or promote products derived from this software without specific 16 | // prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | // DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 22 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #import "UIView+Positioning.h" 30 | #import "UIView+Size.h" 31 | 32 | @implementation UIView (Positioning) 33 | 34 | - (void)centerInRect:(CGRect)rect; 35 | { 36 | [self setCenter:CGPointMake(floorf(CGRectGetMidX(rect)) + ((int)floorf([self width]) % 2 ? .5 : 0) , floorf(CGRectGetMidY(rect)) + ((int)floorf([self height]) % 2 ? .5 : 0))]; 37 | } 38 | 39 | - (void)centerVerticallyInRect:(CGRect)rect; 40 | { 41 | [self setCenter:CGPointMake([self center].x, floorf(CGRectGetMidY(rect)) + ((int)floorf([self height]) % 2 ? .5 : 0))]; 42 | } 43 | 44 | - (void)centerHorizontallyInRect:(CGRect)rect; 45 | { 46 | [self setCenter:CGPointMake(floorf(CGRectGetMidX(rect)) + ((int)floorf([self width]) % 2 ? .5 : 0), [self center].y)]; 47 | } 48 | 49 | - (void)centerInSuperView; 50 | { 51 | [self centerInRect:[[self superview] bounds]]; 52 | } 53 | - (void)centerVerticallyInSuperView; 54 | { 55 | [self centerVerticallyInRect:[[self superview] bounds]]; 56 | } 57 | - (void)centerHorizontallyInSuperView; 58 | { 59 | [self centerHorizontallyInRect:[[self superview] bounds]]; 60 | } 61 | 62 | - (void)centerHorizontallyBelow:(UIView *)view padding:(CGFloat)padding; 63 | { 64 | // for now, could use screen relative positions. 65 | NSAssert([self superview] == [view superview], @"views must have the same parent"); 66 | 67 | [self setCenter:CGPointMake([view center].x, 68 | floorf(padding + CGRectGetMaxY([view frame]) + ([self height] / 2)))]; 69 | } 70 | 71 | - (void)centerHorizontallyBelow:(UIView *)view; 72 | { 73 | [self centerHorizontallyBelow:view padding:0]; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /JMTabView/Classes/Categories/UIView+Size.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Kevin O'Neill 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // * Neither the name UsefulBits nor the names of its contributors may be used 15 | // to endorse or promote products derived from this software without specific 16 | // prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | // DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 22 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #import 30 | 31 | @interface UIView (Size) 32 | 33 | @property (nonatomic, assign) CGSize size; 34 | 35 | @property (nonatomic, assign) CGFloat left; 36 | @property (nonatomic, assign) CGFloat right; 37 | @property (nonatomic, assign) CGFloat top; 38 | @property (nonatomic, assign) CGFloat bottom; 39 | 40 | @property (nonatomic, assign) CGFloat centerX; 41 | @property (nonatomic, assign) CGFloat centerY; 42 | 43 | @property (nonatomic, assign) CGFloat width; 44 | @property (nonatomic, assign) CGFloat height; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /JMTabView/Classes/Categories/UIView+Size.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, Kevin O'Neill 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // * Neither the name UsefulBits nor the names of its contributors may be used 15 | // to endorse or promote products derived from this software without specific 16 | // prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | // DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 22 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #import "UIView+Size.h" 30 | 31 | @implementation UIView (Size) 32 | 33 | - (void)setSize:(CGSize)size; 34 | { 35 | CGPoint origin = [self frame].origin; 36 | 37 | [self setFrame:CGRectMake(origin.x, origin.y, size.width, size.height)]; 38 | } 39 | 40 | - (CGSize)size; 41 | { 42 | return [self frame].size; 43 | } 44 | 45 | - (CGFloat)left; 46 | { 47 | return CGRectGetMinX([self frame]); 48 | } 49 | 50 | - (void)setLeft:(CGFloat)x; 51 | { 52 | CGRect frame = [self frame]; 53 | frame.origin.x = x; 54 | [self setFrame:frame]; 55 | } 56 | 57 | - (CGFloat)top; 58 | { 59 | return CGRectGetMinY([self frame]); 60 | } 61 | 62 | - (void)setTop:(CGFloat)y; 63 | { 64 | CGRect frame = [self frame]; 65 | frame.origin.y = y; 66 | [self setFrame:frame]; 67 | } 68 | 69 | - (CGFloat)right; 70 | { 71 | return CGRectGetMaxX([self frame]); 72 | } 73 | 74 | - (void)setRight:(CGFloat)right; 75 | { 76 | CGRect frame = [self frame]; 77 | frame.origin.x = right - frame.size.width; 78 | 79 | [self setFrame:frame]; 80 | } 81 | 82 | - (CGFloat)bottom; 83 | { 84 | return CGRectGetMaxY([self frame]); 85 | } 86 | 87 | - (void)setBottom:(CGFloat)bottom; 88 | { 89 | CGRect frame = [self frame]; 90 | frame.origin.y = bottom - frame.size.height; 91 | 92 | [self setFrame:frame]; 93 | } 94 | 95 | - (CGFloat)centerX; 96 | { 97 | return [self center].x; 98 | } 99 | 100 | - (void)setCenterX:(CGFloat)centerX; 101 | { 102 | [self setCenter:CGPointMake(centerX, self.center.y)]; 103 | } 104 | 105 | - (CGFloat)centerY; 106 | { 107 | return [self center].y; 108 | } 109 | 110 | - (void)setCenterY:(CGFloat)centerY; 111 | { 112 | [self setCenter:CGPointMake(self.center.x, centerY)]; 113 | } 114 | 115 | - (CGFloat)width; 116 | { 117 | return CGRectGetWidth([self frame]); 118 | } 119 | 120 | - (void)setWidth:(CGFloat)width; 121 | { 122 | CGRect frame = [self frame]; 123 | frame.size.width = width; 124 | 125 | [self setFrame:CGRectStandardize(frame)]; 126 | } 127 | 128 | - (CGFloat)height; 129 | { 130 | return CGRectGetHeight([self frame]); 131 | } 132 | 133 | - (void)setHeight:(CGFloat)height; 134 | { 135 | CGRect frame = [self frame]; 136 | frame.size.height = height; 137 | 138 | [self setFrame:CGRectStandardize(frame)]; 139 | } 140 | 141 | @end 142 | -------------------------------------------------------------------------------- /JMTabView/Classes/Subviews/JMSelectionView.h: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import 4 | 5 | @interface JMSelectionView : UIView 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /JMTabView/Classes/Subviews/JMSelectionView.m: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import 4 | #import "JMSelectionView.h" 5 | #import "UIColor+Hex.h" 6 | #import "UIView+InnerShadow.h" 7 | 8 | @implementation JMSelectionView 9 | 10 | - (id)initWithFrame:(CGRect)frame 11 | { 12 | self = [super initWithFrame:frame]; 13 | if (self) 14 | { 15 | [self setBackgroundColor:[UIColor clearColor]]; 16 | self.layer.shadowOffset = CGSizeMake(0, 1); 17 | self.layer.shadowRadius = 1.; 18 | self.layer.shadowColor = [[UIColor whiteColor] CGColor]; 19 | self.layer.shadowOpacity = 0.4; 20 | self.clipsToBounds = NO; 21 | } 22 | return self; 23 | } 24 | 25 | - (void)layoutSubviews; 26 | { 27 | [self setNeedsDisplay]; 28 | } 29 | 30 | - (void)drawRect:(CGRect)rect 31 | { 32 | [self drawInnerShadowInRect:rect fillColor:[UIColor colorWithHex:0x252525]]; 33 | } 34 | 35 | - (void)dealloc 36 | { 37 | [super dealloc]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /JMTabView/Classes/Subviews/JMTabConstants.h: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #define kTabItemFont [UIFont boldSystemFontOfSize:12.] 4 | #define kTabItemTextColor [UIColor whiteColor] 5 | #define kTabItemPadding CGSizeMake(16., 10.) 6 | #define kTabItemIconMargin 8. 7 | #define kTabSpacing 10. 8 | #define kTabSelectionAnimationDuration 0.3 9 | 10 | -------------------------------------------------------------------------------- /JMTabView/Classes/Subviews/JMTabContainer.h: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import 4 | #import "JMTabItem.h" 5 | #import "JMSelectionView.h" 6 | 7 | @interface JMTabContainer : UIView 8 | { 9 | CGSize containerSize_; 10 | } 11 | 12 | @property (nonatomic,retain) JMSelectionView * selectionView; 13 | @property (assign) BOOL momentary; 14 | @property (assign) NSUInteger selectedIndex; 15 | @property (assign) CGFloat itemSpacing; 16 | 17 | - (BOOL)isItemSelected:(JMTabItem *)tabItem; 18 | - (void)itemSelected:(JMTabItem *)tabItem; 19 | - (void)addTabItem:(JMTabItem *)tabItem; 20 | - (void)removeTabItem:(JMTabItem *)tabItem; 21 | - (void)removeAllTabItems; 22 | - (void)animateSelectionToItemAtIndex:(NSUInteger)itemIndex; 23 | - (NSUInteger)numberOfTabItems; 24 | - (JMTabItem *)tabItemAtIndex:(NSUInteger)index; 25 | @end 26 | -------------------------------------------------------------------------------- /JMTabView/Classes/Subviews/JMTabContainer.m: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import "JMTabContainer.h" 4 | #import "JMTabView.h" 5 | #import "UIView+Positioning.h" 6 | 7 | #define kSelectionAnimation @"kSelectionAnimation" 8 | 9 | @interface JMTabContainer() 10 | @property (nonatomic,retain) NSMutableArray * tabItems; 11 | @end 12 | 13 | @implementation JMTabContainer 14 | 15 | @synthesize tabItems = tabItems_; 16 | @synthesize selectionView = selectionView_; 17 | @synthesize selectedIndex = selectedIndex_; 18 | @synthesize momentary = momentary_; 19 | @synthesize itemSpacing = itemSpacing_; 20 | 21 | - (void)dealloc 22 | { 23 | self.tabItems = nil; 24 | self.selectionView = nil; 25 | [super dealloc]; 26 | } 27 | 28 | - (id)initWithFrame:(CGRect)frame 29 | { 30 | self = [super initWithFrame:frame]; 31 | if (self) 32 | { 33 | self.tabItems = [NSMutableArray array]; 34 | self.selectionView = [[[JMSelectionView alloc] initWithFrame:CGRectZero] autorelease]; 35 | self.itemSpacing = kTabSpacing; 36 | [self addSubview:self.selectionView]; 37 | } 38 | return self; 39 | } 40 | 41 | - (void)layoutSubviews; 42 | { 43 | CGFloat xOffset = 0.; 44 | CGFloat yOffset = 0.; 45 | CGFloat itemHeight = 0.; 46 | 47 | for (JMTabItem * item in self.tabItems) 48 | { 49 | [item sizeToFit]; 50 | [item setFrame:CGRectMake(xOffset, yOffset, item.frame.size.width, item.frame.size.height)]; 51 | 52 | xOffset += item.frame.size.width; 53 | 54 | if (item != [self.tabItems lastObject]) 55 | { 56 | xOffset += self.itemSpacing; 57 | } 58 | 59 | itemHeight = item.frame.size.height; 60 | } 61 | 62 | containerSize_.width = xOffset; 63 | containerSize_.height = itemHeight; 64 | 65 | [self sizeToFit]; 66 | [self centerInSuperView]; 67 | } 68 | 69 | - (CGSize)sizeThatFits:(CGSize)size; 70 | { 71 | return containerSize_; 72 | } 73 | 74 | - (void)addTabItem:(JMTabItem *)tabItem; 75 | { 76 | [self.tabItems addObject:tabItem]; 77 | [self addSubview:tabItem]; 78 | [self setNeedsLayout]; 79 | } 80 | 81 | - (void)removeTabItem:(JMTabItem *)tabItem { 82 | [self.tabItems removeObject:tabItem]; 83 | [tabItem removeFromSuperview]; 84 | [self setNeedsLayout]; 85 | } 86 | 87 | - (void)removeAllTabItems { 88 | for (JMTabItem *tabItem in self.tabItems) { 89 | [tabItem removeFromSuperview]; 90 | } 91 | [[self tabItems] removeAllObjects]; 92 | [self setNeedsLayout]; 93 | } 94 | 95 | - (BOOL)isItemSelected:(JMTabItem *)tabItem; 96 | { 97 | return ([self.tabItems indexOfObject:tabItem] == self.selectedIndex); 98 | } 99 | 100 | - (void)itemSelected:(JMTabItem *)tabItem; 101 | { 102 | self.selectedIndex = [self.tabItems indexOfObject:tabItem]; 103 | 104 | if (!self.momentary) 105 | { 106 | [self animateSelectionToItemAtIndex:self.selectedIndex]; 107 | } 108 | 109 | for (JMTabItem * item in self.tabItems) 110 | { 111 | [item setNeedsDisplay]; 112 | } 113 | 114 | // notify parent tabView 115 | JMTabView * tabView = (JMTabView *)[self superview]; 116 | [tabView didSelectItemAtIndex:self.selectedIndex]; 117 | } 118 | 119 | - (void)animateSelectionToItemAtIndex:(NSUInteger)itemIndex; 120 | { 121 | JMTabItem * tabItem = [self.tabItems objectAtIndex:itemIndex]; 122 | [UIView beginAnimations:kSelectionAnimation context:self.selectionView]; 123 | [UIView setAnimationBeginsFromCurrentState:YES]; 124 | [UIView setAnimationDuration:(CGRectIsEmpty(self.selectionView.frame) ? 0. : kTabSelectionAnimationDuration)]; 125 | [UIView setAnimationCurve:UIViewAnimationCurveEaseOut]; 126 | self.selectionView.frame = tabItem.frame; 127 | [UIView commitAnimations]; 128 | } 129 | 130 | - (NSUInteger)numberOfTabItems; 131 | { 132 | return [self.tabItems count]; 133 | } 134 | 135 | #pragma mark - Tab Item Accessors 136 | 137 | - (JMTabItem *)tabItemAtIndex:(NSUInteger)index { 138 | return [self.tabItems objectAtIndex:index]; 139 | } 140 | 141 | @end 142 | -------------------------------------------------------------------------------- /JMTabView/Classes/Subviews/JMTabItem.h: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import 4 | #import 5 | 6 | #if NS_BLOCKS_AVAILABLE 7 | typedef void(^JMTabExecutionBlock)(void); 8 | #endif 9 | 10 | @interface JMTabItem : UIButton 11 | 12 | @property (nonatomic,retain) NSString * title; 13 | @property (nonatomic,retain) UIImage * icon; 14 | @property (nonatomic) CGFloat fixedWidth; 15 | @property (nonatomic,assign) CGSize padding; 16 | 17 | - (id)initWithTitle:(NSString *)title icon:(UIImage *)icon; 18 | -(BOOL)isSelectedTabItem; 19 | 20 | + (JMTabItem *)tabItemWithTitle:(NSString *)title icon:(UIImage *)icon; 21 | + (JMTabItem *)tabItemWithFixedWidth:(CGFloat)fixedWidth; 22 | 23 | #if NS_BLOCKS_AVAILABLE 24 | @property (nonatomic,copy) JMTabExecutionBlock executeBlock; 25 | + (JMTabItem *)tabItemWithTitle:(NSString *)title icon:(UIImage *)icon executeBlock:(JMTabExecutionBlock)executeBlock; 26 | #endif 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /JMTabView/Classes/Subviews/JMTabItem.m: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import "JMTabView.h" 4 | #import "JMTabItem.h" 5 | #import "JMTabContainer.h" 6 | 7 | @implementation JMTabItem 8 | 9 | @synthesize title = title_; 10 | @synthesize icon = icon_; 11 | @synthesize fixedWidth = fixedWidth_; 12 | @synthesize executeBlock = executeBlock_; 13 | @synthesize padding = padding_; 14 | - (void)setPadding:(CGSize)padding { 15 | padding_ = padding; 16 | 17 | [self setNeedsDisplay]; 18 | } 19 | 20 | - (void)dealloc; 21 | { 22 | self.title = nil; 23 | self.icon = nil; 24 | self.executeBlock = nil; 25 | [super dealloc]; 26 | } 27 | 28 | - (id)initWithTitle:(NSString *)title icon:(UIImage *)icon; 29 | { 30 | self = [super init]; 31 | if (self) 32 | { 33 | self.title = title; 34 | self.icon = icon; 35 | self.backgroundColor = [UIColor clearColor]; 36 | 37 | [self setIsAccessibilityElement:YES]; 38 | [self setAccessibilityLabel:title]; 39 | 40 | [self addTarget:self action:@selector(itemTapped) forControlEvents:UIControlEventTouchUpInside]; 41 | } 42 | return self; 43 | } 44 | 45 | - (CGSize) sizeThatFits:(CGSize)size; 46 | { 47 | CGSize titleSize = [self.title sizeWithFont:kTabItemFont]; 48 | 49 | CGFloat width = titleSize.width; 50 | 51 | if (self.icon) 52 | { 53 | width += [self.icon size].width; 54 | } 55 | 56 | if (self.icon && self.title) 57 | { 58 | width += kTabItemIconMargin; 59 | } 60 | 61 | width += (self.padding.width * 2); 62 | 63 | CGFloat height = (titleSize.height > [self.icon size].height) ? titleSize.height : [self.icon size].height; 64 | 65 | height += (self.padding.height * 2); 66 | 67 | if (self.fixedWidth > 0) 68 | { 69 | width = self.fixedWidth; 70 | height = 1.; 71 | } 72 | 73 | return CGSizeMake(width, height); 74 | } 75 | 76 | - (void)drawRect:(CGRect)rect; 77 | { 78 | CGContextRef context = UIGraphicsGetCurrentContext(); 79 | UIColor * shadowColor = [UIColor blackColor]; 80 | CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 1.0f), 1.0f, [shadowColor CGColor]); 81 | CGContextSaveGState(context); 82 | 83 | if (self.highlighted) 84 | { 85 | CGRect bounds = CGRectInset(rect, 2., 2.); 86 | CGFloat radius = 0.5f * CGRectGetHeight(bounds); 87 | UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:radius]; 88 | [[UIColor colorWithWhite:1. alpha:0.3] set]; 89 | path.lineWidth = 2.; 90 | [path stroke]; 91 | } 92 | 93 | CGFloat xOffset = self.padding.width; 94 | 95 | if (self.icon) 96 | { 97 | [self.icon drawAtPoint:CGPointMake(xOffset, self.padding.height)]; 98 | xOffset += [self.icon size].width + kTabItemIconMargin; 99 | } 100 | 101 | [kTabItemTextColor set]; 102 | 103 | CGFloat heightTitle = [self.title sizeWithFont:kTabItemFont].height; 104 | CGFloat titleYOffset = (self.bounds.size.height - heightTitle) / 2; 105 | [self.title drawAtPoint:CGPointMake(xOffset, titleYOffset) withFont:kTabItemFont]; 106 | 107 | CGContextRestoreGState(context); 108 | } 109 | 110 | - (void)setHighlighted:(BOOL)highlighted; 111 | { 112 | [super setHighlighted:highlighted]; 113 | [self setNeedsDisplay]; 114 | } 115 | 116 | -(BOOL)isSelectedTabItem; 117 | { 118 | JMTabContainer *tabContainer = (JMTabContainer *)[self superview]; 119 | return [tabContainer isItemSelected:self]; 120 | } 121 | 122 | - (void)itemTapped; 123 | { 124 | JMTabContainer *tabContainer = (JMTabContainer *)[self superview]; 125 | [tabContainer itemSelected:self]; 126 | 127 | #ifdef NS_BLOCKS_AVAILABLE 128 | if (executeBlock_) 129 | { 130 | executeBlock_(); 131 | } 132 | #endif 133 | } 134 | 135 | + (JMTabItem *)tabItemWithTitle:(NSString *)title icon:(UIImage *)icon; 136 | { 137 | JMTabItem * tabItem = [[[JMTabItem alloc] initWithTitle:title icon:icon] autorelease]; 138 | return tabItem; 139 | } 140 | 141 | + (JMTabItem *)tabItemWithFixedWidth:(CGFloat)fixedWidth; 142 | { 143 | JMTabItem * tabItem = [JMTabItem tabItemWithTitle:nil icon:nil]; 144 | tabItem.fixedWidth = fixedWidth; 145 | return tabItem; 146 | } 147 | 148 | #ifdef NS_BLOCKS_AVAILABLE 149 | + (JMTabItem *)tabItemWithTitle:(NSString *)title icon:(UIImage *)icon executeBlock:(JMTabExecutionBlock)executeBlock; 150 | { 151 | JMTabItem * tabItem = [JMTabItem tabItemWithTitle:title icon:icon]; 152 | tabItem.executeBlock = executeBlock; 153 | return tabItem; 154 | } 155 | #endif 156 | 157 | @end 158 | -------------------------------------------------------------------------------- /JMTabView/Classes/Subviews/JMTabView.h: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import 4 | #import "JMTabConstants.h" 5 | #import "JMTabItem.h" 6 | #import "JMSelectionView.h" 7 | 8 | @class JMTabView; 9 | 10 | #pragma Mark - 11 | #pragma Mark - JMTabViewDelegate 12 | 13 | @protocol JMTabViewDelegate 14 | -(void)tabView:(JMTabView *)tabView didSelectTabAtIndex:(NSUInteger)itemIndex; 15 | @end 16 | 17 | #pragma Mark - 18 | #pragma Mark - JMTabView 19 | 20 | @interface JMTabView : UIView 21 | 22 | - (void)setMomentary:(BOOL)momentary; 23 | - (void)didSelectItemAtIndex:(NSUInteger)itemIndex; 24 | - (void)addTabItem:(JMTabItem *)tabItem; 25 | - (void)addTabItemWithTitle:(NSString *)title icon:(UIImage *)icon; 26 | 27 | - (void)removeTabItemAtIndex:(NSUInteger)index; 28 | - (void)removeAllTabItems; 29 | 30 | @property (nonatomic, assign) NSUInteger selectedIndex; 31 | @property (nonatomic,assign) IBOutlet id delegate; 32 | @property (nonatomic,assign) CGSize itemPadding; 33 | 34 | #if NS_BLOCKS_AVAILABLE 35 | - (void)addTabItemWithTitle:(NSString *)title icon:(UIImage *)icon executeBlock:(JMTabExecutionBlock)executeBlock; 36 | #endif 37 | 38 | #pragma Mark - 39 | #pragma Mark - Customisation 40 | 41 | - (void)setSelectionView:(JMSelectionView *)selectionView; 42 | - (void)setItemSpacing:(CGFloat)itemSpacing; 43 | - (void)setBackgroundLayer:(CALayer *)backgroundLayer; 44 | 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /JMTabView/Classes/Subviews/JMTabView.m: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import "JMTabView.h" 4 | #import "JMTabContainer.h" 5 | #import "BarBackgroundLayer.h" 6 | #import "UIView+Positioning.h" 7 | 8 | @interface JMTabView() 9 | @property (nonatomic,retain) JMTabContainer * tabContainer; 10 | @end 11 | 12 | @implementation JMTabView 13 | 14 | @synthesize delegate = delegate_; 15 | @synthesize selectedIndex = selectedIndex_; 16 | @synthesize tabContainer = tabContainer_; 17 | @synthesize itemPadding = itemPadding_; 18 | 19 | - (void)dealloc; 20 | { 21 | self.tabContainer = nil; 22 | self.delegate = nil; 23 | [super dealloc]; 24 | } 25 | 26 | - (id)initWithFrame:(CGRect)frame 27 | { 28 | self = [super initWithFrame:frame]; 29 | if (self) 30 | { 31 | [self setBackgroundLayer:[[[BarBackgroundLayer alloc] init] autorelease]]; 32 | self.backgroundColor = [UIColor clearColor]; 33 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth; 34 | self.tabContainer = [[[JMTabContainer alloc] initWithFrame:self.bounds] autorelease]; 35 | self.itemPadding = kTabItemPadding; 36 | [self addSubview:self.tabContainer]; 37 | } 38 | return self; 39 | } 40 | 41 | - (id)initWithCoder:(NSCoder *)aDecoder { 42 | self = [super initWithCoder:aDecoder]; 43 | if (self) 44 | { 45 | [self setBackgroundLayer:[[[BarBackgroundLayer alloc] init] autorelease]]; 46 | [self setTabContainer:[[[JMTabContainer alloc] initWithFrame:self.bounds] autorelease]]; 47 | self.itemPadding = kTabItemPadding; 48 | [self addSubview:self.tabContainer]; 49 | } 50 | return self; 51 | } 52 | 53 | - (void)setBackgroundLayer:(CALayer *)backgroundLayer; 54 | { 55 | CALayer * oldBackground = [[self.layer sublayers] objectAtIndex:0]; 56 | if (oldBackground) 57 | { 58 | [self.layer replaceSublayer:oldBackground with:backgroundLayer]; 59 | } 60 | else 61 | { 62 | [self.layer insertSublayer:backgroundLayer atIndex:0]; 63 | } 64 | } 65 | 66 | - (void)layoutSubviews; 67 | { 68 | [self.tabContainer centerInSuperView]; 69 | } 70 | 71 | #pragma Mark - 72 | #pragma Mark Notifying Delegates 73 | 74 | - (void)didSelectItemAtIndex:(NSUInteger)itemIndex; 75 | { 76 | if (self.delegate && [self.delegate respondsToSelector:@selector(tabView:didSelectTabAtIndex:)]) 77 | { 78 | [self.delegate tabView:self didSelectTabAtIndex:itemIndex]; 79 | } 80 | } 81 | 82 | #pragma Mark - 83 | #pragma External Interface 84 | 85 | - (void)setMomentary:(BOOL)momentary; 86 | { 87 | [self.tabContainer setMomentary:momentary]; 88 | } 89 | 90 | - (void)addTabItem:(JMTabItem *)tabItem; 91 | { 92 | [tabItem setPadding:[self itemPadding]]; 93 | 94 | [self.tabContainer addTabItem:tabItem]; 95 | } 96 | 97 | - (void)addTabItemWithTitle:(NSString *)title icon:(UIImage *)icon; 98 | { 99 | JMTabItem * tabItem = [JMTabItem tabItemWithTitle:title icon:icon]; 100 | [self addTabItem:tabItem]; 101 | } 102 | 103 | - (void)removeTabItemAtIndex:(NSUInteger)index { 104 | [self.tabContainer removeTabItem:[self.tabContainer tabItemAtIndex:index]]; 105 | } 106 | 107 | - (void)removeAllTabItems { 108 | [self.tabContainer removeAllTabItems]; 109 | } 110 | 111 | #if NS_BLOCKS_AVAILABLE 112 | - (void)addTabItemWithTitle:(NSString *)title icon:(UIImage *)icon executeBlock:(JMTabExecutionBlock)executeBlock; 113 | { 114 | JMTabItem * tabItem = [JMTabItem tabItemWithTitle:title icon:icon executeBlock:executeBlock]; 115 | 116 | [self addTabItem:tabItem]; 117 | } 118 | #endif 119 | 120 | - (void)setSelectedIndex:(NSUInteger)itemIndex; 121 | { 122 | [self.tabContainer layoutSubviews]; 123 | [self.tabContainer animateSelectionToItemAtIndex:itemIndex]; 124 | } 125 | 126 | - (NSUInteger)selectedIndex { 127 | return [self.tabContainer selectedIndex]; 128 | } 129 | 130 | - (void)setTabItemPadding:(CGSize)itemPadding { 131 | self.itemPadding = itemPadding; 132 | 133 | NSUInteger tabItemIndex, numberOfTabItems = [[self tabContainer] numberOfTabItems]; 134 | 135 | for (tabItemIndex=0; tabItemIndex 4 | #import 5 | #import 6 | 7 | @interface BarBackgroundLayer : CALayer 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /JMTabView/Classes/Subviews/Layers/BarBackgroundLayer.m: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import "BarBackgroundLayer.h" 4 | #import "UIColor+Hex.h" 5 | #import "JMTabConstants.h" 6 | 7 | @implementation BarBackgroundLayer 8 | 9 | -(id)init; 10 | { 11 | self = [super init]; 12 | if (self) 13 | { 14 | CAGradientLayer * gradientLayer = [[[CAGradientLayer alloc] init] autorelease]; 15 | UIColor * startColor = [UIColor colorWithHex:0x282928]; 16 | UIColor * endColor = [UIColor colorWithHex:0x4a4b4a]; 17 | gradientLayer.frame = CGRectMake(0, 0, 1024, 60); 18 | gradientLayer.colors = [NSArray arrayWithObjects:(id)[startColor CGColor], (id)[endColor CGColor], nil]; 19 | [self insertSublayer:gradientLayer atIndex:0]; 20 | } 21 | return self; 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /JMTabView/JMTabView-Prefix.pch: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #ifdef __OBJC__ 4 | #import 5 | #endif 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Software Copyright License Agreement (BSD License) 2 | 3 | Copyright (c) 2010, Jason Morrissey 4 | All rights reserved. 5 | 6 | Redistribution and use of this software in source and binary forms, 7 | with or without modification, are permitted provided that the following 8 | conditions are met: 9 | 10 | * Redistributions of source code must retain the above 11 | copyright notice, this list of conditions and the 12 | following disclaimer. 13 | 14 | * Redistributions in binary form must reproduce the above 15 | copyright notice, this list of conditions and the 16 | following disclaimer in the documentation and/or other 17 | materials provided with the distribution. 18 | 19 | * Neither the name of Jason Morrissey nor the names of 20 | contributors may be used to endorse or promote products 21 | derived from this software without specific prior 22 | written permission of Jason Morrissey. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 25 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 27 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 28 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 30 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /TabDemo/Classes/Custom/CustomBackgroundLayer.h: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import 4 | #import 5 | 6 | @interface CustomBackgroundLayer : CALayer { 7 | 8 | } 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /TabDemo/Classes/Custom/CustomBackgroundLayer.m: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import "CustomBackgroundLayer.h" 4 | #import "UIColor+Hex.h" 5 | 6 | @implementation CustomBackgroundLayer 7 | 8 | -(id)init; 9 | { 10 | self = [super init]; 11 | if (self) 12 | { 13 | CAGradientLayer * gradientLayer = [[[CAGradientLayer alloc] init] autorelease]; 14 | UIColor * startColor = [UIColor colorWithHex:0x4a4b4a]; 15 | UIColor * midColor = [UIColor colorWithHex:0x282928]; 16 | UIColor * endColor = [UIColor colorWithHex:0x4a4b4a]; 17 | gradientLayer.frame = CGRectMake(0, 8., 1024, 60); 18 | gradientLayer.colors = [NSArray arrayWithObjects:(id)[startColor CGColor], (id)[midColor CGColor], (id)[endColor CGColor], nil]; 19 | [self insertSublayer:gradientLayer atIndex:0]; 20 | } 21 | return self; 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /TabDemo/Classes/Custom/CustomNoiseBackgroundView.h: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import 4 | 5 | @interface CustomNoiseBackgroundView : UIView 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /TabDemo/Classes/Custom/CustomNoiseBackgroundView.m: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import "CustomNoiseBackgroundView.h" 4 | 5 | @implementation CustomNoiseBackgroundView 6 | 7 | - (id)initWithFrame:(CGRect)frame; 8 | { 9 | self = [super initWithFrame:frame]; 10 | if (self) 11 | { 12 | self.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1.]; 13 | } 14 | return self; 15 | } 16 | 17 | - (void)drawRect:(CGRect)rect 18 | { 19 | CGContextRef context = UIGraphicsGetCurrentContext(); 20 | CGContextSaveGState(context); 21 | 22 | CGRect bounds = self.bounds; 23 | 24 | UIBezierPath * path = [UIBezierPath bezierPathWithRect:bounds]; 25 | 26 | CGContextAddPath(context, [path CGPath]); 27 | CGContextClip(context); 28 | 29 | CGContextSetBlendMode(context, kCGBlendModeMultiply); 30 | UIImage * noise = [UIImage imageNamed:@"noise-tile.png"]; 31 | [noise drawAsPatternInRect:bounds]; 32 | 33 | CGContextRestoreGState(context); 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /TabDemo/Classes/Custom/CustomSelectionView.h: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import 4 | 5 | @interface CustomSelectionView : JMSelectionView 6 | 7 | + (CustomSelectionView *) createSelectionView; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /TabDemo/Classes/Custom/CustomSelectionView.m: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import "CustomSelectionView.h" 4 | #import "UIView+InnerShadow.h" 5 | #import "UIColor+Hex.h" 6 | 7 | #define kTriangleHeight 8. 8 | 9 | @implementation CustomSelectionView 10 | 11 | - (void)drawRect:(CGRect)rect 12 | { 13 | [[UIColor colorWithHex:0x252525] set]; 14 | CGRect squareRect = CGRectOffset(rect, 0, kTriangleHeight); 15 | squareRect.size.height -= kTriangleHeight; 16 | UIBezierPath * squarePath = [UIBezierPath bezierPathWithRoundedRect:squareRect cornerRadius:4.]; 17 | [squarePath fill]; 18 | 19 | UIBezierPath *trianglePath = [UIBezierPath bezierPath]; 20 | [trianglePath moveToPoint:CGPointMake(rect.size.width / 2 - kTriangleHeight, kTriangleHeight)]; 21 | [trianglePath addLineToPoint:CGPointMake(rect.size.width / 2, 0.)]; 22 | [trianglePath addLineToPoint:CGPointMake(rect.size.width / 2 + kTriangleHeight, kTriangleHeight)]; 23 | [trianglePath closePath]; 24 | [trianglePath fill]; 25 | } 26 | 27 | + (CustomSelectionView *) createSelectionView; 28 | { 29 | CustomSelectionView * selectionView = [[[CustomSelectionView alloc] initWithFrame:CGRectZero] autorelease]; 30 | return selectionView; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /TabDemo/Classes/Custom/CustomTabItem.h: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import 4 | 5 | @interface CustomTabItem : JMTabItem 6 | 7 | @property (nonatomic,retain) UIImage * alternateIcon; 8 | 9 | + (CustomTabItem *)tabItemWithTitle:(NSString *)title icon:(UIImage *)icon alternateIcon:(UIImage *)icon; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /TabDemo/Classes/Custom/CustomTabItem.m: -------------------------------------------------------------------------------- 1 | #import "CustomTabItem.h" 2 | 3 | #define kTabDemoVerticalItemPaddingSize CGSizeMake(18., 5.) 4 | #define kTabDemoVerticalItemFont [UIFont boldSystemFontOfSize:11.] 5 | 6 | @implementation CustomTabItem 7 | 8 | @synthesize alternateIcon = alternateIcon_; 9 | 10 | - (void) dealloc; 11 | { 12 | self.alternateIcon = nil; 13 | [super dealloc]; 14 | } 15 | 16 | - (CGSize) sizeThatFits:(CGSize)size; 17 | { 18 | CGSize titleSize = [self.title sizeWithFont:kTabDemoVerticalItemFont]; 19 | 20 | CGFloat titleWidth = titleSize.width; 21 | 22 | CGFloat iconWidth = [self.icon size].width; 23 | 24 | CGFloat width = (iconWidth > titleWidth) ? iconWidth : titleWidth; 25 | 26 | width += (kTabDemoVerticalItemPaddingSize.width * 2); 27 | 28 | CGFloat height = 56.; 29 | 30 | return CGSizeMake(width, height); 31 | } 32 | 33 | - (void)drawRect:(CGRect)rect; 34 | { 35 | CGRect bounds = rect; 36 | CGFloat yOffset = kTabDemoVerticalItemPaddingSize.height + 10.; 37 | 38 | UIImage * iconImage = (self.highlighted || [self isSelectedTabItem]) ? self.alternateIcon : self.icon; 39 | 40 | // calculate icon position 41 | CGFloat iconWidth = [iconImage size].width; 42 | CGFloat iconMarginWidth = (bounds.size.width - iconWidth) / 2; 43 | 44 | [iconImage drawAtPoint:CGPointMake(iconMarginWidth, yOffset)]; 45 | 46 | // calculate title position 47 | CGFloat titleWidth = [self.title sizeWithFont:kTabDemoVerticalItemFont].width; 48 | CGFloat titleMarginWidth = (bounds.size.width - titleWidth) / 2; 49 | 50 | UIColor * textColor = self.highlighted ? [UIColor lightGrayColor] : [UIColor whiteColor]; 51 | [textColor set]; 52 | [self.title drawAtPoint:CGPointMake(titleMarginWidth, yOffset + 22.) withFont:kTabDemoVerticalItemFont]; 53 | } 54 | 55 | + (CustomTabItem *)tabItemWithTitle:(NSString *)title icon:(UIImage *)icon alternateIcon:(UIImage *)alternativeIcon; 56 | { 57 | CustomTabItem * tabItem = [[[CustomTabItem alloc] initWithTitle:title icon:icon] autorelease]; 58 | tabItem.alternateIcon = alternativeIcon; 59 | return tabItem; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /TabDemo/Classes/TabDemoAppDelegate.h: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | @interface TabDemoAppDelegate : NSObject 4 | { 5 | UIWindow *window_; 6 | UINavigationController *UINavigationController_; 7 | } 8 | 9 | @property (nonatomic, retain) UIWindow *window; 10 | @property (nonatomic, retain) UINavigationController *navigationController; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /TabDemo/Classes/TabDemoAppDelegate.m: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import "TabDemoAppDelegate.h" 4 | #import "TabDemoViewController.h" 5 | 6 | @implementation TabDemoAppDelegate 7 | 8 | @synthesize window = window_; 9 | @synthesize navigationController = navigationController_; 10 | 11 | #pragma mark - 12 | #pragma mark Application lifecycle 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 15 | { 16 | TabDemoViewController * demoViewController = [[[TabDemoViewController alloc] initWithNibName:nil bundle:nil] autorelease]; 17 | self.navigationController = [[[UINavigationController alloc] initWithRootViewController:demoViewController] autorelease]; 18 | [self.navigationController setNavigationBarHidden:YES]; 19 | 20 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 21 | [self.window addSubview:self.navigationController.view]; 22 | [self.window makeKeyAndVisible]; 23 | 24 | return YES; 25 | } 26 | 27 | - (void)dealloc 28 | { 29 | self.navigationController = nil; 30 | self.window = nil; 31 | [super dealloc]; 32 | } 33 | 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /TabDemo/Classes/TabDemoViewController.h: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import 4 | #import 5 | 6 | @interface TabDemoViewController : UIViewController 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /TabDemo/Classes/TabDemoViewController.m: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import "TabDemoViewController.h" 4 | #import "CustomTabItem.h" 5 | #import "CustomSelectionView.h" 6 | #import "CustomBackgroundLayer.h" 7 | #import "CustomNoiseBackgroundView.h" 8 | #import "UIView+Positioning.h" 9 | 10 | @interface TabDemoViewController() 11 | -(void)addStandardTabView; 12 | -(void)addCustomTabView; 13 | @end 14 | 15 | @implementation TabDemoViewController 16 | 17 | 18 | -(void)addStandardTabView; 19 | { 20 | JMTabView * tabView = [[[JMTabView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 60.)] autorelease]; 21 | 22 | [tabView setDelegate:self]; 23 | 24 | [tabView addTabItemWithTitle:@"One" icon:[UIImage imageNamed:@"icon1.png"]]; 25 | [tabView addTabItemWithTitle:@"Two" icon:[UIImage imageNamed:@"icon2.png"]]; 26 | [tabView addTabItemWithTitle:@"Three" icon:[UIImage imageNamed:@"icon3.png"]]; 27 | 28 | // You can run blocks by specifiying an executeBlock: paremeter 29 | // #if NS_BLOCKS_AVAILABLE 30 | // [tabView addTabItemWithTitle:@"One" icon:nil executeBlock:^{NSLog(@"abc");}]; 31 | // #endif 32 | 33 | [tabView setSelectedIndex:0]; 34 | 35 | [self.view addSubview:tabView]; 36 | } 37 | 38 | -(void)addCustomTabView; 39 | { 40 | JMTabView * tabView = [[[JMTabView alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height - 60., self.view.bounds.size.width, 60.)] autorelease]; 41 | tabView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth; 42 | 43 | [tabView setDelegate:self]; 44 | 45 | UIImage * standardIcon = [UIImage imageNamed:@"icon3.png"]; 46 | UIImage * highlightedIcon = [UIImage imageNamed:@"icon2.png"]; 47 | 48 | CustomTabItem * tabItem1 = [CustomTabItem tabItemWithTitle:@"One" icon:standardIcon alternateIcon:highlightedIcon]; 49 | CustomTabItem * tabItem2 = [CustomTabItem tabItemWithTitle:@"Two" icon:standardIcon alternateIcon:highlightedIcon]; 50 | CustomTabItem * tabItem3 = [CustomTabItem tabItemWithTitle:@"Three" icon:standardIcon alternateIcon:highlightedIcon]; 51 | CustomTabItem * tabItem4 = [CustomTabItem tabItemWithTitle:@"Four" icon:standardIcon alternateIcon:highlightedIcon]; 52 | CustomTabItem * tabItem5 = [CustomTabItem tabItemWithTitle:@"Five" icon:standardIcon alternateIcon:highlightedIcon]; 53 | 54 | [tabView addTabItem:tabItem1]; 55 | [tabView addTabItem:tabItem2]; 56 | [tabView addTabItem:tabItem3]; 57 | [tabView addTabItem:tabItem4]; 58 | [tabView addTabItem:tabItem5]; 59 | 60 | [tabView setSelectionView:[CustomSelectionView createSelectionView]]; 61 | [tabView setItemSpacing:1.]; 62 | [tabView setBackgroundLayer:[[[CustomBackgroundLayer alloc] init] autorelease]]; 63 | 64 | [tabView setSelectedIndex:0]; 65 | 66 | [self.view addSubview:tabView]; 67 | } 68 | 69 | -(void)loadView; 70 | { 71 | CustomNoiseBackgroundView * noiseBackgroundView = [[[CustomNoiseBackgroundView alloc] init] autorelease]; 72 | self.view = noiseBackgroundView; 73 | 74 | 75 | [self addStandardTabView]; 76 | 77 | [self addCustomTabView]; 78 | } 79 | 80 | 81 | -(void)tabView:(JMTabView *)tabView didSelectTabAtIndex:(NSUInteger)itemIndex; 82 | { 83 | NSLog(@"Selected Tab Index: %d", itemIndex); 84 | } 85 | 86 | -(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation; 87 | { 88 | return YES; 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /TabDemo/Resources/icon1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonmorrissey/JMTabView/bb99afcbd2f69f1a991d53f5a1a1774aa6b58ef6/TabDemo/Resources/icon1.png -------------------------------------------------------------------------------- /TabDemo/Resources/icon1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonmorrissey/JMTabView/bb99afcbd2f69f1a991d53f5a1a1774aa6b58ef6/TabDemo/Resources/icon1@2x.png -------------------------------------------------------------------------------- /TabDemo/Resources/icon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonmorrissey/JMTabView/bb99afcbd2f69f1a991d53f5a1a1774aa6b58ef6/TabDemo/Resources/icon2.png -------------------------------------------------------------------------------- /TabDemo/Resources/icon2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonmorrissey/JMTabView/bb99afcbd2f69f1a991d53f5a1a1774aa6b58ef6/TabDemo/Resources/icon2@2x.png -------------------------------------------------------------------------------- /TabDemo/Resources/icon3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonmorrissey/JMTabView/bb99afcbd2f69f1a991d53f5a1a1774aa6b58ef6/TabDemo/Resources/icon3.png -------------------------------------------------------------------------------- /TabDemo/Resources/icon3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonmorrissey/JMTabView/bb99afcbd2f69f1a991d53f5a1a1774aa6b58ef6/TabDemo/Resources/icon3@2x.png -------------------------------------------------------------------------------- /TabDemo/Resources/noise-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonmorrissey/JMTabView/bb99afcbd2f69f1a991d53f5a1a1774aa6b58ef6/TabDemo/Resources/noise-tile.png -------------------------------------------------------------------------------- /TabDemo/Resources/noise-tile@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonmorrissey/JMTabView/bb99afcbd2f69f1a991d53f5a1a1774aa6b58ef6/TabDemo/Resources/noise-tile@2x.png -------------------------------------------------------------------------------- /TabDemo/TabDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | com.designshed.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | 1.0 27 | LSRequiresIPhoneOS 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UIStatusBarStyle 36 | UIStatusBarStyleBlackOpaque 37 | 38 | 39 | -------------------------------------------------------------------------------- /TabDemo/TabDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'TabDemo' target in the 'TabDemo' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_3_0 8 | #warning "This project uses features only available in iPhone SDK 3.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /TabDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /TabDemo/main.m: -------------------------------------------------------------------------------- 1 | // Created by Jason Morrissey 2 | 3 | #import 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 8 | int retVal = UIApplicationMain(argc, argv, nil, @"TabDemoAppDelegate"); 9 | [pool release]; 10 | return retVal; 11 | } 12 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## JMTabView 2 | 3 | This is a dark-themed and easy to use tab view created entirely using Core Graphics, so that it is easy to drop-in to your projects and operates smoothly on iOS devices. The library itself will also be used in Alien Blue's upcoming iOS updates. 4 | 5 | ## How it looks 6 | 7 | Here's what JMTabView looks like: 8 | 9 | 10 | 11 | And on a retina display: 12 | 13 | 14 | 15 | ## Usage 16 | 17 | A demo project is included in this repository so that you can dive right in. In brief, to draw the tab view, you can do the following: 18 | 19 | `JMTabView * tabView = [[[JMTabView alloc] initWithFrame:frame] autorelease];` 20 | 21 | `[tabView setDelegate:self];` 22 | 23 | You can then add tab items: 24 | 25 | `[tabView addTabItemWithTitle:@"One" icon:[UIImage imageNamed:@"icon1.png"]];` 26 | 27 | By implementing the `tabView:didSelectTabAtIndex:` method, you will receive a callback when the tab selection changes. 28 | 29 | ## Flexibility 30 | 31 | You can set the `icon:` to `nil` if you prefer to use text only, or `title:` to `nil` to show icons only. 32 | 33 | If you prefer to use JMTabView as a **toolbar** you can use the `setMomentary:` method to change its selection behaviour. 34 | 35 | JMTabView also supports the execution of blocks so that you can embed your logic per tab item, like this: 36 | 37 | `[tabView addTabItemWithTitle:@"Tab" icon:nil executeBlock:^{ 38 | // do stuff after item has been selected 39 | }];` 40 | 41 | ## Customisation 42 | 43 | You can subclass `JMTabItem` and `JMSelectionView` to completely customize the view of the tabs. For example, in the inclusive Demo you can see how I've customized the view to render tabs like this: 44 | 45 | 46 | 47 | ## Installation 48 | 49 | If you prefer to use JMTabView as a dynamic library in your project, please be sure to add `-ObjC -all_load` to your target's `Other Linker Flags`. Alternatively, you can reference the .h and .m files directly. 50 | 51 | ## Acknowledgements 52 | 53 | This project uses the UIView+Positioning and UIView+Size categories developed by the very talented [Kevin O'Neill](https://github.com/kevinoneill/Useful-Bits). 54 | 55 | ## License 56 | 57 | JMTabView is BSD licensed, so you can freely use it in commercial applications. 58 | --------------------------------------------------------------------------------