From c1f57cdd419971368f43b35acd2fa51a3501f9e4 Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 00:29:07 -0700 Subject: [PATCH 01/15] First try at version 2. --- LiveFrost/LFDisplayBridge.h | 4 +- LiveFrost/LFDisplayBridge.m | 34 ++-- LiveFrost/LFGlassLayer.h | 47 +++++ LiveFrost/LFGlassLayer.m | 387 ++++++++++++++++++++++++++++++++++++ LiveFrost/LFGlassView.h | 7 +- LiveFrost/LFGlassView.m | 276 ++++--------------------- LiveFrost/LiveFrost.h | 1 + 7 files changed, 502 insertions(+), 254 deletions(-) create mode 100644 LiveFrost/LFGlassLayer.h create mode 100644 LiveFrost/LFGlassLayer.m diff --git a/LiveFrost/LFDisplayBridge.h b/LiveFrost/LFDisplayBridge.h index 788d767..6994473 100644 --- a/LiveFrost/LFDisplayBridge.h +++ b/LiveFrost/LFDisplayBridge.h @@ -31,7 +31,7 @@ + (instancetype) sharedInstance; @property (nonatomic, readonly, assign) CFMutableSetRef subscribedViews; -- (void) addSubscribedViewsObject:(UIView *)object; -- (void) removeSubscribedViewsObject:(UIView *)object; +- (void) addSubscribedLayer:(CALayer *)object; +- (void) removeSubscribedLayer:(CALayer *)object; @end diff --git a/LiveFrost/LFDisplayBridge.m b/LiveFrost/LFDisplayBridge.m index 52599c0..5c88545 100644 --- a/LiveFrost/LFDisplayBridge.m +++ b/LiveFrost/LFDisplayBridge.m @@ -22,24 +22,24 @@ #import "LFDisplayBridge.h" -void LF_refreshAllSubscribedViewsApplierFunction(const void *value, void *context); +void LF_refreshAllSubscribedLayersApplierFunction(const void *value, void *context); @interface LFDisplayBridge () -@property (nonatomic, readwrite, assign) CFMutableSetRef subscribedViews; +@property (nonatomic, readwrite, assign) CFMutableSetRef subscribedLayers; @property (nonatomic, readonly, strong) CADisplayLink *displayLink; @end -void LF_refreshAllSubscribedViewsApplierFunction(const void *value, void *context) { +void LF_refreshAllSubscribedLayersApplierFunction(const void *value, void *context) { [(__bridge UIView *)value refresh]; } #if !__has_feature(objc_arc) - #error This implementation file must be compiled with Objective-C ARC. +#error This implementation file must be compiled with Objective-C ARC. - #error Compile this file with the -fobjc-arc flag under your target's Build Phases, - #error or convert your project to Objective-C ARC. +#error Compile this file with the -fobjc-arc flag under your target's Build Phases, +#error or convert your project to Objective-C ARC. #endif @implementation LFDisplayBridge @@ -55,32 +55,32 @@ + (instancetype) sharedInstance { - (id) init { if (self = [super init]) { - _subscribedViews = CFSetCreateMutable(kCFAllocatorDefault, 0, NULL); + _subscribedLayers = CFSetCreateMutable(kCFAllocatorDefault, 0, NULL); _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)]; [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; } return self; } -- (void) addSubscribedViewsObject:(UIView *)object { - CFSetAddValue(_subscribedViews, (__bridge const void*)object); +- (void) dealloc { + [_displayLink invalidate]; + CFRelease(_subscribedLayers); } -- (void) removeSubscribedViewsObject:(UIView *)object { - CFSetRemoveValue(_subscribedViews, (__bridge const void*)object); +- (void) addSubscribedLayer:(CALayer *)object { + CFSetAddValue(_subscribedLayers, (__bridge const void*)object); } -- (void) handleDisplayLink:(CADisplayLink *)displayLink { - [self refresh]; +- (void) removeSubscribedLayer:(CALayer *)object { + CFSetRemoveValue(_subscribedLayers, (__bridge const void*)object); } -- (void) dealloc { - [_displayLink invalidate]; - CFRelease(_subscribedViews); +- (void) handleDisplayLink:(CADisplayLink *)displayLink { + [self refresh]; } - (void) refresh { - CFSetApplyFunction(_subscribedViews, LF_refreshAllSubscribedViewsApplierFunction, NULL); + CFSetApplyFunction(_subscribedLayers, LF_refreshAllSubscribedLayersApplierFunction, NULL); } @end diff --git a/LiveFrost/LFGlassLayer.h b/LiveFrost/LFGlassLayer.h new file mode 100644 index 0000000..9034c22 --- /dev/null +++ b/LiveFrost/LFGlassLayer.h @@ -0,0 +1,47 @@ +// +// Copyright (c) 2013-2014 Evadne Wu and Nicholas Levin +// +// 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. +// +// Contains contributions from Nam Kennic +// + +#import +#import +#import "LFDisplayBridge.h" + +@interface LFGlassLayer : CALayer + +@property (nonatomic, assign) CGFloat blurRadius; +@property (nonatomic, assign) CGFloat scaleFactor; + +@property (nonatomic, assign) NSUInteger frameInterval; + +- (BOOL) blurOnceIfPossible; + +// optional properties for greater customization +@property (nonatomic, weak) CALayer *customBlurTargetLayer; +@property (nonatomic, assign) CGRect customBlurBounds; +@property (nonatomic, assign) CGPoint customBlurPosition; +@property (nonatomic, assign) CGPoint customBlurAnchorPoint; +@property (nonatomic, assign) CGRect customBlurFrame; + +- (void) resetCustomPositioning; + +@end diff --git a/LiveFrost/LFGlassLayer.m b/LiveFrost/LFGlassLayer.m new file mode 100644 index 0000000..f7550be --- /dev/null +++ b/LiveFrost/LFGlassLayer.m @@ -0,0 +1,387 @@ +// +// Copyright (c) 2013-2014 Evadne Wu and Nicholas Levin +// +// 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. +// +// Contains contributions from Nam Kennic +// + +#import "LFGlassLayer.h" + +@interface LFGlassLayer () + +@property (nonatomic, assign, readonly) CGSize cachedBufferSize; +@property (nonatomic, assign, readonly) CGSize scaledSize; + +@property (nonatomic, assign, readonly) CGContextRef effectInContext; +@property (nonatomic, assign, readonly) CGContextRef effectOutContext; + +@property (nonatomic, assign, readonly) vImage_Buffer effectInBuffer; +@property (nonatomic, assign, readonly) vImage_Buffer effectOutBuffer; + +@property (nonatomic, assign, readonly) uint32_t precalculatedBlurKernel; + +@property (nonatomic, assign, readonly) NSUInteger currentFrameInterval; + +@property (nonatomic, strong, readonly) CALayer *backgroundColorLayer; + +- (void) setupLFGlassLayerInstance; +- (void) adjustLayerAndImageBuffersFromFrame:(CGRect)fromFrame; +- (CGRect) visibleBoundsToBlur; +- (void) recalculateFrame; +- (CGRect) visibleFrameToBlur; +- (void) recreateImageBuffers; +- (void) forceRefresh; + +@end + +#if !__has_feature(objc_arc) +#error This implementation file must be compiled with Objective-C ARC. + +#error Compile this file with the -fobjc-arc flag under your target's Build Phases, +#error or convert your project to Objective-C ARC. +#endif + +@implementation LFGlassLayer +@dynamic blurRadius; +@dynamic scaledSize; + +- (id) init { + if (self = [super init]) { + [self setupLFGlassLayerInstance]; + } + return self; +} + +- (id) initWithLayer:(id)layer { + if (self = [super initWithLayer:layer]) { + LFGlassLayer *originalLayer = (LFGlassLayer*)layer; + self.blurRadius = originalLayer.blurRadius; + _scaleFactor = originalLayer.scaleFactor; + _frameInterval = originalLayer.frameInterval; + } + return self; +} + +- (id) initWithCoder:(NSCoder *)aDecoder { + if (self = [super initWithCoder:aDecoder]) { + [self setupLFGlassLayerInstance]; + } + return self; +} + +- (void) setupLFGlassLayerInstance { + self.blurRadius = 4.0f; + _backgroundColorLayer = [CALayer layer]; + _backgroundColorLayer.actions = @{ + @"bounds": [NSNull null], + @"position": [NSNull null] + }; + self.backgroundColor = [[UIColor clearColor] CGColor]; + self.scaleFactor = 0.25f; + self.opaque = NO; + self.actions = @{ + @"contents": [NSNull null], + @"hidden": [NSNull null] + }; + _frameInterval = 1; + _currentFrameInterval = 0; +} + +- (void) dealloc { + if (_effectInContext) { + CGContextRelease(_effectInContext); + } + if (_effectOutContext) { + CGContextRelease(_effectOutContext); + } +} + +- (void) setBackgroundColor:(CGColorRef)backgroundColor { + [super setBackgroundColor:backgroundColor]; + + _backgroundColorLayer.backgroundColor = backgroundColor; + + if (CGColorGetAlpha(backgroundColor)) { + [self insertSublayer:self.backgroundColorLayer atIndex:0]; + } else { + [self.backgroundColorLayer removeFromSuperlayer]; + } +} + +- (void) setBounds:(CGRect)bounds { + CGRect oldFrame = [self visibleFrameToBlur]; + [super setBounds:bounds]; + [self adjustLayerAndImageBuffersFromFrame:oldFrame]; +} + +- (void) setPosition:(CGPoint)position { + CGRect oldFrame = [self visibleFrameToBlur]; + [super setPosition:position]; + [self adjustLayerAndImageBuffersFromFrame:oldFrame]; +} + +- (void) setAnchorPoint:(CGPoint)anchorPoint { + CGRect oldFrame = [self visibleFrameToBlur]; + [super setAnchorPoint:anchorPoint]; + [self adjustLayerAndImageBuffersFromFrame:oldFrame]; +} + +- (void) adjustLayerAndImageBuffersFromFrame:(CGRect)fromFrame { + if (CGRectEqualToRect(fromFrame, [self visibleFrameToBlur])) { + return; + } + + _backgroundColorLayer.frame = self.bounds; + + if (!CGRectIsEmpty(self.bounds)) { + [self recreateImageBuffers]; + } +} + +- (void) setScaleFactor:(CGFloat)scaleFactor { + _scaleFactor = scaleFactor; + CGSize scaledSize = self.scaledSize; + if (!CGSizeEqualToSize(_cachedBufferSize, scaledSize)) { + _cachedBufferSize = scaledSize; + [self recreateImageBuffers]; + } +} + +- (CGSize) scaledSize { + CGRect visibleBounds = [self visibleBoundsToBlur]; + CGSize scaledSize = (CGSize){ + _scaleFactor * CGRectGetWidth(visibleBounds), + _scaleFactor * CGRectGetHeight(visibleBounds) + }; + return scaledSize; +} + +- (void) setFrameInterval:(NSUInteger)frameInterval { + if (frameInterval == _frameInterval) { + return; + } + if (frameInterval == 0) { + NSLog(@"warning: attempted to set frameInterval to 0; frameInterval must be 1 or greater"); + return; + } + + _frameInterval = frameInterval; +} + +- (BOOL) blurOnceIfPossible { + if (!CGRectIsEmpty(self.bounds) && self.presentationLayer) { + [self forceRefresh]; + return YES; + } else { + return NO; + } +} + +- (void) setCustomBlurBounds:(CGRect)blurBounds { + _customBlurBounds = blurBounds; + [self recalculateFrame]; +} + +- (void) setCustomBlurAnchorPoint:(CGPoint)blurAnchorPoint { + _customBlurAnchorPoint = blurAnchorPoint; + [self recalculateFrame]; +} + +- (void) setCustomBlurPosition:(CGPoint)blurPosition { + _customBlurPosition = blurPosition; + [self recalculateFrame]; +} + +- (void) setCustomBlurFrame:(CGRect)blurFrame { + CGRect newBlurBounds; + newBlurBounds.origin.x = 0; + newBlurBounds.origin.y = 0; + newBlurBounds.size.width = blurFrame.size.width; + newBlurBounds.size.height = blurFrame.size.height; + _customBlurBounds = newBlurBounds; + + CGPoint newBlurAnchorPoint; + newBlurAnchorPoint.x = 0.5; + newBlurAnchorPoint.y = 0.5; + _customBlurAnchorPoint = newBlurAnchorPoint; + + CGPoint newBlurPosition; + newBlurPosition.x = blurFrame.origin.x + (newBlurBounds.size.width / 2.0); + newBlurPosition.y = blurFrame.origin.y + (newBlurBounds.size.height / 2.0); + _customBlurPosition = newBlurPosition; + + _customBlurFrame = blurFrame; +} + +- (void) resetCustomPositioning { + _customBlurBounds = CGRectZero; + _customBlurAnchorPoint = CGPointZero; + _customBlurPosition = CGPointZero; + _customBlurFrame = CGRectZero; +} + +- (CGRect) visibleBoundsToBlur { + return !CGRectEqualToRect(_customBlurBounds, CGRectZero) ? _customBlurBounds : self.bounds;; +} + +- (CGPoint) visibleAnchorPointToBlur { + return !CGPointEqualToPoint(_customBlurAnchorPoint, CGPointZero) ? _customBlurAnchorPoint : self.anchorPoint; +} + +- (CGPoint) visiblePositionToBlur { + return !CGPointEqualToPoint(_customBlurPosition, CGPointZero) ? _customBlurPosition : self.position; +} + +- (void) recalculateFrame { + CGRect frame; + CGRect bounds = [self visibleBoundsToBlur]; + CGPoint anchorPoint = [self visibleAnchorPointToBlur]; + CGPoint position = [self visiblePositionToBlur]; + + frame.size.width = bounds.size.width; + frame.size.height = bounds.size.height; + frame.origin.x = -bounds.size.width * anchorPoint.x; + frame.origin.y = -bounds.size.height * anchorPoint.y; + + frame.origin.x += position.x; + frame.origin.y += position.y; + + _customBlurFrame = frame; +} + +- (CGRect) visibleFrameToBlur { + return !CGRectEqualToRect(_customBlurFrame, CGRectZero) ? _customBlurFrame : self.frame; +} + +- (void) recreateImageBuffers { + CGRect visibleRect = [self visibleFrameToBlur]; + CGSize bufferSize = self.scaledSize; + if (bufferSize.width == 0.0 || bufferSize.height == 0.0) { + return; + } + + size_t bufferWidth = (size_t)rint(bufferSize.width); + size_t bufferHeight = (size_t)rint(bufferSize.height); + if (bufferWidth == 0) { + bufferWidth = 1; + } + if (bufferHeight == 0) { + bufferHeight = 1; + } + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + CGContextRef effectInContext = CGBitmapContextCreate(NULL, bufferWidth, bufferHeight, 8, bufferWidth * 8, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + + CGContextRef effectOutContext = CGBitmapContextCreate(NULL, bufferWidth, bufferHeight, 8, bufferWidth * 8, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + + CGColorSpaceRelease(colorSpace); + + CGContextConcatCTM(effectInContext, (CGAffineTransform){ + 1.0, 0.0, 0.0, -1.0, 0.0, bufferSize.height + }); + CGContextScaleCTM(effectInContext, _scaleFactor, _scaleFactor); + CGContextTranslateCTM(effectInContext, -visibleRect.origin.x, -visibleRect.origin.y); + + if (_effectInContext) { + CGContextRelease(_effectInContext); + } + _effectInContext = effectInContext; + + if (_effectOutContext) { + CGContextRelease(_effectOutContext); + } + _effectOutContext = effectOutContext; + + _effectInBuffer = (vImage_Buffer){ + .data = CGBitmapContextGetData(effectInContext), + .width = CGBitmapContextGetWidth(effectInContext), + .height = CGBitmapContextGetHeight(effectInContext), + .rowBytes = CGBitmapContextGetBytesPerRow(effectInContext) + }; + + _effectOutBuffer = (vImage_Buffer){ + .data = CGBitmapContextGetData(effectOutContext), + .width = CGBitmapContextGetWidth(effectOutContext), + .height = CGBitmapContextGetHeight(effectOutContext), + .rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext) + }; +} + +- (void) forceRefresh { + _currentFrameInterval = _frameInterval - 1; + [self refresh]; +} + +- (void) refresh { + if (++_currentFrameInterval < _frameInterval) { + return; + } + _currentFrameInterval = 0; + + CALayer *blurTargetLayer = _customBlurTargetLayer ? _customBlurTargetLayer : self.superlayer; + + // generates a shadow copy + LFGlassLayer *presentationLayer = ((LFGlassLayer*)self.presentationLayer); + +#ifdef DEBUG + NSParameterAssert(blurTargetLayer); + NSParameterAssert(presentationLayer); + NSParameterAssert(_effectInContext); + NSParameterAssert(_effectOutContext); +#endif + + CGContextRef effectInContext = CGContextRetain(_effectInContext); + CGContextRef effectOutContext = CGContextRetain(_effectOutContext); + vImage_Buffer effectInBuffer = _effectInBuffer; + vImage_Buffer effectOutBuffer = _effectOutBuffer; + + self.hidden = YES; + [blurTargetLayer renderInContext:effectInContext]; + self.hidden = NO; + + uint32_t radius = (uint32_t)floor(presentationLayer.blurRadius * 1.87997120597325 + 0.5); + // NOTE: this is "(uint32_t)floor(presentationLayer.blurRadius * 0.75 * sqrt(2 * M_PI) + 0.5)" + radius += (radius + 1) % 2; + uint32_t blurKernel = radius; + + vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, blurKernel, blurKernel, 0, kvImageEdgeExtend); + vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, blurKernel, blurKernel, 0, kvImageEdgeExtend); + vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, blurKernel, blurKernel, 0, kvImageEdgeExtend); + + CGImageRef outImage = CGBitmapContextCreateImage(effectOutContext); + self.contents = (__bridge id)(outImage); + CGImageRelease(outImage); + + CGContextRelease(effectInContext); + CGContextRelease(effectOutContext); +} + +- (id) actionForKey:(NSString *)event { + if ([event isEqualToString:@"blurRadius"]) { + CABasicAnimation *blurAnimation = [CABasicAnimation animationWithKeyPath:event]; + blurAnimation.fromValue = [self.presentationLayer valueForKey:event]; + return blurAnimation; + } + + return [super actionForKey:event]; +} + +@end diff --git a/LiveFrost/LFGlassView.h b/LiveFrost/LFGlassView.h index d8bd7eb..db7fa47 100644 --- a/LiveFrost/LFGlassView.h +++ b/LiveFrost/LFGlassView.h @@ -19,13 +19,12 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // -// Contains contributions from Nam Kennic -// #import #import +#import "LFGlassLayer.h" -@interface LFGlassView : UIView +@interface LFGlassView : UIView @property (nonatomic, assign) CGFloat blurRadius; @property (nonatomic, assign) CGFloat scaleFactor; @@ -36,4 +35,6 @@ - (BOOL) blurOnceIfPossible; +@property (nonatomic, weak, readonly) LFGlassLayer *glassLayer; + @end diff --git a/LiveFrost/LFGlassView.m b/LiveFrost/LFGlassView.m index 17d536d..f060569 100644 --- a/LiveFrost/LFGlassView.m +++ b/LiveFrost/LFGlassView.m @@ -19,171 +19,101 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // -// Contains contributions from Nam Kennic -// #import "LFGlassView.h" -#import "LFDisplayBridge.h" - -@interface LFGlassView () - -@property (nonatomic, assign, readonly) CGSize cachedBufferSize; -@property (nonatomic, assign, readonly) CGSize scaledSize; -@property (nonatomic, assign, readonly) CGContextRef effectInContext; -@property (nonatomic, assign, readonly) CGContextRef effectOutContext; - -@property (nonatomic, assign, readonly) vImage_Buffer effectInBuffer; -@property (nonatomic, assign, readonly) vImage_Buffer effectOutBuffer; - -@property (nonatomic, assign, readonly) uint32_t precalculatedBlurKernel; +@interface LFGlassView () @property (nonatomic, assign, readonly) BOOL shouldLiveBlur; -@property (nonatomic, assign, readonly) NSUInteger currentFrameInterval; - -@property (nonatomic, strong, readonly) CALayer *backgroundColorLayer; - -- (void) updatePrecalculatedBlurKernel; -- (void) adjustImageBuffersAndLayerFromFrame:(CGRect)fromFrame; -- (void) recreateImageBuffers; -- (void) startLiveBlurringIfReady; -- (void) stopLiveBlurring; -- (BOOL) isReadyToLiveBlur; -- (void) forceRefresh; +- (void) setupLFGlassViewInstance; +- (void) handleBlurringOnBoundsChange; @end #if !__has_feature(objc_arc) - #error This implementation file must be compiled with Objective-C ARC. +#error This implementation file must be compiled with Objective-C ARC. - #error Compile this file with the -fobjc-arc flag under your target's Build Phases, - #error or convert your project to Objective-C ARC. +#error Compile this file with the -fobjc-arc flag under your target's Build Phases, +#error or convert your project to Objective-C ARC. #endif @implementation LFGlassView -@dynamic scaledSize; @dynamic liveBlurring; ++ (Class) layerClass { + return [LFGlassLayer class]; +} + - (id) initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { - [self setup]; + [self setupLFGlassViewInstance]; } return self; } - (id) initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]) { - [self setup]; + [self setupLFGlassViewInstance]; } return self; } -- (void) setup { +- (void) setupLFGlassViewInstance { + _glassLayer = (LFGlassLayer*)self.layer; self.clipsToBounds = YES; - self.blurRadius = 4.0f; - _backgroundColorLayer = [CALayer layer]; - _backgroundColorLayer.actions = @{ - @"backgroundColor": [NSNull null], - @"bounds": [NSNull null], - @"position": [NSNull null] - }; - self.backgroundColor = [UIColor clearColor]; - self.scaleFactor = 0.25f; - self.opaque = NO; self.userInteractionEnabled = NO; - self.layer.actions = @{ - @"contents": [NSNull null] - }; _shouldLiveBlur = YES; - _frameInterval = 1; - _currentFrameInterval = 0; } - (void) dealloc { - if (_effectInContext) { - CGContextRelease(_effectInContext); - } - if (_effectOutContext) { - CGContextRelease(_effectOutContext); - } [self stopLiveBlurring]; } -- (void) setBlurRadius:(CGFloat)blurRadius { - _blurRadius = blurRadius; - [self updatePrecalculatedBlurKernel]; +- (void) setBounds:(CGRect)bounds { + [super setBounds:bounds]; + [self handleBlurringOnBoundsChange]; } -- (void) updatePrecalculatedBlurKernel { - uint32_t radius = (uint32_t)floor(_blurRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5); - radius += (radius + 1) % 2; - _precalculatedBlurKernel = radius; +- (void) setFrame:(CGRect)frame { + [super setFrame:frame]; + [self handleBlurringOnBoundsChange]; } -- (void) setScaleFactor:(CGFloat)scaleFactor { - _scaleFactor = scaleFactor; - CGSize scaledSize = self.scaledSize; - if (!CGSizeEqualToSize(_cachedBufferSize, scaledSize)) { - _cachedBufferSize = scaledSize; - [self recreateImageBuffers]; +- (void) handleBlurringOnBoundsChange { + if (CGRectIsEmpty(self.bounds)) { + [self stopLiveBlurring]; + } else { + [self startLiveBlurringIfReady]; } } -- (CGSize) scaledSize { - CGSize scaledSize = (CGSize){ - _scaleFactor * CGRectGetWidth(self.bounds), - _scaleFactor * CGRectGetHeight(self.bounds) - }; - return scaledSize; +- (CGFloat) blurRadius { + return [_glassLayer blurRadius]; } -- (void) setFrame:(CGRect)frame { - CGRect fromFrame = self.frame; - [super setFrame:frame]; - [self adjustImageBuffersAndLayerFromFrame:fromFrame]; +- (void) setBlurRadius:(CGFloat)blurRadius { + [_glassLayer setBlurRadius:blurRadius]; } -- (void) setBounds:(CGRect)bounds { - CGRect fromFrame = self.frame; - [super setBounds:bounds]; - [self adjustImageBuffersAndLayerFromFrame:fromFrame]; +- (CGFloat) scaleFactor { + return [_glassLayer scaleFactor]; } -- (void) setCenter:(CGPoint)center { - CGRect fromFrame = self.frame; - [super setCenter:center]; - [self adjustImageBuffersAndLayerFromFrame:fromFrame]; +- (void) setScaleFactor:(CGFloat)scaleFactor { + [_glassLayer setScaleFactor:scaleFactor]; } -- (void) setBackgroundColor:(UIColor *)color { - [super setBackgroundColor:color]; - - CGColorRef backgroundCGColor = [color CGColor]; - - if (CGColorGetAlpha(backgroundCGColor)) { - _backgroundColorLayer.backgroundColor = backgroundCGColor; - [self.layer insertSublayer:_backgroundColorLayer atIndex:0]; - } else { - [_backgroundColorLayer removeFromSuperlayer]; - } +- (NSUInteger) frameInterval { + return [_glassLayer frameInterval]; } -- (void) adjustImageBuffersAndLayerFromFrame:(CGRect)fromFrame { - if (CGRectEqualToRect(fromFrame, self.frame)) { - return; - } - - _backgroundColorLayer.frame = self.bounds; - - if (!CGRectIsEmpty(self.bounds)) { - [self recreateImageBuffers]; - } else { - [self stopLiveBlurring]; - return; - } - - [self startLiveBlurringIfReady]; +- (void) setFrameInterval:(NSUInteger)frameInterval { + [_glassLayer setFrameInterval:frameInterval]; +} + +- (BOOL) blurOnceIfPossible { + return [_glassLayer blurOnceIfPossible]; } - (void) didMoveToSuperview { @@ -220,135 +150,17 @@ - (void) setLiveBlurring:(BOOL)liveBlurring { - (void) startLiveBlurringIfReady { if ([self isReadyToLiveBlur]) { - [self forceRefresh]; - [[LFDisplayBridge sharedInstance] addSubscribedViewsObject:self]; + [self blurOnceIfPossible]; + [[LFDisplayBridge sharedInstance] addSubscribedLayer:_glassLayer]; } } - (void) stopLiveBlurring { - [[LFDisplayBridge sharedInstance] removeSubscribedViewsObject:self]; + [[LFDisplayBridge sharedInstance] removeSubscribedLayer:_glassLayer]; } - (BOOL) isReadyToLiveBlur { return (!CGRectIsEmpty(self.bounds) && self.superview && self.window && _shouldLiveBlur); } -- (BOOL) blurOnceIfPossible { - if (!CGRectIsEmpty(self.bounds) && self.layer.presentationLayer) { - [self forceRefresh]; - return YES; - } else { - return NO; - } -} - -- (void) setFrameInterval:(NSUInteger)frameInterval { - if (frameInterval == _frameInterval) { - return; - } - if (frameInterval == 0) { - NSLog(@"warning: attempted to set frameInterval to 0; frameInterval must be 1 or greater"); - return; - } - - _frameInterval = frameInterval; -} - -- (void) recreateImageBuffers { - CGRect visibleRect = self.frame; - CGSize bufferSize = self.scaledSize; - if (bufferSize.width == 0.0 || bufferSize.height == 0.0) { - return; - } - - size_t bufferWidth = (size_t)rint(bufferSize.width); - size_t bufferHeight = (size_t)rint(bufferSize.height); - if (bufferWidth == 0) { - bufferWidth = 1; - } - if (bufferHeight == 0) { - bufferHeight = 1; - } - - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - - CGContextRef effectInContext = CGBitmapContextCreate(NULL, bufferWidth, bufferHeight, 8, bufferWidth * 8, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); - - CGContextRef effectOutContext = CGBitmapContextCreate(NULL, bufferWidth, bufferHeight, 8, bufferWidth * 8, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); - - CGColorSpaceRelease(colorSpace); - - CGContextConcatCTM(effectInContext, (CGAffineTransform){ - 1.0, 0.0, 0.0, -1.0, 0.0, bufferSize.height - }); - CGContextScaleCTM(effectInContext, _scaleFactor, _scaleFactor); - CGContextTranslateCTM(effectInContext, -visibleRect.origin.x, -visibleRect.origin.y); - - if (_effectInContext) { - CGContextRelease(_effectInContext); - } - _effectInContext = effectInContext; - - if (_effectOutContext) { - CGContextRelease(_effectOutContext); - } - _effectOutContext = effectOutContext; - - _effectInBuffer = (vImage_Buffer){ - .data = CGBitmapContextGetData(effectInContext), - .width = CGBitmapContextGetWidth(effectInContext), - .height = CGBitmapContextGetHeight(effectInContext), - .rowBytes = CGBitmapContextGetBytesPerRow(effectInContext) - }; - - _effectOutBuffer = (vImage_Buffer){ - .data = CGBitmapContextGetData(effectOutContext), - .width = CGBitmapContextGetWidth(effectOutContext), - .height = CGBitmapContextGetHeight(effectOutContext), - .rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext) - }; -} - -- (void) forceRefresh { - _currentFrameInterval = _frameInterval - 1; - [self refresh]; -} - -- (void) refresh { - if (++_currentFrameInterval < _frameInterval) { - return; - } - _currentFrameInterval = 0; - - UIView *superview = self.superview; -#ifdef DEBUG - NSParameterAssert(superview); - NSParameterAssert(self.window); - NSParameterAssert(_effectInContext); - NSParameterAssert(_effectOutContext); -#endif - - CGContextRef effectInContext = CGContextRetain(_effectInContext); - CGContextRef effectOutContext = CGContextRetain(_effectOutContext); - vImage_Buffer effectInBuffer = _effectInBuffer; - vImage_Buffer effectOutBuffer = _effectOutBuffer; - - self.hidden = YES; - [superview.layer renderInContext:effectInContext]; - self.hidden = NO; - - uint32_t blurKernel = _precalculatedBlurKernel; - - vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, blurKernel, blurKernel, 0, kvImageEdgeExtend); - vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, blurKernel, blurKernel, 0, kvImageEdgeExtend); - vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, blurKernel, blurKernel, 0, kvImageEdgeExtend); - - CGImageRef outImage = CGBitmapContextCreateImage(effectOutContext); - self.layer.contents = (__bridge id)(outImage); - CGImageRelease(outImage); - - CGContextRelease(effectInContext); - CGContextRelease(effectOutContext); -} - @end diff --git a/LiveFrost/LiveFrost.h b/LiveFrost/LiveFrost.h index 24e4e0f..de87a80 100644 --- a/LiveFrost/LiveFrost.h +++ b/LiveFrost/LiveFrost.h @@ -20,5 +20,6 @@ // THE SOFTWARE. // +#import #import #import From 5446dbd0abd11f6beac6faf4e89bdad2e7d04301 Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 11:00:57 -0700 Subject: [PATCH 02/15] Second round of changes, easy ones based on Evadne's feedback --- LiveFrost.podspec | 2 +- LiveFrost/LFDefines.c | 26 ++++++ LiveFrost/LFDefines.h | 25 ++++++ LiveFrost/LFDisplayBridge.h | 12 +-- LiveFrost/LFDisplayBridge.m | 20 ++--- LiveFrost/LFDisplayBridgeTriggering.h | 25 ++++++ LiveFrost/LFGlassLayer.h | 6 +- LiveFrost/LFGlassLayer.m | 125 ++++++++++++++++---------- LiveFrost/LFGlassView.h | 4 +- LiveFrost/LFGlassView.m | 14 +-- LiveFrost/LiveFrost.h | 3 +- 11 files changed, 183 insertions(+), 79 deletions(-) create mode 100644 LiveFrost/LFDefines.c create mode 100644 LiveFrost/LFDefines.h create mode 100644 LiveFrost/LFDisplayBridgeTriggering.h diff --git a/LiveFrost.podspec b/LiveFrost.podspec index 580ff1c..486f3cf 100644 --- a/LiveFrost.podspec +++ b/LiveFrost.podspec @@ -10,7 +10,7 @@ Pod::Spec.new do |s| } s.source = { :git => "https://github.com/radi/LiveFrost.git", :tag => "1.1.2" } s.platform = :ios, '6.0' - s.source_files = 'LiveFrost', 'LiveFrost/**/*.{h,m}' + s.source_files = 'LiveFrost', 'LiveFrost/**/*.{h,m,c}' s.exclude_files = 'LiveFrost/Exclude' s.frameworks = 'Accelerate', 'QuartzCore', 'UIKit' s.requires_arc = true diff --git a/LiveFrost/LFDefines.c b/LiveFrost/LFDefines.c new file mode 100644 index 0000000..dffe44a --- /dev/null +++ b/LiveFrost/LFDefines.c @@ -0,0 +1,26 @@ +// +// Copyright (c) 2014 Evadne Wu and Nicholas Levin +// +// 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. +// + +#include "LFDefines.h" +#include + +const CGPoint LFPointNull = (CGPoint){INFINITY, INFINITY}; diff --git a/LiveFrost/LFDefines.h b/LiveFrost/LFDefines.h new file mode 100644 index 0000000..e4518ee --- /dev/null +++ b/LiveFrost/LFDefines.h @@ -0,0 +1,25 @@ +// +// Copyright (c) 2014 Evadne Wu and Nicholas Levin +// +// 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. +// + +#include + +extern const CGPoint LFPointNull; diff --git a/LiveFrost/LFDisplayBridge.h b/LiveFrost/LFDisplayBridge.h index 6994473..1aa79b8 100644 --- a/LiveFrost/LFDisplayBridge.h +++ b/LiveFrost/LFDisplayBridge.h @@ -20,18 +20,14 @@ // THE SOFTWARE. // -#import - -@protocol LFDisplayBridgeTriggering -- (void) refresh; -@end +@import QuartzCore.CADisplayLink; +#import "LFDisplayBridgeTriggering.h" @interface LFDisplayBridge : NSObject + (instancetype) sharedInstance; -@property (nonatomic, readonly, assign) CFMutableSetRef subscribedViews; -- (void) addSubscribedLayer:(CALayer *)object; -- (void) removeSubscribedLayer:(CALayer *)object; +- (void) addSubscribedObject:(id)object; +- (void) removeSubscribedObject:(id)object; @end diff --git a/LiveFrost/LFDisplayBridge.m b/LiveFrost/LFDisplayBridge.m index 5c88545..7d921d8 100644 --- a/LiveFrost/LFDisplayBridge.m +++ b/LiveFrost/LFDisplayBridge.m @@ -22,16 +22,16 @@ #import "LFDisplayBridge.h" -void LF_refreshAllSubscribedLayersApplierFunction(const void *value, void *context); +void LF_refreshAllSubscribedObjectsApplierFunction(const void *value, void *context); @interface LFDisplayBridge () -@property (nonatomic, readwrite, assign) CFMutableSetRef subscribedLayers; +@property (nonatomic, readwrite, assign) CFMutableSetRef subscribedObjects; @property (nonatomic, readonly, strong) CADisplayLink *displayLink; @end -void LF_refreshAllSubscribedLayersApplierFunction(const void *value, void *context) { +void LF_refreshAllSubscribedObjectsApplierFunction(const void *value, void *context) { [(__bridge UIView *)value refresh]; } @@ -55,7 +55,7 @@ + (instancetype) sharedInstance { - (id) init { if (self = [super init]) { - _subscribedLayers = CFSetCreateMutable(kCFAllocatorDefault, 0, NULL); + _subscribedObjects = CFSetCreateMutable(kCFAllocatorDefault, 0, NULL); _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)]; [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; } @@ -64,15 +64,15 @@ - (id) init { - (void) dealloc { [_displayLink invalidate]; - CFRelease(_subscribedLayers); + CFRelease(_subscribedObjects); } -- (void) addSubscribedLayer:(CALayer *)object { - CFSetAddValue(_subscribedLayers, (__bridge const void*)object); +- (void) addSubscribedObject:(id)object { + CFSetAddValue(_subscribedObjects, (__bridge const void*)object); } -- (void) removeSubscribedLayer:(CALayer *)object { - CFSetRemoveValue(_subscribedLayers, (__bridge const void*)object); +- (void) removeSubscribedObject:(id)object { + CFSetRemoveValue(_subscribedObjects, (__bridge const void*)object); } - (void) handleDisplayLink:(CADisplayLink *)displayLink { @@ -80,7 +80,7 @@ - (void) handleDisplayLink:(CADisplayLink *)displayLink { } - (void) refresh { - CFSetApplyFunction(_subscribedLayers, LF_refreshAllSubscribedLayersApplierFunction, NULL); + CFSetApplyFunction(_subscribedObjects, LF_refreshAllSubscribedObjectsApplierFunction, NULL); } @end diff --git a/LiveFrost/LFDisplayBridgeTriggering.h b/LiveFrost/LFDisplayBridgeTriggering.h new file mode 100644 index 0000000..5fcd471 --- /dev/null +++ b/LiveFrost/LFDisplayBridgeTriggering.h @@ -0,0 +1,25 @@ +// +// Copyright (c) 2013-2014 Evadne Wu and Nicholas Levin +// +// 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. +// + +@protocol LFDisplayBridgeTriggering +- (void) refresh; +@end diff --git a/LiveFrost/LFGlassLayer.h b/LiveFrost/LFGlassLayer.h index 9034c22..ba0bc5e 100644 --- a/LiveFrost/LFGlassLayer.h +++ b/LiveFrost/LFGlassLayer.h @@ -22,9 +22,9 @@ // Contains contributions from Nam Kennic // -#import -#import -#import "LFDisplayBridge.h" +@import Accelerate.vImage; +@import QuartzCore.CALayer; +#import "LFDisplayBridgeTriggering.h" @interface LFGlassLayer : CALayer diff --git a/LiveFrost/LFGlassLayer.m b/LiveFrost/LFGlassLayer.m index f7550be..0393280 100644 --- a/LiveFrost/LFGlassLayer.m +++ b/LiveFrost/LFGlassLayer.m @@ -23,6 +23,7 @@ // #import "LFGlassLayer.h" +#include "LFDefines.h" @interface LFGlassLayer () @@ -41,7 +42,10 @@ @interface LFGlassLayer () @property (nonatomic, strong, readonly) CALayer *backgroundColorLayer; -- (void) setupLFGlassLayerInstance; +@property (nonatomic, assign, readonly) void *blurRadiusObserverContext; + +- (void) setup; +- (void) updatePrecalculatedBlurKernel; - (void) adjustLayerAndImageBuffersFromFrame:(CGRect)fromFrame; - (CGRect) visibleBoundsToBlur; - (void) recalculateFrame; @@ -58,21 +62,29 @@ - (void) forceRefresh; #error or convert your project to Objective-C ARC. #endif +void *LFGlassLayerBlurRadiusObserverContext = &LFGlassLayerBlurRadiusObserverContext; + @implementation LFGlassLayer @dynamic blurRadius; @dynamic scaledSize; - (id) init { if (self = [super init]) { - [self setupLFGlassLayerInstance]; + [self setup]; } return self; } - (id) initWithLayer:(id)layer { if (self = [super initWithLayer:layer]) { +#ifdef DEBUG + NSParameterAssert([layer isKindOfClass:[LFGlassLayer class]]); +#endif LFGlassLayer *originalLayer = (LFGlassLayer*)layer; + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; self.blurRadius = originalLayer.blurRadius; + [CATransaction commit]; _scaleFactor = originalLayer.scaleFactor; _frameInterval = originalLayer.frameInterval; } @@ -81,13 +93,24 @@ - (id) initWithLayer:(id)layer { - (id) initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]) { - [self setupLFGlassLayerInstance]; + [self setup]; } return self; } -- (void) setupLFGlassLayerInstance { +- (void) setup { + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; self.blurRadius = 4.0f; + [self updatePrecalculatedBlurKernel]; + [CATransaction commit]; + + [self addObserver:self + forKeyPath:@"blurRadius" + options:0 + context:LFGlassLayerBlurRadiusObserverContext]; + _blurRadiusObserverContext = LFGlassLayerBlurRadiusObserverContext; + _backgroundColorLayer = [CALayer layer]; _backgroundColorLayer.actions = @{ @"bounds": [NSNull null], @@ -102,9 +125,16 @@ - (void) setupLFGlassLayerInstance { }; _frameInterval = 1; _currentFrameInterval = 0; + [self resetCustomPositioning]; } - (void) dealloc { + if (_blurRadiusObserverContext) { + [self removeObserver:self + forKeyPath:@"blurRadius" + context:LFGlassLayerBlurRadiusObserverContext]; + } + if (_effectInContext) { CGContextRelease(_effectInContext); } @@ -113,6 +143,12 @@ - (void) dealloc { } } +- (void) updatePrecalculatedBlurKernel { + uint32_t radius = (uint32_t)floor(self.blurRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5); + radius += (radius + 1) % 2; + _precalculatedBlurKernel = radius; +} + - (void) setBackgroundColor:(CGColorRef)backgroundColor { [super setBackgroundColor:backgroundColor]; @@ -210,64 +246,53 @@ - (void) setCustomBlurPosition:(CGPoint)blurPosition { } - (void) setCustomBlurFrame:(CGRect)blurFrame { - CGRect newBlurBounds; - newBlurBounds.origin.x = 0; - newBlurBounds.origin.y = 0; - newBlurBounds.size.width = blurFrame.size.width; - newBlurBounds.size.height = blurFrame.size.height; + CGRect newBlurBounds = (CGRect){ CGPointZero, blurFrame.size }; _customBlurBounds = newBlurBounds; - CGPoint newBlurAnchorPoint; - newBlurAnchorPoint.x = 0.5; - newBlurAnchorPoint.y = 0.5; - _customBlurAnchorPoint = newBlurAnchorPoint; + _customBlurAnchorPoint = (CGPoint){ 0.5, 0.5 }; - CGPoint newBlurPosition; - newBlurPosition.x = blurFrame.origin.x + (newBlurBounds.size.width / 2.0); - newBlurPosition.y = blurFrame.origin.y + (newBlurBounds.size.height / 2.0); - _customBlurPosition = newBlurPosition; + _customBlurPosition = (CGPoint){ + blurFrame.origin.x + 0.5 * CGRectGetWidth(newBlurBounds), + blurFrame.origin.y + 0.5 * CGRectGetHeight(newBlurBounds) + }; _customBlurFrame = blurFrame; } - (void) resetCustomPositioning { - _customBlurBounds = CGRectZero; - _customBlurAnchorPoint = CGPointZero; - _customBlurPosition = CGPointZero; - _customBlurFrame = CGRectZero; + _customBlurBounds = CGRectNull; + _customBlurAnchorPoint = LFPointNull; + _customBlurPosition = LFPointNull; + _customBlurFrame = CGRectNull; } - (CGRect) visibleBoundsToBlur { - return !CGRectEqualToRect(_customBlurBounds, CGRectZero) ? _customBlurBounds : self.bounds;; + return CGRectEqualToRect(_customBlurBounds, CGRectNull) ? self.bounds : _customBlurBounds; } - (CGPoint) visibleAnchorPointToBlur { - return !CGPointEqualToPoint(_customBlurAnchorPoint, CGPointZero) ? _customBlurAnchorPoint : self.anchorPoint; + return CGPointEqualToPoint(_customBlurAnchorPoint, LFPointNull) ? self.anchorPoint : _customBlurAnchorPoint; } - (CGPoint) visiblePositionToBlur { - return !CGPointEqualToPoint(_customBlurPosition, CGPointZero) ? _customBlurPosition : self.position; + return CGPointEqualToPoint(_customBlurPosition, LFPointNull) ? self.position : _customBlurPosition; } - (void) recalculateFrame { - CGRect frame; CGRect bounds = [self visibleBoundsToBlur]; CGPoint anchorPoint = [self visibleAnchorPointToBlur]; CGPoint position = [self visiblePositionToBlur]; - - frame.size.width = bounds.size.width; - frame.size.height = bounds.size.height; - frame.origin.x = -bounds.size.width * anchorPoint.x; - frame.origin.y = -bounds.size.height * anchorPoint.y; - - frame.origin.x += position.x; - frame.origin.y += position.y; - - _customBlurFrame = frame; + + _customBlurFrame = (CGRect){ + -bounds.size.width * anchorPoint.x + position.x, + -bounds.size.height * anchorPoint.y + position.y, + bounds.size.width, + bounds.size.height + }; } - (CGRect) visibleFrameToBlur { - return !CGRectEqualToRect(_customBlurFrame, CGRectZero) ? _customBlurFrame : self.frame; + return CGRectEqualToRect(_customBlurFrame, CGRectNull) ? self.frame : _customBlurFrame; } - (void) recreateImageBuffers { @@ -338,12 +363,10 @@ - (void) refresh { CALayer *blurTargetLayer = _customBlurTargetLayer ? _customBlurTargetLayer : self.superlayer; - // generates a shadow copy - LFGlassLayer *presentationLayer = ((LFGlassLayer*)self.presentationLayer); - #ifdef DEBUG + // generates a shadow copy + NSParameterAssert(self.presentationLayer); NSParameterAssert(blurTargetLayer); - NSParameterAssert(presentationLayer); NSParameterAssert(_effectInContext); NSParameterAssert(_effectOutContext); #endif @@ -357,10 +380,7 @@ - (void) refresh { [blurTargetLayer renderInContext:effectInContext]; self.hidden = NO; - uint32_t radius = (uint32_t)floor(presentationLayer.blurRadius * 1.87997120597325 + 0.5); - // NOTE: this is "(uint32_t)floor(presentationLayer.blurRadius * 0.75 * sqrt(2 * M_PI) + 0.5)" - radius += (radius + 1) % 2; - uint32_t blurKernel = radius; + uint32_t blurKernel = _precalculatedBlurKernel; vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, blurKernel, blurKernel, 0, kvImageEdgeExtend); vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, blurKernel, blurKernel, 0, kvImageEdgeExtend); @@ -374,11 +394,22 @@ - (void) refresh { CGContextRelease(effectOutContext); } +- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + if (context == LFGlassLayerBlurRadiusObserverContext) { + [self updatePrecalculatedBlurKernel]; + } else { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } +} + - (id) actionForKey:(NSString *)event { if ([event isEqualToString:@"blurRadius"]) { - CABasicAnimation *blurAnimation = [CABasicAnimation animationWithKeyPath:event]; - blurAnimation.fromValue = [self.presentationLayer valueForKey:event]; - return blurAnimation; + NSLog(@"actionForKey: for a blur Radius was asked for and reached!"); +// CABasicAnimation *blurAnimation = [CABasicAnimation animationWithKeyPath:event]; +// blurAnimation.fromValue = [self.presentationLayer valueForKey:event]; +// return blurAnimation; + + // TODO: Use a CATransaction instead of a CABasicAnimation to handle this. Consider copying the layer, then perform a fade transition to make things look good. } return [super actionForKey:event]; diff --git a/LiveFrost/LFGlassView.h b/LiveFrost/LFGlassView.h index db7fa47..2e56772 100644 --- a/LiveFrost/LFGlassView.h +++ b/LiveFrost/LFGlassView.h @@ -20,9 +20,7 @@ // THE SOFTWARE. // -#import -#import -#import "LFGlassLayer.h" +@class LFGlassLayer; @interface LFGlassView : UIView diff --git a/LiveFrost/LFGlassView.m b/LiveFrost/LFGlassView.m index f060569..2c5d433 100644 --- a/LiveFrost/LFGlassView.m +++ b/LiveFrost/LFGlassView.m @@ -21,12 +21,14 @@ // #import "LFGlassView.h" +#import "LFGlassLayer.h" +#import "LFDisplayBridge.h" @interface LFGlassView () @property (nonatomic, assign, readonly) BOOL shouldLiveBlur; -- (void) setupLFGlassViewInstance; +- (void) setup; - (void) handleBlurringOnBoundsChange; @end @@ -47,19 +49,19 @@ + (Class) layerClass { - (id) initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { - [self setupLFGlassViewInstance]; + [self setup]; } return self; } - (id) initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]) { - [self setupLFGlassViewInstance]; + [self setup]; } return self; } -- (void) setupLFGlassViewInstance { +- (void) setup { _glassLayer = (LFGlassLayer*)self.layer; self.clipsToBounds = YES; self.userInteractionEnabled = NO; @@ -151,12 +153,12 @@ - (void) setLiveBlurring:(BOOL)liveBlurring { - (void) startLiveBlurringIfReady { if ([self isReadyToLiveBlur]) { [self blurOnceIfPossible]; - [[LFDisplayBridge sharedInstance] addSubscribedLayer:_glassLayer]; + [[LFDisplayBridge sharedInstance] addSubscribedObject:_glassLayer]; } } - (void) stopLiveBlurring { - [[LFDisplayBridge sharedInstance] removeSubscribedLayer:_glassLayer]; + [[LFDisplayBridge sharedInstance] removeSubscribedObject:_glassLayer]; } - (BOOL) isReadyToLiveBlur { diff --git a/LiveFrost/LiveFrost.h b/LiveFrost/LiveFrost.h index de87a80..923d602 100644 --- a/LiveFrost/LiveFrost.h +++ b/LiveFrost/LiveFrost.h @@ -20,6 +20,7 @@ // THE SOFTWARE. // +#import +#import #import #import -#import From e7f963d37a335f22e44a60da0da28bea4abb1fda Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 11:04:04 -0700 Subject: [PATCH 03/15] Spacing --- LiveFrost/LFGlassLayer.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/LiveFrost/LFGlassLayer.m b/LiveFrost/LFGlassLayer.m index 0393280..63c14a3 100644 --- a/LiveFrost/LFGlassLayer.m +++ b/LiveFrost/LFGlassLayer.m @@ -106,9 +106,9 @@ - (void) setup { [CATransaction commit]; [self addObserver:self - forKeyPath:@"blurRadius" - options:0 - context:LFGlassLayerBlurRadiusObserverContext]; + forKeyPath:@"blurRadius" + options:0 + context:LFGlassLayerBlurRadiusObserverContext]; _blurRadiusObserverContext = LFGlassLayerBlurRadiusObserverContext; _backgroundColorLayer = [CALayer layer]; @@ -131,8 +131,8 @@ - (void) setup { - (void) dealloc { if (_blurRadiusObserverContext) { [self removeObserver:self - forKeyPath:@"blurRadius" - context:LFGlassLayerBlurRadiusObserverContext]; + forKeyPath:@"blurRadius" + context:LFGlassLayerBlurRadiusObserverContext]; } if (_effectInContext) { From b3383ff401ed2443af3a2ef8c7abc48fa01aafb9 Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 13:12:38 -0700 Subject: [PATCH 04/15] Using built in message forwarding facilities to forward a specific set of messages from the view to the layer. --- LiveFrost/LFGlassView.m | 52 +++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/LiveFrost/LFGlassView.m b/LiveFrost/LFGlassView.m index 2c5d433..3f0eddd 100644 --- a/LiveFrost/LFGlassView.m +++ b/LiveFrost/LFGlassView.m @@ -28,6 +28,9 @@ @interface LFGlassView () @property (nonatomic, assign, readonly) BOOL shouldLiveBlur; +@property (nonatomic, assign, readonly) BOOL blurOnceIfPossible; +@property (nonatomic, strong, readonly) NSSet *methodNamesToForwardToLayer; + - (void) setup; - (void) handleBlurringOnBoundsChange; @@ -41,7 +44,12 @@ - (void) handleBlurringOnBoundsChange; #endif @implementation LFGlassView + +@dynamic blurRadius; +@dynamic scaleFactor; +@dynamic frameInterval; @dynamic liveBlurring; +@dynamic blurOnceIfPossible; + (Class) layerClass { return [LFGlassLayer class]; @@ -66,6 +74,7 @@ - (void) setup { self.clipsToBounds = YES; self.userInteractionEnabled = NO; _shouldLiveBlur = YES; + _methodNamesToForwardToLayer = [NSSet setWithObjects:@"blurRadius", @"setBlurRadius:", @"scaleFactor", @"setScaleFactor:", @"frameInterval", @"setFrameInterval:", @"blurOnceIfPossible", nil]; } - (void) dealloc { @@ -90,34 +99,6 @@ - (void) handleBlurringOnBoundsChange { } } -- (CGFloat) blurRadius { - return [_glassLayer blurRadius]; -} - -- (void) setBlurRadius:(CGFloat)blurRadius { - [_glassLayer setBlurRadius:blurRadius]; -} - -- (CGFloat) scaleFactor { - return [_glassLayer scaleFactor]; -} - -- (void) setScaleFactor:(CGFloat)scaleFactor { - [_glassLayer setScaleFactor:scaleFactor]; -} - -- (NSUInteger) frameInterval { - return [_glassLayer frameInterval]; -} - -- (void) setFrameInterval:(NSUInteger)frameInterval { - [_glassLayer setFrameInterval:frameInterval]; -} - -- (BOOL) blurOnceIfPossible { - return [_glassLayer blurOnceIfPossible]; -} - - (void) didMoveToSuperview { [super didMoveToSuperview]; [self startLiveBlurringIfReady]; @@ -165,4 +146,19 @@ - (BOOL) isReadyToLiveBlur { return (!CGRectIsEmpty(self.bounds) && self.superview && self.window && _shouldLiveBlur); } +- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { + if ([_methodNamesToForwardToLayer containsObject:NSStringFromSelector(aSelector)]) { + return [[self.glassLayer class] instanceMethodSignatureForSelector:aSelector]; + } + return [super methodSignatureForSelector:aSelector]; +} + +- (void)forwardInvocation:(NSInvocation *)anInvocation { + if ([self.glassLayer respondsToSelector:[anInvocation selector]]) { + [anInvocation invokeWithTarget:self.glassLayer]; + } else { + return [super forwardInvocation:anInvocation]; + } +} + @end From 93257a856244f541606a466c35fb2bcdb97ac88c Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 15:08:41 -0700 Subject: [PATCH 05/15] Added the new solution for animating changes to the blur radius. Works great, too. --- LiveFrost/LFGlassLayer.m | 27 +++++++++++++++------------ LiveFrost/LFGlassView.m | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/LiveFrost/LFGlassLayer.m b/LiveFrost/LFGlassLayer.m index 63c14a3..d1ed698 100644 --- a/LiveFrost/LFGlassLayer.m +++ b/LiveFrost/LFGlassLayer.m @@ -23,7 +23,7 @@ // #import "LFGlassLayer.h" -#include "LFDefines.h" +#import "LFDefines.h" @interface LFGlassLayer () @@ -87,6 +87,11 @@ - (id) initWithLayer:(id)layer { [CATransaction commit]; _scaleFactor = originalLayer.scaleFactor; _frameInterval = originalLayer.frameInterval; + _customBlurTargetLayer = originalLayer.customBlurTargetLayer; + _customBlurBounds = originalLayer.customBlurBounds; + _customBlurPosition = originalLayer.customBlurPosition; + _customBlurAnchorPoint = originalLayer.customBlurAnchorPoint; + _customBlurFrame = originalLayer.customBlurFrame; } return self; } @@ -99,18 +104,17 @@ - (id) initWithCoder:(NSCoder *)aDecoder { } - (void) setup { - [CATransaction begin]; - [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; - self.blurRadius = 4.0f; - [self updatePrecalculatedBlurKernel]; - [CATransaction commit]; - [self addObserver:self forKeyPath:@"blurRadius" options:0 context:LFGlassLayerBlurRadiusObserverContext]; _blurRadiusObserverContext = LFGlassLayerBlurRadiusObserverContext; + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; + self.blurRadius = 4.0f; + [CATransaction commit]; + _backgroundColorLayer = [CALayer layer]; _backgroundColorLayer.actions = @{ @"bounds": [NSNull null], @@ -404,12 +408,11 @@ - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:( - (id) actionForKey:(NSString *)event { if ([event isEqualToString:@"blurRadius"]) { - NSLog(@"actionForKey: for a blur Radius was asked for and reached!"); -// CABasicAnimation *blurAnimation = [CABasicAnimation animationWithKeyPath:event]; -// blurAnimation.fromValue = [self.presentationLayer valueForKey:event]; -// return blurAnimation; - // TODO: Use a CATransaction instead of a CABasicAnimation to handle this. Consider copying the layer, then perform a fade transition to make things look good. + CATransition *blurAnimation = [CATransition animation]; + blurAnimation.type = kCATransitionFade; + + return blurAnimation; } return [super actionForKey:event]; diff --git a/LiveFrost/LFGlassView.m b/LiveFrost/LFGlassView.m index 3f0eddd..cc3b853 100644 --- a/LiveFrost/LFGlassView.m +++ b/LiveFrost/LFGlassView.m @@ -157,7 +157,7 @@ - (void)forwardInvocation:(NSInvocation *)anInvocation { if ([self.glassLayer respondsToSelector:[anInvocation selector]]) { [anInvocation invokeWithTarget:self.glassLayer]; } else { - return [super forwardInvocation:anInvocation]; + [super forwardInvocation:anInvocation]; } } From 8bda0e41be498083c02d4df1c18d558375bfef1c Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 15:16:47 -0700 Subject: [PATCH 06/15] Spacing --- LiveFrost/LFGlassLayer.m | 1 - 1 file changed, 1 deletion(-) diff --git a/LiveFrost/LFGlassLayer.m b/LiveFrost/LFGlassLayer.m index d1ed698..cd47d32 100644 --- a/LiveFrost/LFGlassLayer.m +++ b/LiveFrost/LFGlassLayer.m @@ -411,7 +411,6 @@ - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:( CATransition *blurAnimation = [CATransition animation]; blurAnimation.type = kCATransitionFade; - return blurAnimation; } From c72ce75e27d95e42c46cb54ef65e8b7d5b8e83ab Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 15:22:37 -0700 Subject: [PATCH 07/15] Added CONTRIBUTORS --- CONTRIBUTORS | 1 + LiveFrost/LFGlassLayer.h | 2 -- LiveFrost/LFGlassLayer.m | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) create mode 100644 CONTRIBUTORS diff --git a/CONTRIBUTORS b/CONTRIBUTORS new file mode 100644 index 0000000..50fd607 --- /dev/null +++ b/CONTRIBUTORS @@ -0,0 +1 @@ +- Nam Kennic wrote the initial implementation for -setBackgroundColor:, on what was then the LFGlassView. diff --git a/LiveFrost/LFGlassLayer.h b/LiveFrost/LFGlassLayer.h index ba0bc5e..99147a4 100644 --- a/LiveFrost/LFGlassLayer.h +++ b/LiveFrost/LFGlassLayer.h @@ -19,8 +19,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // -// Contains contributions from Nam Kennic -// @import Accelerate.vImage; @import QuartzCore.CALayer; diff --git a/LiveFrost/LFGlassLayer.m b/LiveFrost/LFGlassLayer.m index cd47d32..61c4557 100644 --- a/LiveFrost/LFGlassLayer.m +++ b/LiveFrost/LFGlassLayer.m @@ -19,8 +19,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // -// Contains contributions from Nam Kennic -// #import "LFGlassLayer.h" #import "LFDefines.h" From 248e11605dc8958a55590ab59641b8a256c0bff9 Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 15:38:17 -0700 Subject: [PATCH 08/15] More spacing --- LiveFrost/LFGlassLayer.m | 3 ++- LiveFrost/LFGlassView.m | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/LiveFrost/LFGlassLayer.m b/LiveFrost/LFGlassLayer.m index 61c4557..05a254f 100644 --- a/LiveFrost/LFGlassLayer.m +++ b/LiveFrost/LFGlassLayer.m @@ -19,6 +19,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // +// Contains contributions from Nam Kennic +// #import "LFGlassLayer.h" #import "LFDefines.h" @@ -406,7 +408,6 @@ - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:( - (id) actionForKey:(NSString *)event { if ([event isEqualToString:@"blurRadius"]) { - CATransition *blurAnimation = [CATransition animation]; blurAnimation.type = kCATransitionFade; return blurAnimation; diff --git a/LiveFrost/LFGlassView.m b/LiveFrost/LFGlassView.m index cc3b853..73123cc 100644 --- a/LiveFrost/LFGlassView.m +++ b/LiveFrost/LFGlassView.m @@ -44,7 +44,6 @@ - (void) handleBlurringOnBoundsChange; #endif @implementation LFGlassView - @dynamic blurRadius; @dynamic scaleFactor; @dynamic frameInterval; @@ -146,14 +145,14 @@ - (BOOL) isReadyToLiveBlur { return (!CGRectIsEmpty(self.bounds) && self.superview && self.window && _shouldLiveBlur); } -- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { +- (NSMethodSignature *) methodSignatureForSelector:(SEL)aSelector { if ([_methodNamesToForwardToLayer containsObject:NSStringFromSelector(aSelector)]) { return [[self.glassLayer class] instanceMethodSignatureForSelector:aSelector]; } return [super methodSignatureForSelector:aSelector]; } -- (void)forwardInvocation:(NSInvocation *)anInvocation { +- (void) forwardInvocation:(NSInvocation *)anInvocation { if ([self.glassLayer respondsToSelector:[anInvocation selector]]) { [anInvocation invokeWithTarget:self.glassLayer]; } else { From 47887d5a16cf1f0e8dbf388c70b37f2d54c2770e Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 15:41:34 -0700 Subject: [PATCH 09/15] Removed outdated reference to UIView object --- LiveFrost/LFDisplayBridge.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LiveFrost/LFDisplayBridge.m b/LiveFrost/LFDisplayBridge.m index 7d921d8..fd1d6bc 100644 --- a/LiveFrost/LFDisplayBridge.m +++ b/LiveFrost/LFDisplayBridge.m @@ -32,7 +32,7 @@ @interface LFDisplayBridge () @end void LF_refreshAllSubscribedObjectsApplierFunction(const void *value, void *context) { - [(__bridge UIView *)value refresh]; + [(__bridge id)value refresh]; } #if !__has_feature(objc_arc) From 273f55e89ea8e9c184caa0246e224b58552ef26f Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 15:54:15 -0700 Subject: [PATCH 10/15] Missed this one when updating CONTRIBUTIONS --- LiveFrost/LFGlassLayer.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/LiveFrost/LFGlassLayer.m b/LiveFrost/LFGlassLayer.m index 05a254f..b84bb70 100644 --- a/LiveFrost/LFGlassLayer.m +++ b/LiveFrost/LFGlassLayer.m @@ -19,8 +19,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // -// Contains contributions from Nam Kennic -// #import "LFGlassLayer.h" #import "LFDefines.h" From 710959cc56448f9cbcd183afff91ba75f0cebbeb Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 15:56:40 -0700 Subject: [PATCH 11/15] C style header protection --- LiveFrost/LFDefines.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/LiveFrost/LFDefines.h b/LiveFrost/LFDefines.h index e4518ee..f9e2b21 100644 --- a/LiveFrost/LFDefines.h +++ b/LiveFrost/LFDefines.h @@ -20,6 +20,11 @@ // THE SOFTWARE. // +#ifndef LiveFrost_LFDefines_h +#define LiveFrost_LFDefines_h + #include extern const CGPoint LFPointNull; + +#endif From 6b34e9715aa020520dc3aa6600d05ce4fdf27a74 Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 21:05:55 -0700 Subject: [PATCH 12/15] Added option (by default) to suppress animating blur radius on the LFGlassView. This keeps the defaults as they were, as before with the v2 changes, any change to blur radius would trigger an animation. --- LiveFrost/LFGlassLayer.m | 5 ++--- LiveFrost/LFGlassView.h | 1 + LiveFrost/LFGlassView.m | 7 +++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/LiveFrost/LFGlassLayer.m b/LiveFrost/LFGlassLayer.m index b84bb70..80c9bd4 100644 --- a/LiveFrost/LFGlassLayer.m +++ b/LiveFrost/LFGlassLayer.m @@ -404,14 +404,13 @@ - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:( } } -- (id) actionForKey:(NSString *)event { ++ (id) defaultActionForKey:(NSString *)event { if ([event isEqualToString:@"blurRadius"]) { CATransition *blurAnimation = [CATransition animation]; blurAnimation.type = kCATransitionFade; return blurAnimation; } - - return [super actionForKey:event]; + return [super defaultActionForKey:event]; } @end diff --git a/LiveFrost/LFGlassView.h b/LiveFrost/LFGlassView.h index 2e56772..e355d5c 100644 --- a/LiveFrost/LFGlassView.h +++ b/LiveFrost/LFGlassView.h @@ -25,6 +25,7 @@ @interface LFGlassView : UIView @property (nonatomic, assign) CGFloat blurRadius; +@property (nonatomic, assign) BOOL animateBlurRadius; @property (nonatomic, assign) CGFloat scaleFactor; @property (nonatomic, assign) NSUInteger frameInterval; diff --git a/LiveFrost/LFGlassView.m b/LiveFrost/LFGlassView.m index 73123cc..c073e75 100644 --- a/LiveFrost/LFGlassView.m +++ b/LiveFrost/LFGlassView.m @@ -160,4 +160,11 @@ - (void) forwardInvocation:(NSInvocation *)anInvocation { } } +- (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { + if ([event isEqualToString:@"blurRadius"] && self.animateBlurRadius) { + return nil; + } + return [super actionForLayer:layer forKey:event]; +} + @end From 44c0e58b0c029aa7f5baf4ca291ac678e7b5a539 Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 21:15:32 -0700 Subject: [PATCH 13/15] Renamed --- LiveFrost/LFGlassView.h | 2 +- LiveFrost/LFGlassView.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LiveFrost/LFGlassView.h b/LiveFrost/LFGlassView.h index e355d5c..0ffa57f 100644 --- a/LiveFrost/LFGlassView.h +++ b/LiveFrost/LFGlassView.h @@ -25,7 +25,7 @@ @interface LFGlassView : UIView @property (nonatomic, assign) CGFloat blurRadius; -@property (nonatomic, assign) BOOL animateBlurRadius; +@property (nonatomic, assign) BOOL blurRadiusAnimationEnabled; @property (nonatomic, assign) CGFloat scaleFactor; @property (nonatomic, assign) NSUInteger frameInterval; diff --git a/LiveFrost/LFGlassView.m b/LiveFrost/LFGlassView.m index c073e75..4754359 100644 --- a/LiveFrost/LFGlassView.m +++ b/LiveFrost/LFGlassView.m @@ -161,7 +161,7 @@ - (void) forwardInvocation:(NSInvocation *)anInvocation { } - (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { - if ([event isEqualToString:@"blurRadius"] && self.animateBlurRadius) { + if ([event isEqualToString:@"blurRadius"] && self.blurRadiusAnimationEnabled) { return nil; } return [super actionForLayer:layer forKey:event]; From eec885a9fad360ed6ba894613a9875297901d041 Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 21:17:49 -0700 Subject: [PATCH 14/15] Spacing --- LiveFrost/LFGlassView.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LiveFrost/LFGlassView.m b/LiveFrost/LFGlassView.m index 4754359..1a5caa0 100644 --- a/LiveFrost/LFGlassView.m +++ b/LiveFrost/LFGlassView.m @@ -160,7 +160,7 @@ - (void) forwardInvocation:(NSInvocation *)anInvocation { } } -- (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { +- (id) actionForLayer:(CALayer *)layer forKey:(NSString *)event { if ([event isEqualToString:@"blurRadius"] && self.blurRadiusAnimationEnabled) { return nil; } From 7a3eadfbb2e4f14f011735433a2dbb6ee5c6349f Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Tue, 20 May 2014 21:18:50 -0700 Subject: [PATCH 15/15] Updating the podspec before anyone forgets (though no corresponding 2.0 tag has been made yet, and it may be a few days before that happens.) --- LiveFrost.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LiveFrost.podspec b/LiveFrost.podspec index 486f3cf..e5a68cb 100644 --- a/LiveFrost.podspec +++ b/LiveFrost.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LiveFrost" - s.version = "1.1.2" + s.version = "2.0" s.summary = "Real time blurring." s.homepage = "https://github.com/radi/LiveFrost" s.license = 'MIT' @@ -8,7 +8,7 @@ Pod::Spec.new do |s| "Evadne Wu" => "ev@radi.ws", "Nicholas Gabriel Levin" => "nl@radi.ws" } - s.source = { :git => "https://github.com/radi/LiveFrost.git", :tag => "1.1.2" } + s.source = { :git => "https://github.com/radi/LiveFrost.git", :tag => "2.0" } s.platform = :ios, '6.0' s.source_files = 'LiveFrost', 'LiveFrost/**/*.{h,m,c}' s.exclude_files = 'LiveFrost/Exclude'