Release 0.1.0
This commit is contained in:
1 parent
fd2c9b2650
commit
bb19fe0f7d
66 files changed
+3772
-14
No files matched your search
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:HamburgerButton.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -8,5 +8,4 @@ end
|
|||||||
target 'HamburgerButton_Tests', :exclusive => true do
|
target 'HamburgerButton_Tests', :exclusive => true do
|
||||||
pod "HamburgerButton", :path => "../"
|
pod "HamburgerButton", :path => "../"
|
||||||
|
|
||||||
pod 'FBSnapshotTestCase'
|
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
PODS:
|
||||||
|
- FBSnapshotTestCase (2.0.3):
|
||||||
|
- FBSnapshotTestCase/SwiftSupport (= 2.0.3)
|
||||||
|
- FBSnapshotTestCase/Core (2.0.3)
|
||||||
|
- FBSnapshotTestCase/SwiftSupport (2.0.3):
|
||||||
|
- FBSnapshotTestCase/Core
|
||||||
|
- HamburgerButton (0.1.0)
|
||||||
|
|
||||||
|
DEPENDENCIES:
|
||||||
|
- FBSnapshotTestCase
|
||||||
|
- HamburgerButton (from `../`)
|
||||||
|
|
||||||
|
EXTERNAL SOURCES:
|
||||||
|
HamburgerButton:
|
||||||
|
:path: ../
|
||||||
|
|
||||||
|
SPEC CHECKSUMS:
|
||||||
|
FBSnapshotTestCase: d0eeca6bf87958e088b254396ae66873f971cfb1
|
||||||
|
HamburgerButton: 7c98f3258cc3b794872532fe825da3ef797adbb3
|
||||||
|
|
||||||
|
COCOAPODS: 0.38.2
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// Created by Gabriel Handford on 3/1/09.
|
||||||
|
// Copyright 2009-2013. All rights reserved.
|
||||||
|
// Created by John Boiles on 10/20/11.
|
||||||
|
// Copyright (c) 2011. All rights reserved
|
||||||
|
// Modified by Felix Schulze on 2/11/13.
|
||||||
|
// Copyright 2013. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface UIImage (Compare)
|
||||||
|
|
||||||
|
- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance;
|
||||||
|
|
||||||
|
@end
|
||||||
+134
@@ -0,0 +1,134 @@
|
|||||||
|
//
|
||||||
|
// Created by Gabriel Handford on 3/1/09.
|
||||||
|
// Copyright 2009-2013. All rights reserved.
|
||||||
|
// Created by John Boiles on 10/20/11.
|
||||||
|
// Copyright (c) 2011. All rights reserved
|
||||||
|
// Modified by Felix Schulze on 2/11/13.
|
||||||
|
// Copyright 2013. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 <FBSnapshotTestCase/UIImage+Compare.h>
|
||||||
|
|
||||||
|
// This makes debugging much more fun
|
||||||
|
typedef union {
|
||||||
|
uint32_t raw;
|
||||||
|
unsigned char bytes[4];
|
||||||
|
struct {
|
||||||
|
char red;
|
||||||
|
char green;
|
||||||
|
char blue;
|
||||||
|
char alpha;
|
||||||
|
} __attribute__ ((packed)) pixels;
|
||||||
|
} FBComparePixel;
|
||||||
|
|
||||||
|
@implementation UIImage (Compare)
|
||||||
|
|
||||||
|
- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance
|
||||||
|
{
|
||||||
|
NSAssert(CGSizeEqualToSize(self.size, image.size), @"Images must be same size.");
|
||||||
|
|
||||||
|
CGSize referenceImageSize = CGSizeMake(CGImageGetWidth(self.CGImage), CGImageGetHeight(self.CGImage));
|
||||||
|
CGSize imageSize = CGSizeMake(CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage));
|
||||||
|
|
||||||
|
// The images have the equal size, so we could use the smallest amount of bytes because of byte padding
|
||||||
|
size_t minBytesPerRow = MIN(CGImageGetBytesPerRow(self.CGImage), CGImageGetBytesPerRow(image.CGImage));
|
||||||
|
size_t referenceImageSizeBytes = referenceImageSize.height * minBytesPerRow;
|
||||||
|
void *referenceImagePixels = calloc(1, referenceImageSizeBytes);
|
||||||
|
void *imagePixels = calloc(1, referenceImageSizeBytes);
|
||||||
|
|
||||||
|
if (!referenceImagePixels || !imagePixels) {
|
||||||
|
free(referenceImagePixels);
|
||||||
|
free(imagePixels);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGContextRef referenceImageContext = CGBitmapContextCreate(referenceImagePixels,
|
||||||
|
referenceImageSize.width,
|
||||||
|
referenceImageSize.height,
|
||||||
|
CGImageGetBitsPerComponent(self.CGImage),
|
||||||
|
minBytesPerRow,
|
||||||
|
CGImageGetColorSpace(self.CGImage),
|
||||||
|
(CGBitmapInfo)kCGImageAlphaPremultipliedLast
|
||||||
|
);
|
||||||
|
CGContextRef imageContext = CGBitmapContextCreate(imagePixels,
|
||||||
|
imageSize.width,
|
||||||
|
imageSize.height,
|
||||||
|
CGImageGetBitsPerComponent(image.CGImage),
|
||||||
|
minBytesPerRow,
|
||||||
|
CGImageGetColorSpace(image.CGImage),
|
||||||
|
(CGBitmapInfo)kCGImageAlphaPremultipliedLast
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!referenceImageContext || !imageContext) {
|
||||||
|
CGContextRelease(referenceImageContext);
|
||||||
|
CGContextRelease(imageContext);
|
||||||
|
free(referenceImagePixels);
|
||||||
|
free(imagePixels);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGContextDrawImage(referenceImageContext, CGRectMake(0, 0, referenceImageSize.width, referenceImageSize.height), self.CGImage);
|
||||||
|
CGContextDrawImage(imageContext, CGRectMake(0, 0, imageSize.width, imageSize.height), image.CGImage);
|
||||||
|
|
||||||
|
CGContextRelease(referenceImageContext);
|
||||||
|
CGContextRelease(imageContext);
|
||||||
|
|
||||||
|
BOOL imageEqual = YES;
|
||||||
|
|
||||||
|
// Do a fast compare if we can
|
||||||
|
if (tolerance == 0) {
|
||||||
|
imageEqual = (memcmp(referenceImagePixels, imagePixels, referenceImageSizeBytes) == 0);
|
||||||
|
} else {
|
||||||
|
// Go through each pixel in turn and see if it is different
|
||||||
|
const NSInteger pixelCount = self.size.width * self.size.height;
|
||||||
|
|
||||||
|
FBComparePixel *p1 = referenceImagePixels;
|
||||||
|
FBComparePixel *p2 = imagePixels;
|
||||||
|
|
||||||
|
NSInteger numDiffPixels = 0;
|
||||||
|
for (int n = 0; n < pixelCount; ++n) {
|
||||||
|
// If this pixel is different, increment the pixel diff count and see
|
||||||
|
// if we have hit our limit.
|
||||||
|
if (p1->raw != p2->raw) {
|
||||||
|
numDiffPixels ++;
|
||||||
|
|
||||||
|
CGFloat percent = (CGFloat)numDiffPixels / pixelCount;
|
||||||
|
if (percent > tolerance) {
|
||||||
|
imageEqual = NO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p1++;
|
||||||
|
p2++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(referenceImagePixels);
|
||||||
|
free(imagePixels);
|
||||||
|
|
||||||
|
return imageEqual;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// Created by Gabriel Handford on 3/1/09.
|
||||||
|
// Copyright 2009-2013. All rights reserved.
|
||||||
|
// Created by John Boiles on 10/20/11.
|
||||||
|
// Copyright (c) 2011. All rights reserved
|
||||||
|
// Modified by Felix Schulze on 2/11/13.
|
||||||
|
// Copyright 2013. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface UIImage (Diff)
|
||||||
|
|
||||||
|
- (UIImage *)fb_diffWithImage:(UIImage *)image;
|
||||||
|
|
||||||
|
@end
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
//
|
||||||
|
// Created by Gabriel Handford on 3/1/09.
|
||||||
|
// Copyright 2009-2013. All rights reserved.
|
||||||
|
// Created by John Boiles on 10/20/11.
|
||||||
|
// Copyright (c) 2011. All rights reserved
|
||||||
|
// Modified by Felix Schulze on 2/11/13.
|
||||||
|
// Copyright 2013. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 <FBSnapshotTestCase/UIImage+Diff.h>
|
||||||
|
|
||||||
|
@implementation UIImage (Diff)
|
||||||
|
|
||||||
|
- (UIImage *)fb_diffWithImage:(UIImage *)image
|
||||||
|
{
|
||||||
|
if (!image) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
CGSize imageSize = CGSizeMake(MAX(self.size.width, image.size.width), MAX(self.size.height, image.size.height));
|
||||||
|
UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0);
|
||||||
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
|
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
|
||||||
|
CGContextSetAlpha(context, 0.5);
|
||||||
|
CGContextBeginTransparencyLayer(context, NULL);
|
||||||
|
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
|
||||||
|
CGContextSetBlendMode(context, kCGBlendModeDifference);
|
||||||
|
CGContextSetFillColorWithColor(context,[UIColor whiteColor].CGColor);
|
||||||
|
CGContextFillRect(context, CGRectMake(0, 0, self.size.width, self.size.height));
|
||||||
|
CGContextEndTransparencyLayer(context);
|
||||||
|
UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
|
return returnImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface UIImage (Snapshot)
|
||||||
|
|
||||||
|
/// Uses renderInContext: to get a snapshot of the layer.
|
||||||
|
+ (UIImage *)fb_imageForLayer:(CALayer *)layer;
|
||||||
|
|
||||||
|
/// Uses renderInContext: to get a snapshot of the view layer.
|
||||||
|
+ (UIImage *)fb_imageForViewLayer:(UIView *)view;
|
||||||
|
|
||||||
|
/// Uses drawViewHierarchyInRect: to get a snapshot of the view and adds the view into a window if needed.
|
||||||
|
+ (UIImage *)fb_imageForView:(UIView *)view;
|
||||||
|
|
||||||
|
@end
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <FBSnapshotTestCase/UIImage+Snapshot.h>
|
||||||
|
|
||||||
|
@implementation UIImage (Snapshot)
|
||||||
|
|
||||||
|
+ (UIImage *)fb_imageForLayer:(CALayer *)layer
|
||||||
|
{
|
||||||
|
CGRect bounds = layer.bounds;
|
||||||
|
NSAssert1(CGRectGetWidth(bounds), @"Zero width for layer %@", layer);
|
||||||
|
NSAssert1(CGRectGetHeight(bounds), @"Zero height for layer %@", layer);
|
||||||
|
|
||||||
|
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
|
||||||
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
|
NSAssert1(context, @"Could not generate context for layer %@", layer);
|
||||||
|
CGContextSaveGState(context);
|
||||||
|
[layer layoutIfNeeded];
|
||||||
|
[layer renderInContext:context];
|
||||||
|
CGContextRestoreGState(context);
|
||||||
|
|
||||||
|
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (UIImage *)fb_imageForViewLayer:(UIView *)view
|
||||||
|
{
|
||||||
|
[view layoutIfNeeded];
|
||||||
|
return [self fb_imageForLayer:view.layer];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (UIImage *)fb_imageForView:(UIView *)view
|
||||||
|
{
|
||||||
|
CGRect bounds = view.bounds;
|
||||||
|
NSAssert1(CGRectGetWidth(bounds), @"Zero width for view %@", view);
|
||||||
|
NSAssert1(CGRectGetHeight(bounds), @"Zero height for view %@", view);
|
||||||
|
|
||||||
|
UIWindow *window = view.window;
|
||||||
|
if (window == nil) {
|
||||||
|
window = [[UIWindow alloc] initWithFrame:bounds];
|
||||||
|
[window addSubview:view];
|
||||||
|
[window makeKeyAndVisible];
|
||||||
|
}
|
||||||
|
|
||||||
|
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
|
||||||
|
[view layoutIfNeeded];
|
||||||
|
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
|
||||||
|
|
||||||
|
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
|
||||||
|
|
||||||
|
#import <QuartzCore/QuartzCore.h>
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
|
||||||
|
@param view The view to snapshot
|
||||||
|
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
|
||||||
|
@param suffixes An NSOrderedSet of strings for the different suffixes
|
||||||
|
@param tolerance The percentage of pixels that can differ and still count as an 'identical' view
|
||||||
|
*/
|
||||||
|
#define FBSnapshotVerifyViewWithOptions(view__, identifier__, suffixes__, tolerance__) \
|
||||||
|
{ \
|
||||||
|
NSString *envReferenceImageDirectory = [NSProcessInfo processInfo].environment[@"FB_REFERENCE_IMAGE_DIR"]; \
|
||||||
|
NSError *error__ = nil; \
|
||||||
|
BOOL comparisonSuccess__; \
|
||||||
|
XCTAssertTrue((suffixes__.count > 0), @"Suffixes set cannot be empty %@", suffixes__); \
|
||||||
|
XCTAssertNotNil(envReferenceImageDirectory, @"Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.");\
|
||||||
|
for (NSString *suffix__ in suffixes__) { \
|
||||||
|
NSString *referenceImagesDirectory__ = [NSString stringWithFormat:@"%@%@", envReferenceImageDirectory, suffix__]; \
|
||||||
|
comparisonSuccess__ = [self compareSnapshotOfView:(view__) referenceImagesDirectory:referenceImagesDirectory__ identifier:(identifier__) tolerance:(tolerance__) error:&error__]; \
|
||||||
|
if (comparisonSuccess__ || self.recordMode) break; \
|
||||||
|
} \
|
||||||
|
XCTAssertTrue(comparisonSuccess__, @"Snapshot comparison failed: %@", error__); \
|
||||||
|
XCTAssertFalse(self.recordMode, @"Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FBSnapshotVerifyView(view__, identifier__) \
|
||||||
|
{ \
|
||||||
|
FBSnapshotVerifyViewWithOptions(view__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
|
||||||
|
@param layer The layer to snapshot
|
||||||
|
@param identifier An optional identifier, used is there are multiple snapshot tests in a given -test method.
|
||||||
|
@param suffixes An NSOrderedSet of strings for the different suffixes
|
||||||
|
@param tolerance The percentage of pixels that can differ and still count as an 'identical' layer
|
||||||
|
*/
|
||||||
|
#define FBSnapshotVerifyLayerWithOptions(layer__, identifier__, suffixes__, tolerance__) \
|
||||||
|
{ \
|
||||||
|
NSString *envReferenceImageDirectory = [NSProcessInfo processInfo].environment[@"FB_REFERENCE_IMAGE_DIR"]; \
|
||||||
|
NSError *error__ = nil; \
|
||||||
|
BOOL comparisonSuccess__; \
|
||||||
|
XCTAssertTrue((suffixes__.count > 0), @"Suffixes set cannot be empty %@", suffixes__); \
|
||||||
|
XCTAssertNotNil(envReferenceImageDirectory, @"Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.");\
|
||||||
|
for (NSString *suffix__ in suffixes__) { \
|
||||||
|
NSString *referenceImagesDirectory__ = [NSString stringWithFormat:@"%@%@", envReferenceImageDirectory, suffix__]; \
|
||||||
|
comparisonSuccess__ = [self compareSnapshotOfLayer:(layer__) referenceImagesDirectory:referenceImagesDirectory__ identifier:(identifier__) tolerance:(tolerance__) error:&error__]; \
|
||||||
|
if (comparisonSuccess__ || self.recordMode) break; \
|
||||||
|
} \
|
||||||
|
XCTAssertTrue(comparisonSuccess__, @"Snapshot comparison failed: %@", error__); \
|
||||||
|
XCTAssertFalse(self.recordMode, @"Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FBSnapshotVerifyLayer(layer__, identifier__) \
|
||||||
|
{ \
|
||||||
|
FBSnapshotVerifyLayerWithOptions(layer__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
The base class of view snapshotting tests. If you have small UI component, it's often easier to configure it in a test
|
||||||
|
and compare an image of the view to a reference image that write lots of complex layout-code tests.
|
||||||
|
|
||||||
|
In order to flip the tests in your subclass to record the reference images set @c recordMode to @c YES.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
@code
|
||||||
|
- (void)setUp
|
||||||
|
{
|
||||||
|
[super setUp];
|
||||||
|
self.recordMode = YES;
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
*/
|
||||||
|
@interface FBSnapshotTestCase : XCTestCase
|
||||||
|
|
||||||
|
/**
|
||||||
|
When YES, the test macros will save reference images, rather than performing an actual test.
|
||||||
|
*/
|
||||||
|
@property (readwrite, nonatomic, assign) BOOL recordMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
When YES, renders a snapshot of the complete view hierarchy as visible onscreen.
|
||||||
|
There are several things that do not work if renderInContext: is used.
|
||||||
|
- UIVisualEffect #70
|
||||||
|
- UIAppearance #91
|
||||||
|
- Size Classes #92
|
||||||
|
|
||||||
|
@attention If the view does't belong to a UIWindow, it will create one and add the view as a subview.
|
||||||
|
*/
|
||||||
|
@property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect;
|
||||||
|
|
||||||
|
- (void)setUp NS_REQUIRES_SUPER;
|
||||||
|
- (void)tearDown NS_REQUIRES_SUPER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Performs the comparison or records a snapshot of the layer if recordMode is YES.
|
||||||
|
@param layer The Layer to snapshot
|
||||||
|
@param referenceImagesDirectory The directory in which reference images are stored.
|
||||||
|
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
|
||||||
|
@param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
|
||||||
|
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||||
|
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||||
|
*/
|
||||||
|
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||||
|
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
tolerance:(CGFloat)tolerance
|
||||||
|
error:(NSError **)errorPtr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Performs the comparison or records a snapshot of the view if recordMode is YES.
|
||||||
|
@param view The view to snapshot
|
||||||
|
@param referenceImagesDirectory The directory in which reference images are stored.
|
||||||
|
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
|
||||||
|
@param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
|
||||||
|
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||||
|
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||||
|
*/
|
||||||
|
- (BOOL)compareSnapshotOfView:(UIView *)view
|
||||||
|
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
tolerance:(CGFloat)tolerance
|
||||||
|
error:(NSError **)errorPtr;
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <FBSnapshotTestCase/FBSnapshotTestCase.h>
|
||||||
|
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
|
||||||
|
|
||||||
|
@implementation FBSnapshotTestCase
|
||||||
|
{
|
||||||
|
FBSnapshotTestController *_snapshotController;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Overrides
|
||||||
|
|
||||||
|
- (void)setUp
|
||||||
|
{
|
||||||
|
[super setUp];
|
||||||
|
_snapshotController = [[FBSnapshotTestController alloc] initWithTestName:NSStringFromClass([self class])];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tearDown
|
||||||
|
{
|
||||||
|
_snapshotController = nil;
|
||||||
|
[super tearDown];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)recordMode
|
||||||
|
{
|
||||||
|
return _snapshotController.recordMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setRecordMode:(BOOL)recordMode
|
||||||
|
{
|
||||||
|
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
|
||||||
|
_snapshotController.recordMode = recordMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)usesDrawViewHierarchyInRect
|
||||||
|
{
|
||||||
|
return _snapshotController.usesDrawViewHierarchyInRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect
|
||||||
|
{
|
||||||
|
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
|
||||||
|
_snapshotController.usesDrawViewHierarchyInRect = usesDrawViewHierarchyInRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public API
|
||||||
|
|
||||||
|
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||||
|
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
tolerance:(CGFloat)tolerance
|
||||||
|
error:(NSError **)errorPtr
|
||||||
|
{
|
||||||
|
return [self _compareSnapshotOfViewOrLayer:layer
|
||||||
|
referenceImagesDirectory:referenceImagesDirectory
|
||||||
|
identifier:identifier
|
||||||
|
tolerance:tolerance
|
||||||
|
error:errorPtr];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)compareSnapshotOfView:(UIView *)view
|
||||||
|
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
tolerance:(CGFloat)tolerance
|
||||||
|
error:(NSError **)errorPtr
|
||||||
|
{
|
||||||
|
return [self _compareSnapshotOfViewOrLayer:view
|
||||||
|
referenceImagesDirectory:referenceImagesDirectory
|
||||||
|
identifier:identifier
|
||||||
|
tolerance:tolerance
|
||||||
|
error:errorPtr];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private API
|
||||||
|
|
||||||
|
- (BOOL)_compareSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||||
|
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
tolerance:(CGFloat)tolerance
|
||||||
|
error:(NSError **)errorPtr
|
||||||
|
{
|
||||||
|
_snapshotController.referenceImagesDirectory = referenceImagesDirectory;
|
||||||
|
return [_snapshotController compareSnapshotOfViewOrLayer:viewOrLayer
|
||||||
|
selector:self.invocation.selector
|
||||||
|
identifier:identifier
|
||||||
|
tolerance:tolerance
|
||||||
|
error:errorPtr];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns a Boolean value that indicates whether the snapshot test is running in 64Bit.
|
||||||
|
This method is a convenience for creating the suffixes set based on the architecture
|
||||||
|
that the test is running.
|
||||||
|
|
||||||
|
@returns @c YES if the test is running in 64bit, otherwise @c NO.
|
||||||
|
*/
|
||||||
|
BOOL FBSnapshotTestCaseIs64Bit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns a default set of strings that is used to append a suffix based on the architectures.
|
||||||
|
@warning Do not modify this function, you can create your own and use it with @c FBSnapshotVerifyViewWithOptions()
|
||||||
|
|
||||||
|
@returns An @c NSOrderedSet object containing strings that are appended to the reference images directory.
|
||||||
|
*/
|
||||||
|
NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
|
||||||
|
|
||||||
|
BOOL FBSnapshotTestCaseIs64Bit(void)
|
||||||
|
{
|
||||||
|
#if __LP64__
|
||||||
|
return YES;
|
||||||
|
#else
|
||||||
|
return NO;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void)
|
||||||
|
{
|
||||||
|
NSMutableOrderedSet *suffixesSet = [[NSMutableOrderedSet alloc] init];
|
||||||
|
[suffixesSet addObject:@"_32"];
|
||||||
|
[suffixesSet addObject:@"_64"];
|
||||||
|
if (FBSnapshotTestCaseIs64Bit()) {
|
||||||
|
return [suffixesSet reversedOrderedSet];
|
||||||
|
}
|
||||||
|
return [suffixesSet copy];
|
||||||
|
}
|
||||||
+145
@@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSInteger, FBSnapshotTestControllerErrorCode) {
|
||||||
|
FBSnapshotTestControllerErrorCodeUnknown,
|
||||||
|
FBSnapshotTestControllerErrorCodeNeedsRecord,
|
||||||
|
FBSnapshotTestControllerErrorCodePNGCreationFailed,
|
||||||
|
FBSnapshotTestControllerErrorCodeImagesDifferentSizes,
|
||||||
|
FBSnapshotTestControllerErrorCodeImagesDifferent,
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
Errors returned by the methods of FBSnapshotTestController use this domain.
|
||||||
|
*/
|
||||||
|
extern NSString *const FBSnapshotTestControllerErrorDomain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
|
||||||
|
*/
|
||||||
|
extern NSString *const FBReferenceImageFilePathKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Provides the heavy-lifting for FBSnapshotTestCase. It loads and saves images, along with performing the actual pixel-
|
||||||
|
by-pixel comparison of images.
|
||||||
|
Instances are initialized with the test class, and directories to read and write to.
|
||||||
|
*/
|
||||||
|
@interface FBSnapshotTestController : NSObject
|
||||||
|
|
||||||
|
/**
|
||||||
|
Record snapshots.
|
||||||
|
*/
|
||||||
|
@property (readwrite, nonatomic, assign) BOOL recordMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Uses drawViewHierarchyInRect:afterScreenUpdates: to draw the image instead of renderInContext:
|
||||||
|
*/
|
||||||
|
@property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
The directory in which referfence images are stored.
|
||||||
|
*/
|
||||||
|
@property (readwrite, nonatomic, copy) NSString *referenceImagesDirectory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
@param testClass The subclass of FBSnapshotTestCase that is using this controller.
|
||||||
|
@returns An instance of FBSnapshotTestController.
|
||||||
|
*/
|
||||||
|
- (instancetype)initWithTestClass:(Class)testClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Designated initializer.
|
||||||
|
@param testName The name of the tests.
|
||||||
|
@returns An instance of FBSnapshotTestController.
|
||||||
|
*/
|
||||||
|
- (instancetype)initWithTestName:(NSString *)testName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Performs the comparison of the layer.
|
||||||
|
@param layer The Layer to snapshot.
|
||||||
|
@param selector The test method being run.
|
||||||
|
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
|
||||||
|
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||||
|
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||||
|
*/
|
||||||
|
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||||
|
selector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
error:(NSError **)errorPtr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Performs the comparison of the view.
|
||||||
|
@param view The view to snapshot.
|
||||||
|
@param selector The test method being run.
|
||||||
|
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
|
||||||
|
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||||
|
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||||
|
*/
|
||||||
|
- (BOOL)compareSnapshotOfView:(UIView *)view
|
||||||
|
selector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
error:(NSError **)errorPtr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Performs the comparison of a view or layer.
|
||||||
|
@param view The view or layer to snapshot.
|
||||||
|
@param selector The test method being run.
|
||||||
|
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
|
||||||
|
@param tolerance The percentage of pixels that can differ and still be considered 'identical'
|
||||||
|
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||||
|
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||||
|
*/
|
||||||
|
- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||||
|
selector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
tolerance:(CGFloat)tolerance
|
||||||
|
error:(NSError **)errorPtr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Loads a reference image.
|
||||||
|
@param selector The test method being run.
|
||||||
|
@param identifier The optional identifier, used when multiple images are tested in a single -test method.
|
||||||
|
@param errorPtr An error, if this methods returns nil, the error will be something useful.
|
||||||
|
@returns An image.
|
||||||
|
*/
|
||||||
|
- (UIImage *)referenceImageForSelector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
error:(NSError **)errorPtr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Performs a pixel-by-pixel comparison of the two images with an allowable margin of error.
|
||||||
|
@param referenceImage The reference (correct) image.
|
||||||
|
@param image The image to test against the reference.
|
||||||
|
@param tolerance The percentage of pixels that can differ and still be considered 'identical'
|
||||||
|
@param errorPtr An error that indicates why the comparison failed if it does.
|
||||||
|
@returns YES if the comparison succeeded and the images are the same(ish).
|
||||||
|
*/
|
||||||
|
- (BOOL)compareReferenceImage:(UIImage *)referenceImage
|
||||||
|
toImage:(UIImage *)image
|
||||||
|
tolerance:(CGFloat)tolerance
|
||||||
|
error:(NSError **)errorPtr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Saves the reference image and the test image to `failedOutputDirectory`.
|
||||||
|
@param referenceImage The reference (correct) image.
|
||||||
|
@param testImage The image to test against the reference.
|
||||||
|
@param selector The test method being run.
|
||||||
|
@param identifier The optional identifier, used when multiple images are tested in a single -test method.
|
||||||
|
@param errorPtr An error that indicates why the comparison failed if it does.
|
||||||
|
@returns YES if the save succeeded.
|
||||||
|
*/
|
||||||
|
- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
|
||||||
|
testImage:(UIImage *)testImage
|
||||||
|
selector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
error:(NSError **)errorPtr;
|
||||||
|
@end
|
||||||
+348
@@ -0,0 +1,348 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
|
||||||
|
#import <FBSnapshotTestCase/UIImage+Compare.h>
|
||||||
|
#import <FBSnapshotTestCase/UIImage+Diff.h>
|
||||||
|
#import <FBSnapshotTestCase/UIImage+Snapshot.h>
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
NSString *const FBSnapshotTestControllerErrorDomain = @"FBSnapshotTestControllerErrorDomain";
|
||||||
|
NSString *const FBReferenceImageFilePathKey = @"FBReferenceImageFilePathKey";
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
|
||||||
|
FBTestSnapshotFileNameTypeReference,
|
||||||
|
FBTestSnapshotFileNameTypeFailedReference,
|
||||||
|
FBTestSnapshotFileNameTypeFailedTest,
|
||||||
|
FBTestSnapshotFileNameTypeFailedTestDiff,
|
||||||
|
};
|
||||||
|
|
||||||
|
@implementation FBSnapshotTestController
|
||||||
|
{
|
||||||
|
NSString *_testName;
|
||||||
|
NSFileManager *_fileManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Initializers
|
||||||
|
|
||||||
|
- (instancetype)initWithTestClass:(Class)testClass;
|
||||||
|
{
|
||||||
|
return [self initWithTestName:NSStringFromClass(testClass)];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithTestName:(NSString *)testName
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
_testName = [testName copy];
|
||||||
|
_fileManager = [[NSFileManager alloc] init];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Overrides
|
||||||
|
|
||||||
|
- (NSString *)description
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat:@"%@ %@", [super description], _referenceImagesDirectory];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public API
|
||||||
|
|
||||||
|
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||||
|
selector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
error:(NSError **)errorPtr
|
||||||
|
{
|
||||||
|
return [self compareSnapshotOfViewOrLayer:layer
|
||||||
|
selector:selector
|
||||||
|
identifier:identifier
|
||||||
|
tolerance:0
|
||||||
|
error:errorPtr];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)compareSnapshotOfView:(UIView *)view
|
||||||
|
selector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
error:(NSError **)errorPtr
|
||||||
|
{
|
||||||
|
return [self compareSnapshotOfViewOrLayer:view
|
||||||
|
selector:selector
|
||||||
|
identifier:identifier
|
||||||
|
tolerance:0
|
||||||
|
error:errorPtr];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||||
|
selector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
tolerance:(CGFloat)tolerance
|
||||||
|
error:(NSError **)errorPtr
|
||||||
|
{
|
||||||
|
if (self.recordMode) {
|
||||||
|
return [self _recordSnapshotOfViewOrLayer:viewOrLayer selector:selector identifier:identifier error:errorPtr];
|
||||||
|
} else {
|
||||||
|
return [self _performPixelComparisonWithViewOrLayer:viewOrLayer selector:selector identifier:identifier tolerance:tolerance error:errorPtr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImage *)referenceImageForSelector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
error:(NSError **)errorPtr
|
||||||
|
{
|
||||||
|
NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
|
||||||
|
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
|
||||||
|
if (nil == image && NULL != errorPtr) {
|
||||||
|
BOOL exists = [_fileManager fileExistsAtPath:filePath];
|
||||||
|
if (!exists) {
|
||||||
|
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||||
|
code:FBSnapshotTestControllerErrorCodeNeedsRecord
|
||||||
|
userInfo:@{
|
||||||
|
FBReferenceImageFilePathKey: filePath,
|
||||||
|
NSLocalizedDescriptionKey: @"Unable to load reference image.",
|
||||||
|
NSLocalizedFailureReasonErrorKey: @"Reference image not found. You need to run the test in record mode",
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||||
|
code:FBSnapshotTestControllerErrorCodeUnknown
|
||||||
|
userInfo:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)compareReferenceImage:(UIImage *)referenceImage
|
||||||
|
toImage:(UIImage *)image
|
||||||
|
tolerance:(CGFloat)tolerance
|
||||||
|
error:(NSError **)errorPtr
|
||||||
|
{
|
||||||
|
if (CGSizeEqualToSize(referenceImage.size, image.size)) {
|
||||||
|
BOOL imagesEqual = [referenceImage fb_compareWithImage:image tolerance:tolerance];
|
||||||
|
if (NULL != errorPtr) {
|
||||||
|
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||||
|
code:FBSnapshotTestControllerErrorCodeImagesDifferent
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey: @"Images different",
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
return imagesEqual;
|
||||||
|
}
|
||||||
|
if (NULL != errorPtr) {
|
||||||
|
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||||
|
code:FBSnapshotTestControllerErrorCodeImagesDifferentSizes
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey: @"Images different sizes",
|
||||||
|
NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"referenceImage:%@, image:%@",
|
||||||
|
NSStringFromCGSize(referenceImage.size),
|
||||||
|
NSStringFromCGSize(image.size)],
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
|
||||||
|
testImage:(UIImage *)testImage
|
||||||
|
selector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
error:(NSError **)errorPtr
|
||||||
|
{
|
||||||
|
NSData *referencePNGData = UIImagePNGRepresentation(referenceImage);
|
||||||
|
NSData *testPNGData = UIImagePNGRepresentation(testImage);
|
||||||
|
|
||||||
|
NSString *referencePath = [self _failedFilePathForSelector:selector
|
||||||
|
identifier:identifier
|
||||||
|
fileNameType:FBTestSnapshotFileNameTypeFailedReference];
|
||||||
|
|
||||||
|
NSError *creationError = nil;
|
||||||
|
BOOL didCreateDir = [_fileManager createDirectoryAtPath:[referencePath stringByDeletingLastPathComponent]
|
||||||
|
withIntermediateDirectories:YES
|
||||||
|
attributes:nil
|
||||||
|
error:&creationError];
|
||||||
|
if (!didCreateDir) {
|
||||||
|
if (NULL != errorPtr) {
|
||||||
|
*errorPtr = creationError;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![referencePNGData writeToFile:referencePath options:NSDataWritingAtomic error:errorPtr]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *testPath = [self _failedFilePathForSelector:selector
|
||||||
|
identifier:identifier
|
||||||
|
fileNameType:FBTestSnapshotFileNameTypeFailedTest];
|
||||||
|
|
||||||
|
if (![testPNGData writeToFile:testPath options:NSDataWritingAtomic error:errorPtr]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *diffPath = [self _failedFilePathForSelector:selector
|
||||||
|
identifier:identifier
|
||||||
|
fileNameType:FBTestSnapshotFileNameTypeFailedTestDiff];
|
||||||
|
|
||||||
|
UIImage *diffImage = [referenceImage fb_diffWithImage:testImage];
|
||||||
|
NSData *diffImageData = UIImagePNGRepresentation(diffImage);
|
||||||
|
|
||||||
|
if (![diffImageData writeToFile:diffPath options:NSDataWritingAtomic error:errorPtr]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSLog(@"If you have Kaleidoscope installed you can run this command to see an image diff:\n"
|
||||||
|
@"ksdiff \"%@\" \"%@\"", referencePath, testPath);
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private API
|
||||||
|
|
||||||
|
- (NSString *)_fileNameForSelector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
fileNameType:(FBTestSnapshotFileNameType)fileNameType
|
||||||
|
{
|
||||||
|
NSString *fileName = nil;
|
||||||
|
switch (fileNameType) {
|
||||||
|
case FBTestSnapshotFileNameTypeFailedReference:
|
||||||
|
fileName = @"reference_";
|
||||||
|
break;
|
||||||
|
case FBTestSnapshotFileNameTypeFailedTest:
|
||||||
|
fileName = @"failed_";
|
||||||
|
break;
|
||||||
|
case FBTestSnapshotFileNameTypeFailedTestDiff:
|
||||||
|
fileName = @"diff_";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fileName = @"";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fileName = [fileName stringByAppendingString:NSStringFromSelector(selector)];
|
||||||
|
if (0 < identifier.length) {
|
||||||
|
fileName = [fileName stringByAppendingFormat:@"_%@", identifier];
|
||||||
|
}
|
||||||
|
if ([[UIScreen mainScreen] scale] > 1) {
|
||||||
|
fileName = [fileName stringByAppendingFormat:@"@%.fx", [[UIScreen mainScreen] scale]];
|
||||||
|
}
|
||||||
|
fileName = [fileName stringByAppendingPathExtension:@"png"];
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)_referenceFilePathForSelector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
{
|
||||||
|
NSString *fileName = [self _fileNameForSelector:selector
|
||||||
|
identifier:identifier
|
||||||
|
fileNameType:FBTestSnapshotFileNameTypeReference];
|
||||||
|
NSString *filePath = [_referenceImagesDirectory stringByAppendingPathComponent:_testName];
|
||||||
|
filePath = [filePath stringByAppendingPathComponent:fileName];
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)_failedFilePathForSelector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
fileNameType:(FBTestSnapshotFileNameType)fileNameType
|
||||||
|
{
|
||||||
|
NSString *fileName = [self _fileNameForSelector:selector
|
||||||
|
identifier:identifier
|
||||||
|
fileNameType:fileNameType];
|
||||||
|
NSString *folderPath = NSTemporaryDirectory();
|
||||||
|
if (getenv("IMAGE_DIFF_DIR")) {
|
||||||
|
folderPath = @(getenv("IMAGE_DIFF_DIR"));
|
||||||
|
}
|
||||||
|
NSString *filePath = [folderPath stringByAppendingPathComponent:_testName];
|
||||||
|
filePath = [filePath stringByAppendingPathComponent:fileName];
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)_performPixelComparisonWithViewOrLayer:(id)viewOrLayer
|
||||||
|
selector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
tolerance:(CGFloat)tolerance
|
||||||
|
error:(NSError **)errorPtr
|
||||||
|
{
|
||||||
|
UIImage *referenceImage = [self referenceImageForSelector:selector identifier:identifier error:errorPtr];
|
||||||
|
if (nil != referenceImage) {
|
||||||
|
UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
|
||||||
|
BOOL imagesSame = [self compareReferenceImage:referenceImage toImage:snapshot tolerance:tolerance error:errorPtr];
|
||||||
|
if (!imagesSame) {
|
||||||
|
[self saveFailedReferenceImage:referenceImage
|
||||||
|
testImage:snapshot
|
||||||
|
selector:selector
|
||||||
|
identifier:identifier
|
||||||
|
error:errorPtr];
|
||||||
|
}
|
||||||
|
return imagesSame;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)_recordSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||||
|
selector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
error:(NSError **)errorPtr
|
||||||
|
{
|
||||||
|
UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
|
||||||
|
return [self _saveReferenceImage:snapshot selector:selector identifier:identifier error:errorPtr];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)_saveReferenceImage:(UIImage *)image
|
||||||
|
selector:(SEL)selector
|
||||||
|
identifier:(NSString *)identifier
|
||||||
|
error:(NSError **)errorPtr
|
||||||
|
{
|
||||||
|
BOOL didWrite = NO;
|
||||||
|
if (nil != image) {
|
||||||
|
NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
|
||||||
|
NSData *pngData = UIImagePNGRepresentation(image);
|
||||||
|
if (nil != pngData) {
|
||||||
|
NSError *creationError = nil;
|
||||||
|
BOOL didCreateDir = [_fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent]
|
||||||
|
withIntermediateDirectories:YES
|
||||||
|
attributes:nil
|
||||||
|
error:&creationError];
|
||||||
|
if (!didCreateDir) {
|
||||||
|
if (NULL != errorPtr) {
|
||||||
|
*errorPtr = creationError;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
didWrite = [pngData writeToFile:filePath options:NSDataWritingAtomic error:errorPtr];
|
||||||
|
if (didWrite) {
|
||||||
|
NSLog(@"Reference image save at: %@", filePath);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (nil != errorPtr) {
|
||||||
|
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||||
|
code:FBSnapshotTestControllerErrorCodePNGCreationFailed
|
||||||
|
userInfo:@{
|
||||||
|
FBReferenceImageFilePathKey: filePath,
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return didWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImage *)_imageForViewOrLayer:(id)viewOrLayer
|
||||||
|
{
|
||||||
|
if ([viewOrLayer isKindOfClass:[UIView class]]) {
|
||||||
|
if (_usesDrawViewHierarchyInRect) {
|
||||||
|
return [UIImage fb_imageForView:viewOrLayer];
|
||||||
|
} else {
|
||||||
|
return [UIImage fb_imageForViewLayer:viewOrLayer];
|
||||||
|
}
|
||||||
|
} else if ([viewOrLayer isKindOfClass:[CALayer class]]) {
|
||||||
|
return [UIImage fb_imageForLayer:viewOrLayer];
|
||||||
|
} else {
|
||||||
|
[NSException raise:@"Only UIView and CALayer classes can be snapshotted" format:@"%@", viewOrLayer];
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
public extension FBSnapshotTestCase {
|
||||||
|
public func FBSnapshotVerifyView(view: UIView, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) {
|
||||||
|
let envReferenceImageDirectory = NSProcessInfo.processInfo().environment["FB_REFERENCE_IMAGE_DIR"] as? String
|
||||||
|
var error: NSError?
|
||||||
|
|
||||||
|
if let envReferenceImageDirectory = envReferenceImageDirectory {
|
||||||
|
for suffix in suffixes {
|
||||||
|
let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)"
|
||||||
|
let comparisonSuccess = compareSnapshotOfView(view, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: 0, error: &error)
|
||||||
|
if comparisonSuccess || recordMode {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line)
|
||||||
|
assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert(false, message: "Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.", file: file, line: line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func FBSnapshotVerifyLayer(layer: CALayer, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) {
|
||||||
|
let envReferenceImageDirectory = NSProcessInfo.processInfo().environment["FB_REFERENCE_IMAGE_DIR"] as? String
|
||||||
|
var error: NSError?
|
||||||
|
var comparisonSuccess = false
|
||||||
|
|
||||||
|
if let envReferenceImageDirectory = envReferenceImageDirectory {
|
||||||
|
for suffix in suffixes {
|
||||||
|
let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)"
|
||||||
|
comparisonSuccess = compareSnapshotOfLayer(layer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: 0, error: &error)
|
||||||
|
if comparisonSuccess || recordMode {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line)
|
||||||
|
assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XCTFail("Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assert(assertion: Bool, message: String, file: String, line: UInt) {
|
||||||
|
if !assertion {
|
||||||
|
XCTFail(message, file: file, line: line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+29
@@ -0,0 +1,29 @@
|
|||||||
|
BSD License
|
||||||
|
|
||||||
|
For the FBSnapshotTestCase software
|
||||||
|
|
||||||
|
Copyright (c) 2013, Facebook, Inc.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
* Neither the name Facebook nor the names of its contributors may be used to
|
||||||
|
endorse or promote products derived from this software without specific
|
||||||
|
prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
+98
@@ -0,0 +1,98 @@
|
|||||||
|
FBSnapshotTestCase
|
||||||
|
======================
|
||||||
|
|
||||||
|
[](https://travis-ci.org/facebook/ios-snapshot-test-case) [](http://cocoadocs.org/docsets/FBSnapshotTestCase/)
|
||||||
|
|
||||||
|
What it does
|
||||||
|
------------
|
||||||
|
|
||||||
|
A "snapshot test case" takes a configured `UIView` or `CALayer` and uses the
|
||||||
|
`renderInContext:` method to get an image snapshot of its contents. It
|
||||||
|
compares this snapshot to a "reference image" stored in your source code
|
||||||
|
repository and fails the test if the two images don't match.
|
||||||
|
|
||||||
|
Why?
|
||||||
|
----
|
||||||
|
|
||||||
|
At Facebook we write a lot of UI code. As you might imagine, each type of
|
||||||
|
feed story is rendered using a subclass of `UIView`. There are a lot of edge
|
||||||
|
cases that we want to handle correctly:
|
||||||
|
|
||||||
|
- What if there is more text than can fit in the space available?
|
||||||
|
- What if an image doesn't match the size of an image view?
|
||||||
|
- What should the highlighted state look like?
|
||||||
|
|
||||||
|
It's straightforward to test logic code, but less obvious how you should test
|
||||||
|
views. You can do a lot of rectangle asserts, but these are hard to understand
|
||||||
|
or visualize. Looking at an image diff shows you exactly what changed and how
|
||||||
|
it will look to users.
|
||||||
|
|
||||||
|
We developed `FBSnapshotTestCase` to make snapshot tests easy.
|
||||||
|
|
||||||
|
Installation with CocoaPods
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
1. Add the following lines to your Podfile:
|
||||||
|
|
||||||
|
```
|
||||||
|
target "Tests" do
|
||||||
|
pod 'FBSnapshotTestCase'
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
If you support iOS 7 use `FBSnapshotTestCase/Core` instead, which doesn't contain Swift support.
|
||||||
|
|
||||||
|
Replace "Tests" with the name of your test project.
|
||||||
|
|
||||||
|
2. Define `FB_REFERENCE_IMAGE_DIR` in your scheme. This should
|
||||||
|
point to the directory where you want reference images to be stored. At Facebook,
|
||||||
|
we normally use this:
|
||||||
|
|
||||||
|
|Name|Value|
|
||||||
|
|:---|:----|
|
||||||
|
|`FB_REFERENCE_IMAGE_DIR`|`$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages`|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Creating a snapshot test
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
1. Subclass `FBSnapshotTestCase` instead of `XCTestCase`.
|
||||||
|
2. From within your test, use `FBSnapshotVerifyView`.
|
||||||
|
3. Run the test once with `self.recordMode = YES;` in the test's `-setUp`
|
||||||
|
method. (This creates the reference images on disk.)
|
||||||
|
4. Remove the line enabling record mode and run the test.
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Automatically names reference images on disk according to test class and
|
||||||
|
selector.
|
||||||
|
- Prints a descriptive error message to the console on failure. (Bonus:
|
||||||
|
failure message includes a one-line command to see an image diff if
|
||||||
|
you have [Kaleidoscope](http://www.kaleidoscopeapp.com) installed.)
|
||||||
|
- Supply an optional "identifier" if you want to perform multiple snapshots
|
||||||
|
in a single test method.
|
||||||
|
- Support for `CALayer` via `FBSnapshotVerifyLayer`.
|
||||||
|
- `usesDrawViewHierarchyInRect` to handle cases like `UIVisualEffect`, `UIAppearance` and Size Classes.
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
|
||||||
|
Your unit test must be an "application test", not a "logic test." (That is, it
|
||||||
|
must be run within the Simulator so that it has access to UIKit.) In Xcode 5
|
||||||
|
and later new projects only offer application tests, but older projects will
|
||||||
|
have separate targets for the two types.
|
||||||
|
|
||||||
|
Authors
|
||||||
|
-------
|
||||||
|
|
||||||
|
`FBSnapshotTestCase` was written at Facebook by
|
||||||
|
[Jonathan Dann](https://facebook.com/j.p.dann) with significant contributions by
|
||||||
|
[Todd Krabach](https://facebook.com/toddkrabach).
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
`FBSnapshotTestCase` is BSD-licensed. See `LICENSE`.
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.h
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.h
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.h
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.h
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.h
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "HamburgerButton",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"summary": "A short description of HamburgerButton.",
|
||||||
|
"description": "An optional longer description of HamburgerButton\n\n* Markdown format.\n* Don't worry about the indent, we strip it!",
|
||||||
|
"homepage": "https://github.com/<GITHUB_USERNAME>/HamburgerButton",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": {
|
||||||
|
"Giuseppe Nucifora": "me@giuseppenucifora.com"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"git": "https://github.com/<GITHUB_USERNAME>/HamburgerButton.git",
|
||||||
|
"tag": "0.1.0"
|
||||||
|
},
|
||||||
|
"platforms": {
|
||||||
|
"ios": "7.0"
|
||||||
|
},
|
||||||
|
"requires_arc": true,
|
||||||
|
"source_files": "Pod/Classes/**/*",
|
||||||
|
"resource_bundles": {
|
||||||
|
"HamburgerButton": [
|
||||||
|
"Pod/Assets/*.png"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+21
@@ -0,0 +1,21 @@
|
|||||||
|
PODS:
|
||||||
|
- FBSnapshotTestCase (2.0.3):
|
||||||
|
- FBSnapshotTestCase/SwiftSupport (= 2.0.3)
|
||||||
|
- FBSnapshotTestCase/Core (2.0.3)
|
||||||
|
- FBSnapshotTestCase/SwiftSupport (2.0.3):
|
||||||
|
- FBSnapshotTestCase/Core
|
||||||
|
- HamburgerButton (0.1.0)
|
||||||
|
|
||||||
|
DEPENDENCIES:
|
||||||
|
- FBSnapshotTestCase
|
||||||
|
- HamburgerButton (from `../`)
|
||||||
|
|
||||||
|
EXTERNAL SOURCES:
|
||||||
|
HamburgerButton:
|
||||||
|
:path: ../
|
||||||
|
|
||||||
|
SPEC CHECKSUMS:
|
||||||
|
FBSnapshotTestCase: d0eeca6bf87958e088b254396ae66873f971cfb1
|
||||||
|
HamburgerButton: 7c98f3258cc3b794872532fe825da3ef797adbb3
|
||||||
|
|
||||||
|
COCOAPODS: 0.38.2
|
||||||
+1019
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "0700"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "B3CCD41E9DE816A276EC9495"
|
||||||
|
BuildableName = "HamburgerButton.framework"
|
||||||
|
BlueprintName = "HamburgerButton"
|
||||||
|
ReferencedContainer = "container:Pods.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
#include "FBSnapshotTestCase.xcconfig"
|
||||||
|
FRAMEWORK_SEARCH_PATHS = ${FBSNAPSHOTTESTCASE_FRAMEWORK_SEARCH_PATHS}
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||||
|
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/FBSnapshotTestCase" "${PODS_ROOT}/Headers/Public"
|
||||||
|
OTHER_LDFLAGS = ${FBSNAPSHOTTESTCASE_OTHER_LDFLAGS}
|
||||||
|
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
|
||||||
|
PODS_ROOT = ${SRCROOT}
|
||||||
|
SKIP_INSTALL = YES
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
@interface PodsDummy_FBSnapshotTestCase : NSObject
|
||||||
|
@end
|
||||||
|
@implementation PodsDummy_FBSnapshotTestCase
|
||||||
|
@end
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#ifdef __OBJC__
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
#import "FBSnapshotTestCase.h"
|
||||||
|
#import "FBSnapshotTestCasePlatform.h"
|
||||||
|
#import "UIImage+Compare.h"
|
||||||
|
#import "UIImage+Diff.h"
|
||||||
|
#import "UIImage+Snapshot.h"
|
||||||
|
#import "FBSnapshotTestCase.h"
|
||||||
|
#import "FBSnapshotTestCasePlatform.h"
|
||||||
|
#import "FBSnapshotTestController.h"
|
||||||
|
|
||||||
|
FOUNDATION_EXPORT double FBSnapshotTestCaseVersionNumber;
|
||||||
|
FOUNDATION_EXPORT const unsigned char FBSnapshotTestCaseVersionString[];
|
||||||
|
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
framework module FBSnapshotTestCase {
|
||||||
|
umbrella header "FBSnapshotTestCase-umbrella.h"
|
||||||
|
|
||||||
|
export *
|
||||||
|
module * { export * }
|
||||||
|
|
||||||
|
private header "FBSnapshotTestController.h"
|
||||||
|
}
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
FBSNAPSHOTTESTCASE_FRAMEWORK_SEARCH_PATHS = $(inherited) "$(SDKROOT)/Developer/Library/Frameworks" "$(PLATFORM_DIR)/Developer/Library/Frameworks"
|
||||||
|
FBSNAPSHOTTESTCASE_OTHER_LDFLAGS = -framework "XCTest"
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>org.cocoapods.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>2.0.3</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#include "HamburgerButton.xcconfig"
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||||
|
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/HamburgerButton" "${PODS_ROOT}/Headers/Public"
|
||||||
|
PODS_ROOT = ${SRCROOT}
|
||||||
|
SKIP_INSTALL = YES
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
@interface PodsDummy_HamburgerButton : NSObject
|
||||||
|
@end
|
||||||
|
@implementation PodsDummy_HamburgerButton
|
||||||
|
@end
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#ifdef __OBJC__
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
|
||||||
|
FOUNDATION_EXPORT double HamburgerButtonVersionNumber;
|
||||||
|
FOUNDATION_EXPORT const unsigned char HamburgerButtonVersionString[];
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
framework module HamburgerButton {
|
||||||
|
umbrella header "HamburgerButton-umbrella.h"
|
||||||
|
|
||||||
|
export *
|
||||||
|
module * { export * }
|
||||||
|
}
|
||||||
File renamed without changes.
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>org.cocoapods.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>0.1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>org.cocoapods.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
# Acknowledgements
|
||||||
|
This application makes use of the following third party libraries:
|
||||||
|
|
||||||
|
## HamburgerButton
|
||||||
|
|
||||||
|
Copyright (c) 2015 Giuseppe Nucifora <me@giuseppenucifora.com>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Generated by CocoaPods - http://cocoapods.org
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PreferenceSpecifiers</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>FooterText</key>
|
||||||
|
<string>This application makes use of the following third party libraries:</string>
|
||||||
|
<key>Title</key>
|
||||||
|
<string>Acknowledgements</string>
|
||||||
|
<key>Type</key>
|
||||||
|
<string>PSGroupSpecifier</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>FooterText</key>
|
||||||
|
<string>Copyright (c) 2015 Giuseppe Nucifora <me@giuseppenucifora.com>
|
||||||
|
|
||||||
|
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.
|
||||||
|
</string>
|
||||||
|
<key>Title</key>
|
||||||
|
<string>HamburgerButton</string>
|
||||||
|
<key>Type</key>
|
||||||
|
<string>PSGroupSpecifier</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>FooterText</key>
|
||||||
|
<string>Generated by CocoaPods - http://cocoapods.org</string>
|
||||||
|
<key>Title</key>
|
||||||
|
<string></string>
|
||||||
|
<key>Type</key>
|
||||||
|
<string>PSGroupSpecifier</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>StringsTable</key>
|
||||||
|
<string>Acknowledgements</string>
|
||||||
|
<key>Title</key>
|
||||||
|
<string>Acknowledgements</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
Generated
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
@interface PodsDummy_Pods_HamburgerButton_Example : NSObject
|
||||||
|
@end
|
||||||
|
@implementation PodsDummy_Pods_HamburgerButton_Example
|
||||||
|
@end
|
||||||
Generated
Executable
+59
@@ -0,0 +1,59 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||||
|
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||||
|
|
||||||
|
SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
|
||||||
|
|
||||||
|
install_framework()
|
||||||
|
{
|
||||||
|
if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
|
||||||
|
local source="${BUILT_PRODUCTS_DIR}/$1"
|
||||||
|
else
|
||||||
|
local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||||
|
|
||||||
|
if [ -L "${source}" ]; then
|
||||||
|
echo "Symlinked..."
|
||||||
|
source="$(readlink "${source}")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# use filter instead of exclude so missing patterns dont' throw errors
|
||||||
|
echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
|
||||||
|
rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
|
||||||
|
|
||||||
|
# Resign the code if required by the build settings to avoid unstable apps
|
||||||
|
code_sign_if_enabled "${destination}/$(basename "$1")"
|
||||||
|
|
||||||
|
# Embed linked Swift runtime libraries
|
||||||
|
local basename
|
||||||
|
basename="$(basename "$1" | sed -E s/\\..+// && exit ${PIPESTATUS[0]})"
|
||||||
|
local swift_runtime_libs
|
||||||
|
swift_runtime_libs=$(xcrun otool -LX "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/${basename}.framework/${basename}" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
|
||||||
|
for lib in $swift_runtime_libs; do
|
||||||
|
echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
|
||||||
|
rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
|
||||||
|
code_sign_if_enabled "${destination}/${lib}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Signs a framework with the provided identity
|
||||||
|
code_sign_if_enabled() {
|
||||||
|
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
|
||||||
|
# Use the current code_sign_identitiy
|
||||||
|
echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
|
||||||
|
echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\""
|
||||||
|
/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if [[ "$CONFIGURATION" == "Debug" ]]; then
|
||||||
|
install_framework 'Pods-HamburgerButton_Example/HamburgerButton.framework'
|
||||||
|
fi
|
||||||
|
if [[ "$CONFIGURATION" == "Release" ]]; then
|
||||||
|
install_framework 'Pods-HamburgerButton_Example/HamburgerButton.framework'
|
||||||
|
fi
|
||||||
Generated
Executable
+95
@@ -0,0 +1,95 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||||
|
|
||||||
|
RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
|
||||||
|
> "$RESOURCES_TO_COPY"
|
||||||
|
|
||||||
|
XCASSET_FILES=()
|
||||||
|
|
||||||
|
realpath() {
|
||||||
|
DIRECTORY="$(cd "${1%/*}" && pwd)"
|
||||||
|
FILENAME="${1##*/}"
|
||||||
|
echo "$DIRECTORY/$FILENAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_resource()
|
||||||
|
{
|
||||||
|
case $1 in
|
||||||
|
*.storyboard)
|
||||||
|
echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}"
|
||||||
|
ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}"
|
||||||
|
;;
|
||||||
|
*.xib)
|
||||||
|
echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}"
|
||||||
|
ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}"
|
||||||
|
;;
|
||||||
|
*.framework)
|
||||||
|
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||||
|
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||||
|
echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||||
|
rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||||
|
;;
|
||||||
|
*.xcdatamodel)
|
||||||
|
echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\""
|
||||||
|
xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom"
|
||||||
|
;;
|
||||||
|
*.xcdatamodeld)
|
||||||
|
echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\""
|
||||||
|
xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd"
|
||||||
|
;;
|
||||||
|
*.xcmappingmodel)
|
||||||
|
echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\""
|
||||||
|
xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm"
|
||||||
|
;;
|
||||||
|
*.xcassets)
|
||||||
|
ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1")
|
||||||
|
XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
|
||||||
|
;;
|
||||||
|
/*)
|
||||||
|
echo "$1"
|
||||||
|
echo "$1" >> "$RESOURCES_TO_COPY"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "${PODS_ROOT}/$1"
|
||||||
|
echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||||
|
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||||
|
if [[ "${ACTION}" == "install" ]]; then
|
||||||
|
mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||||
|
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||||
|
fi
|
||||||
|
rm -f "$RESOURCES_TO_COPY"
|
||||||
|
|
||||||
|
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
|
||||||
|
then
|
||||||
|
case "${TARGETED_DEVICE_FAMILY}" in
|
||||||
|
1,2)
|
||||||
|
TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
TARGET_DEVICE_ARGS="--target-device iphone"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
TARGET_DEVICE_ARGS="--target-device ipad"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
TARGET_DEVICE_ARGS="--target-device mac"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Find all other xcassets (this unfortunately includes those of path pods and other targets).
|
||||||
|
OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
|
||||||
|
while read line; do
|
||||||
|
if [[ $line != "`realpath $PODS_ROOT`*" ]]; then
|
||||||
|
XCASSET_FILES+=("$line")
|
||||||
|
fi
|
||||||
|
done <<<"$OTHER_XCASSETS"
|
||||||
|
|
||||||
|
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||||
|
fi
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
|
||||||
|
FOUNDATION_EXPORT double Pods_HamburgerButton_ExampleVersionNumber;
|
||||||
|
FOUNDATION_EXPORT const unsigned char Pods_HamburgerButton_ExampleVersionString[];
|
||||||
|
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||||
|
OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/HamburgerButton.framework/Headers"
|
||||||
|
OTHER_LDFLAGS = $(inherited) -framework "HamburgerButton"
|
||||||
|
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-HamburgerButton_Example
|
||||||
|
PODS_ROOT = ${SRCROOT}/Pods
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
framework module Pods_HamburgerButton_Example {
|
||||||
|
umbrella header "Pods-HamburgerButton_Example-umbrella.h"
|
||||||
|
|
||||||
|
export *
|
||||||
|
module * { export * }
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||||
|
OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/HamburgerButton.framework/Headers"
|
||||||
|
OTHER_LDFLAGS = $(inherited) -framework "HamburgerButton"
|
||||||
|
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-HamburgerButton_Example
|
||||||
|
PODS_ROOT = ${SRCROOT}/Pods
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>org.cocoapods.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
# Acknowledgements
|
||||||
|
This application makes use of the following third party libraries:
|
||||||
|
|
||||||
|
## HamburgerButton
|
||||||
|
|
||||||
|
Copyright (c) 2015 Giuseppe Nucifora <me@giuseppenucifora.com>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
## FBSnapshotTestCase
|
||||||
|
|
||||||
|
BSD License
|
||||||
|
|
||||||
|
For the FBSnapshotTestCase software
|
||||||
|
|
||||||
|
Copyright (c) 2013, Facebook, Inc.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
* Neither the name Facebook nor the names of its contributors may be used to
|
||||||
|
endorse or promote products derived from this software without specific
|
||||||
|
prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
Generated by CocoaPods - http://cocoapods.org
|
||||||
+93
@@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PreferenceSpecifiers</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>FooterText</key>
|
||||||
|
<string>This application makes use of the following third party libraries:</string>
|
||||||
|
<key>Title</key>
|
||||||
|
<string>Acknowledgements</string>
|
||||||
|
<key>Type</key>
|
||||||
|
<string>PSGroupSpecifier</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>FooterText</key>
|
||||||
|
<string>Copyright (c) 2015 Giuseppe Nucifora <me@giuseppenucifora.com>
|
||||||
|
|
||||||
|
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.
|
||||||
|
</string>
|
||||||
|
<key>Title</key>
|
||||||
|
<string>HamburgerButton</string>
|
||||||
|
<key>Type</key>
|
||||||
|
<string>PSGroupSpecifier</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>FooterText</key>
|
||||||
|
<string>BSD License
|
||||||
|
|
||||||
|
For the FBSnapshotTestCase software
|
||||||
|
|
||||||
|
Copyright (c) 2013, Facebook, Inc.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
* Neither the name Facebook nor the names of its contributors may be used to
|
||||||
|
endorse or promote products derived from this software without specific
|
||||||
|
prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
</string>
|
||||||
|
<key>Title</key>
|
||||||
|
<string>FBSnapshotTestCase</string>
|
||||||
|
<key>Type</key>
|
||||||
|
<string>PSGroupSpecifier</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>FooterText</key>
|
||||||
|
<string>Generated by CocoaPods - http://cocoapods.org</string>
|
||||||
|
<key>Title</key>
|
||||||
|
<string></string>
|
||||||
|
<key>Type</key>
|
||||||
|
<string>PSGroupSpecifier</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>StringsTable</key>
|
||||||
|
<string>Acknowledgements</string>
|
||||||
|
<key>Title</key>
|
||||||
|
<string>Acknowledgements</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
Generated
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
@interface PodsDummy_Pods_HamburgerButton_Tests : NSObject
|
||||||
|
@end
|
||||||
|
@implementation PodsDummy_Pods_HamburgerButton_Tests
|
||||||
|
@end
|
||||||
Generated
Executable
+61
@@ -0,0 +1,61 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||||
|
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||||
|
|
||||||
|
SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
|
||||||
|
|
||||||
|
install_framework()
|
||||||
|
{
|
||||||
|
if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
|
||||||
|
local source="${BUILT_PRODUCTS_DIR}/$1"
|
||||||
|
else
|
||||||
|
local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||||
|
|
||||||
|
if [ -L "${source}" ]; then
|
||||||
|
echo "Symlinked..."
|
||||||
|
source="$(readlink "${source}")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# use filter instead of exclude so missing patterns dont' throw errors
|
||||||
|
echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
|
||||||
|
rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
|
||||||
|
|
||||||
|
# Resign the code if required by the build settings to avoid unstable apps
|
||||||
|
code_sign_if_enabled "${destination}/$(basename "$1")"
|
||||||
|
|
||||||
|
# Embed linked Swift runtime libraries
|
||||||
|
local basename
|
||||||
|
basename="$(basename "$1" | sed -E s/\\..+// && exit ${PIPESTATUS[0]})"
|
||||||
|
local swift_runtime_libs
|
||||||
|
swift_runtime_libs=$(xcrun otool -LX "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/${basename}.framework/${basename}" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
|
||||||
|
for lib in $swift_runtime_libs; do
|
||||||
|
echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
|
||||||
|
rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
|
||||||
|
code_sign_if_enabled "${destination}/${lib}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Signs a framework with the provided identity
|
||||||
|
code_sign_if_enabled() {
|
||||||
|
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
|
||||||
|
# Use the current code_sign_identitiy
|
||||||
|
echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
|
||||||
|
echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\""
|
||||||
|
/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if [[ "$CONFIGURATION" == "Debug" ]]; then
|
||||||
|
install_framework 'Pods-HamburgerButton_Tests/HamburgerButton.framework'
|
||||||
|
install_framework 'Pods-HamburgerButton_Tests/FBSnapshotTestCase.framework'
|
||||||
|
fi
|
||||||
|
if [[ "$CONFIGURATION" == "Release" ]]; then
|
||||||
|
install_framework 'Pods-HamburgerButton_Tests/HamburgerButton.framework'
|
||||||
|
install_framework 'Pods-HamburgerButton_Tests/FBSnapshotTestCase.framework'
|
||||||
|
fi
|
||||||
Example/Pods/Target Support Files/Pods-HamburgerButton_Tests/Pods-HamburgerButton_Tests-resources.sh
Generated
Executable
+95
@@ -0,0 +1,95 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||||
|
|
||||||
|
RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
|
||||||
|
> "$RESOURCES_TO_COPY"
|
||||||
|
|
||||||
|
XCASSET_FILES=()
|
||||||
|
|
||||||
|
realpath() {
|
||||||
|
DIRECTORY="$(cd "${1%/*}" && pwd)"
|
||||||
|
FILENAME="${1##*/}"
|
||||||
|
echo "$DIRECTORY/$FILENAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_resource()
|
||||||
|
{
|
||||||
|
case $1 in
|
||||||
|
*.storyboard)
|
||||||
|
echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}"
|
||||||
|
ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}"
|
||||||
|
;;
|
||||||
|
*.xib)
|
||||||
|
echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}"
|
||||||
|
ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}"
|
||||||
|
;;
|
||||||
|
*.framework)
|
||||||
|
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||||
|
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||||
|
echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||||
|
rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||||
|
;;
|
||||||
|
*.xcdatamodel)
|
||||||
|
echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\""
|
||||||
|
xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom"
|
||||||
|
;;
|
||||||
|
*.xcdatamodeld)
|
||||||
|
echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\""
|
||||||
|
xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd"
|
||||||
|
;;
|
||||||
|
*.xcmappingmodel)
|
||||||
|
echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\""
|
||||||
|
xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm"
|
||||||
|
;;
|
||||||
|
*.xcassets)
|
||||||
|
ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1")
|
||||||
|
XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
|
||||||
|
;;
|
||||||
|
/*)
|
||||||
|
echo "$1"
|
||||||
|
echo "$1" >> "$RESOURCES_TO_COPY"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "${PODS_ROOT}/$1"
|
||||||
|
echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||||
|
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||||
|
if [[ "${ACTION}" == "install" ]]; then
|
||||||
|
mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||||
|
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||||
|
fi
|
||||||
|
rm -f "$RESOURCES_TO_COPY"
|
||||||
|
|
||||||
|
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
|
||||||
|
then
|
||||||
|
case "${TARGETED_DEVICE_FAMILY}" in
|
||||||
|
1,2)
|
||||||
|
TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
TARGET_DEVICE_ARGS="--target-device iphone"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
TARGET_DEVICE_ARGS="--target-device ipad"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
TARGET_DEVICE_ARGS="--target-device mac"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Find all other xcassets (this unfortunately includes those of path pods and other targets).
|
||||||
|
OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
|
||||||
|
while read line; do
|
||||||
|
if [[ $line != "`realpath $PODS_ROOT`*" ]]; then
|
||||||
|
XCASSET_FILES+=("$line")
|
||||||
|
fi
|
||||||
|
done <<<"$OTHER_XCASSETS"
|
||||||
|
|
||||||
|
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||||
|
fi
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
|
||||||
|
FOUNDATION_EXPORT double Pods_HamburgerButton_TestsVersionNumber;
|
||||||
|
FOUNDATION_EXPORT const unsigned char Pods_HamburgerButton_TestsVersionString[];
|
||||||
|
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||||
|
OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/FBSnapshotTestCase.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/HamburgerButton.framework/Headers"
|
||||||
|
OTHER_LDFLAGS = $(inherited) -framework "FBSnapshotTestCase" -framework "HamburgerButton"
|
||||||
|
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
|
||||||
|
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-HamburgerButton_Tests
|
||||||
|
PODS_ROOT = ${SRCROOT}/Pods
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
framework module Pods_HamburgerButton_Tests {
|
||||||
|
umbrella header "Pods-HamburgerButton_Tests-umbrella.h"
|
||||||
|
|
||||||
|
export *
|
||||||
|
module * { export * }
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||||
|
OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/FBSnapshotTestCase.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/HamburgerButton.framework/Headers"
|
||||||
|
OTHER_LDFLAGS = $(inherited) -framework "FBSnapshotTestCase" -framework "HamburgerButton"
|
||||||
|
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
|
||||||
|
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-HamburgerButton_Tests
|
||||||
|
PODS_ROOT = ${SRCROOT}/Pods
|
||||||
+6
-10
@@ -10,18 +10,14 @@
|
|||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = "HamburgerButton"
|
s.name = "HamburgerButton"
|
||||||
s.version = "0.1.0"
|
s.version = "0.1.0"
|
||||||
s.summary = "A short description of HamburgerButton."
|
s.summary = "Is a fork of original repository https://github.com/luongbang152/HamburgerButton with Pod support"
|
||||||
s.description = <<-DESC
|
s.description = "LBHamburgerButton is an iOS control that displays 3 lines (hamburger) and other icon (back, close...) without using image. It has a beautiful, clean and smooth transition between 2 state like Google Material Design or \"close button\" of Paper app."
|
||||||
An optional longer description of HamburgerButton
|
|
||||||
|
|
||||||
* Markdown format.
|
s.homepage = "https://github.com/giuseppenucifora/HamburgerButton"
|
||||||
* Don't worry about the indent, we strip it!
|
|
||||||
DESC
|
|
||||||
s.homepage = "https://github.com/<GITHUB_USERNAME>/HamburgerButton"
|
|
||||||
# s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2"
|
# s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2"
|
||||||
s.license = 'MIT'
|
s.license = 'MIT'
|
||||||
s.author = { "Giuseppe Nucifora" => "me@giuseppenucifora.com" }
|
s.author = { "Giuseppe Nucifora" => "Giuseppe Nucifora" }
|
||||||
s.source = { :git => "https://github.com/<GITHUB_USERNAME>/HamburgerButton.git", :tag => s.version.to_s }
|
s.source = { :git => "https://github.com/giuseppenucifora/HamburgerButton.git", :tag => s.version.to_s }
|
||||||
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
|
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
|
||||||
|
|
||||||
s.platform = :ios, '7.0'
|
s.platform = :ios, '7.0'
|
||||||
@@ -33,6 +29,6 @@ Pod::Spec.new do |s|
|
|||||||
}
|
}
|
||||||
|
|
||||||
# s.public_header_files = 'Pod/Classes/**/*.h'
|
# s.public_header_files = 'Pod/Classes/**/*.h'
|
||||||
# s.frameworks = 'UIKit', 'MapKit'
|
s.frameworks = 'QuartzCore'
|
||||||
# s.dependency 'AFNetworking', '~> 2.3'
|
# s.dependency 'AFNetworking', '~> 2.3'
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
//
|
||||||
|
// LBHamburgerButton.h
|
||||||
|
// Version 1.0
|
||||||
|
//
|
||||||
|
// Created by Bang Nguyen on 26/07/2014.
|
||||||
|
//
|
||||||
|
|
||||||
|
// This code is distributed under the terms and conditions of the MIT license.
|
||||||
|
|
||||||
|
// Copyright (c) 2014 Bang Nguyen
|
||||||
|
//
|
||||||
|
// 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 <UIKit/UIKit.h>
|
||||||
|
#import <QuartzCore/QuartzCore.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/** Show back (<-) button after switch state/animate. */
|
||||||
|
LBHamburgerButtonTypeBackButton = 0,
|
||||||
|
/** Show close (X) button after switch state/animate. */
|
||||||
|
LBHamburgerButtonTypeCloseButton
|
||||||
|
|
||||||
|
// other type
|
||||||
|
|
||||||
|
} LBHamburgerButtonType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/** Initialize state, with 3 lines (hamburger). */
|
||||||
|
LBHamburgerButtonStateHamburger = 0,
|
||||||
|
/** State happened after animate. */
|
||||||
|
LBHamburgerButtonStateNotHamburger
|
||||||
|
} LBHamburgerButtonState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LBHamburgerButton is a sub-class of UIButton, displays a hamburger button
|
||||||
|
* and other state button without using image
|
||||||
|
*
|
||||||
|
* This is a simple button with 2 state. At initialization, it has state "Hamburger"
|
||||||
|
* which means 3 lines. After switch state, it has state "Not hamburger" and
|
||||||
|
* displays a back button/a close button.
|
||||||
|
*
|
||||||
|
* This button support 2 type:
|
||||||
|
* - LBHamburgerButtonTypeBackButton - display <- after switch state
|
||||||
|
* - LBHamburgerButtonTypeCloseButton - display X after switch state
|
||||||
|
*
|
||||||
|
* Those type can be setted when initialization or manually at any time/any event.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@interface LBHamburgerButton : UIButton
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current state of hamburger button.
|
||||||
|
*/
|
||||||
|
@property (readonly) LBHamburgerButtonState hamburgerState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of hamburger button.
|
||||||
|
*/
|
||||||
|
@property LBHamburgerButtonType hamburgerType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time for animation. Default is 0.5f.
|
||||||
|
*/
|
||||||
|
@property CFTimeInterval hamburgerAnimationDuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full contructor that initializes the button with default state.
|
||||||
|
*
|
||||||
|
* @param frame Frame of the button in superview.
|
||||||
|
* @param type Type of button, Back or Close or etc.
|
||||||
|
* @param lineWidth Width of each line in hamburger.
|
||||||
|
* @param lineHeight Height of each line in hamburger.
|
||||||
|
* @param lineSpacing Distance between lines.
|
||||||
|
* @param lineCenter Position of 2nd line in hamburger (which means center of group of 3)
|
||||||
|
* @param color Color of lines.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame
|
||||||
|
withHamburgerType:(LBHamburgerButtonType)type
|
||||||
|
lineWidth:(CGFloat)lineWidth
|
||||||
|
lineHeight:(CGFloat)lineHeight
|
||||||
|
lineSpacing:(CGFloat)lineSpacing
|
||||||
|
lineCenter:(CGPoint)lineCenter
|
||||||
|
color:(UIColor*)color;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up default state of button which has 3 lines.
|
||||||
|
*
|
||||||
|
* @param type Type of button, Back or Close or etc.
|
||||||
|
* @param lineWidth Width of each line in hamburger.
|
||||||
|
* @param lineHeight Height of each line in hamburger.
|
||||||
|
* @param lineSpacing Distance between lines.
|
||||||
|
* @param lineCenter Position of 2nd line in hamburger (which means center of group of 3)
|
||||||
|
* @param color Color of lines.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
- (void)setUpHamburgerWithType:(LBHamburgerButtonType)type
|
||||||
|
lineWidth:(CGFloat)lineWidth
|
||||||
|
lineHeight:(CGFloat)lineHeight
|
||||||
|
lineSpacing:(CGFloat)lineSpacing
|
||||||
|
lineCenter:(CGPoint)lineCenter
|
||||||
|
color:(UIColor*)color;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animate from current state to other state.
|
||||||
|
*/
|
||||||
|
- (void)switchState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animate from current state to specfied state.
|
||||||
|
*/
|
||||||
|
- (void)setState:(LBHamburgerButtonState)state;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supporting category of CALayer
|
||||||
|
*/
|
||||||
|
@interface CALayer (LBAddAnimationAndValue)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add both animation and its end value to CALayer
|
||||||
|
*/
|
||||||
|
- (void)addAnimation:(CAAnimation *)anim andValue:(NSValue*)value forKeyPath:(NSString *)keyPath;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supporting category of UIBezierPath
|
||||||
|
* Helps creating a curve path for animation
|
||||||
|
*/
|
||||||
|
@interface UIBezierPath (LBAnimateBezierPath)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a curve path with ease
|
||||||
|
*/
|
||||||
|
+ (UIBezierPath*)animateBezierPathFrom:(CGPoint)startPoint toPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
//
|
||||||
|
// LBHamburgerButton.m
|
||||||
|
// HamburgerButton
|
||||||
|
//
|
||||||
|
// Created by Bang Nguyen on 26/07/2014.
|
||||||
|
// Copyright (c) Năm 2014 Bang Nguyen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "LBHamburgerButton.h"
|
||||||
|
|
||||||
|
@interface LBHamburgerButton () {
|
||||||
|
|
||||||
|
CGFloat _lineHeight, _lineWidth, _lineSpacing;
|
||||||
|
CGPoint _lineCenter;
|
||||||
|
NSArray* _lineArray;
|
||||||
|
BOOL _lineCreated;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation LBHamburgerButton
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark - Public functions
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame withHamburgerType:(LBHamburgerButtonType)type lineWidth:(CGFloat)lineWidth lineHeight:(CGFloat)lineHeight lineSpacing:(CGFloat)lineSpacing lineCenter:(CGPoint)lineCenter color:(UIColor *)color {
|
||||||
|
|
||||||
|
if (self = [super initWithFrame:frame]) {
|
||||||
|
|
||||||
|
_hamburgerAnimationDuration = 0.5;
|
||||||
|
_hamburgerState = LBHamburgerButtonStateHamburger;
|
||||||
|
|
||||||
|
[self setUpHamburgerWithType:type
|
||||||
|
lineWidth:lineWidth
|
||||||
|
lineHeight:lineHeight
|
||||||
|
lineSpacing:lineSpacing
|
||||||
|
lineCenter:lineCenter
|
||||||
|
color:color];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setUpHamburgerWithType:(LBHamburgerButtonType)type lineWidth:(CGFloat)lineWidth lineHeight:(CGFloat)lineHeight lineSpacing:(CGFloat)lineSpacing lineCenter:(CGPoint)lineCenter color:(UIColor *)color {
|
||||||
|
|
||||||
|
if (_lineCreated) {
|
||||||
|
// Lines have been created, do nothing.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_hamburgerType = type;
|
||||||
|
_lineWidth = lineWidth;
|
||||||
|
_lineHeight = lineHeight;
|
||||||
|
_lineSpacing = lineSpacing;
|
||||||
|
_lineCenter = lineCenter;
|
||||||
|
|
||||||
|
CAShapeLayer* topLine = [self createLayerWithFrame:CGRectMake(0, 0, lineWidth, lineHeight) andColor:color atPosition:CGPointMake(lineCenter.x, lineCenter.y - lineHeight - lineSpacing)];
|
||||||
|
CAShapeLayer* middleLine = [self createLayerWithFrame:CGRectMake(0, 0, lineWidth, lineHeight) andColor:color atPosition:lineCenter];
|
||||||
|
CAShapeLayer* bottomLine = [self createLayerWithFrame:CGRectMake(0, 0, lineWidth, lineHeight) andColor:color atPosition:CGPointMake(lineCenter.x, lineCenter.y + lineHeight + lineSpacing)];
|
||||||
|
|
||||||
|
_lineArray = @[ topLine, middleLine, bottomLine ];
|
||||||
|
for (CAShapeLayer* layer in _lineArray) {
|
||||||
|
[self.layer addSublayer:layer];
|
||||||
|
}
|
||||||
|
_lineCreated = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)switchState {
|
||||||
|
[self animateButtonForward:(_hamburgerState == LBHamburgerButtonStateHamburger)];
|
||||||
|
_hamburgerState = (_hamburgerState == LBHamburgerButtonStateHamburger) ? LBHamburgerButtonStateNotHamburger : LBHamburgerButtonStateHamburger;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)setState:(LBHamburgerButtonState)state
|
||||||
|
{
|
||||||
|
if(_hamburgerState == state)
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
[self switchState];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark - Private functions
|
||||||
|
|
||||||
|
- (void)animateButtonForward:(BOOL)forward {
|
||||||
|
|
||||||
|
NSArray* anims = @[ [self keyframeAnimationsOfLineIndex:0 forward:forward],
|
||||||
|
[self keyframeAnimationsOfLineIndex:1 forward:forward],
|
||||||
|
[self keyframeAnimationsOfLineIndex:2 forward:forward] ];
|
||||||
|
|
||||||
|
[CATransaction begin];
|
||||||
|
[CATransaction setAnimationDuration:_hamburgerAnimationDuration];
|
||||||
|
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithControlPoints:0.4 :0.0 :0.2 :1.0]];
|
||||||
|
|
||||||
|
for (NSArray* animations in anims) {
|
||||||
|
NSInteger index = [anims indexOfObject:animations];
|
||||||
|
for (CAKeyframeAnimation* anim in animations) {
|
||||||
|
CAShapeLayer* layer = _lineArray[index];
|
||||||
|
if (!anim.removedOnCompletion) {
|
||||||
|
[layer addAnimation:anim forKey:anim.keyPath];
|
||||||
|
} else {
|
||||||
|
[layer addAnimation:anim andValue:anim.values.lastObject forKeyPath:anim.keyPath];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[CATransaction commit];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray*)keyframeAnimationsOfLineIndex:(NSInteger)index forward:(BOOL)forward {
|
||||||
|
|
||||||
|
switch (_hamburgerType) {
|
||||||
|
case LBHamburgerButtonTypeBackButton:
|
||||||
|
{
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
CAKeyframeAnimation* animRotate = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
|
||||||
|
animRotate.values = forward ? @[ @0, @(M_PI*5/4) ] : @[ @(M_PI*5/4), @0];
|
||||||
|
animRotate.calculationMode = kCAAnimationCubic;
|
||||||
|
animRotate.keyTimes = @[ @0, @0.33, @0.73, @1.0];
|
||||||
|
|
||||||
|
CGPoint startPoint = forward ? CGPointMake(_lineCenter.x, _lineCenter.y - _lineHeight - _lineSpacing) : CGPointMake(_lineCenter.x - [self scale:10], _lineCenter.y + _lineHeight + [self scale:7.2]);
|
||||||
|
CGPoint endPoint = forward ? CGPointMake(_lineCenter.x - [self scale:10], _lineCenter.y + _lineHeight + [self scale:7.2]) : CGPointMake(_lineCenter.x, _lineCenter.y - _lineHeight - _lineSpacing);
|
||||||
|
CGPoint controlPoint = CGPointMake(_lineCenter.x + [self scale:15], _lineCenter.y);
|
||||||
|
|
||||||
|
CAKeyframeAnimation* animPosition = [CAKeyframeAnimation animationWithKeyPath:@"position"];
|
||||||
|
animPosition.path = [UIBezierPath animateBezierPathFrom:startPoint toPoint:endPoint controlPoint:controlPoint].CGPath;
|
||||||
|
animPosition.removedOnCompletion = NO;
|
||||||
|
animPosition.fillMode = kCAFillModeForwards;
|
||||||
|
|
||||||
|
return @[ animRotate, animPosition ];
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
CAKeyframeAnimation* animRotate = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
|
||||||
|
animRotate.values = forward ? @[ @0, @(M_PI) ] : @[ @(M_PI), @0];
|
||||||
|
|
||||||
|
return @[ animRotate ];
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
CAKeyframeAnimation* animRotate = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
|
||||||
|
animRotate.values = forward ? @[ @0, @(-M_PI*5/4) ] : @[ @(-M_PI*5/4), @0];
|
||||||
|
animRotate.calculationMode = kCAAnimationCubic;
|
||||||
|
animRotate.keyTimes = @[ @0, @0.33, @0.73, @1.0];
|
||||||
|
|
||||||
|
CGPoint startPoint = forward ? CGPointMake(_lineCenter.x, _lineCenter.y + _lineHeight + _lineSpacing) : CGPointMake(_lineCenter.x - [self scale:10], _lineCenter.y - _lineHeight - [self scale:7.2]);
|
||||||
|
CGPoint endPoint = forward ? CGPointMake(_lineCenter.x - [self scale:10], _lineCenter.y - _lineHeight - [self scale:7.2]) : CGPointMake(_lineCenter.x, _lineCenter.y + _lineHeight + _lineSpacing);
|
||||||
|
CGPoint controlPoint = CGPointMake(_lineCenter.x + [self scale:15], _lineCenter.y);
|
||||||
|
|
||||||
|
CAKeyframeAnimation* animPosition = [CAKeyframeAnimation animationWithKeyPath:@"position"];
|
||||||
|
animPosition.path = [UIBezierPath animateBezierPathFrom:startPoint toPoint:endPoint controlPoint:controlPoint].CGPath;
|
||||||
|
animPosition.removedOnCompletion = NO;
|
||||||
|
animPosition.fillMode = kCAFillModeForwards;
|
||||||
|
|
||||||
|
return @[ animRotate, animPosition ];
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LBHamburgerButtonTypeCloseButton:
|
||||||
|
{
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
CAKeyframeAnimation* animRotate = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
|
||||||
|
animRotate.values = forward ? @[ @0, @(-M_PI*5/4) ] : @[ @(-M_PI*5/4), @0];
|
||||||
|
animRotate.calculationMode = kCAAnimationCubic;
|
||||||
|
animRotate.keyTimes = @[ @0, @0.33, @0.73, @1.0];
|
||||||
|
|
||||||
|
CGPoint startPoint = forward ? CGPointMake(_lineCenter.x, _lineCenter.y - _lineHeight - _lineSpacing) : _lineCenter;
|
||||||
|
CGPoint endPoint = forward ? _lineCenter : CGPointMake(_lineCenter.x, _lineCenter.y - _lineHeight - _lineSpacing);
|
||||||
|
CGPoint controlPoint = CGPointMake(_lineCenter.x + [self scale:20], _lineCenter.y - _lineHeight - [self scale:5]);
|
||||||
|
|
||||||
|
CAKeyframeAnimation* animPosition = [CAKeyframeAnimation animationWithKeyPath:@"position"];
|
||||||
|
animPosition.path = [UIBezierPath animateBezierPathFrom:startPoint toPoint:endPoint controlPoint:controlPoint].CGPath;
|
||||||
|
animPosition.removedOnCompletion = NO;
|
||||||
|
animPosition.fillMode = kCAFillModeForwards;
|
||||||
|
|
||||||
|
return @[ animRotate, animPosition ];
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
CAKeyframeAnimation* animRotate = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
|
||||||
|
animRotate.values = forward ? @[ @0, @(M_PI) ] : @[ @(M_PI), @0];
|
||||||
|
|
||||||
|
CAKeyframeAnimation* animScale = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale.x"];
|
||||||
|
animScale.values = forward ? @[ @1, @0.1 ] : @[ @0.1, @1 ];
|
||||||
|
|
||||||
|
return @[ animRotate, animScale ];
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
CAKeyframeAnimation* animRotate = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
|
||||||
|
animRotate.values = forward ? @[ @0, @(M_PI*5/4) ] : @[ @(M_PI*5/4), @0];
|
||||||
|
animRotate.calculationMode = kCAAnimationCubic;
|
||||||
|
animRotate.keyTimes = @[ @0, @0.33, @0.73, @1.0];
|
||||||
|
|
||||||
|
CGPoint startPoint = forward ? CGPointMake(_lineCenter.x, _lineCenter.y + _lineHeight + _lineSpacing) : _lineCenter;
|
||||||
|
CGPoint endPoint = forward ? _lineCenter : CGPointMake(_lineCenter.x, _lineCenter.y + _lineHeight + _lineSpacing);
|
||||||
|
CGPoint controlPoint = CGPointMake(_lineCenter.x - [self scale:20], _lineCenter.y + _lineHeight + [self scale:5]);
|
||||||
|
|
||||||
|
CAKeyframeAnimation* animPosition = [CAKeyframeAnimation animationWithKeyPath:@"position"];
|
||||||
|
animPosition.path = [UIBezierPath animateBezierPathFrom:startPoint toPoint:endPoint controlPoint:controlPoint].CGPath;
|
||||||
|
animPosition.removedOnCompletion = NO;
|
||||||
|
animPosition.fillMode = kCAFillModeForwards;
|
||||||
|
|
||||||
|
return @[ animRotate, animPosition ];
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CAShapeLayer*)createLayerWithFrame:(CGRect)frame andColor:(UIColor*)color atPosition:(CGPoint)point {
|
||||||
|
|
||||||
|
CAShapeLayer* layer = [CAShapeLayer new];
|
||||||
|
|
||||||
|
UIBezierPath* path = [UIBezierPath new];
|
||||||
|
[path moveToPoint:CGPointMake(0, 0)];
|
||||||
|
[path addLineToPoint:CGPointMake(frame.size.width, 0)];
|
||||||
|
|
||||||
|
layer.path = path.CGPath;
|
||||||
|
layer.lineWidth = frame.size.height;
|
||||||
|
layer.strokeColor = color.CGColor;
|
||||||
|
|
||||||
|
CGPathRef bound = CGPathCreateCopyByStrokingPath(layer.path, nil, layer.lineWidth, kCGLineCapButt, kCGLineJoinMiter, layer.miterLimit);
|
||||||
|
layer.bounds = CGPathGetBoundingBox(bound);
|
||||||
|
|
||||||
|
layer.position = point;
|
||||||
|
|
||||||
|
return layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)scale:(CGFloat)value {
|
||||||
|
return value/(50/_lineWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark - Supporting Category
|
||||||
|
#pragma mark - CALayer
|
||||||
|
|
||||||
|
@implementation CALayer (LBAddAnimationAndValue)
|
||||||
|
|
||||||
|
- (void)addAnimation:(CAAnimation *)anim andValue:(NSValue *)value forKeyPath:(NSString *)keyPath {
|
||||||
|
[self addAnimation:anim forKey:keyPath];
|
||||||
|
[self setValue:value forKeyPath:keyPath];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#pragma mark - UIBezierPath
|
||||||
|
|
||||||
|
@implementation UIBezierPath (LBAnimateBezierPath)
|
||||||
|
|
||||||
|
+ (UIBezierPath*)animateBezierPathFrom:(CGPoint)startPoint toPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint {
|
||||||
|
|
||||||
|
UIBezierPath* path = [UIBezierPath new];
|
||||||
|
[path moveToPoint:startPoint];
|
||||||
|
[path addQuadCurveToPoint:endPoint controlPoint:controlPoint];
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -5,11 +5,68 @@
|
|||||||
[](http://cocoapods.org/pods/HamburgerButton)
|
[](http://cocoapods.org/pods/HamburgerButton)
|
||||||
[](http://cocoapods.org/pods/HamburgerButton)
|
[](http://cocoapods.org/pods/HamburgerButton)
|
||||||
|
|
||||||
## Usage
|
## Overview ##
|
||||||
|
|
||||||
To run the example project, clone the repo, and run `pod install` from the Example directory first.
|
LBHamburgerButton is an iOS control that displays 3 lines (hamburger) and other icon (back, close...) without using image. It has a beautiful, clean and smooth transition between 2 state like Google Material Design or "close button" of Paper app.
|
||||||
|
|
||||||
## Requirements
|
## Requirement ##
|
||||||
|
|
||||||
|
LBHamburgerButton use QuartzCore framework, so dont forget to add this framework. ARC (iOS 5+) is also required.
|
||||||
|
|
||||||
|
## How to use ##
|
||||||
|
|
||||||
|
First, you need to copy 2 files LBHamburgerButton.h/.m to your project. Then, import them to your class
|
||||||
|
|
||||||
|
```objc
|
||||||
|
#import "LBHamburgerButton.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
This is a sub-class of UIButton. So you can init a simple button and call set-up method:
|
||||||
|
|
||||||
|
```objc
|
||||||
|
buttonHamburger = [[LBHamburgerButton alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
|
||||||
|
[buttonHamburger setUpHamburgerWithType:LBHamburgerButtonTypeCloseButton
|
||||||
|
lineWidth:50
|
||||||
|
lineHeight:50/6
|
||||||
|
lineSpacing:5
|
||||||
|
lineCenter:CGPointMake(50, 50)
|
||||||
|
color:[UIColor whiteColor]];
|
||||||
|
|
||||||
|
|
||||||
|
or use full contructor like this:
|
||||||
|
|
||||||
|
|
||||||
|
```objc
|
||||||
|
buttonHamburger = [[LBHamburgerButton alloc] initWithFrame:CGRectMake(0, 0, 100, 100)
|
||||||
|
withHamburgerType:LBHamburgerButtonTypeCloseButton
|
||||||
|
lineWidth:50
|
||||||
|
lineHeight:50/6
|
||||||
|
lineSpacing:5
|
||||||
|
lineCenter:CGPointMake(50, 50)
|
||||||
|
color:[UIColor whiteColor]];
|
||||||
|
```
|
||||||
|
|
||||||
|
Set other propertises, add target add it to view like UIButton.
|
||||||
|
|
||||||
|
```objc
|
||||||
|
[buttonHamburger setCenter:CGPointMake(120, 120)];
|
||||||
|
[buttonHamburger setBackgroundColor:[UIColor blackColor]];
|
||||||
|
[buttonHamburger addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||||
|
|
||||||
|
[self.view addSubview:buttonHamburger];
|
||||||
|
```
|
||||||
|
|
||||||
|
When you want to switch state, just call `switchState`.
|
||||||
|
|
||||||
|
```objc
|
||||||
|
- (void)buttonPressed:(id)sender {
|
||||||
|
LBHamburgerButton* btn = (LBHamburgerButton*)sender;
|
||||||
|
[btn switchState];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## MIT License
|
||||||
|
Copyright (c) 2014 Bang Nguyen
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -24,6 +81,8 @@ pod "HamburgerButton"
|
|||||||
|
|
||||||
Giuseppe Nucifora, me@giuseppenucifora.com
|
Giuseppe Nucifora, me@giuseppenucifora.com
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
HamburgerButton is available under the MIT license. See the LICENSE file for more info.
|
HamburgerButton is available under the MIT license. See the LICENSE file for more info.
|
||||||
Reference in new issue
Block a user