From c28028997d88ca9a1cb5725507590160ae0678af Mon Sep 17 00:00:00 2001 From: Oliver Date: Mon, 27 Nov 2017 18:06:25 +1100 Subject: [PATCH] Add iPhone X support Safe areas are calculated for toolbar, done button and photo frame. This accounts for orientation and fall back for pre-iOS 11 devices. --- Classes/IDMPhotoBrowser.m | 46 ++++- Classes/IDMUtils.h | 12 ++ Classes/IDMUtils.m | 52 +++++ .../project.pbxproj | 183 +++++++++++++++++- Demo/PhotoBrowserDemoTests/IDMUtilsTest.m | 115 +++++++++++ Demo/PhotoBrowserDemoTests/Info.plist | 22 +++ .../PhotoBrowserDemoTests.m | 15 ++ 7 files changed, 436 insertions(+), 9 deletions(-) create mode 100644 Classes/IDMUtils.h create mode 100644 Classes/IDMUtils.m create mode 100644 Demo/PhotoBrowserDemoTests/IDMUtilsTest.m create mode 100644 Demo/PhotoBrowserDemoTests/Info.plist create mode 100644 Demo/PhotoBrowserDemoTests/PhotoBrowserDemoTests.m diff --git a/Classes/IDMPhotoBrowser.m b/Classes/IDMPhotoBrowser.m index 125785fa..8549f3a4 100644 --- a/Classes/IDMPhotoBrowser.m +++ b/Classes/IDMPhotoBrowser.m @@ -9,6 +9,7 @@ #import #import "IDMPhotoBrowser.h" #import "IDMZoomingScrollView.h" +#import "IDMUtils.h" #import "pop/POP.h" @@ -62,6 +63,7 @@ @interface IDMPhotoBrowser () { BOOL _viewIsActive; // active as in it's in the view heirarchy BOOL _autoHide; NSInteger _initalPageIndex; + CGFloat _statusBarHeight; BOOL _isdraggingPhoto; @@ -190,8 +192,11 @@ - (id)init { _isdraggingPhoto = NO; + _statusBarHeight = 20.f; _doneButtonRightInset = 20.f; - _doneButtonTopInset = 30.f; + // relative to status bar and safeAreaInsets + _doneButtonTopInset = 10.f; + _doneButtonSize = CGSizeMake(55.f, 26.f); if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) { @@ -478,8 +483,19 @@ - (CGRect)animationFrameForImage:(UIImage *)image presenting:(BOOL)presenting sc CGSize imageSize = image.size; - CGFloat maxWidth = CGRectGetWidth(_applicationWindow.bounds); - CGFloat maxHeight = CGRectGetHeight(_applicationWindow.bounds); + CGRect bounds = _applicationWindow.bounds; + // adjust bounds as the photo browser does + if (@available(iOS 11.0, *)) { + // use the windows safe area inset + UIWindow *window = [UIApplication sharedApplication].keyWindow; + UIEdgeInsets insets = UIEdgeInsetsMake(_statusBarHeight, 0, 0, 0); + if (window != NULL) { + insets = window.safeAreaInsets; + } + bounds = [self adjustForSafeArea:bounds adjustForStatusBar:NO forInsets:insets]; + } + CGFloat maxWidth = CGRectGetWidth(bounds); + CGFloat maxHeight = CGRectGetHeight(bounds); CGRect animationFrame = CGRectZero; @@ -497,7 +513,6 @@ - (CGRect)animationFrameForImage:(UIImage *)image presenting:(BOOL)presenting sc if (!presenting) { animationFrame.origin.y += scrollView.frame.origin.y; } - return animationFrame; } @@ -1078,6 +1093,7 @@ - (CGRect)frameForPagingScrollView { CGRect frame = self.view.bounds; frame.origin.x -= PADDING; frame.size.width += (2 * PADDING); + frame = [self adjustForSafeArea:frame adjustForStatusBar:false]; return frame; } @@ -1116,16 +1132,18 @@ - (CGRect)frameForToolbarAtOrientation:(UIInterfaceOrientation)orientation { if ([self isLandscape:orientation]) height = 32; - return CGRectMake(0, self.view.bounds.size.height - height, self.view.bounds.size.width, height); + CGRect rtn = CGRectMake(0, self.view.bounds.size.height - height, self.view.bounds.size.width, height); + rtn = [self adjustForSafeArea:rtn adjustForStatusBar:true]; + return rtn; } - (CGRect)frameForDoneButtonAtOrientation:(UIInterfaceOrientation)orientation { CGRect screenBound = self.view.bounds; CGFloat screenWidth = screenBound.size.width; - // if ([self isLandscape:orientation]) screenWidth = screenBound.size.height; - - return CGRectMake(screenWidth - self.doneButtonRightInset - self.doneButtonSize.width, self.doneButtonTopInset, self.doneButtonSize.width, self.doneButtonSize.height); + CGRect rtn = CGRectMake(screenWidth - self.doneButtonRightInset - self.doneButtonSize.width, self.doneButtonTopInset, self.doneButtonSize.width, self.doneButtonSize.height); + rtn = [self adjustForSafeArea:rtn adjustForStatusBar:true]; + return rtn; } - (CGRect)frameForCaptionView:(IDMCaptionView *)captionView atIndex:(NSUInteger)index { @@ -1137,6 +1155,18 @@ - (CGRect)frameForCaptionView:(IDMCaptionView *)captionView atIndex:(NSUInteger) return captionFrame; } +- (CGRect)adjustForSafeArea:(CGRect)rect adjustForStatusBar:(BOOL)adjust { + if (@available(iOS 11.0, *)) { + return [self adjustForSafeArea:rect adjustForStatusBar:adjust forInsets:self.view.safeAreaInsets]; + } + UIEdgeInsets insets = UIEdgeInsetsMake(_statusBarHeight, 0, 0, 0); + return [self adjustForSafeArea:rect adjustForStatusBar:adjust forInsets:insets]; +} + +- (CGRect)adjustForSafeArea:(CGRect)rect adjustForStatusBar:(BOOL)adjust forInsets:(UIEdgeInsets) insets { + return [IDMUtils adjustRect:rect forSafeAreaInsets:insets forBounds:self.view.bounds adjustForStatusBar:adjust statusBarHeight:_statusBarHeight]; +} + #pragma mark - UIScrollView Delegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { diff --git a/Classes/IDMUtils.h b/Classes/IDMUtils.h new file mode 100644 index 00000000..933bad40 --- /dev/null +++ b/Classes/IDMUtils.h @@ -0,0 +1,12 @@ +// +// IDMUtils.h +// PhotoBrowserDemo +// +// Created by Oliver ONeill on 2/12/17. +// + +#import + +@interface IDMUtils : NSObject ++ (CGRect)adjustRect:(CGRect)rect forSafeAreaInsets:(UIEdgeInsets)insets forBounds:(CGRect)bounds adjustForStatusBar:(BOOL)adjust statusBarHeight:(int)statusBarHeight; +@end diff --git a/Classes/IDMUtils.m b/Classes/IDMUtils.m new file mode 100644 index 00000000..10eea0a3 --- /dev/null +++ b/Classes/IDMUtils.m @@ -0,0 +1,52 @@ +// +// IDMUtils.m +// PhotoBrowserDemo +// +// Created by Oliver ONeill on 2/12/17. +// + +#import "IDMUtils.h" + +@implementation IDMUtils +/** + * Adjust a rect to be moved into a safe area specified by `insets`. + * + * NOTE: this does not cover all cases. Given a rect it will reposition it if it + * falls into an unsafe area according to `insets` and `bounds`. When + * `adjustForStatusBar` is true, the rect y position will be based from the edge + * of the safe area, otherwise it will be based from zero. This allows views to + * sit behind the status bar. Status bar height is also used + * to keep positioning consistent when toggling the status bar on and off + */ ++ (CGRect)adjustRect:(CGRect)rect forSafeAreaInsets:(UIEdgeInsets)insets forBounds:(CGRect)bounds adjustForStatusBar:(BOOL)adjust statusBarHeight:(int)statusBarHeight { + BOOL isLeft = rect.origin.x <= insets.left; + // If the safe area is not specified via insets we should fall back to the + // status bar height + CGFloat insetTop = insets.top > 0 ? insets.top : statusBarHeight; + // Don't adjust for y positioning when adjustForStatusBar is false + BOOL isAtTop = (rect.origin.y <= insetTop); + BOOL isRight = rect.origin.x + rect.size.width >= bounds.size.width - insets.right; + BOOL isAtBottom = rect.origin.y + rect.size.height >= bounds.size.height - insets.bottom; + if ((isLeft) && (isRight)) { + rect.origin.x += insets.left; + rect.size.width -= insets.right + insets.left; + } else if (isLeft) { + rect.origin.x += insets.left; + } else if (isRight) { + rect.origin.x -= insets.right; + } + // if we're adjusting for status bar then we should move the view out of + // the inset + if ((adjust) && (isAtTop) && (isAtBottom)) { + rect.origin.y += insetTop; + rect.size.height -= insets.bottom + insetTop; + } else if ((adjust) && (isAtTop)) { + rect.origin.y += insetTop; + } else if ((isAtTop) && (isAtBottom)) { + rect.size.height -= insets.bottom; + } else if (isAtBottom) { + rect.origin.y -= insets.bottom; + } + return rect; +} +@end diff --git a/Demo/PhotoBrowserDemo.xcodeproj/project.pbxproj b/Demo/PhotoBrowserDemo.xcodeproj/project.pbxproj index 792fdeed..5d9c8df1 100644 --- a/Demo/PhotoBrowserDemo.xcodeproj/project.pbxproj +++ b/Demo/PhotoBrowserDemo.xcodeproj/project.pbxproj @@ -22,6 +22,9 @@ 5736E0DD544CC84515494E76 /* libPods-PhotoBrowserDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64F5714840012BFE47F8331B /* libPods-PhotoBrowserDemo.a */; }; 727988B1187B3B5400966C66 /* IDMPBLocalizations.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 727988B0187B3B5400966C66 /* IDMPBLocalizations.bundle */; }; 727988B7187B3CF100966C66 /* IDMPhotoBrowser.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 727988B6187B3CF100966C66 /* IDMPhotoBrowser.podspec */; }; + 7D2B79B81FD2501300F2094F /* IDMUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D2B79B71FD2501200F2094F /* IDMUtils.m */; }; + 7D2B79C01FD25B7600F2094F /* PhotoBrowserDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D2B79BF1FD25B7600F2094F /* PhotoBrowserDemoTests.m */; }; + 7D2B79C81FD25BF300F2094F /* IDMUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D2B79C71FD25BF300F2094F /* IDMUtilsTest.m */; }; D114BB411A32269C00E677FE /* Launch Screen.xib in Resources */ = {isa = PBXBuildFile; fileRef = D114BB401A32269C00E677FE /* Launch Screen.xib */; }; D1574968178DB94900B0211A /* IDMCaptionView.m in Sources */ = {isa = PBXBuildFile; fileRef = D157494F178DB94900B0211A /* IDMCaptionView.m */; }; D1574969178DB94900B0211A /* IDMPhoto.m in Sources */ = {isa = PBXBuildFile; fileRef = D1574951178DB94900B0211A /* IDMPhoto.m */; }; @@ -54,6 +57,16 @@ D1FA3A911805C340000FFE18 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1FA3A901805C340000FFE18 /* Security.framework */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 7D2B79C21FD25B7600F2094F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C6F978414AF734800F8389A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4C6F978C14AF734800F8389A; + remoteInfo = PhotoBrowserDemo; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ 4C6F978D14AF734900F8389A /* PhotoBrowserDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PhotoBrowserDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4C6F979114AF734900F8389A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; @@ -73,6 +86,14 @@ 64F5714840012BFE47F8331B /* libPods-PhotoBrowserDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PhotoBrowserDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 727988B0187B3B5400966C66 /* IDMPBLocalizations.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = IDMPBLocalizations.bundle; sourceTree = ""; }; 727988B6187B3CF100966C66 /* IDMPhotoBrowser.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = IDMPhotoBrowser.podspec; path = ../../IDMPhotoBrowser.podspec; sourceTree = ""; }; + 7D2B79B61FD2501200F2094F /* IDMUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IDMUtils.h; sourceTree = ""; }; + 7D2B79B71FD2501200F2094F /* IDMUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IDMUtils.m; sourceTree = ""; }; + 7D2B79BD1FD25B7600F2094F /* PhotoBrowserDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PhotoBrowserDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 7D2B79BF1FD25B7600F2094F /* PhotoBrowserDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PhotoBrowserDemoTests.m; sourceTree = ""; }; + 7D2B79C11FD25B7600F2094F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 7D2B79C71FD25BF300F2094F /* IDMUtilsTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IDMUtilsTest.m; sourceTree = ""; }; + 7D7D66691E09D6D400410A67 /* IDMBrowserDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IDMBrowserDelegate.h; sourceTree = ""; }; + 7D7D666A1E09D6DA00410A67 /* IDMPhotoDataSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IDMPhotoDataSource.h; sourceTree = ""; }; D00FA320EEACA8C835D9D626 /* Pods-PhotoBrowserDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PhotoBrowserDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PhotoBrowserDemo/Pods-PhotoBrowserDemo.debug.xcconfig"; sourceTree = ""; }; D11464B61802FFC4005B7BC3 /* IDMPBConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IDMPBConstants.h; sourceTree = ""; }; D114BB401A32269C00E677FE /* Launch Screen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = "Launch Screen.xib"; sourceTree = ""; }; @@ -136,6 +157,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 7D2B79BA1FD25B7600F2094F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -147,6 +175,7 @@ 4C6F97CA14AF75D300F8389A /* Sample Photos */, D19D825517E8D40700A5859E /* Custom Images */, 4C6F979814AF734900F8389A /* Supporting Files */, + 7D2B79BE1FD25B7600F2094F /* PhotoBrowserDemoTests */, 4C6F979014AF734900F8389A /* Frameworks */, 4C6F978E14AF734900F8389A /* Products */, 6D1B0039D6199AD450E98829 /* Pods */, @@ -157,6 +186,7 @@ isa = PBXGroup; children = ( 4C6F978D14AF734900F8389A /* PhotoBrowserDemo.app */, + 7D2B79BD1FD25B7600F2094F /* PhotoBrowserDemoTests.xctest */, ); name = Products; sourceTree = ""; @@ -231,6 +261,16 @@ name = Pods; sourceTree = ""; }; + 7D2B79BE1FD25B7600F2094F /* PhotoBrowserDemoTests */ = { + isa = PBXGroup; + children = ( + 7D2B79BF1FD25B7600F2094F /* PhotoBrowserDemoTests.m */, + 7D2B79C71FD25BF300F2094F /* IDMUtilsTest.m */, + 7D2B79C11FD25B7600F2094F /* Info.plist */, + ); + path = PhotoBrowserDemoTests; + sourceTree = ""; + }; D1574931178DB94900B0211A /* IDMPhotoBrowser */ = { isa = PBXGroup; children = ( @@ -241,6 +281,9 @@ D1574951178DB94900B0211A /* IDMPhoto.m */, D1574953178DB94900B0211A /* IDMPhotoBrowser.h */, D1574954178DB94900B0211A /* IDMPhotoBrowser.m */, + 7D2B79B61FD2501200F2094F /* IDMUtils.h */, + 7D2B79B71FD2501200F2094F /* IDMUtils.m */, + 7D7D666A1E09D6DA00410A67 /* IDMPhotoDataSource.h */, D1574955178DB94900B0211A /* IDMPhotoProtocol.h */, D1574956178DB94900B0211A /* IDMTapDetectingImageView.h */, D1574957178DB94900B0211A /* IDMTapDetectingImageView.m */, @@ -296,6 +339,24 @@ productReference = 4C6F978D14AF734900F8389A /* PhotoBrowserDemo.app */; productType = "com.apple.product-type.application"; }; + 7D2B79BC1FD25B7600F2094F /* PhotoBrowserDemoTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7D2B79C41FD25B7600F2094F /* Build configuration list for PBXNativeTarget "PhotoBrowserDemoTests" */; + buildPhases = ( + 7D2B79B91FD25B7600F2094F /* Sources */, + 7D2B79BA1FD25B7600F2094F /* Frameworks */, + 7D2B79BB1FD25B7600F2094F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 7D2B79C31FD25B7600F2094F /* PBXTargetDependency */, + ); + name = PhotoBrowserDemoTests; + productName = PhotoBrowserDemoTests; + productReference = 7D2B79BD1FD25B7600F2094F /* PhotoBrowserDemoTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -307,6 +368,11 @@ 4C6F978C14AF734800F8389A = { LastSwiftMigration = 0810; }; + 7D2B79BC1FD25B7600F2094F = { + CreatedOnToolsVersion = 9.1; + ProvisioningStyle = Automatic; + TestTargetID = 4C6F978C14AF734800F8389A; + }; }; }; buildConfigurationList = 4C6F978714AF734800F8389A /* Build configuration list for PBXProject "PhotoBrowserDemo" */; @@ -324,6 +390,7 @@ projectRoot = ""; targets = ( 4C6F978C14AF734800F8389A /* PhotoBrowserDemo */, + 7D2B79BC1FD25B7600F2094F /* PhotoBrowserDemoTests */, ); }; /* End PBXProject section */ @@ -361,6 +428,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 7D2B79BB1FD25B7600F2094F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -391,7 +465,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; F88445D630091F1AA9A2F89A /* [CP] Copy Pods Resources */ = { @@ -416,6 +490,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7D2B79B81FD2501300F2094F /* IDMUtils.m in Sources */, D1574968178DB94900B0211A /* IDMCaptionView.m in Sources */, D1A9ED541DEB685F00C238DA /* MenuViewController.swift in Sources */, D1574969178DB94900B0211A /* IDMPhoto.m in Sources */, @@ -427,8 +502,25 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 7D2B79B91FD25B7600F2094F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7D2B79C81FD25BF300F2094F /* IDMUtilsTest.m in Sources */, + 7D2B79C01FD25B7600F2094F /* PhotoBrowserDemoTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 7D2B79C31FD25B7600F2094F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C6F978C14AF734800F8389A /* PhotoBrowserDemo */; + targetProxy = 7D2B79C21FD25B7600F2094F /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ 4C6F97AC14AF734900F8389A /* Debug */ = { isa = XCBuildConfiguration; @@ -560,6 +652,86 @@ }; name = Release; }; + 7D2B79C51FD25B7600F2094F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = PhotoBrowserDemoTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.appkraft.PhotoBrowserDemoTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PhotoBrowserDemo.app/PhotoBrowserDemo"; + }; + name = Debug; + }; + 7D2B79C61FD25B7600F2094F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = PhotoBrowserDemoTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = com.appkraft.PhotoBrowserDemoTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PhotoBrowserDemo.app/PhotoBrowserDemo"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -581,6 +753,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 7D2B79C41FD25B7600F2094F /* Build configuration list for PBXNativeTarget "PhotoBrowserDemoTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7D2B79C51FD25B7600F2094F /* Debug */, + 7D2B79C61FD25B7600F2094F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 4C6F978414AF734800F8389A /* Project object */; diff --git a/Demo/PhotoBrowserDemoTests/IDMUtilsTest.m b/Demo/PhotoBrowserDemoTests/IDMUtilsTest.m new file mode 100644 index 00000000..cfbdb9c7 --- /dev/null +++ b/Demo/PhotoBrowserDemoTests/IDMUtilsTest.m @@ -0,0 +1,115 @@ +// +// IDMUtilsTest.m +// PhotoBrowserDemoTests +// +// Created by Oliver ONeill on 2/12/17. +// + +#import +#import "IDMUtils.h" + +@interface IDMUtilsTest : XCTestCase + +@end + +@implementation IDMUtilsTest + +- (void)setUp { + [super setUp]; +} + +- (void)tearDown { + [super tearDown]; +} + +- (void)testAdjustRectForSafeAreaInsets { + // given + CGRect rect = CGRectMake(0, 0, 100, 200); + CGRect bounds = rect; + UIEdgeInsets insets = UIEdgeInsetsMake(10, 10, 10, 10); + BOOL adjust = YES; + int statusBarHeight = 0; + // when + CGRect result = [IDMUtils adjustRect:rect forSafeAreaInsets:insets forBounds:bounds adjustForStatusBar:adjust statusBarHeight:statusBarHeight]; + // then + // since its moved 10 down and 10 to the left, the width and height are then + // decreased by 20 + CGRect expected = CGRectMake(10, 10, 80, 180); + XCTAssert(CGRectEqualToRect(result, expected)); +} + +- (void)testAdjustRectForSafeAreaInsetsDoesntModifyGivenZeroInsets { + // given + CGRect rect = CGRectMake(0, 0, 100, 200); + CGRect bounds = rect; + // no inset changes + UIEdgeInsets insets = UIEdgeInsetsZero; + BOOL adjust = YES; + int statusBarHeight = 0; + // when + CGRect result = [IDMUtils adjustRect:rect forSafeAreaInsets:insets forBounds:bounds adjustForStatusBar:adjust statusBarHeight:statusBarHeight]; + // then + // since there were no insets, the result should not change the rect + XCTAssert(CGRectEqualToRect(result, rect)); +} + +- (void)testAdjustRectForSafeAreaInsetsWithSmallBounds { + // given + CGRect rect = CGRectMake(0, 0, 100, 200); + // small bounds should not affect the view + CGRect bounds = CGRectMake(10, 10, 10, 20); + UIEdgeInsets insets = UIEdgeInsetsZero; + BOOL adjust = YES; + int statusBarHeight = 0; + // when + CGRect result = [IDMUtils adjustRect:rect forSafeAreaInsets:insets forBounds:bounds adjustForStatusBar:adjust statusBarHeight:statusBarHeight]; + // then + XCTAssert(CGRectEqualToRect(result, rect)); +} + +- (void)testAdjustRectForSafeAreaInsetsWithoutStatusBarAdjustment { + // given + CGRect rect = CGRectMake(0, 0, 100, 200); + CGRect bounds = CGRectMake(0, 0, 100, 200); + UIEdgeInsets insets = UIEdgeInsetsMake(10, 10, 10, 10); + BOOL adjust = NO; + int statusBarHeight = 0; + // when + CGRect result = [IDMUtils adjustRect:rect forSafeAreaInsets:insets forBounds:bounds adjustForStatusBar:adjust statusBarHeight:statusBarHeight]; + // then + // since its moved 10 down and 10 to the left, the width and height are then + // decreased by 20 + CGRect expected = CGRectMake(10, 0, 80, 190); + XCTAssert(CGRectEqualToRect(result, expected)); +} + +- (void)testAdjustRectForSafeAreaInsetsShiftsViewsUpInsteadOfResize { + // given + CGRect rect = CGRectMake(0, 20, 100, 200); + CGRect bounds = CGRectMake(0, 0, 100, 200); + UIEdgeInsets insets = UIEdgeInsetsMake(10, 10, 10, 10); + BOOL adjust = NO; + int statusBarHeight = 0; + // when + CGRect result = [IDMUtils adjustRect:rect forSafeAreaInsets:insets forBounds:bounds adjustForStatusBar:adjust statusBarHeight:statusBarHeight]; + // then + // the view was moved up by 10 on the y axis to move above the inset + CGRect expected = CGRectMake(10, 10, 80, 200); + XCTAssert(CGRectEqualToRect(result, expected)); +} + +- (void)testAdjustRectForSafeAreaInsetsUsesStatusBarHeight { + // given + CGRect rect = CGRectMake(0, 0, 100, 200); + CGRect bounds = CGRectMake(0, 0, 100, 200); + UIEdgeInsets insets = UIEdgeInsetsZero; + BOOL adjust = YES; + int statusBarHeight = 10; + // when + CGRect result = [IDMUtils adjustRect:rect forSafeAreaInsets:insets forBounds:bounds adjustForStatusBar:adjust statusBarHeight:statusBarHeight]; + // then + // the view is moved down by 10 and the height is decreased + CGRect expected = CGRectMake(0, 10, 100, 190); + XCTAssert(CGRectEqualToRect(result, expected)); +} +@end diff --git a/Demo/PhotoBrowserDemoTests/Info.plist b/Demo/PhotoBrowserDemoTests/Info.plist new file mode 100644 index 00000000..6c40a6cd --- /dev/null +++ b/Demo/PhotoBrowserDemoTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Demo/PhotoBrowserDemoTests/PhotoBrowserDemoTests.m b/Demo/PhotoBrowserDemoTests/PhotoBrowserDemoTests.m new file mode 100644 index 00000000..32f07286 --- /dev/null +++ b/Demo/PhotoBrowserDemoTests/PhotoBrowserDemoTests.m @@ -0,0 +1,15 @@ +// +// PhotoBrowserDemoTests.m +// PhotoBrowserDemoTests +// +// Created by Oliver ONeill on 2/12/17. +// + +#import + +@interface PhotoBrowserDemoTests : XCTestCase + +@end + +@implementation PhotoBrowserDemoTests +@end