Skip to content
Browse files

v1.0

  • Loading branch information...
0 parents commit eba2bf9f09688b089d07f10de2d9633e9d9274fb Sudeep Yegnashankaran committed
34 .gitignore
@@ -0,0 +1,34 @@
+# xcode noise
+build/*
+build
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspectivev3
+*.tmproj
+build
+Build
+Products
+xcuserdata
+xcuserdata/*
+project.xcworkspace
+project.xcworkspace/*
+
+# osx noise
+.DS_Store
+profile
+
+RADAR.txt
+
+anticrack_scan_result.h
+
+*.pdf
+todo
+.DS_Store
+src/Three20/*.h
+src/Three20Core/*.h
+src/Three20Network/*.h
+src/Three20Style/*.h
+src/Three20UICommon/*.h
+src/Three20UINavigator/*.h
+src/Three20UI/*.h
344 Demo/Demo.xcodeproj/project.pbxproj
@@ -0,0 +1,344 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 6E156E0C151F968400DA6E4F /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6E4790781512C79E00B1AD40 /* CoreGraphics.framework */; };
+ 6E156E0D151F971200DA6E4F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6E4790761512C79E00B1AD40 /* Foundation.framework */; };
+ 6E156E3C151F9DC500DA6E4F /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6E156E3A151F9DC500DA6E4F /* Localizable.strings */; };
+ 6E4790751512C79E00B1AD40 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6E4790741512C79E00B1AD40 /* UIKit.framework */; };
+ 6E47907F1512C79E00B1AD40 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6E47907D1512C79E00B1AD40 /* InfoPlist.strings */; };
+ 6E4790811512C79E00B1AD40 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E4790801512C79E00B1AD40 /* main.m */; };
+ 6E4790851512C79E00B1AD40 /* EDAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E4790841512C79E00B1AD40 /* EDAppDelegate.m */; };
+ 6E47908D1512C80200B1AD40 /* LIExposeController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E47908C1512C80200B1AD40 /* LIExposeController.m */; };
+ 6E47909B1512C83500B1AD40 /* add.png in Resources */ = {isa = PBXBuildFile; fileRef = 6E4790901512C83500B1AD40 /* add.png */; };
+ 6E47909C1512C83500B1AD40 /* add@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6E4790911512C83500B1AD40 /* add@2x.png */; };
+ 6E47909F1512C83500B1AD40 /* deleteBtn.png in Resources */ = {isa = PBXBuildFile; fileRef = 6E4790941512C83500B1AD40 /* deleteBtn.png */; };
+ 6E4790A01512C83500B1AD40 /* deleteBtn@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6E4790951512C83500B1AD40 /* deleteBtn@2x.png */; };
+ 6E4790A31512C83500B1AD40 /* icon-iPad.png in Resources */ = {isa = PBXBuildFile; fileRef = 6E4790981512C83500B1AD40 /* icon-iPad.png */; };
+ 6E4790A41512C83500B1AD40 /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 6E4790991512C83500B1AD40 /* icon.png */; };
+ 6E4790A51512C83500B1AD40 /* icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6E47909A1512C83500B1AD40 /* icon@2x.png */; };
+ 6E4790AF1512C86200B1AD40 /* EDViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E4790AE1512C86200B1AD40 /* EDViewController.m */; };
+ 6E4790B11512C93400B1AD40 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6E4790B01512C93400B1AD40 /* QuartzCore.framework */; };
+ 6E7F4F881520D2C5005543BB /* EDDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E7F4F871520D2C5005543BB /* EDDetailViewController.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 6E156E3B151F9DC500DA6E4F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 6E4790701512C79E00B1AD40 /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 6E4790741512C79E00B1AD40 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ 6E4790761512C79E00B1AD40 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ 6E4790781512C79E00B1AD40 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+ 6E47907C1512C79E00B1AD40 /* Demo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Demo-Info.plist"; sourceTree = "<group>"; };
+ 6E47907E1512C79E00B1AD40 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 6E4790801512C79E00B1AD40 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 6E4790821512C79E00B1AD40 /* Demo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Demo-Prefix.pch"; sourceTree = "<group>"; };
+ 6E4790831512C79E00B1AD40 /* EDAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EDAppDelegate.h; sourceTree = "<group>"; };
+ 6E4790841512C79E00B1AD40 /* EDAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EDAppDelegate.m; sourceTree = "<group>"; };
+ 6E47908B1512C80200B1AD40 /* LIExposeController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LIExposeController.h; path = ../../LIExposeController.h; sourceTree = "<group>"; };
+ 6E47908C1512C80200B1AD40 /* LIExposeController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LIExposeController.m; path = ../../LIExposeController.m; sourceTree = "<group>"; };
+ 6E4790901512C83500B1AD40 /* add.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = add.png; sourceTree = "<group>"; };
+ 6E4790911512C83500B1AD40 /* add@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "add@2x.png"; sourceTree = "<group>"; };
+ 6E4790941512C83500B1AD40 /* deleteBtn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = deleteBtn.png; sourceTree = "<group>"; };
+ 6E4790951512C83500B1AD40 /* deleteBtn@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "deleteBtn@2x.png"; sourceTree = "<group>"; };
+ 6E4790981512C83500B1AD40 /* icon-iPad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-iPad.png"; sourceTree = "<group>"; };
+ 6E4790991512C83500B1AD40 /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = "<group>"; };
+ 6E47909A1512C83500B1AD40 /* icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon@2x.png"; sourceTree = "<group>"; };
+ 6E4790AD1512C86200B1AD40 /* EDViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EDViewController.h; sourceTree = "<group>"; };
+ 6E4790AE1512C86200B1AD40 /* EDViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EDViewController.m; sourceTree = "<group>"; };
+ 6E4790B01512C93400B1AD40 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
+ 6E7F4F861520D2C5005543BB /* EDDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EDDetailViewController.h; sourceTree = "<group>"; };
+ 6E7F4F871520D2C5005543BB /* EDDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EDDetailViewController.m; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 6E47906D1512C79E00B1AD40 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 6E4790751512C79E00B1AD40 /* UIKit.framework in Frameworks */,
+ 6E156E0D151F971200DA6E4F /* Foundation.framework in Frameworks */,
+ 6E4790B11512C93400B1AD40 /* QuartzCore.framework in Frameworks */,
+ 6E156E0C151F968400DA6E4F /* CoreGraphics.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 6E4790651512C79E00B1AD40 = {
+ isa = PBXGroup;
+ children = (
+ 6E47907A1512C79E00B1AD40 /* Demo */,
+ 6E47908E1512C83500B1AD40 /* Resources */,
+ 6E4790731512C79E00B1AD40 /* Frameworks */,
+ 6E4790711512C79E00B1AD40 /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ 6E4790711512C79E00B1AD40 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 6E4790701512C79E00B1AD40 /* Demo.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 6E4790731512C79E00B1AD40 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 6E4790741512C79E00B1AD40 /* UIKit.framework */,
+ 6E4790761512C79E00B1AD40 /* Foundation.framework */,
+ 6E4790B01512C93400B1AD40 /* QuartzCore.framework */,
+ 6E4790781512C79E00B1AD40 /* CoreGraphics.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 6E47907A1512C79E00B1AD40 /* Demo */ = {
+ isa = PBXGroup;
+ children = (
+ 6E4790831512C79E00B1AD40 /* EDAppDelegate.h */,
+ 6E4790841512C79E00B1AD40 /* EDAppDelegate.m */,
+ 6E4790AD1512C86200B1AD40 /* EDViewController.h */,
+ 6E4790AE1512C86200B1AD40 /* EDViewController.m */,
+ 6E7F4F861520D2C5005543BB /* EDDetailViewController.h */,
+ 6E7F4F871520D2C5005543BB /* EDDetailViewController.m */,
+ 6E47908B1512C80200B1AD40 /* LIExposeController.h */,
+ 6E47908C1512C80200B1AD40 /* LIExposeController.m */,
+ 6E47907B1512C79E00B1AD40 /* Supporting Files */,
+ );
+ path = Demo;
+ sourceTree = "<group>";
+ };
+ 6E47907B1512C79E00B1AD40 /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 6E4790821512C79E00B1AD40 /* Demo-Prefix.pch */,
+ 6E47907C1512C79E00B1AD40 /* Demo-Info.plist */,
+ 6E4790801512C79E00B1AD40 /* main.m */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ 6E47908E1512C83500B1AD40 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 6E47907D1512C79E00B1AD40 /* InfoPlist.strings */,
+ 6E156E3A151F9DC500DA6E4F /* Localizable.strings */,
+ 6E47908F1512C83500B1AD40 /* images */,
+ );
+ path = Resources;
+ sourceTree = "<group>";
+ };
+ 6E47908F1512C83500B1AD40 /* images */ = {
+ isa = PBXGroup;
+ children = (
+ 6E4790901512C83500B1AD40 /* add.png */,
+ 6E4790911512C83500B1AD40 /* add@2x.png */,
+ 6E4790941512C83500B1AD40 /* deleteBtn.png */,
+ 6E4790951512C83500B1AD40 /* deleteBtn@2x.png */,
+ 6E4790981512C83500B1AD40 /* icon-iPad.png */,
+ 6E4790991512C83500B1AD40 /* icon.png */,
+ 6E47909A1512C83500B1AD40 /* icon@2x.png */,
+ );
+ path = images;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 6E47906F1512C79E00B1AD40 /* Demo */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 6E4790881512C79E00B1AD40 /* Build configuration list for PBXNativeTarget "Demo" */;
+ buildPhases = (
+ 6E47906C1512C79E00B1AD40 /* Sources */,
+ 6E47906D1512C79E00B1AD40 /* Frameworks */,
+ 6E47906E1512C79E00B1AD40 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Demo;
+ productName = Demo;
+ productReference = 6E4790701512C79E00B1AD40 /* Demo.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 6E4790671512C79E00B1AD40 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ CLASSPREFIX = ED;
+ LastUpgradeCheck = 0430;
+ ORGANIZATIONNAME = "LinkedIn, Inc.";
+ };
+ buildConfigurationList = 6E47906A1512C79E00B1AD40 /* Build configuration list for PBXProject "Demo" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 6E4790651512C79E00B1AD40;
+ productRefGroup = 6E4790711512C79E00B1AD40 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 6E47906F1512C79E00B1AD40 /* Demo */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 6E47906E1512C79E00B1AD40 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 6E47907F1512C79E00B1AD40 /* InfoPlist.strings in Resources */,
+ 6E47909B1512C83500B1AD40 /* add.png in Resources */,
+ 6E47909C1512C83500B1AD40 /* add@2x.png in Resources */,
+ 6E47909F1512C83500B1AD40 /* deleteBtn.png in Resources */,
+ 6E4790A01512C83500B1AD40 /* deleteBtn@2x.png in Resources */,
+ 6E4790A31512C83500B1AD40 /* icon-iPad.png in Resources */,
+ 6E4790A41512C83500B1AD40 /* icon.png in Resources */,
+ 6E4790A51512C83500B1AD40 /* icon@2x.png in Resources */,
+ 6E156E3C151F9DC500DA6E4F /* Localizable.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 6E47906C1512C79E00B1AD40 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 6E4790811512C79E00B1AD40 /* main.m in Sources */,
+ 6E4790851512C79E00B1AD40 /* EDAppDelegate.m in Sources */,
+ 6E47908D1512C80200B1AD40 /* LIExposeController.m in Sources */,
+ 6E4790AF1512C86200B1AD40 /* EDViewController.m in Sources */,
+ 6E7F4F881520D2C5005543BB /* EDDetailViewController.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 6E156E3A151F9DC500DA6E4F /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 6E156E3B151F9DC500DA6E4F /* en */,
+ );
+ name = Localizable.strings;
+ sourceTree = "<group>";
+ };
+ 6E47907D1512C79E00B1AD40 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 6E47907E1512C79E00B1AD40 /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 6E4790861512C79E00B1AD40 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 4.3;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 6E4790871512C79E00B1AD40 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 4.3;
+ OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 6E4790891512C79E00B1AD40 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "Demo/Demo-Prefix.pch";
+ INFOPLIST_FILE = "Demo/Demo-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+ 6E47908A1512C79E00B1AD40 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "Demo/Demo-Prefix.pch";
+ INFOPLIST_FILE = "Demo/Demo-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 6E47906A1512C79E00B1AD40 /* Build configuration list for PBXProject "Demo" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 6E4790861512C79E00B1AD40 /* Debug */,
+ 6E4790871512C79E00B1AD40 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 6E4790881512C79E00B1AD40 /* Build configuration list for PBXNativeTarget "Demo" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 6E4790891512C79E00B1AD40 /* Debug */,
+ 6E47908A1512C79E00B1AD40 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 6E4790671512C79E00B1AD40 /* Project object */;
+}
53 Demo/Demo/Demo-Info.plist
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>LSHasLocalizedDisplayName</key>
+ <true/>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string>icon.png</string>
+ <key>CFBundleIconFiles</key>
+ <array>
+ <string>icon.png</string>
+ <string>icon@2x.png</string>
+ <string>icon-iPad.png</string>
+ </array>
+ <key>CFBundleIdentifier</key>
+ <string>com.linkedin.Expose${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
26 Demo/Demo/Demo-Prefix.pch
@@ -0,0 +1,26 @@
+/*
+ Copyright 2012 LinkedIn, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import <Availability.h>
+
+#ifndef __IPHONE_3_0
+#warning "This project uses features only available in iOS SDK 3.0 and later."
+#endif
+
+#ifdef __OBJC__
+ #import <UIKit/UIKit.h>
+ #import <Foundation/Foundation.h>
+#endif
23 Demo/Demo/EDAppDelegate.h
@@ -0,0 +1,23 @@
+/*
+ Copyright 2012 LinkedIn, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "LIExposeController.h"
+
+@interface EDAppDelegate : UIResponder <UIApplicationDelegate, LIExposeControllerDelegate, LIExposeControllerDataSource>
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end
192 Demo/Demo/EDAppDelegate.m
@@ -0,0 +1,192 @@
+/*
+ Copyright 2012 LinkedIn, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "EDAppDelegate.h"
+#import "EDViewController.h"
+
+NSString * const ADD_BUTTON_IMAGE = @"add.png";
+
+
+@interface EDAppDelegate ()
+
+- (UIViewController *)newViewControllerForExposeController:(LIExposeController *)exposeController;
+
+@end
+
+@implementation EDAppDelegate
+
+static int _viewControllerId = 0;
+
+@synthesize window=_window;
+
+#pragma mark - LIExposeControllerDelegate Methods
+
+- (BOOL)canAddViewControllersForExposeController:(LIExposeController *)exposeController {
+ return YES;
+}
+
+- (BOOL)exposeController:(LIExposeController *)exposeController canDeleteViewController:(UIViewController *)viewController {
+ return YES;
+}
+
+#pragma mark - LIExposeControllerDataSource Methods
+
+- (UIView *)backgroundViewForExposeController:(LIExposeController *)exposeController {
+ UIView *v = [[[UIView alloc] initWithFrame:CGRectMake(0,
+ 0,
+ exposeController.view.frame.size.width,
+ exposeController.view.frame.size.height)] autorelease];
+ v.backgroundColor = [UIColor darkGrayColor];
+ return v;
+}
+
+- (void)shouldAddViewControllerForExposeController:(LIExposeController *)exposeController {
+ [exposeController addNewViewController:[self newViewControllerForExposeController:exposeController]
+ animated:YES];
+}
+
+- (UIView *)addViewForExposeController:(LIExposeController *)exposeController {
+ UIView *addView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:ADD_BUTTON_IMAGE]] autorelease];
+ return addView;
+}
+
+- (UIView *)exposeController:(LIExposeController *)exposeController overlayViewForViewController:(UIViewController *)viewController {
+ if ([viewController isKindOfClass:[UINavigationController class]]) {
+ viewController = [(UINavigationController *)viewController topViewController];
+ }
+ if ([viewController isKindOfClass:[EDViewController class]]) {
+ UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(0,
+ 0,
+ viewController.view.bounds.size.width,
+ viewController.view.bounds.size.height)] autorelease];
+ label.backgroundColor = [UIColor colorWithWhite:0 alpha:0.4];
+ label.text = viewController.title;
+ label.textColor = [UIColor whiteColor];
+ label.textAlignment = UITextAlignmentCenter;
+ label.font = [UIFont boldSystemFontOfSize:48];
+ label.adjustsFontSizeToFitWidth = YES;
+ label.shadowColor = [UIColor blackColor];
+ label.shadowOffset = CGSizeMake(1, 1);
+ return label;
+ } else {
+ return nil;
+ }
+}
+
+- (UILabel *)exposeController:(LIExposeController *)exposeController labelForViewController:(UIViewController *)viewController {
+ if ([viewController isKindOfClass:[UINavigationController class]]) {
+ viewController = [(UINavigationController *)viewController topViewController];
+ }
+ if ([viewController isKindOfClass:[EDViewController class]]) {
+ UILabel *label = [[[UILabel alloc] init] autorelease];
+ label.backgroundColor = [UIColor clearColor];
+ label.text = viewController.title;
+ label.textColor = [UIColor whiteColor];
+ label.textAlignment = UITextAlignmentCenter;
+ label.font = [UIFont boldSystemFontOfSize:14];
+ label.shadowColor = [UIColor blackColor];
+ label.shadowOffset = CGSizeMake(1, 1);
+ [label sizeToFit];
+ CGRect frame = label.frame;
+ frame.origin.y = 4;
+ label.frame = frame;
+ return label;
+ } else {
+ return nil;
+ }
+}
+
+/**
+ Optional Header View
+ */
+//- (UIView *)headerViewForExposeController:(LIExposeController *)exposeController {
+// UINavigationBar *headerBar = [[[UINavigationBar alloc] initWithFrame:CGRectMake(0,
+// 0,
+// exposeController.view.frame.size.width,
+// 44)] autorelease];
+// UILabel *titleView = [[[UILabel alloc] init] autorelease];
+// titleView.backgroundColor = [UIColor clearColor];
+// titleView.text = NSLocalizedString(@"expose_title", @"expose_title");
+// titleView.textColor = [UIColor whiteColor];
+// titleView.shadowColor = [UIColor darkGrayColor];
+// titleView.shadowOffset = CGSizeMake(0, -0.5);
+// titleView.userInteractionEnabled = YES;
+// titleView.font = [UIFont boldSystemFontOfSize:20];
+// [titleView sizeToFit];
+// UITapGestureRecognizer *exposeGesture = [[[UITapGestureRecognizer alloc] initWithTarget:exposeController action:@selector(toggleExpose)] autorelease];
+// [titleView addGestureRecognizer:exposeGesture];
+// UINavigationItem *navItem = [[[UINavigationItem alloc] init] autorelease];
+// navItem.titleView = titleView;
+// headerBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
+// headerBar.autoresizingMask |= UIViewAutoresizingFlexibleHeight;
+// }
+// headerBar.items = [NSArray arrayWithObject:navItem];
+// return headerBar;
+//}
+
+/**
+ Optional Footer View
+ */
+//- (UIView *)footerViewForExposeController:(LIExposeController *)exposeController {
+// UIToolbar *toolBar = [[[UIToolbar alloc] initWithFrame:CGRectMake(0,
+// 0,
+// exposeController.view.frame.size.width,
+// 44)] autorelease];
+// toolBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
+// toolBar.autoresizingMask |= UIViewAutoresizingFlexibleHeight;
+// }
+// toolBar.items = [NSArray arrayWithObject:exposeController.editButtonItem];
+// return toolBar;
+//}
+
+#pragma mark - Helper Methods
+
+- (UIViewController *)newViewControllerForExposeController:(LIExposeController *)exposeController {
+ UIViewController *vc = [[[EDViewController alloc] init] autorelease];
+ vc.title = [NSString stringWithFormat:NSLocalizedString(@"view_title_format_string", @"view_title_format_string"), _viewControllerId];
+ _viewControllerId++;
+ return [[[UINavigationController alloc] initWithRootViewController:vc] autorelease];
+}
+
+#pragma mark - UIApplicationDelegate Methods
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ LIExposeController *exposeController = [[[LIExposeController alloc] init] autorelease];
+ exposeController.exposeDelegate = self;
+ exposeController.exposeDataSource = self;
+ exposeController.editing = YES;
+
+ exposeController.viewControllers = [NSMutableArray arrayWithObjects:
+ [self newViewControllerForExposeController:exposeController],
+ [self newViewControllerForExposeController:exposeController],
+ [self newViewControllerForExposeController:exposeController],
+ nil];
+
+ self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
+ self.window.rootViewController = exposeController;
+ [self.window makeKeyAndVisible];
+ return YES;
+}
+
+- (void)dealloc
+{
+ [_window release];
+ [super dealloc];
+}
+
+@end
21 Demo/Demo/EDDetailViewController.h
@@ -0,0 +1,21 @@
+/*
+ Copyright 2012 LinkedIn, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "LIExposeController.h"
+
+@interface EDDetailViewController : UIViewController <LIExposeControllerChildViewControllerDelegate>
+
+@end
77 Demo/Demo/EDDetailViewController.m
@@ -0,0 +1,77 @@
+/*
+ Copyright 2012 LinkedIn, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "EDDetailViewController.h"
+
+@implementation EDDetailViewController
+
+- (void)loadView {
+ [super loadView];
+ self.view.backgroundColor = [UIColor blueColor];
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
+ return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ self.title = NSLocalizedString(@"detail_title", @"detail_title");
+ self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"expose_title", @"expose_title")
+ style:UIBarButtonItemStyleBordered
+ target:self.navigationController.exposeController
+ action:@selector(toggleExpose)] autorelease];
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
+ NSLog(@"viewWillAppear:%d, %@", animated, self);
+}
+
+- (void)viewDidAppear:(BOOL)animated {
+ [super viewDidAppear:animated];
+ NSLog(@"viewDidAppear:%d, %@", animated, self);
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+ [super viewWillDisappear:animated];
+ NSLog(@"viewWillDisappear:%d, %@", animated, self);
+}
+
+- (void)viewDidDisappear:(BOOL)animated {
+ [super viewDidDisappear:animated];
+ NSLog(@"viewDidDisappear:%d, %@", animated, self);
+}
+
+- (void)viewWillShrinkInExposeController:(LIExposeController *)exposeController animated:(BOOL)animated {
+ NSLog(@"viewWillShrinkInExposeController:%d, %@", animated, self);
+ [self.navigationController setNavigationBarHidden:YES animated:animated];
+}
+
+- (void)viewDidShrinkInExposeController:(LIExposeController *)exposeController animated:(BOOL)animated {
+ NSLog(@"viewDidShrinkInExposeController:%d, %@", animated, self);
+}
+
+- (void)viewWillExpandInExposeController:(LIExposeController *)exposeController animated:(BOOL)animated {
+ NSLog(@"viewWillExpandInExposeController:%d, %@", animated, self);
+ [self.navigationController setNavigationBarHidden:NO animated:animated];
+}
+
+- (void)viewDidExpandInExposeController:(LIExposeController *)exposeController animated:(BOOL)animated {
+ NSLog(@"viewDidExpandInExposeController:%d, %@", animated, self);
+}
+
+@end
22 Demo/Demo/EDViewController.h
@@ -0,0 +1,22 @@
+/*
+ Copyright 2012 LinkedIn, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "LIExposeController.h"
+
+@interface EDViewController : UIViewController <LIExposeControllerChildViewControllerDelegate> {
+}
+
+@end
107 Demo/Demo/EDViewController.m
@@ -0,0 +1,107 @@
+/*
+ Copyright 2012 LinkedIn, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "EDViewController.h"
+#import "EDDetailViewController.h"
+
+@interface EDViewController (Private)
+
+- (void)setupDetailButton;
+- (void)pushDetailViewController:(id)sender;
+
+@end
+
+@implementation EDViewController
+
+- (void)loadView {
+ [super loadView];
+ self.view.backgroundColor = [UIColor redColor];
+
+ // Optional button to demonstrate navigation stack
+// [self setupDetailButton];
+}
+
+- (void)setupDetailButton {
+ UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
+ [button setTitle:NSLocalizedString(@"detail_button_title", @"detail_button_title")
+ forState:UIControlStateNormal];
+ [button sizeToFit];
+ button.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
+ button.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin|
+ UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin;
+ [button addTarget:self action:@selector(pushDetailViewController:) forControlEvents:UIControlEventTouchUpInside];
+ [self.view addSubview:button];
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
+ // Return YES for supported orientations
+ return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ self.navigationController.navigationBarHidden = YES;
+ self.navigationController.toolbarHidden = YES;
+ self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"expose_title", @"expose_title")
+ style:UIBarButtonItemStyleBordered
+ target:self.navigationController.exposeController
+ action:@selector(toggleExpose)] autorelease];
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
+ NSLog(@"viewWillAppear:%d, %@", animated, self);
+}
+
+- (void)viewDidAppear:(BOOL)animated {
+ [super viewDidAppear:animated];
+ NSLog(@"viewDidAppear:%d, %@", animated, self);
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+ [super viewWillDisappear:animated];
+ NSLog(@"viewWillDisappear:%d, %@", animated, self);
+}
+
+- (void)viewDidDisappear:(BOOL)animated {
+ [super viewDidDisappear:animated];
+ NSLog(@"viewDidDisappear:%d, %@", animated, self);
+}
+
+- (void)viewWillShrinkInExposeController:(LIExposeController *)exposeController animated:(BOOL)animated {
+ NSLog(@"viewWillShrinkInExposeController:%d, %@", animated, self);
+ [self.navigationController setNavigationBarHidden:YES animated:animated];
+}
+
+- (void)viewDidShrinkInExposeController:(LIExposeController *)exposeController animated:(BOOL)animated {
+ NSLog(@"viewDidShrinkInExposeController:%d, %@", animated, self);
+}
+
+- (void)viewWillExpandInExposeController:(LIExposeController *)exposeController animated:(BOOL)animated {
+ NSLog(@"viewWillExpandInExposeController:%d, %@", animated, self);
+ [self.navigationController setNavigationBarHidden:NO animated:animated];
+}
+
+- (void)viewDidExpandInExposeController:(LIExposeController *)exposeController animated:(BOOL)animated {
+ NSLog(@"viewDidExpandInExposeController:%d, %@", animated, self);
+}
+
+- (void)pushDetailViewController:(id)sender {
+ EDDetailViewController *detailViewController = [[[EDDetailViewController alloc] init] autorelease];
+ [self.navigationController pushViewController:detailViewController animated:YES];
+}
+
+@end
24 Demo/Demo/main.m
@@ -0,0 +1,24 @@
+/*
+ Copyright 2012 LinkedIn, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "EDAppDelegate.h"
+
+int main(int argc, char *argv[])
+{
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([EDAppDelegate class]));
+ }
+}
19 Demo/Resources/en.lproj/InfoPlist.strings
@@ -0,0 +1,19 @@
+/*
+ Copyright 2012 LinkedIn, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+/* Localized versions of Info.plist keys */
+
+CFBundleDisplayName = "ExposeDemo";
20 Demo/Resources/en.lproj/Localizable.strings
@@ -0,0 +1,20 @@
+/*
+ Copyright 2012 LinkedIn, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+"expose_title" = "Expose";
+"view_title_format_string" = "View %d";
+"detail_title" = "Detail";
+"detail_button_title" = "View Detail Screen";
BIN Demo/Resources/images/add.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN Demo/Resources/images/add@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN Demo/Resources/images/deleteBtn.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN Demo/Resources/images/deleteBtn@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN Demo/Resources/images/icon-iPad.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN Demo/Resources/images/icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN Demo/Resources/images/icon@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
176 LICENSE
@@ -0,0 +1,176 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
148 LIExposeController.h
@@ -0,0 +1,148 @@
+/*
+ Copyright 2012 LinkedIn, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+/**
+
+ Authors:
+ Sudeep Yegnashankaran, Peter Shih
+
+ Frameworks Required:
+ 1. UIKit
+ 2. Foundation
+ 3. QuartzCore
+ 4. CoreGraphics
+
+ Instructions:
+ 1) Add LIExposeController.h and LIExposeController.m to your Xcode project.
+ 2) Create an instance like so: exposeController = [[LIExposeController alloc] init]
+ 3) Add your view controllers: exposeController.viewControllers = [NSArray arrayWithObjects:..., nil];
+ 4) Add expose controller to your view hierarchy: window.rootViewController = exposeController
+ 5) Enjoy!
+
+ */
+
+#import <UIKit/UIKit.h>
+
+@protocol LIExposeControllerDelegate;
+@protocol LIExposeControllerDataSource;
+@protocol LIExposeControllerChildViewControllerDelegate;
+
+
+@interface LIExposeController : UIViewController <UIGestureRecognizerDelegate, UIScrollViewDelegate>
+
+/**
+ Initialization and Setup Methods
+ */
+- (id)init;
+@property (nonatomic, retain) NSMutableArray *viewControllers;
+@property (nonatomic, assign) id<LIExposeControllerDelegate> exposeDelegate;
+@property (nonatomic, assign) id<LIExposeControllerDataSource> exposeDataSource;
+
+/**
+ Display Configuration Methods
+ */
+@property (nonatomic, assign) CGFloat cornerRadius;
+@property (nonatomic, assign) CGFloat scaleFactor;
+@property (nonatomic, assign) CGFloat rowOffset;
+@property (nonatomic, assign) BOOL showsTouchDown;
+@property (nonatomic, assign) CGFloat animationDuration;
+@property (nonatomic, assign) NSUInteger numRows;
+@property (nonatomic, assign) NSUInteger numCols;
+- (void)setNumRows:(NSUInteger)numRows animated:(BOOL)animated;
+- (void)setNumCols:(NSUInteger)numCols animated:(BOOL)animated;
+
+/**
+ Toggles Between the Expanded/Collapsed State for Expose
+ */
+- (void)toggleExpose;
+- (void)toggleExpose:(BOOL)animated;
+
+/**
+ Adds a New View Controller to the List
+ */
+- (void)addNewViewController:(UIViewController *)viewController animated:(BOOL)animated;
+
+/**
+ Check the Current State
+ */
+@property (nonatomic, readonly) BOOL isZoomedOut;
+@property (nonatomic, readonly) NSUInteger selectedIndex;
+@property (nonatomic, readonly) UIViewController *selectedViewController;
+
+/**
+ Access the Header and Footer Views
+ */
+@property (nonatomic, readonly, retain) UIView *headerView;
+@property (nonatomic, readonly, retain) UIView *footerView;
+
+@end
+
+
+/**
+ Delegate Protocol for LIExposeController
+ */
+@protocol LIExposeControllerDelegate <NSObject>
+
+@optional
+- (BOOL)canAddViewControllersForExposeController:(LIExposeController *)exposeController;
+- (void)shouldAddViewControllerForExposeController:(LIExposeController *)exposeController;
+- (void)exposeControllerWillZoomOut:(LIExposeController *)exposeController animated:(BOOL)animated;
+- (void)exposeControllerDidZoomOut:(LIExposeController *)exposeController animated:(BOOL)animated;
+- (void)exposeControllerWillZoomIn:(LIExposeController *)exposeController animated:(BOOL)animated;
+- (void)exposeControllerDidZoomIn:(LIExposeController *)exposeController animated:(BOOL)animated;
+- (void)exposeController:(LIExposeController *)exposeController willSwitchFromViewController:(UIViewController *)oldViewController toViewController:(UIViewController *)newViewController;
+- (void)exposeController:(LIExposeController *)exposeController didSelectViewController:(UIViewController *)viewController;
+- (BOOL)exposeController:(LIExposeController *)exposeController canDeleteViewController:(UIViewController *)viewController;
+- (void)exposeController:(LIExposeController *)exposeController didDeleteViewController:(UIViewController *)viewController atIndex:(NSUInteger)index;
+@end
+
+
+/**
+ Data Source Protocol for LIExposeController
+ */
+@protocol LIExposeControllerDataSource <NSObject>
+
+@optional
+- (UIView *)headerViewForExposeController:(LIExposeController *)exposeController;
+- (UIView *)footerViewForExposeController:(LIExposeController *)exposeController;
+- (UIView *)backgroundViewForExposeController:(LIExposeController *)exposeController;
+- (UIView *)addViewForExposeController:(LIExposeController *)exposeController;
+- (UILabel *)exposeController:(LIExposeController *)exposeController labelForViewController:(UIViewController *)viewController;
+- (UIView *)exposeController:(LIExposeController *)exposeController overlayViewForViewController:(UIViewController *)viewController;
+@end
+
+
+/**
+ Delegate Protocol for Child Views of LIExposeController
+ */
+@protocol LIExposeControllerChildViewControllerDelegate <NSObject>
+
+@optional
+- (void)viewWillShrinkInExposeController:(LIExposeController *)exposeController animated:(BOOL)animated;
+- (void)viewDidShrinkInExposeController:(LIExposeController *)exposeController animated:(BOOL)animated;
+- (void)viewWillExpandInExposeController:(LIExposeController *)exposeController animated:(BOOL)animated;
+- (void)viewDidExpandInExposeController:(LIExposeController *)exposeController animated:(BOOL)animated;
+@end
+
+
+/**
+ Easy Access Property for View Controllers to Access a Parent Expose Controller
+ */
+@interface UIViewController (LIExposeController)
+
+@property (nonatomic, readonly, retain) LIExposeController *exposeController;
+
+@end
1,035 LIExposeController.m
@@ -0,0 +1,1035 @@
+/*
+ Copyright 2012 LinkedIn, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "LIExposeController.h"
+#import <QuartzCore/QuartzCore.h>
+#import <objc/runtime.h>
+
+NSString * const DELETE_BUTTON_IMAGE = @"deleteBtn.png";
+
+
+/**
+ Methods are named this way to avoid potential conflicts with anyone else
+ who may have created a category of UIView
+ */
+@interface UIView (Expose_Additions)
+
+@property(nonatomic) CGFloat exposeLeft;
+@property(nonatomic) CGFloat exposeTop;
+@property(nonatomic,readonly) CGFloat exposeRight;
+@property(nonatomic,readonly) CGFloat exposeBottom;
+@property(nonatomic) CGFloat exposeWidth;
+@property(nonatomic) CGFloat exposeHeight;
+
+@end
+
+
+@interface UIViewController (LIExposeController_Private)
+
+@property (nonatomic, retain) LIExposeController *exposeController;
+
+@end
+
+
+@interface LIExposeController ()
+
+/**
+ Subviews
+ */
+@property (nonatomic, retain) UIView *headerView;
+@property (nonatomic, retain) UIView *footerView;
+@property (nonatomic, retain) UIScrollView *scrollView;
+@property (nonatomic, retain) UIPageControl *pageControl;
+@property (nonatomic, retain) UIView *addChildButton;
+@property (nonatomic, retain) UIView *backgroundView;
+// Element is a button/NSNull used to delete its respective child view controller
+@property (nonatomic, retain) NSMutableArray *deleteButtons;
+// Element is a view containing overlay, label and has a tap gesture
+@property (nonatomic, retain) NSMutableArray *containerViews;
+
+/**
+ Current State
+ */
+@property (nonatomic, assign) BOOL isZoomedOut;
+@property (nonatomic, assign) NSUInteger selectedIndex;
+@property (nonatomic, assign) UIViewController *selectedViewController;
+@property (nonatomic, readonly) UIViewController *selectedContentViewController;
+@property (nonatomic, readonly) NSUInteger numPerPage;
+@property (nonatomic, assign) NSUInteger currentPage;
+
+/**
+ Setting Up View Hierarchy
+ */
+- (void)setupContentView;
+- (void)setupBackgroundView;
+- (void)setupViewControllers;
+
+/**
+ Layout
+ */
+- (void)layoutGrid:(BOOL)animated completion:(void (^)(BOOL finished))completion;
+- (void)calculateContentSize;
+- (CGPoint)centerWithIndex:(NSUInteger)index;
+- (void)setPage;
+- (void)setExposeZoomedOut:(BOOL)zoomedOut animated:(BOOL)animated;
+
+/**
+ Helpers
+ */
+- (BOOL)isPad;
+- (void)newViewController:(UIViewController *)viewController index:(NSUInteger)index;
++ (UIViewController *)getContentViewControllerFromContainer:(UIViewController *)containerViewController;
+- (void)bounceView:(UIView *)view;
+
+/**
+ Gesture Actions
+ */
+- (void)toggleGestureRecognizer:(BOOL)toggle forView:(UIView *)view;
+- (void)selectView:(UITapGestureRecognizer *)gestureRecognizer;
+- (void)selectViewController:(UIViewController *)viewController;
+- (void)selectViewControllerAtIndex:(NSUInteger)index;
+- (void)addViewController:(UITapGestureRecognizer *)gestureRecognizer;
+- (void)deleteViewController:(id)sender;
+- (void)keepButtonHighlighted:(UIButton *)button;
+
+/**
+ Zooming Callbacks
+ */
+- (void)exposeZoomedOut:(BOOL)animated;
+- (void)exposeZoomedIn:(BOOL)animated;
+
+@end
+
+
+@implementation LIExposeController
+
+@synthesize viewControllers=_viewControllers;
+@synthesize selectedIndex=_selectedIndex;
+@synthesize exposeDelegate=_exposeDelegate;
+@synthesize exposeDataSource=_exposeDataSource;
+@synthesize isZoomedOut=_isZoomedOut;
+@synthesize numRows=_numRows;
+@synthesize numCols=_numCols;
+@synthesize cornerRadius=_cornerRadius;
+@synthesize scaleFactor=_scaleFactor;
+@synthesize showsTouchDown=_showsTouchDown;
+@synthesize animationDuration=_animationDuration;
+
+@synthesize headerView=_headerView;
+@synthesize footerView=_footerView;
+@synthesize scrollView=_scrollView;
+@synthesize pageControl=_pageControl;
+@synthesize addChildButton=_addChildButton;
+@synthesize backgroundView=_backgroundView;
+@synthesize deleteButtons=_deleteButtons;
+@synthesize selectedViewController=_selectedViewController;
+@synthesize selectedContentViewController=_selectedContentViewController;
+@synthesize rowOffset=_rowOffset;
+@synthesize numPerPage=_numPerPage;
+@synthesize currentPage=_currentPage;
+@synthesize containerViews=_containerViews;
+
+#pragma mark - Initialization/Deallocation Methods
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ _deleteButtons = [[NSMutableArray array] retain];
+ _containerViews = [[NSMutableArray array] retain];
+ _viewControllers = nil;
+ _selectedViewController = nil;
+ _selectedContentViewController = nil;
+ _selectedIndex = 0;
+ _exposeDelegate = nil;
+ _exposeDataSource = nil;
+ _isZoomedOut = NO;
+ _currentPage = 0;
+ _cornerRadius = 20.0;
+ _animationDuration = 0.3;
+ if ([self isPad]) {
+ _numRows = 3;
+ _numCols = 3;
+ _scaleFactor = 0.28;
+ _rowOffset = -10.0;
+ } else {
+ _numRows = 2;
+ _numCols = 2;
+ _scaleFactor = 0.36;
+ _rowOffset = -10.0;
+ }
+ _numPerPage = _numRows * _numCols;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ self.addChildButton = nil;
+ self.headerView = nil;
+ self.footerView = nil;
+ self.scrollView = nil;
+ self.pageControl = nil;
+ self.backgroundView = nil;
+ self.viewControllers = nil;
+ self.deleteButtons = nil;
+ self.containerViews = nil;
+
+ [super dealloc];
+}
+
+#pragma mark - Setters
+
+- (void)setViewControllers:(NSMutableArray *)controllers {
+ // Clear Arrays
+ NSUInteger index = 0;
+ for (UIViewController *viewController in _viewControllers) {
+ viewController.exposeController = nil;
+ [viewController.view removeFromSuperview];
+ [(UIView *)[self.containerViews objectAtIndex:index] removeFromSuperview];
+ id deleteButton = [self.deleteButtons objectAtIndex:index];
+ if (deleteButton != [NSNull null]) {
+ [(UIView *)deleteButton removeFromSuperview];
+ }
+ index++;
+ }
+ [_viewControllers autorelease];
+ [self.deleteButtons removeAllObjects];
+ [self.containerViews removeAllObjects];
+
+ _viewControllers = [controllers retain];
+ self.selectedViewController = nil;
+ self.selectedIndex = 0;
+ if (_viewControllers.count > self.selectedIndex) {
+ self.selectedViewController = [_viewControllers objectAtIndex:self.selectedIndex];
+ }
+
+ if ([self isViewLoaded]) {
+ [self setupViewControllers];
+ if (self.isZoomedOut) {
+ [self layoutGrid:NO completion:nil];
+ }
+ [self setExposeZoomedOut:self.isZoomedOut animated:NO];
+ }
+}
+
+- (void)setNumRows:(NSUInteger)rows {
+ [self setNumRows:rows animated:NO];
+}
+
+- (void)setNumCols:(NSUInteger)cols {
+ [self setNumCols:cols animated:NO];
+}
+
+- (void)setNumRows:(NSUInteger)rows animated:(BOOL)animated {
+ _numRows = rows;
+ _numPerPage = _numRows * _numCols;
+ if (self.isZoomedOut) {
+ [self layoutGrid:animated completion:nil];
+ }
+}
+
+- (void)setNumCols:(NSUInteger)cols animated:(BOOL)animated {
+ _numCols = cols;
+ _numPerPage = _numRows * _numCols;
+ if (self.isZoomedOut) {
+ [self layoutGrid:animated completion:nil];
+ }
+}
+
+- (void)setCornerRadius:(CGFloat)radius {
+ _cornerRadius = radius;
+
+ if ([self isViewLoaded]) {
+ if (self.isZoomedOut) {
+ for (UIViewController *viewController in self.viewControllers) {
+ viewController.view.layer.cornerRadius = _cornerRadius;
+ }
+ }
+
+ for (UIView *containerView in self.containerViews) {
+ if (containerView.subviews.count) {
+ ((UIView *)[containerView.subviews objectAtIndex:0]).layer.cornerRadius = _cornerRadius * self.scaleFactor;
+ }
+ }
+ }
+}
+
+- (void)setScaleFactor:(CGFloat)scale {
+ _scaleFactor = scale;
+
+ if ([self isViewLoaded] && self.isZoomedOut) {
+ self.addChildButton.transform = CGAffineTransformMakeScale(_scaleFactor, _scaleFactor);
+ for (UIView *containerView in self.containerViews) {
+ if (containerView.subviews.count) {
+ ((UIView *)[containerView.subviews objectAtIndex:0]).layer.cornerRadius = _cornerRadius * self.scaleFactor;
+ }
+ }
+ [self layoutGrid:NO completion:nil];
+ }
+}
+
+- (void)setRowOffset:(CGFloat)offset {
+ _rowOffset = offset;
+
+ if ([self isViewLoaded] && self.isZoomedOut) {
+ [self layoutGrid:NO completion:nil];
+ }
+}
+
+#pragma mark - Getters
+
+- (UIViewController *)selectedContentViewController {
+ return [[self class] getContentViewControllerFromContainer:self.selectedViewController];
+}
+
+- (UIView *)addChildButton {
+ if (!_addChildButton) {
+ if ([self.exposeDelegate respondsToSelector:@selector(canAddViewControllersForExposeController:)]) {
+ if ([self.exposeDelegate canAddViewControllersForExposeController:self]) {
+ if ([self.exposeDataSource respondsToSelector:@selector(addViewForExposeController:)]) {
+ _addChildButton = [[self.exposeDataSource addViewForExposeController:self] retain];
+ _addChildButton.transform = CGAffineTransformMakeScale(self.scaleFactor, self.scaleFactor);
+ _addChildButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin|
+ UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin;
+ _addChildButton.userInteractionEnabled = YES;
+ NSUInteger addIndex = self.viewControllers.count;
+ _addChildButton.center = [self centerWithIndex:addIndex];
+
+ // Add gesture
+ UITapGestureRecognizer *addGesture = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(addViewController:)] autorelease];
+ addGesture.numberOfTapsRequired = 1;
+ [_addChildButton addGestureRecognizer:addGesture];
+ }
+ }
+ }
+ }
+ return _addChildButton;
+}
+
+#pragma mark - View Hierarchy
+
+- (void)loadView {
+ [super loadView];
+ self.view.autoresizingMask = ~UIViewAutoresizingNone;
+ [self setupContentView];
+ [self setupViewControllers];
+ [self setupBackgroundView];
+ [self layoutGrid:NO completion:nil];
+ [self setExposeZoomedOut:NO animated:NO];
+}
+
+#pragma mark - View Setup
+
+- (void)setupContentView {
+ // Header View
+ if ([self.exposeDataSource respondsToSelector:@selector(headerViewForExposeController:)]) {
+ self.headerView = [self.exposeDataSource headerViewForExposeController:self];
+ self.headerView.autoresizingMask |= UIViewAutoresizingFlexibleBottomMargin;
+ [self.view addSubview:self.headerView];
+ }
+
+ // Footer View
+ if ([self.exposeDataSource respondsToSelector:@selector(footerViewForExposeController:)]) {
+ self.footerView = [self.exposeDataSource footerViewForExposeController:self];
+ self.footerView.exposeTop = self.view.exposeHeight - self.footerView.exposeHeight;
+ self.footerView.autoresizingMask |= UIViewAutoresizingFlexibleTopMargin;
+ [self.view addSubview:self.footerView];
+ }
+
+ // Scroll View
+ self.scrollView = [[[UIScrollView alloc] initWithFrame:CGRectMake(0, self.headerView.exposeHeight, self.view.exposeWidth, self.view.exposeHeight - self.headerView.exposeHeight - self.footerView.exposeHeight)] autorelease];
+ self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|
+ UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin;
+ if (self.headerView.autoresizingMask & UIViewAutoresizingFlexibleHeight) {
+ self.scrollView.autoresizingMask |= UIViewAutoresizingFlexibleTopMargin;
+ }
+ if (self.footerView.autoresizingMask & UIViewAutoresizingFlexibleHeight) {
+ self.scrollView.autoresizingMask |= UIViewAutoresizingFlexibleBottomMargin;
+ }
+ self.scrollView.delegate = self;
+ self.scrollView.scrollEnabled = NO;
+ self.scrollView.pagingEnabled = YES;
+ self.scrollView.showsVerticalScrollIndicator = NO;
+ self.scrollView.showsHorizontalScrollIndicator = NO;
+ self.scrollView.scrollsToTop = NO;
+ self.scrollView.backgroundColor = [UIColor clearColor];
+ [self.view addSubview:self.scrollView];
+
+ //Add page control
+ self.pageControl = [[[UIPageControl alloc] init] autorelease];
+ self.pageControl.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|
+ UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin|
+ UIViewAutoresizingFlexibleTopMargin;
+ if (self.footerView.autoresizingMask & UIViewAutoresizingFlexibleHeight) {
+ self.pageControl.autoresizingMask |= UIViewAutoresizingFlexibleBottomMargin;
+ }
+ self.pageControl.hidesForSinglePage = YES;
+ self.pageControl.center = CGPointMake(floorf(self.view.exposeWidth / 2),
+ self.scrollView.exposeBottom - 10);
+ self.pageControl.alpha = 0.0;
+ [self.view addSubview:self.pageControl];
+
+ [self.scrollView addSubview:self.addChildButton];
+}
+
+- (void)setupBackgroundView {
+ if ([self.exposeDataSource respondsToSelector:@selector(backgroundViewForExposeController:)]) {
+ [self.backgroundView removeFromSuperview];
+ self.backgroundView = [self.exposeDataSource backgroundViewForExposeController:self];
+ self.backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
+ [self.view insertSubview:self.backgroundView atIndex:0];
+ }
+}
+
+- (void)setupViewControllers {
+ NSUInteger i = 0;
+ for (UIViewController *viewController in self.viewControllers) {
+ [self newViewController:viewController index:i];
+ i++;
+ }
+
+ [self calculateContentSize];
+}
+
+- (CGPoint)centerWithIndex:(NSUInteger)index {
+ CGFloat page = index / self.numPerPage;
+ CGFloat col = (index % self.numCols);
+ CGFloat row = (index % self.numPerPage) / self.numCols;
+
+ CGFloat left = 0;
+ CGFloat top = 0;
+ CGPoint c = CGPointZero;
+ CGFloat width = floorf(self.scrollView.exposeWidth * self.scaleFactor);
+ CGFloat height = floorf((self.scrollView.exposeHeight - self.pageControl.exposeHeight) * self.scaleFactor);
+ CGFloat colSpacing = floorf((self.scrollView.exposeWidth - (self.numCols * width)) / (self.numCols + 1));
+ CGFloat rowSpacing = floorf((self.scrollView.exposeHeight - (self.numRows * height)) / (self.numRows + 1));
+
+ left = colSpacing + (col * colSpacing) + (col * width) + (page * self.scrollView.exposeWidth);
+ top = rowSpacing + (row * rowSpacing) + (row * height) + self.rowOffset;
+ c = CGPointMake(left + floorf(width / 2), top + floorf(height / 2));
+
+ return c;
+}
+
+- (void)newViewController:(UIViewController *)viewController index:(NSUInteger)index {
+ // Real View
+ viewController.view.frame = self.scrollView.bounds;
+ viewController.view.autoresizingMask = ~UIViewAutoresizingNone;
+
+ // Apply Transforms to real view
+ CGPoint c = [self centerWithIndex:index];
+
+ CGFloat tx = c.x - viewController.view.center.x;
+ CGFloat ty = c.y - viewController.view.center.y;
+ CGAffineTransform t = CGAffineTransformMakeTranslation(tx, ty);
+ t = CGAffineTransformScale(t, self.scaleFactor, self.scaleFactor);
+ viewController.view.transform = t;
+ [self.scrollView addSubview:viewController.view];
+
+ // Add container view
+ UIView *containerView = [[[UIView alloc] initWithFrame:viewController.view.bounds] autorelease];
+ containerView.autoresizingMask = viewController.view.autoresizingMask;
+ containerView.clipsToBounds = NO;
+ containerView.frame = viewController.view.frame;
+ containerView.alpha = self.editing ? 1.0 : 0.0;
+ [self.containerViews addObject:containerView];
+ [self.scrollView addSubview:containerView];
+
+ // Tap to select gesture
+ UITapGestureRecognizer *selectGesture = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(selectView:)] autorelease];
+ selectGesture.enabled = NO;
+ selectGesture.delegate = self;
+ [containerView addGestureRecognizer:selectGesture];
+
+ // Optional Overlay View
+ if ([self.exposeDataSource respondsToSelector:@selector(exposeController:overlayViewForViewController:)]) {
+ UIView *overlayView = [self.exposeDataSource exposeController:self overlayViewForViewController:viewController];
+ overlayView.autoresizingMask = containerView.autoresizingMask;
+ overlayView.layer.cornerRadius = self.cornerRadius * self.scaleFactor;
+ overlayView.frame = containerView.bounds;
+ [containerView addSubview:overlayView];
+ }
+
+ // Optional Label
+ if ([self.exposeDataSource respondsToSelector:@selector(exposeController:labelForViewController:)]) {
+ UILabel *label = [self.exposeDataSource exposeController:self labelForViewController:viewController];
+ label.autoresizingMask |= UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin|
+ UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin|
+ UIViewAutoresizingFlexibleHeight;
+ label.frame = CGRectMake((containerView.exposeWidth - label.exposeWidth) / 2, containerView.exposeHeight + label.exposeTop, label.exposeWidth, label.exposeHeight);
+ [containerView addSubview:label];
+ }
+
+ // Add delete buttons (optional)
+ if ([self.exposeDelegate respondsToSelector:@selector(exposeController:canDeleteViewController:)] &&
+ [self.exposeDelegate exposeController:self canDeleteViewController:viewController]) {
+ UIImage *deleteImage = [UIImage imageNamed:DELETE_BUTTON_IMAGE];
+ UIButton *deleteBtn = [[[UIButton alloc] initWithFrame:CGRectMake(containerView.exposeLeft - floorf(deleteImage.size.width / 2),
+ containerView.exposeTop - floorf(deleteImage.size.height / 2),
+ deleteImage.size.width,
+ deleteImage.size.height)] autorelease];
+ [deleteBtn setImage:deleteImage forState:UIControlStateNormal];
+ [deleteBtn addTarget:self action:@selector(deleteViewController:) forControlEvents:UIControlEventTouchUpInside];
+ deleteBtn.hidden = !self.editing;
+ deleteBtn.autoresizingMask = UIViewAutoresizingFlexibleRightMargin|UIViewAutoresizingFlexibleLeftMargin|
+ UIViewAutoresizingFlexibleBottomMargin|UIViewAutoresizingFlexibleTopMargin;
+ [self.scrollView addSubview:deleteBtn];
+ [self.deleteButtons addObject:deleteBtn];
+ }
+ else {
+ [self.deleteButtons addObject:[NSNull null]];
+ }
+}
+
+#pragma mark - Select
+
+- (void)selectView:(UITapGestureRecognizer *)gestureRecognizer {
+ NSUInteger index = [self.containerViews indexOfObject:gestureRecognizer.view];
+ [self selectViewControllerAtIndex:index];
+}
+
+- (void)selectViewController:(UIViewController *)viewController {
+ NSUInteger index = [self.viewControllers indexOfObject:viewController];
+ [self selectViewControllerAtIndex:index];
+}
+
+- (void)selectViewControllerAtIndex:(NSUInteger)index {
+ UIViewController *oldViewController = self.selectedViewController;
+ self.selectedIndex = index;
+ self.selectedViewController = [self.viewControllers objectAtIndex:self.selectedIndex];
+
+ // Will Switch Callback
+ if ([self.exposeDelegate respondsToSelector:@selector(exposeController:willSwitchFromViewController:toViewController:)]) {
+ [self.exposeDelegate exposeController:self
+ willSwitchFromViewController:oldViewController
+ toViewController:self.selectedViewController];
+ }
+
+ [self toggleExpose:YES]; // This will collapse spaces
+}
+
+#pragma mark - Add a new view controller
+
+- (void)addViewController:(id)sender {
+ if ([self.exposeDelegate respondsToSelector:@selector(shouldAddViewControllerForExposeController:)]) {
+ [self.exposeDelegate shouldAddViewControllerForExposeController:self];
+ }
+}
+
+- (void)addNewViewController:(UIViewController *)viewController animated:(BOOL)animated {
+ viewController.exposeController = self;
+ [self.viewControllers addObject:viewController];
+
+ NSUInteger i = self.viewControllers.count - 1;
+ [self newViewController:viewController index:i];
+ [self toggleGestureRecognizer:YES forView:self.containerViews.lastObject];
+ [self layoutGrid:animated completion:nil];
+}
+
+#pragma mark - Delete a view controller
+
+- (void)keepButtonHighlighted:(UIButton *)button {
+ button.highlighted = YES;
+ button.selected = YES;
+}
+
+- (void)deleteViewController:(id)sender {
+ if (![sender isKindOfClass:[UIButton class]]) {
+ return;
+ }
+
+ UIButton *button = (UIButton *)sender;
+ [self performSelector:@selector(keepButtonHighlighted:) withObject:button afterDelay:0];
+ NSUInteger deleteIndex = [self.deleteButtons indexOfObject:button];
+ UIViewController *viewController = [self.viewControllers objectAtIndex:deleteIndex];
+ UIView *containerView = [self.containerViews objectAtIndex:deleteIndex];
+
+ [UIView animateWithDuration:self.animationDuration
+ animations:^{
+ viewController.view.alpha = 0.0;
+ containerView.alpha = 0.0;
+ button.alpha = 0.0;
+ }
+ completion:^(BOOL finished) {
+ if ([self.exposeDelegate respondsToSelector:@selector(exposeController:didDeleteViewController:atIndex:)]) {
+ [self.exposeDelegate exposeController:self didDeleteViewController:viewController atIndex:deleteIndex];
+ }
+
+ [viewController.view removeFromSuperview];
+ [containerView removeFromSuperview];
+ [button removeFromSuperview];
+ viewController.exposeController = nil;
+ [self.viewControllers removeObject:viewController];
+ [self.deleteButtons removeObject:button];
+ [self.containerViews removeObject:containerView];
+
+ if ([viewController isEqual:self.selectedViewController]) {
+ self.selectedViewController = nil;
+ self.selectedIndex = 0;
+ if (self.viewControllers.count > self.selectedIndex) {
+ self.selectedViewController = [self.viewControllers objectAtIndex:self.selectedIndex];
+ }
+ }
+
+ [self layoutGrid:YES completion:nil];
+ }];
+}
+
+#pragma mark - Expose
+
+- (void)toggleExpose {
+ [self toggleExpose:YES];
+}
+
+- (void)toggleExpose:(BOOL)animated {
+ [self setExposeZoomedOut:!self.isZoomedOut animated:animated];
+}
+
+- (void)setExposeZoomedOut:(BOOL)zoomedOut animated:(BOOL)animated {
+ CGFloat animateDuration = animated ? self.animationDuration : 0.0;
+ self.isZoomedOut = zoomedOut;
+
+ if (self.isZoomedOut) {
+ // Will Shrink Callback
+ if ([self.selectedContentViewController conformsToProtocol:@protocol(LIExposeControllerChildViewControllerDelegate)] &&
+ [self.selectedContentViewController respondsToSelector:@selector(viewWillShrinkInExposeController:animated:)]) {
+ [(id<LIExposeControllerChildViewControllerDelegate>)self.selectedContentViewController viewWillShrinkInExposeController:self animated:animated];
+ }
+
+ self.pageControl.alpha = 1.0;
+
+ // Will Zoom Out Callback
+ if ([self.exposeDelegate respondsToSelector:@selector(exposeControllerWillZoomOut:animated:)]) {
+ [self.exposeDelegate exposeControllerWillZoomOut:self animated:animated];
+ }
+
+ // Will Appear Callback
+ for (NSUInteger i = 0; i < self.viewControllers.count; i++) {
+ if (i != self.selectedIndex) {
+ UIViewController *vc = [self.viewControllers objectAtIndex:i];
+ [vc viewWillAppear:animated];
+ [self.scrollView sendSubviewToBack:vc.view];
+ }
+ }
+
+ // Perform Frame Adjustment
+ [self layoutGrid:animated completion:^(BOOL finished) {
+ [self exposeZoomedOut:animated];
+ }];
+
+ } else {
+ // Will Expand Callback
+ if ([self.selectedContentViewController conformsToProtocol:@protocol(LIExposeControllerChildViewControllerDelegate)] &&
+ [self.selectedContentViewController respondsToSelector:@selector(viewWillExpandInExposeController:animated:)]) {
+ [(id<LIExposeControllerChildViewControllerDelegate>)self.selectedContentViewController viewWillExpandInExposeController:self animated:animated];
+ }
+
+ self.pageControl.alpha = 0;
+
+ // Bring the selected view to the top of the stack
+ [self.scrollView bringSubviewToFront:self.selectedViewController.view];
+
+ // Will Zoom In Callback
+ if ([self.exposeDelegate respondsToSelector:@selector(exposeControllerWillZoomIn:animated:)]) {
+ [self.exposeDelegate exposeControllerWillZoomIn:self animated:animated];
+ }
+
+ // Perform Frame Adjustment
+ int i = 0;
+ for (UIViewController *viewController in self.viewControllers) {
+ UIView *containerView = [self.containerViews objectAtIndex:i];
+ UIButton *deleteButton = nil;
+ if (self.deleteButtons.count > 0) {
+ if ([self.deleteButtons objectAtIndex:i] != [NSNull null]) {
+ deleteButton = [self.deleteButtons objectAtIndex:i];
+ }
+ }
+
+ // Will Disappear Callback
+ if (![viewController isEqual:self.selectedViewController]) {
+ [viewController viewWillDisappear:animated];
+ }
+
+ // Animate (optional)
+ [UIView animateWithDuration:animateDuration
+ delay:0.0
+ options:UIViewAnimationOptionCurveEaseOut
+ animations:^{
+ containerView.alpha = 0.0;
+ deleteButton.alpha = 0.0;
+
+ if ([viewController isEqual:self.selectedViewController]) {
+ viewController.view.transform = CGAffineTransformIdentity;
+ viewController.view.layer.cornerRadius = 0;
+ viewController.view.frame = CGRectMake(self.pageControl.currentPage * self.scrollView.exposeWidth, 0, self.scrollView.exposeWidth, self.scrollView.exposeHeight);
+ } else {
+ viewController.view.alpha = 0.0;
+ }
+ }
+ completion:^(BOOL finished) {
+ self.scrollView.scrollEnabled = NO;
+ if ([viewController isEqual:self.selectedViewController]) {
+ [self exposeZoomedIn:animated];
+ } else {
+ [viewController viewDidDisappear:animated];
+ }
+ }];
+ i++;
+ }
+
+ // Did Select Callback
+ if ([self.exposeDelegate respondsToSelector:@selector(exposeController:didSelectViewController:)]) {
+ [self.exposeDelegate exposeController:self didSelectViewController:self.selectedViewController];
+ }
+ }
+}
+
+- (void)calculateContentSize {
+ NSInteger numPages = (NSInteger)ceilf((CGFloat)self.viewControllers.count / self.numPerPage);
+ if ([self.exposeDelegate respondsToSelector:@selector(canAddViewControllersForExposeController:)]) {
+ if ([self.exposeDelegate canAddViewControllersForExposeController:self]) {
+ numPages = (NSInteger)ceilf((CGFloat)(self.viewControllers.count + 1) / self.numPerPage);
+ }
+ }
+
+ self.scrollView.contentSize = CGSizeMake(self.scrollView.exposeWidth * numPages, self.scrollView.exposeHeight);
+ self.pageControl.numberOfPages = numPages;
+}
+
+- (void)layoutGrid:(BOOL)animated completion:(void (^)(BOOL finished))completion {
+ CGFloat animateDuration = animated ? self.animationDuration : 0.0;
+
+ // This is the currently selected view
+ NSUInteger i = 0;
+ for (UIViewController *viewController in self.viewControllers) {
+ // Real View
+ [viewController.view endEditing:YES];
+
+ // Container View
+ UIView *containerView = [self.containerViews objectAtIndex:i];
+ [self.scrollView bringSubviewToFront:containerView];
+
+ // Delete Button
+ UIButton *deleteButton = nil;
+ if (self.deleteButtons.count > 0) {
+ if ([self.deleteButtons objectAtIndex:i] != [NSNull null]) {
+ deleteButton = [self.deleteButtons objectAtIndex:i];
+ [self.scrollView bringSubviewToFront:deleteButton];
+ }
+ }
+
+ // Layout the grid
+ CGPoint c = [self centerWithIndex:i];
+
+ CGFloat tx = c.x - floorf(viewController.view.center.x);
+ CGFloat ty = c.y - floorf(viewController.view.center.y);
+
+ // Animate (optional)
+ [UIView animateWithDuration:animateDuration
+ delay:0.0
+ options:UIViewAnimationOptionCurveEaseOut
+ animations:^{
+ // Transforms
+ CGAffineTransform t = CGAffineTransformMakeTranslation(tx, ty);
+ t = CGAffineTransformScale(t, self.scaleFactor, self.scaleFactor);
+ viewController.view.transform = t;
+ viewController.view.layer.masksToBounds = YES;
+ viewController.view.layer.cornerRadius = self.cornerRadius;
+
+ viewController.view.alpha = 1.0;
+ containerView.alpha = 1.0;
+ containerView.frame = viewController.view.frame;
+ if (deleteButton) {
+ deleteButton.alpha = 1.0;
+ deleteButton.center = containerView.frame.origin;
+ }
+ }
+ completion:^(BOOL finished) {
+ self.scrollView.scrollEnabled = YES;
+ if ([viewController isEqual:self.selectedViewController]) {
+ if (completion) {
+ completion(finished);
+ }
+ }
+ }];
+ i++;
+ }
+
+ // Show Add View
+ [self.scrollView bringSubviewToFront:self.addChildButton];
+
+ // Adjust addChildButton position and frame
+ NSUInteger addIndex = self.viewControllers.count;
+ CGPoint c = [self centerWithIndex:addIndex];
+
+ [UIView animateWithDuration:animateDuration
+ delay:0.0
+ options:UIViewAnimationOptionCurveEaseOut
+ animations:^{
+ self.addChildButton.center = c;
+ }
+ completion:nil];
+
+ // Recalculate content size
+ [self calculateContentSize];
+}
+
+#pragma mark - Editing
+
+- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
+ [super setEditing:editing animated:animated];
+ for (id deleteButton in self.deleteButtons) {
+ if ([deleteButton isKindOfClass:[UIButton class]]) {
+ ((UIButton *)deleteButton).hidden = !editing;
+ }
+ }
+}
+
+#pragma mark - Gesture Recognizers
+
+- (void)toggleGestureRecognizer:(BOOL)toggle forView:(UIView *)view {
+ ((UIGestureRecognizer *)[view.gestureRecognizers objectAtIndex:0]).enabled = toggle;
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
+ return NO;
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
+ if(self.showsTouchDown && self.isZoomedOut && ![gestureRecognizer.view isKindOfClass:[UIImageView class]]) {
+ NSUInteger index = [self.containerViews indexOfObject:gestureRecognizer.view];
+ [self bounceView:((UIViewController *)[self.viewControllers objectAtIndex:index]).view];
+ }
+
+ if ([touch.view isKindOfClass:[UIButton class]]) {
+ return NO;
+ }
+ else {
+ return YES;
+ }
+}
+
+#pragma mark - Animations
+
+- (void)exposeZoomedOut:(BOOL)animated {
+ for (UIView *view in self.containerViews) {
+ [self toggleGestureRecognizer:YES forView:view];
+ }
+ [self bounceView:self.selectedViewController.view];
+ if ([self.selectedContentViewController conformsToProtocol:@protocol(LIExposeControllerChildViewControllerDelegate)] &&
+ [self.selectedContentViewController respondsToSelector:@selector(viewDidShrinkInExposeController:animated:)]) {
+ [(id<LIExposeControllerChildViewControllerDelegate>)self.selectedContentViewController viewDidShrinkInExposeController:self animated:animated];
+ }
+
+ if ([self.exposeDelegate respondsToSelector:@selector(exposeControllerDidZoomOut:animated:)]) {
+ [self.exposeDelegate exposeControllerDidZoomOut:self animated:animated];
+ }
+
+ for (UIViewController *viewController in self.viewControllers) {
+ if (![viewController isEqual:self.selectedViewController]) {
+ [viewController viewDidAppear:animated];
+ }
+ }
+}
+
+- (void)exposeZoomedIn:(BOOL)animated {
+ for (UIView *view in self.containerViews) {
+ [self toggleGestureRecognizer:NO forView:view];
+ }
+ self.selectedViewController.view.layer.masksToBounds = YES;
+ if ([self.selectedContentViewController conformsToProtocol:@protocol(LIExposeControllerChildViewControllerDelegate)] &&
+ [self.selectedContentViewController respondsToSelector:@selector(viewDidExpandInExposeController:animated:)]) {
+ [(id<LIExposeControllerChildViewControllerDelegate>)self.selectedContentViewController viewDidExpandInExposeController:self animated:animated];
+ }
+
+ if ([self.exposeDelegate respondsToSelector:@selector(exposeControllerDidZoomIn:animated:)]) {
+ [self.exposeDelegate exposeControllerDidZoomIn:self animated:animated];
+ }
+}
+
+#pragma mark - Rotation Methods
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
+ BOOL shouldRotate = YES;
+ for (UIViewController *viewController in self.viewControllers) {
+ shouldRotate &= [viewController shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
+ }
+ return shouldRotate;
+}
+
+- (void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
+ [super willAnimateFirstHalfOfRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
+ for (UIViewController *viewController in self.viewControllers) {
+ [viewController willAnimateFirstHalfOfRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
+ }
+}
+
+- (void)willAnimateSecondHalfOfRotationFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation duration:(NSTimeInterval)duration {
+ [super willAnimateSecondHalfOfRotationFromInterfaceOrientation:fromInterfaceOrientation duration:duration];
+ for (UIViewController *viewController in self.viewControllers) {
+ [viewController willAnimateSecondHalfOfRotationFromInterfaceOrientation:fromInterfaceOrientation duration:duration];
+ }
+}
+
+- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
+ [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
+ for (UIViewController *viewController in self.viewControllers) {
+ [viewController willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
+ }
+
+ if (self.isZoomedOut) {
+ [self layoutGrid:YES completion:nil];
+ }
+ self.scrollView.contentOffset = CGPointMake(self.scrollView.exposeWidth * self.currentPage, 0);
+}
+
+- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
+ [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
+ for (UIViewController *viewController in self.viewControllers) {
+ [viewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
+ }
+
+ self.currentPage = self.scrollView.contentOffset.x / self.scrollView.exposeWidth;
+}
+
+- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
+ [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
+ for (UIViewController *viewController in self.viewControllers) {
+ [viewController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
+ }
+
+ [self calculateContentSize];
+}
+
+#pragma mark - UIScrollViewDelegate Methods
+
+- (void)setPage {
+ NSInteger page = (self.scrollView.contentOffset.x + floorf(self.scrollView.exposeWidth / 2)) / self.scrollView.exposeWidth;
+ self.pageControl.currentPage = page;
+}
+
+- (void)scrollViewDidScroll:(UIScrollView *)scroller {
+ if ([scroller isEqual:self.scrollView]) {
+ [self setPage];
+ }
+}
+
+- (void)scrollViewDidEndDecelerating:(UIScrollView *)scroller {
+ if ([scroller isEqual:self.scrollView]) {
+ [self setPage];
+ }
+}
+
+#pragma mark - Helpers Methods
+
+- (BOOL)isPad {
+ return ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad);
+}
+
++ (UIViewController *)getContentViewControllerFromContainer:(UIViewController *)containerViewController {
+ if ([containerViewController isKindOfClass:[UINavigationController class]]) {
+ return [(UINavigationController *)containerViewController topViewController];
+ } else if ([containerViewController isKindOfClass:[UITabBarController class]]) {
+ return [(UITabBarController *)containerViewController selectedViewController];
+ } else {
+ return containerViewController;
+ }
+}
+
+- (void)bounceView:(UIView *)view {
+ CAKeyframeAnimation *bounceAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
+ bounceAnimation.values = [NSArray arrayWithObjects:
+ [NSNumber numberWithFloat:self.scaleFactor],
+ [NSNumber numberWithFloat:self.scaleFactor-0.02],
+ [NSNumber numberWithFloat:self.scaleFactor],
+ nil];
+ bounceAnimation.duration = self.animationDuration;
+ bounceAnimation.removedOnCompletion = NO;
+ [view.layer addAnimation:bounceAnimation forKey:@"bounce"];
+}
+
+@end
+
+
+#pragma mark - UIView+Additions
+
+@implementation UIView (Expose_Additions)
+
+- (CGFloat)exposeLeft {
+ return self.frame.origin.x;
+}
+
+- (void)setExposeLeft:(CGFloat)x {
+ CGRect frame = self.frame;
+ frame.origin.x = x;
+ self.frame = frame;
+}
+
+- (CGFloat)exposeTop {
+ return self.frame.origin.y;
+}
+
+- (void)setExposeTop:(CGFloat)y {
+ CGRect frame = self.frame;
+ frame.origin.y = y;
+ self.frame = frame;
+}
+
+- (CGFloat)exposeRight {
+ return self.frame.origin.x + self.frame.size.width;
+}
+
+- (CGFloat)exposeBottom {
+ return self.frame.origin.y + self.frame.size.height;
+}
+
+- (CGFloat)exposeWidth {
+ return self.frame.size.width;
+}
+
+- (void)setExposeWidth:(CGFloat)width {
+ CGRect frame = self.frame;
+ frame.size.width = width;
+ self.frame = frame;
+}
+
+- (CGFloat)exposeHeight {
+ return self.frame.size.height;
+}
+
+- (void)setExposeHeight:(CGFloat)height {
+ CGRect frame = self.frame;
+ frame.size.height = height;
+ self.frame = frame;
+}
+
+@end
+
+
+#pragma mark - UIViewController+LIExposeController
+
+@implementation UIViewController (LIExposeController)
+
+NSString const * kExposeController = @"exposeController";
+
+- (LIExposeController *)exposeController {
+ return (LIExposeController *)objc_getAssociatedObject(self, kExposeController);
+}
+
+- (void)setExposeController:(LIExposeController *)exposeController {
+ objc_setAssociatedObject(self, kExposeController, exposeController, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+@end
28 README.md
@@ -0,0 +1,28 @@
+# LIExposeController
+LIExposeController is a new navigation paradigm for iOS apps. A great example is [LinkedIn's iPhone app](http://itunes.apple.com/us/app/linkedin/id288429040?mt=8).
+
+LIExposeController acts as a container view controller, much like UINavigationController or UITabBarController. It manages a set of UIViewControllers as separate "stacks" so that users can easily switch between multiple screens.
+
+## Instructions
+1. Add LIExposeController.h and LIExposeController.m to your Xcode project.
+2. Create an instance like so: <pre><code>exposeController = [[LIExposeController alloc] init];</code></pre>
+3. Add your view controllers: <pre><code>exposeController.viewControllers = [NSArray arrayWithObjects:..., nil];</code></pre>
+4. Add expose controller to your view hierarchy: <pre><code>window.rootViewController = exposeController;</code></pre>
+5. Enjoy!
+
+### Frameworks Required
+1. UIKit
+2. Foundation
+3. QuartzCore
+4. CoreGraphics
+
+### Screenshots
+![Screen 1 Alt](LIExposeController/raw/master/Screenshots/screen1.png "Screen 1")
+![Screen 2 Alt](LIExposeController/raw/master/Screenshots/screen2.png "Screen 2")
+
+## Authors
+* Sudeep Yegnashankaran \([LinkedIn](http://www.linkedin.com/in/sudeepy), [github](https://github.com/sudeepy)\)
+* Peter Shih \([LinkedIn](http://www.linkedin.com/in/ptshih), [github](https://github.com/ptshih)\)
+
+## License
+The source code is available under the Apache 2.0 license.
BIN <