From 5e69d896cdf8ddde89d0a10ee06f748dad097f9c Mon Sep 17 00:00:00 2001 From: Oliver Date: Mon, 27 Nov 2017 18:06:25 +1100 Subject: [PATCH] Add iPhone X support Add iPhone X support Fix done button moving when the status bar is hidden I've tried to keep the use of doneButtonTopInset to be the same even with the new requirements for iPhone X Fix iPhone X support bugs with orientation There were issue when switching between landscape and portrait orientation specifically to do with changes iPhone X safe area insets. Fix present animation Cleanup tests Fix done button status bar bug When the status bar is hidden, its height property is set to zero, this makes it difficult to position elements relative to that without them moving when its hidden. To fix this I've hardcoded the value to twenty. There wasn't an easy way of getting this number without displaying the status bar Change present animation to simpler calculation Update IDMUtils header Undo unnecessary safeArea adjustment --- Classes/IDMPhotoBrowser.m | 45 ++++- 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, 435 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..6ab314ed 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 = UIEdgeInsetsZero; + 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,17 @@ - (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]; + } + return rect; +} + +- (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