From 0dfe77ccec5b8b88fee9cbe3c06cb485b3995b91 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 7 Nov 2014 14:48:19 +0100 Subject: [PATCH] Initial Import --- .gitignore | 1 + .../project.pbxproj | 339 +++++++++++++ .../contents.xcworkspacedata | 7 + .../AppDelegate.h | 17 + .../AppDelegate.m | 18 + .../Base.lproj/LaunchScreen.xib | 41 ++ .../Base.lproj/Main.storyboard | 25 + .../AppIcon.appiconset/Contents.json | 68 +++ .../PSTAlertViewControllerSample/Info.plist | 47 ++ .../ViewController.h | 15 + .../ViewController.m | 47 ++ Example/PSTAlertViewControllerSample/main.m | 16 + LICENSE | 13 +- PSTAlertController.podspec | 29 ++ PSTAlertController/PSTAlertController.h | 117 +++++ PSTAlertController/PSTAlertController.m | 446 ++++++++++++++++++ README.md | 17 +- 17 files changed, 1254 insertions(+), 9 deletions(-) create mode 100644 Example/PSTAlertViewControllerSample.xcodeproj/project.pbxproj create mode 100644 Example/PSTAlertViewControllerSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Example/PSTAlertViewControllerSample/AppDelegate.h create mode 100644 Example/PSTAlertViewControllerSample/AppDelegate.m create mode 100644 Example/PSTAlertViewControllerSample/Base.lproj/LaunchScreen.xib create mode 100644 Example/PSTAlertViewControllerSample/Base.lproj/Main.storyboard create mode 100644 Example/PSTAlertViewControllerSample/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Example/PSTAlertViewControllerSample/Info.plist create mode 100644 Example/PSTAlertViewControllerSample/ViewController.h create mode 100644 Example/PSTAlertViewControllerSample/ViewController.m create mode 100644 Example/PSTAlertViewControllerSample/main.m create mode 100644 PSTAlertController.podspec create mode 100644 PSTAlertController/PSTAlertController.h create mode 100644 PSTAlertController/PSTAlertController.m diff --git a/.gitignore b/.gitignore index a3cd143..b5e1481 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ DerivedData *.hmap *.ipa *.xcuserstate +.DS_Store # CocoaPods # diff --git a/Example/PSTAlertViewControllerSample.xcodeproj/project.pbxproj b/Example/PSTAlertViewControllerSample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..7486148 --- /dev/null +++ b/Example/PSTAlertViewControllerSample.xcodeproj/project.pbxproj @@ -0,0 +1,339 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 78E298891A0D0071007953FB /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 78E298881A0D0071007953FB /* main.m */; }; + 78E2988C1A0D0071007953FB /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 78E2988B1A0D0071007953FB /* AppDelegate.m */; }; + 78E2988F1A0D0071007953FB /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 78E2988E1A0D0071007953FB /* ViewController.m */; }; + 78E298921A0D0071007953FB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 78E298901A0D0071007953FB /* Main.storyboard */; }; + 78E298941A0D0071007953FB /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 78E298931A0D0071007953FB /* Images.xcassets */; }; + 78E298971A0D0071007953FB /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 78E298951A0D0071007953FB /* LaunchScreen.xib */; }; + 78E298B21A0D0B25007953FB /* PSTAlertController.m in Sources */ = {isa = PBXBuildFile; fileRef = 78E298B11A0D0B25007953FB /* PSTAlertController.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 78E298831A0D0071007953FB /* PSTAlertViewControllerSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PSTAlertViewControllerSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 78E298871A0D0071007953FB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 78E298881A0D0071007953FB /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 78E2988A1A0D0071007953FB /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 78E2988B1A0D0071007953FB /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 78E2988D1A0D0071007953FB /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 78E2988E1A0D0071007953FB /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 78E298911A0D0071007953FB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 78E298931A0D0071007953FB /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 78E298961A0D0071007953FB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; + 78E298A11A0D0071007953FB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 78E298A21A0D0071007953FB /* PSTAlertViewControllerSampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PSTAlertViewControllerSampleTests.m; sourceTree = ""; }; + 78E298B01A0D0B25007953FB /* PSTAlertController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTAlertController.h; sourceTree = ""; }; + 78E298B11A0D0B25007953FB /* PSTAlertController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTAlertController.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 78E298801A0D0071007953FB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 78E2987A1A0D0071007953FB = { + isa = PBXGroup; + children = ( + 78E298851A0D0071007953FB /* PSTAlertViewControllerSample */, + 78E2989F1A0D0071007953FB /* PSTAlertViewControllerSampleTests */, + 78E298841A0D0071007953FB /* Products */, + ); + sourceTree = ""; + }; + 78E298841A0D0071007953FB /* Products */ = { + isa = PBXGroup; + children = ( + 78E298831A0D0071007953FB /* PSTAlertViewControllerSample.app */, + ); + name = Products; + sourceTree = ""; + }; + 78E298851A0D0071007953FB /* PSTAlertViewControllerSample */ = { + isa = PBXGroup; + children = ( + 78E298AF1A0D0B25007953FB /* PSTAlertController */, + 78E2988A1A0D0071007953FB /* AppDelegate.h */, + 78E2988B1A0D0071007953FB /* AppDelegate.m */, + 78E2988D1A0D0071007953FB /* ViewController.h */, + 78E2988E1A0D0071007953FB /* ViewController.m */, + 78E298901A0D0071007953FB /* Main.storyboard */, + 78E298931A0D0071007953FB /* Images.xcassets */, + 78E298951A0D0071007953FB /* LaunchScreen.xib */, + 78E298861A0D0071007953FB /* Supporting Files */, + ); + path = PSTAlertViewControllerSample; + sourceTree = ""; + }; + 78E298861A0D0071007953FB /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 78E298871A0D0071007953FB /* Info.plist */, + 78E298881A0D0071007953FB /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 78E2989F1A0D0071007953FB /* PSTAlertViewControllerSampleTests */ = { + isa = PBXGroup; + children = ( + 78E298A21A0D0071007953FB /* PSTAlertViewControllerSampleTests.m */, + 78E298A01A0D0071007953FB /* Supporting Files */, + ); + path = PSTAlertViewControllerSampleTests; + sourceTree = ""; + }; + 78E298A01A0D0071007953FB /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 78E298A11A0D0071007953FB /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 78E298AF1A0D0B25007953FB /* PSTAlertController */ = { + isa = PBXGroup; + children = ( + 78E298B01A0D0B25007953FB /* PSTAlertController.h */, + 78E298B11A0D0B25007953FB /* PSTAlertController.m */, + ); + name = PSTAlertController; + path = ../../PSTAlertController; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 78E298821A0D0071007953FB /* PSTAlertViewControllerSample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 78E298A61A0D0071007953FB /* Build configuration list for PBXNativeTarget "PSTAlertViewControllerSample" */; + buildPhases = ( + 78E2987F1A0D0071007953FB /* Sources */, + 78E298801A0D0071007953FB /* Frameworks */, + 78E298811A0D0071007953FB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PSTAlertViewControllerSample; + productName = PSTAlertViewControllerSample; + productReference = 78E298831A0D0071007953FB /* PSTAlertViewControllerSample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 78E2987B1A0D0071007953FB /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = "PSPDFKit GmbH"; + TargetAttributes = { + 78E298821A0D0071007953FB = { + CreatedOnToolsVersion = 6.1; + }; + }; + }; + buildConfigurationList = 78E2987E1A0D0071007953FB /* Build configuration list for PBXProject "PSTAlertViewControllerSample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 78E2987A1A0D0071007953FB; + productRefGroup = 78E298841A0D0071007953FB /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 78E298821A0D0071007953FB /* PSTAlertViewControllerSample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 78E298811A0D0071007953FB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 78E298921A0D0071007953FB /* Main.storyboard in Resources */, + 78E298971A0D0071007953FB /* LaunchScreen.xib in Resources */, + 78E298941A0D0071007953FB /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 78E2987F1A0D0071007953FB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 78E2988F1A0D0071007953FB /* ViewController.m in Sources */, + 78E2988C1A0D0071007953FB /* AppDelegate.m in Sources */, + 78E298B21A0D0B25007953FB /* PSTAlertController.m in Sources */, + 78E298891A0D0071007953FB /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 78E298901A0D0071007953FB /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 78E298911A0D0071007953FB /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 78E298951A0D0071007953FB /* LaunchScreen.xib */ = { + isa = PBXVariantGroup; + children = ( + 78E298961A0D0071007953FB /* Base */, + ); + name = LaunchScreen.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 78E298A41A0D0071007953FB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + 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_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 78E298A51A0D0071007953FB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 78E298A71A0D0071007953FB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = PSTAlertViewControllerSample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 78E298A81A0D0071007953FB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = PSTAlertViewControllerSample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 78E2987E1A0D0071007953FB /* Build configuration list for PBXProject "PSTAlertViewControllerSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 78E298A41A0D0071007953FB /* Debug */, + 78E298A51A0D0071007953FB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 78E298A61A0D0071007953FB /* Build configuration list for PBXNativeTarget "PSTAlertViewControllerSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 78E298A71A0D0071007953FB /* Debug */, + 78E298A81A0D0071007953FB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 78E2987B1A0D0071007953FB /* Project object */; +} diff --git a/Example/PSTAlertViewControllerSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/PSTAlertViewControllerSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..94ba2f1 --- /dev/null +++ b/Example/PSTAlertViewControllerSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Example/PSTAlertViewControllerSample/AppDelegate.h b/Example/PSTAlertViewControllerSample/AppDelegate.h new file mode 100644 index 0000000..0e56f00 --- /dev/null +++ b/Example/PSTAlertViewControllerSample/AppDelegate.h @@ -0,0 +1,17 @@ +// +// AppDelegate.h +// PSTAlertViewControllerSample +// +// Created by Peter Steinberger on 07/11/14. +// Copyright (c) 2014 PSPDFKit GmbH. All rights reserved. +// + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + + +@end + diff --git a/Example/PSTAlertViewControllerSample/AppDelegate.m b/Example/PSTAlertViewControllerSample/AppDelegate.m new file mode 100644 index 0000000..633d656 --- /dev/null +++ b/Example/PSTAlertViewControllerSample/AppDelegate.m @@ -0,0 +1,18 @@ +// +// AppDelegate.m +// PSTAlertViewControllerSample +// +// Created by Peter Steinberger on 07/11/14. +// Copyright (c) 2014 PSPDFKit GmbH. All rights reserved. +// + +#import "AppDelegate.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + return YES; +} + +@end diff --git a/Example/PSTAlertViewControllerSample/Base.lproj/LaunchScreen.xib b/Example/PSTAlertViewControllerSample/Base.lproj/LaunchScreen.xib new file mode 100644 index 0000000..55c8bfb --- /dev/null +++ b/Example/PSTAlertViewControllerSample/Base.lproj/LaunchScreen.xib @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/PSTAlertViewControllerSample/Base.lproj/Main.storyboard b/Example/PSTAlertViewControllerSample/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f56d2f3 --- /dev/null +++ b/Example/PSTAlertViewControllerSample/Base.lproj/Main.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/PSTAlertViewControllerSample/Images.xcassets/AppIcon.appiconset/Contents.json b/Example/PSTAlertViewControllerSample/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..36d2c80 --- /dev/null +++ b/Example/PSTAlertViewControllerSample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/PSTAlertViewControllerSample/Info.plist b/Example/PSTAlertViewControllerSample/Info.plist new file mode 100644 index 0000000..8415384 --- /dev/null +++ b/Example/PSTAlertViewControllerSample/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.pspdfkit.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Example/PSTAlertViewControllerSample/ViewController.h b/Example/PSTAlertViewControllerSample/ViewController.h new file mode 100644 index 0000000..9773c6b --- /dev/null +++ b/Example/PSTAlertViewControllerSample/ViewController.h @@ -0,0 +1,15 @@ +// +// ViewController.h +// PSTAlertViewControllerSample +// +// Created by Peter Steinberger on 07/11/14. +// Copyright (c) 2014 PSPDFKit GmbH. All rights reserved. +// + +#import + +@interface ViewController : UIViewController + + +@end + diff --git a/Example/PSTAlertViewControllerSample/ViewController.m b/Example/PSTAlertViewControllerSample/ViewController.m new file mode 100644 index 0000000..2fbb004 --- /dev/null +++ b/Example/PSTAlertViewControllerSample/ViewController.m @@ -0,0 +1,47 @@ +// +// ViewController.m +// PSTAlertViewControllerSample +// +// Created by Peter Steinberger on 07/11/14. +// Copyright (c) 2014 PSPDFKit GmbH. All rights reserved. +// + +#import "ViewController.h" +#import "PSTAlertController.h" + +@implementation ViewController + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + + [self doTheDance]; +} + +- (void)doTheDance { + PSTAlertController *gotoPageController = [PSTAlertController alertWithTitle:@"Go to page" message:nil]; + [gotoPageController addTextFieldWithConfigurationHandler:^(UITextField *textField) { + textField.keyboardType = UIKeyboardTypeNumberPad; + }]; + [gotoPageController addCancelActionWithHandler:NULL]; + [gotoPageController addAction:[PSTAlertAction actionWithTitle:@"Go to" handler:^(PSTAlertAction *action) { + NSString *pageLabel = action.alertController.textField.text; + PSTAlertController *dismissable = [PSTAlertController presentDismissableAlertWithTitle:@"Result" message:[NSString stringWithFormat:@"You entered %@", pageLabel] controller:self]; + [dismissable addDidDismissBlock:^(PSTAlertAction *action) { + [self doTheDance]; + }]; + }]]; + [gotoPageController addAction:[PSTAlertAction actionWithTitle:@"No" style:PSTAlertActionStyleDestructive handler:^(PSTAlertAction *action) { + PSTAlertController *sheetController = [PSTAlertController actionSheetWithTitle:@"No?"]; + [sheetController addAction:[PSTAlertAction actionWithTitle:@"I've changed my mind" handler:^(PSTAlertAction *action) { + [self doTheDance]; + }]]; + // Cancel action on a sheet should be the last action. + [sheetController addAction:[PSTAlertAction actionWithTitle:@"That's fine." style:PSTAlertActionStyleCancel handler:NULL]]; + [sheetController showWithSender:[NSValue valueWithCGRect:CGRectMake(100.f, 100.f, 1.f, 1.f)] controller:self animated:YES completion:^{ + NSLog(@"Okay, let's do that again..."); + }]; + }]]; + [gotoPageController showWithSender:nil controller:self animated:YES completion:NULL]; +} + +@end diff --git a/Example/PSTAlertViewControllerSample/main.m b/Example/PSTAlertViewControllerSample/main.m new file mode 100644 index 0000000..3d2374a --- /dev/null +++ b/Example/PSTAlertViewControllerSample/main.m @@ -0,0 +1,16 @@ +// +// main.m +// PSTAlertViewControllerSample +// +// Created by Peter Steinberger on 07/11/14. +// Copyright (c) 2014 PSPDFKit GmbH. All rights reserved. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/LICENSE b/LICENSE index 3b4c0a5..acfba8c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,4 @@ -The MIT License (MIT) - -Copyright (c) 2014 Peter Steinberger +Copyright (c) 2014 Peter Steinberger Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,14 +7,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/PSTAlertController.podspec b/PSTAlertController.podspec new file mode 100644 index 0000000..cb4c81e --- /dev/null +++ b/PSTAlertController.podspec @@ -0,0 +1,29 @@ +# +# Be sure to run `pod lib lint PSTAlertController.podspec' to ensure this is a +# valid spec and remove all comments before submitting the spec. +# +# Any lines starting with a # are optional, but encouraged +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# + +Pod::Spec.new do |s| + s.name = "PSTAlertController" + s.version = "1.0.0" + s.summary = "API similar to UIAlertController, backwards compatible to iOS 7. Will use the new shiny API when you run iOS 8. " + s.description = <<-DESC + We cheat a bit by having PSTAlertController superclass be NSObject, but for most use cases it's still a lot more convenient than using UIAlertView/UIActionSheet. + DESC + s.homepage = "https://github.com/steipete/PSTAlertController" + s.license = 'MIT' + s.author = { "Peter Steinberger" => "steipete@gmail.com" } + s.source = { :git => "https://github.com/steipete/PSTAlertController.git", :tag => s.version.to_s } + # s.social_media_url = 'https://twitter.com/steipete' + + s.platform = :ios, '7.0' + s.requires_arc = true + + s.source_files = 'PSTAlertController' + s.public_header_files = 'PSTAlertController/**/*.h' + s.frameworks = 'UIKit' +end \ No newline at end of file diff --git a/PSTAlertController/PSTAlertController.h b/PSTAlertController/PSTAlertController.h new file mode 100644 index 0000000..b80e289 --- /dev/null +++ b/PSTAlertController/PSTAlertController.h @@ -0,0 +1,117 @@ +// +// PSTAlertController.h +// +// Licensed under the MIT license. +// Copyright (c) 2014 Peter Steinberger, PSPDFKit GmbH. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#import +#import + +typedef NS_ENUM(NSInteger, PSTAlertControllerStyle) { + PSTAlertControllerStyleActionSheet = 0, + PSTAlertControllerStyleAlert +}; + +typedef NS_ENUM(NSInteger, PSTAlertActionStyle) { + PSTAlertActionStyleDefault = 0, + PSTAlertActionStyleCancel, + PSTAlertActionStyleDestructive +}; + +@class PSTAlertController; + +@interface PSTAlertAction : NSObject ++ (instancetype)actionWithTitle:(NSString *)title style:(PSTAlertActionStyle)style handler:(void (^)(PSTAlertAction *action))handler; ++ (instancetype)actionWithTitle:(NSString *)title handler:(void (^)(PSTAlertAction *action))handler; +@property (nonatomic, copy, readonly) NSString *title; +@property (nonatomic, readonly) PSTAlertActionStyle style; + +@property (nonatomic, weak) PSTAlertController *alertController; // weak connection +@end + +// Mashup of UIAlertController with fallback methods for iOS 7. +// @note Blocks are generally executed after the dismiss animation is completed. +@interface PSTAlertController : NSObject + +// Generic initializer ++ (instancetype)alertControllerWithTitle:(NSString *)title message:(NSString *)message preferredStyle:(PSTAlertControllerStyle)preferredStyle; + +// Add action. +- (void)addAction:(PSTAlertAction *)action; + +// Add block that is called after the alert controller will be dismissed (before animation). +- (void)addWillDismissBlock:(void (^)(PSTAlertAction *action))willDismissBlock; + +// Add block that is called after the alert view has been dismissed (after animation). +- (void)addDidDismissBlock:(void (^)(PSTAlertAction *action))didDismissBlock; + +@property (nonatomic, copy, readonly) NSArray *actions; + +// Text field support +- (void)addTextFieldWithConfigurationHandler:(void (^)(UITextField *textField))configurationHandler; +@property (nonatomic, readonly) NSArray *textFields; + +@property (nonatomic, copy) NSString *title; +@property (nonatomic, copy) NSString *message; + +@property (nonatomic, readonly) PSTAlertControllerStyle preferredStyle; + +// Presentation and dismissal +- (void)showWithSender:(id)sender controller:(UIViewController *)controller animated:(BOOL)animated completion:(void (^)(void))completion; +- (void)dismissAnimated:(BOOL)animated completion:(void (^)(void))completion; + ++ (BOOL)hasVisibleAlertController; +@property (nonatomic, readonly, getter=isVisible) BOOL visible; + +@end + +@interface PSTAlertController (Convenience) + +// Convenience initializers ++ (instancetype)actionSheetWithTitle:(NSString *)title; ++ (instancetype)alertWithTitle:(NSString *)title message:(NSString *)message; + +// Convenience. Presents a simple alert with a "Dismiss" button. +// Will use the root view controller if `controller` is nil. ++ (instancetype)presentDismissableAlertWithTitle:(NSString *)title message:(NSString *)message controller:(UIViewController *)controller; + +// From Apple's HIG: +// In a two-button alert that proposes a potentially risky action, the button that cancels the action should be on the right (and light-colored). +// In a two-button alert that proposes a benign action that people are likely to want, the button that cancels the action should be on the left (and dark-colored). +- (void)addCancelActionWithHandler:(void (^)(PSTAlertAction *action))handler; // convenience + +@property (nonatomic, readonly) UITextField *textField; + +@end + + +@interface PSTAlertController (Internal) + +@property (nonatomic, strong, readonly) UIAlertController *alertController; + +@property (nonatomic, strong, readonly) UIActionSheet *actionSheet; +@property (nonatomic, strong, readonly) UIAlertView *alertView; + +// One if the above three. +@property (nonatomic, strong, readonly) id presentedObject; + +@end \ No newline at end of file diff --git a/PSTAlertController/PSTAlertController.m b/PSTAlertController/PSTAlertController.m new file mode 100644 index 0000000..c323da2 --- /dev/null +++ b/PSTAlertController/PSTAlertController.m @@ -0,0 +1,446 @@ +// +// PSTAlertController.m +// +// Copyright (c) 2014 PSPDFKit GmbH. All rights reserved. +// +// THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW +// AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. +// UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. +// This notice may not be removed from this file. +// + +#import "PSTAlertController.h" +#import + +#define PROPERTY(property) NSStringFromSelector(@selector(property)) + +@interface PSTAlertAction () +@property (nonatomic, copy) NSString *title; +@property (nonatomic, assign) PSTAlertActionStyle style; +@property (nonatomic, copy) void (^handler)(PSTAlertAction *action); +- (void)performAction; +@end + +@implementation PSTAlertAction + ++ (instancetype)actionWithTitle:(NSString *)title style:(PSTAlertActionStyle)style handler:(void (^)(PSTAlertAction *action))handler { + return [[self alloc] initWithTitle:title style:style handler:handler]; +} + ++ (instancetype)actionWithTitle:(NSString *)title handler:(void (^)(PSTAlertAction *action))handler { + return [[self alloc] initWithTitle:title style:PSTAlertActionStyleDefault handler:handler]; +} + +- (instancetype)initWithTitle:(NSString *)title style:(PSTAlertActionStyle)style handler:(void (^)(PSTAlertAction *action))handler { + if ((self = [super init])) { + _title = [title copy]; + _style = style; + _handler = [handler copy]; + } + return self; +} + +- (void)performAction { + if (self.handler) { + self.handler(self); + self.handler = nil; // nil out after calling to break cycles. + } +} + +@end + +@interface PSPDFExtendedAlertController : UIAlertController +@property (nonatomic, copy) void (^viewWillDisappearBlock)(void); +@property (nonatomic, copy) void (^viewDidDisappearBlock)(void); +@end + +@implementation PSPDFExtendedAlertController + +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + if (self.viewWillDisappearBlock) self.viewWillDisappearBlock(); +} + +- (void)viewDidDisappear:(BOOL)animated { + [super viewDidDisappear:animated]; + if (self.viewDidDisappearBlock) self.viewDidDisappearBlock(); +} + +@end + +@interface PSTAlertController () +- (instancetype)initWithTitle:(NSString *)title message:(NSString *)message preferredStyle:(PSTAlertControllerStyle)preferredStyle NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, copy) NSArray *willDismissBlocks; +@property (nonatomic, copy) NSArray *didDismissBlocks; + +// iOS 8 +@property (nonatomic, strong) PSPDFExtendedAlertController *alertController; + +// Universal +@property (nonatomic, weak) PSTAlertAction *executedAlertAction; + +// iOS 7 +@property (nonatomic, copy) NSArray *actions; +@property (nonatomic, copy) NSArray *textFieldHandlers; +@property (nonatomic, strong, readonly) UIActionSheet *actionSheet; +@property (nonatomic, strong, readonly) UIAlertView *alertView; + +// Storage for actionSheet/alertView +@property (nonatomic, strong) UIView *strongSheetStorage; +@property (nonatomic, weak) UIView *weakSheetStorage; +@end + +@implementation PSTAlertController + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - Initialization + +- (BOOL)alertControllerAvailable { + return [UIAlertController class] != nil; // iOS 8 and later. +} + ++ (instancetype)alertControllerWithTitle:(NSString *)title message:(NSString *)message preferredStyle:(PSTAlertControllerStyle)preferredStyle { + return [[self alloc] initWithTitle:title message:message preferredStyle:preferredStyle]; +} + +- (instancetype)initWithTitle:(NSString *)title message:(NSString *)message preferredStyle:(PSTAlertControllerStyle)preferredStyle { + if ((self = [super init])) { + _title = [title copy]; + _message = [message copy]; + _preferredStyle = preferredStyle; + + if ([self alertControllerAvailable]) { + _alertController = [PSPDFExtendedAlertController alertControllerWithTitle:title message:message preferredStyle:(UIAlertControllerStyle)preferredStyle]; + } else { + if (preferredStyle == PSTAlertControllerStyleActionSheet) { + _strongSheetStorage = [[UIActionSheet alloc] initWithTitle:title delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil]; + } else { + _strongSheetStorage = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:nil otherButtonTitles:nil]; + } + } + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, title:%@, actions:%@>", NSStringFromClass(self.class), self, self.title, self.actions]; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - Accessors + +- (UIAlertView *)alertView { + return (UIAlertView *)(self.strongSheetStorage ?: self.weakSheetStorage); +} + +- (UIActionSheet *)actionSheet { + return (UIActionSheet *)(self.strongSheetStorage ?: self.weakSheetStorage); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - Adding Actions + +- (void)addAction:(PSTAlertAction *)action { + NSAssert([action isKindOfClass:PSTAlertAction.class], @"Must be of type PSTAlertAction"); + + action.alertController = self; // weakly connect + + self.actions = [[NSArray arrayWithArray:self.actions] arrayByAddingObject:action]; + + if ([self alertControllerAvailable]) { + __weak typeof (self) weakSelf = self; + UIAlertAction *alertAction = [UIAlertAction actionWithTitle:action.title style:(UIAlertActionStyle)action.style handler:^(UIAlertAction *uiAction) { + weakSelf.executedAlertAction = action; + [action performAction]; + }]; + [self.alertController addAction:alertAction]; + } else { + if (self.preferredStyle == PSTAlertControllerStyleActionSheet) { + NSUInteger index = [self.actionSheet addButtonWithTitle:action.title]; + + if (action.style == PSTAlertActionStyleDestructive) { + self.actionSheet.destructiveButtonIndex = index; + } else if (action.style == PSTAlertActionStyleCancel) { + self.actionSheet.cancelButtonIndex = index; + } + } else { + NSUInteger index = [self.alertView addButtonWithTitle:action.title]; + + // UIAlertView doesn't support destructive buttons. + if (action.style == PSTAlertActionStyleCancel) { + self.alertView.cancelButtonIndex = index; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - Text Field Support + +- (void)addTextFieldWithConfigurationHandler:(void (^)(UITextField *textField))configurationHandler { + if ([self alertControllerAvailable]) { + [self.alertController addTextFieldWithConfigurationHandler:configurationHandler]; + } else { + NSAssert(self.preferredStyle == PSTAlertControllerStyleAlert, @"Text fields are only supported for alerts."); + self.textFieldHandlers = [[NSArray arrayWithArray:self.textFieldHandlers] arrayByAddingObject:configurationHandler ?: ^(UITextField *textField){}]; + self.alertView.alertViewStyle = self.textFieldHandlers.count > 1 ? UIAlertViewStyleLoginAndPasswordInput : UIAlertViewStylePlainTextInput; + } +} + +- (NSArray *)textFields { + if ([self alertControllerAvailable]) { + return self.alertController.textFields; + } else if (self.preferredStyle == PSTAlertControllerStyleAlert) { + switch (self.alertView.alertViewStyle) { + case UIAlertViewStyleSecureTextInput: + case UIAlertViewStylePlainTextInput: + return @[[self.alertView textFieldAtIndex:0]]; + case UIAlertViewStyleLoginAndPasswordInput: + return @[[self.alertView textFieldAtIndex:0], [self.alertView textFieldAtIndex:1]]; + case UIAlertViewStyleDefault: + return @[]; + } + } + // UIActionSheet doesn't support text fields. + return nil; +} + +- (UITextField *)textField { + return self.textFields.firstObject; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - Presentation + +static NSUInteger PSPDFVisibleAlertsCount = 0; ++ (BOOL)hasVisibleAlertController { + return PSPDFVisibleAlertsCount > 0; +} + +- (BOOL)isVisible { + if ([self alertControllerAvailable]) { + return self.alertController.view.window != nil; + } else { + if (self.preferredStyle == PSTAlertControllerStyleActionSheet) { + return self.actionSheet.isVisible; + } else { + return self.alertView.isVisible; + } + } +} + +- (void)showWithSender:(id)sender controller:(UIViewController *)controller animated:(BOOL)animated completion:(void (^)(void))completion { + if ([self alertControllerAvailable]) { + // As a convenience, allow automatic root view controller fetching if we show an alert. + if (self.preferredStyle == PSTAlertControllerStyleAlert) { + controller = controller ?: UIApplication.sharedApplication.keyWindow.rootViewController; + } + + PSPDFExtendedAlertController *actionController = self.alertController; + UIPopoverPresentationController *popoverPresentationController = actionController.popoverPresentationController; + if (popoverPresentationController) { // nil on iPhone + if ([sender isKindOfClass:UIBarButtonItem.class]) { + popoverPresentationController.barButtonItem = sender; + } else if ([sender isKindOfClass:UIView.class]) { + popoverPresentationController.sourceView = sender; + popoverPresentationController.sourceRect = [sender bounds]; + } else if ([sender isKindOfClass:NSValue.class]) { + popoverPresentationController.sourceView = controller.view; + popoverPresentationController.sourceRect = [sender CGRectValue]; + } else { + popoverPresentationController.sourceView = controller.view; + popoverPresentationController.sourceRect = controller.view.bounds; + } + } + + // Hook up dismiss blocks. + __weak typeof (self) weakSelf = self; + actionController.viewWillDisappearBlock = ^{ + typeof (self) strongSelf = weakSelf; + [strongSelf performBlocks:PROPERTY(willDismissBlocks) withAction:strongSelf.executedAlertAction]; + PSPDFVisibleAlertsCount--; + }; + actionController.viewDidDisappearBlock = ^{ + typeof (self) strongSelf = weakSelf; + [strongSelf performBlocks:PROPERTY(didDismissBlocks) withAction:strongSelf.executedAlertAction]; + }; + + objc_setAssociatedObject(controller, _cmd, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC); // bind lifetime + [controller presentViewController:actionController animated:animated completion:completion]; + + } else { + if (self.preferredStyle == PSTAlertControllerStyleActionSheet) { + [self showActionSheetWithSender:sender fallbackView:controller.view animated:animated]; + [self moveSheetToWeakStorage]; + } else { + // Call text field configuration handlers. + [self.textFieldHandlers enumerateObjectsUsingBlock:^(void (^configurationHandler)(UITextField *textField), NSUInteger idx, BOOL *stop) { + configurationHandler([self.alertView textFieldAtIndex:idx]); + }]; + [self.alertView show]; + [self moveSheetToWeakStorage]; + } + } + PSPDFVisibleAlertsCount++; +} + +- (void)showActionSheetWithSender:(id)sender fallbackView:(UIView *)view animated:(BOOL)animated { + UIActionSheet *actionSheet = self.actionSheet; + BOOL isIPad = UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad; + if (isIPad && [sender isKindOfClass:UIBarButtonItem.class]) { + [actionSheet showFromBarButtonItem:sender animated:animated]; + } else if ([sender isKindOfClass:UIToolbar.class]) { + [actionSheet showFromToolbar:sender]; + } else if ([sender isKindOfClass:UITabBar.class]) { + [actionSheet showFromTabBar:sender]; + } else if ([view isKindOfClass:UIToolbar.class]) { + [actionSheet showFromToolbar:(UIToolbar *)view]; + } else if ([view isKindOfClass:UITabBar.class]) { + [actionSheet showFromTabBar:(UITabBar *)view]; + } else if (isIPad && [sender isKindOfClass:UIView.class]) { + [actionSheet showFromRect:[sender bounds] inView:sender animated:animated]; + } else if ([sender isKindOfClass:NSValue.class]) { + [actionSheet showFromRect:[sender CGRectValue] inView:view animated:animated]; + } else { + [actionSheet showInView:view]; + } +} + +- (void)dismissAnimated:(BOOL)animated completion:(void (^)(void))completion { + if ([self alertControllerAvailable]) { + [self.alertController dismissViewControllerAnimated:animated completion:completion]; + } else { + // Make sure the completion block is called. + if (completion) { + [self addDidDismissBlock:^(PSTAlertAction *action) { completion(); }]; + } + if (self.preferredStyle == PSTAlertControllerStyleActionSheet) { + [self.actionSheet dismissWithClickedButtonIndex:self.actionSheet.cancelButtonIndex animated:animated]; + } else { + [self.alertView dismissWithClickedButtonIndex:self.alertView.cancelButtonIndex animated:animated]; + } + } +} + +- (id)presentedObject { + if ([self alertControllerAvailable]) { + return self.alertController; + } else { + if (self.preferredStyle == PSTAlertControllerStyleActionSheet) { + return self.actionSheet; + } else { + return self.alertView; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - Will/Did Dismiss Observers + +- (void)addWillDismissBlock:(void (^)(PSTAlertAction *action))willDismissBlock { + NSParameterAssert(willDismissBlock); + self.willDismissBlocks = [[NSArray arrayWithArray:self.willDismissBlocks] arrayByAddingObject:willDismissBlock]; +} + +- (void)addDidDismissBlock:(void (^)(PSTAlertAction *action))didDismissBlock { + NSParameterAssert(didDismissBlock); + self.didDismissBlocks = [[NSArray arrayWithArray:self.didDismissBlocks] arrayByAddingObject:didDismissBlock]; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - Memory Management + +- (void)moveSheetToWeakStorage { + NSParameterAssert(self.strongSheetStorage); + + objc_setAssociatedObject(self.strongSheetStorage, _cmd, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC); // bind lifetime + self.weakSheetStorage = self.strongSheetStorage; + self.strongSheetStorage = nil; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - Execute Actions + +- (PSTAlertAction *)actionForButtonIndex:(NSUInteger)index { + return self.actions[index]; +} + +- (void)performBlocks:(NSString *)blocksStorageName withAction:(PSTAlertAction *)alertAction { + // Load variable and nil out. + NSArray *blocks = [self valueForKey:blocksStorageName]; + [self setValue:nil forKey:blocksStorageName]; + + for (void (^block)(PSTAlertAction *action) in blocks) { + block(alertAction); + } +} + +- (void)viewWillDismissWithButtonIndex:(NSInteger)buttonIndex { + PSTAlertAction *action = [self actionForButtonIndex:buttonIndex]; + self.executedAlertAction = action; + + [self performBlocks:PROPERTY(willDismissBlocks) withAction:action]; + self.willDismissBlocks = nil; + + PSPDFVisibleAlertsCount--; +} + +- (void)viewDidDismissWithButtonIndex:(NSInteger)buttonIndex { + PSTAlertAction *action = [self actionForButtonIndex:buttonIndex]; + [action performAction]; + + [self performBlocks:PROPERTY(didDismissBlocks) withAction:action]; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - UIActionSheetDelegate + +- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex { + [self viewWillDismissWithButtonIndex:buttonIndex]; +} + +// Called when a button is clicked. The view will be automatically dismissed after this call returns. +- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex { + [self viewDidDismissWithButtonIndex:buttonIndex]; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - UIAlertViewDelegate + +- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex { + [self viewWillDismissWithButtonIndex:buttonIndex]; +} + +- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { + [self viewDidDismissWithButtonIndex:buttonIndex]; +} + +@end + +@implementation PSTAlertController (Convenience) + ++ (instancetype)actionWithTitle:(NSString *)title handler:(void (^)(PSTAlertAction *action))handler { + return [[self alloc] initWithTitle:title style:PSTAlertActionStyleDefault handler:handler]; +} + ++ (instancetype)alertWithTitle:(NSString *)title message:(NSString *)message { + return [[self alloc] initWithTitle:title message:message preferredStyle:PSTAlertControllerStyleAlert]; +} + ++ (instancetype)actionSheetWithTitle:(NSString *)title { + return [[self alloc] initWithTitle:title message:nil preferredStyle:PSTAlertControllerStyleActionSheet]; +} + ++ (instancetype)presentDismissableAlertWithTitle:(NSString *)title message:(NSString *)message controller:(UIViewController *)controller { + PSTAlertController *alertController = [self alertWithTitle:title message:message]; + [alertController addAction:[PSTAlertAction actionWithTitle:NSLocalizedString(@"Dismiss", @"") style:PSTAlertActionStyleCancel handler:NULL]]; + [alertController showWithSender:nil controller:controller animated:YES completion:NULL]; + return alertController; +} + +- (void)addCancelActionWithHandler:(void (^)(PSTAlertAction *action))handler { + [self addAction:[PSTAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"") style:PSTAlertActionStyleCancel handler:handler]]; +} + +@end diff --git a/README.md b/README.md index cfa0d38..d42ef90 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,19 @@ PSTAlertController ================== -API similar to UIAlertController, backwards compatible to iOS 7. Will use the new shiny API when you run iOS 8. +API similar to `UIAlertController`, backwards compatible to iOS 7. Will use the new shiny API when you run iOS 8. + +We cheat a bit by having `PSTAlertController` superclass be `NSObject`, but for most use cases it's still a lot more convenient than using `UIAlertView`/`UIActionSheet`. + +Functions that are only possible since `UIAlertController` are not back-ported. This is a simple wrapper to make your live more convenient, not a complete rewrite. + +Written for [PSPDFKit, The leading framework for displaying and annotating PDFs in your iOS apps.](https://pspdfkit.com/). +It's a commercial library, but sometimes I just love to share. + +## Compatbility + +Tested with iOS 7 upwards, Xcode 6.1 and ARC. + +## License + +MIT, see LICENSE file. \ No newline at end of file