├── .gitignore ├── LICENSE ├── README.md ├── TOGlintyStringView ├── ARCGPathFromString.h ├── ARCGPathFromString.m ├── TOGlintyGradientLayer.h ├── TOGlintyGradientLayer.m ├── TOGlintyShapeLayer.h ├── TOGlintyShapeLayer.m ├── TOGlintyStringView.h ├── TOGlintyStringView.m └── TOGlintyStringView.xcassets │ ├── Contents.json │ ├── TOGlintyStringViewChevron.imageset │ ├── Contents.json │ ├── UIGlintyStringViewChevron.png │ ├── UIGlintyStringViewChevron@2x.png │ └── UIGlintyStringViewChevron@3x.png │ ├── TOGlintyStringViewReflectionMask.imageset │ ├── Contents.json │ ├── UIGlintyStringViewReflectionMask.png │ ├── UIGlintyStringViewReflectionMask@2x.png │ └── UIGlintyStringViewReflectionMask@3x.png │ └── TOGlintyStringViewShimmerMask.imageset │ ├── Contents.json │ ├── UIGlintyStringViewShimmerMask.png │ ├── UIGlintyStringViewShimmerMask@2x.png │ └── UIGlintyStringViewShimmerMask@3x.png ├── TOGlintyStringViewExample.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── TOGlintyStringViewExample ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── IMG_2875.PNG ├── Info.plist ├── ViewController.h ├── ViewController.m └── main.m └── screenshot.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Tim Oliver 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![TOGlintyStringView](screenshot.jpg) 2 | 3 | # TOGlintyStringView 4 | `TOGlintyStringView` is a complete re-implementation of [`_UIGlintyStringView`](https://github.com/JaviSoto/iOS9-Runtime-Headers/blob/51598b5f73399e4737bc067ed6b9bd5bd9a8b0d1/Frameworks/UIKit.framework/_UIGlintyStringView.h), the internal UIKit `UIView` responsible for the '> slide to unlock' text on the lock screen of every iOS device running iOS 7 through to iOS 9. 5 | 6 | It was created by copying the `UIView` / `CALayer` configuration of `_UIGlintyStringView` after introspecting it via [Reveal app](http://revealapp.com) as well as a lot of console logging. 7 | 8 | As this library (most likely) cannot be used in shipping iOS apps, it's being presented here without warranty in the hopes it may have educational value to fellow developers! 9 | 10 | # How does it work? 11 | 12 | I'll improve this section down the line, but in a nutshell, it is comprised of 6 discrete `CALayer` objects: 13 | 14 | * The top layer contains a bitmap of the text, used as a mask to clip the rest of the content. 15 | * The second layer is a flat, partially transparent grey layer used as the base color of the effect. 16 | * The third layer is the white 'sheen' gradient that results in the pure white section of the effect. 17 | * The fourth layer is a simple `CAGradientLayer` that when blended with the gradient layers above and below it, creates a wedge shape falloff on either side of the sheen effect. 18 | * The fifth layer is a much wider, and less opaque gradient that creates the 'build-up' effect on either side of the sheen. 19 | * The sixth layer is a `CAShapeLayer` that takes a `CGPath` outline of the text, and produces a blurred, dashed-line outline, that is then blended with the gradients to produce that subtle 'fractal' effect. 20 | 21 | All of this is possible in an (officially unsupported on iOS) feature of Core Animation allowing layers to be blended together using a variety of filters. 22 | 23 | # Is it safe for the App Store? 24 | HAHAHAHA absolutely not. In order to make this effect possible, two private APIs needed to be invoked: 25 | 26 | * To enable blending on `CALayer` objects, the `allowsGroupBlending` property must be set to NO. This property is not officially exposed, but trivial to access, and probably easy to obfuscate from the app submission scanner. 27 | * To create the blurring of the dashes in the `CAShapeLayer`, it is necessary to instantiate a `CAFilter` instance. As this class definitely isn't exposed on iOS, using it would immediately be caught by the automatic private API detection system Apple uses in app submissions. 28 | 29 | While you're free to try, I wouldn't recommend trying to submit an app to the App Store using this view. 30 | 31 | # Credits 32 | 33 | `TOGlintyStringView` was created by Tim Oliver as a Core Animation experiment. 34 | 35 | It also incorporates the [`string-to-CGPathRef`](https://github.com/aderussell/string-to-CGPathRef) library, created by Adrian Russell, 2014 36 | 37 | iPhone 6s Plus device mockup by [Pixeden](http://www.pixeden.com). 38 | 39 | # License 40 | 41 | `TOGlintyStringView` is available under the MIT license. Please see the [LICENSE](LICENSE) file for more information. ![analytics](https://ga-beacon.appspot.com/UA-5643664-16/TOGlintyStringView/README.md?pixel) 42 | -------------------------------------------------------------------------------- /TOGlintyStringView/ARCGPathFromString.h: -------------------------------------------------------------------------------- 1 | // 2 | // ARCGPathFromString.h 3 | // 4 | // Created by Adrian Russell on 08/08/2014. 5 | // Copyright (c) 2014 Adrian Russell. All rights reserved. 6 | // 7 | // This software is provided 'as-is', without any express or implied 8 | // warranty. In no event will the authors be held liable for any damages 9 | // arising from the use of this software. Permission is granted to anyone to 10 | // use this software for any purpose, including commercial applications, and to 11 | // alter it and redistribute it freely, subject to the following restrictions: 12 | // 13 | // 1. The origin of this software must not be misrepresented; you must not 14 | // claim that you wrote the original software. If you use this software 15 | // in a product, an acknowledgment in the product documentation would be 16 | // appreciated but is not required. 17 | // 2. Altered source versions must be plainly marked as such, and must not be 18 | // misrepresented as being the original software. 19 | // 3. This notice may not be removed or altered from any source 20 | // distribution. 21 | // 22 | 23 | #import 24 | #import 25 | 26 | // Tim Oliver: To ensure proper cross-platform linking 27 | #if TARGET_OS_IPHONE 28 | #import 29 | #elif TARGET_OS_MAC 30 | #import 31 | #endif 32 | 33 | /** 34 | Creates a CGPath from a specified attributed string. 35 | @param attrString The attributed string to produce the path for. Must not be `nil`. 36 | @return A new CGPath that contains a path with paths for all the glyphs for specifed string. 37 | @discussion This string will always be on a single line even if the string contains linebreaks. 38 | */ 39 | CGPathRef CGPathCreateSingleLineStringWithAttributedString(NSAttributedString *attrString); 40 | 41 | /** 42 | Creates a CGPath from a specified attributed string that can span over multiple lines of text. 43 | @param attrString The attributed string to produce the path for. Must not be `nil`. 44 | @param maxWidth The maximum width of a line, if a line when rendered is longer than this width then the line is broken to a new line. Must be greater than 0. 45 | @param maxHeight The maximum height of the text block. Must be greater than 0. 46 | @return A new CGPath that contains a path with paths for all the glyphs for specifed string. 47 | */ 48 | CGPathRef CGPathCreateMultilineStringWithAttributedString(NSAttributedString *attrString, CGFloat maxWidth, CGFloat maxHeight); -------------------------------------------------------------------------------- /TOGlintyStringView/ARCGPathFromString.m: -------------------------------------------------------------------------------- 1 | // 2 | // ARCGPathFromString.m 3 | // 4 | // Created by Adrian Russell on 08/08/2014. 5 | // Copyright (c) 2014 Adrian Russell. All rights reserved. 6 | // 7 | // This software is provided 'as-is', without any express or implied 8 | // warranty. In no event will the authors be held liable for any damages 9 | // arising from the use of this software. Permission is granted to anyone to 10 | // use this software for any purpose, including commercial applications, and to 11 | // alter it and redistribute it freely, subject to the following restrictions: 12 | // 13 | // 1. The origin of this software must not be misrepresented; you must not 14 | // claim that you wrote the original software. If you use this software 15 | // in a product, an acknowledgment in the product documentation would be 16 | // appreciated but is not required. 17 | // 2. Altered source versions must be plainly marked as such, and must not be 18 | // misrepresented as being the original software. 19 | // 3. This notice may not be removed or altered from any source 20 | // distribution. 21 | // 22 | 23 | #import "ARCGPathFromString.h" 24 | 25 | #pragma mark Single Line String Path 26 | 27 | CGPathRef CGPathCreateSingleLineStringWithAttributedString(NSAttributedString *attrString) 28 | { 29 | CGMutablePathRef letters = CGPathCreateMutable(); 30 | 31 | 32 | CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)attrString); 33 | 34 | CFArrayRef runArray = CTLineGetGlyphRuns(line); 35 | 36 | // for each RUN 37 | for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++) 38 | { 39 | // Get FONT for this run 40 | CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex); 41 | CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName); 42 | 43 | // for each GLYPH in run 44 | for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++) 45 | { 46 | // get Glyph & Glyph-data 47 | CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1); 48 | CGGlyph glyph; 49 | CGPoint position; 50 | CTRunGetGlyphs(run, thisGlyphRange, &glyph); 51 | CTRunGetPositions(run, thisGlyphRange, &position); 52 | 53 | // Get PATH of outline 54 | { 55 | CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL); 56 | CGAffineTransform t = CGAffineTransformMakeTranslation(position.x, position.y); 57 | CGPathAddPath(letters, &t, letter); 58 | CGPathRelease(letter); 59 | } 60 | } 61 | } 62 | 63 | CFRelease(line); 64 | 65 | CGPathRef finalPath = CGPathCreateCopy(letters); 66 | CGPathRelease(letters); 67 | return finalPath; 68 | } 69 | 70 | 71 | #pragma mark - Multiple Line String Path 72 | 73 | CGPathRef CGPathCreateMultilineStringWithAttributedString(NSAttributedString *attrString, CGFloat maxWidth, CGFloat maxHeight) 74 | { 75 | 76 | CGMutablePathRef letters = CGPathCreateMutable(); 77 | 78 | CGRect bounds = CGRectMake(0, 0, maxWidth, maxHeight); 79 | 80 | CGPathRef pathRef = CGPathCreateWithRect(bounds, NULL); 81 | 82 | CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attrString)); 83 | CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), pathRef, NULL); 84 | 85 | CFArrayRef lines = CTFrameGetLines(frame); 86 | 87 | CGPoint *points = malloc(sizeof(CGPoint) * CFArrayGetCount(lines)); 88 | 89 | CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), points); 90 | 91 | NSInteger numLines = CFArrayGetCount(lines); 92 | // for each LINE 93 | for (CFIndex lineIndex = 0; lineIndex < numLines; lineIndex++) 94 | { 95 | CTLineRef lineRef = CFArrayGetValueAtIndex(lines, lineIndex); 96 | 97 | CFRange r = CTLineGetStringRange(lineRef); 98 | 99 | NSParagraphStyle *paragraphStyle = [attrString attribute:NSParagraphStyleAttributeName atIndex:r.location effectiveRange:NULL]; 100 | NSTextAlignment alignment = paragraphStyle.alignment; 101 | 102 | 103 | CGFloat flushFactor = 0.0; 104 | if (alignment == NSTextAlignmentLeft) { 105 | flushFactor = 0.0; 106 | } else if (alignment == NSTextAlignmentCenter) { 107 | flushFactor = 0.5; 108 | } else if (alignment == NSTextAlignmentRight) { 109 | flushFactor = 1.0; 110 | } 111 | 112 | CGFloat penOffset = CTLineGetPenOffsetForFlush(lineRef, flushFactor, maxWidth); 113 | 114 | // create a new justified line if the alignment is justified 115 | if (alignment == NSTextAlignmentJustified) { 116 | lineRef = CTLineCreateJustifiedLine(lineRef, 1.0, maxWidth); 117 | penOffset = 0; 118 | } 119 | 120 | CGFloat lineOffset = numLines == 1 ? 0 : maxHeight - points[lineIndex].y; 121 | 122 | CFArrayRef runArray = CTLineGetGlyphRuns(lineRef); 123 | 124 | // for each RUN 125 | for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++) 126 | { 127 | // Get FONT for this run 128 | CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex); 129 | CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName); 130 | 131 | // for each GLYPH in run 132 | for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++) 133 | { 134 | // get Glyph & Glyph-data 135 | CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1); 136 | CGGlyph glyph; 137 | CGPoint position; 138 | CTRunGetGlyphs(run, thisGlyphRange, &glyph); 139 | CTRunGetPositions(run, thisGlyphRange, &position); 140 | 141 | position.y -= lineOffset; 142 | position.x += penOffset; 143 | 144 | CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL); 145 | CGAffineTransform t = CGAffineTransformMakeTranslation(position.x, position.y); 146 | CGPathAddPath(letters, &t, letter); 147 | CGPathRelease(letter); 148 | } 149 | } 150 | 151 | // if the text is justified then release the new justified line we created. 152 | if (alignment == NSTextAlignmentJustified) { 153 | CFRelease(lineRef); 154 | } 155 | } 156 | 157 | free(points); 158 | 159 | CGPathRelease(pathRef); 160 | CFRelease(frame); 161 | CFRelease(framesetter); 162 | 163 | CGRect pathBounds = CGPathGetBoundingBox(letters); 164 | CGAffineTransform transform = CGAffineTransformMakeTranslation(-pathBounds.origin.x, -pathBounds.origin.y); 165 | CGPathRef finalPath = CGPathCreateCopyByTransformingPath(letters, &transform); 166 | CGPathRelease(letters); 167 | 168 | return finalPath; 169 | } 170 | -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyGradientLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // TOGlintyGradientLayer.h 3 | // 4 | // Copyright 2016 Timothy Oliver. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to 8 | // deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | // sell copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface TOGlintyGradientLayer : CALayer 26 | 27 | @property (nonatomic, strong) CALayer *reflectionLayer; 28 | @property (nonatomic, strong) CAGradientLayer *gradientLayer; 29 | @property (nonatomic, strong) CALayer *shimmerLayer; 30 | 31 | - (void)startAnimation; 32 | - (void)stopAnimation; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyGradientLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // TOGlintyGradientLayer.m 3 | // 4 | // Copyright 2016 Timothy Oliver. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to 8 | // deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | // sell copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | 24 | #import "TOGlintyGradientLayer.h" 25 | 26 | #if TARGET_OS_IPHONE 27 | #import 28 | #elif TARGET_OS_MAC 29 | #import 30 | #endif 31 | 32 | @interface TOGlintyGradientLayer () 33 | 34 | - (void)setUp; 35 | 36 | - (CAKeyframeAnimation *)shimmerAnimation; 37 | - (CAKeyframeAnimation *)reflectionAnimation; 38 | 39 | @end 40 | 41 | @implementation TOGlintyGradientLayer 42 | 43 | - (instancetype)init 44 | { 45 | if (self = [super init]) { 46 | [self setUp]; 47 | } 48 | 49 | return self; 50 | } 51 | 52 | - (void)setUp 53 | { 54 | [self setValue:@(NO) forKey:@"allowsGroupBlending"]; 55 | self.opaque = NO; 56 | self.masksToBounds = YES; 57 | self.allowsGroupOpacity = NO; 58 | 59 | CGImageRef reflectionImage = nil; 60 | CGSize reflectionSize = CGSizeZero; 61 | 62 | CGImageRef shimmerImage = nil; 63 | CGSize shimmerSize = CGSizeZero; 64 | 65 | #if TARGET_OS_IPHONE 66 | UIImage *_reflectionImage = [UIImage imageNamed:@"TOGlintyStringViewReflectionMask"]; 67 | reflectionSize = _reflectionImage.size; 68 | reflectionImage = _reflectionImage.CGImage; 69 | 70 | UIImage *_shimmerImage = [UIImage imageNamed:@"TOGlintyStringViewShimmerMask"]; 71 | shimmerSize = _shimmerImage.size; 72 | shimmerImage = _shimmerImage.CGImage; 73 | #elif TARGET_OS_MAC 74 | NSImage *_reflectionImage = [NSImage imageNamed:@"TOGlintyStringViewReflectionMask"]; 75 | reflectionImage = [_reflectionImage CGImageForProposedRect:(CGRect){CGPointZero, _reflectionImage.size} context:NULL hints:nil]; 76 | 77 | NSImage *_shimmerImage = [NSImage imageNamed:@"TOGlintyStringViewShimmerMask"]; 78 | shimmerImage = [_shimmerImage CGImageForProposedRect:(CGRect){CGPointZero, _shimmerImage.size} context:NULL hints:nil]; 79 | #endif 80 | 81 | self.shimmerLayer = [[CALayer alloc] init]; 82 | self.shimmerLayer.opaque = NO; 83 | self.shimmerLayer.frame = (CGRect){CGPointZero, {646, 46}}; 84 | self.shimmerLayer.opacity = 0.9f; 85 | self.shimmerLayer.contents = (__bridge id)shimmerImage; 86 | self.shimmerLayer.compositingFilter = @"linearLightBlendMode"; 87 | [self.shimmerLayer setValue:@(NO) forKey:@"allowsGroupBlending"]; 88 | self.shimmerLayer.allowsGroupOpacity = NO; 89 | [self addSublayer:self.shimmerLayer]; 90 | 91 | self.gradientLayer = [[CAGradientLayer alloc] init]; 92 | self.gradientLayer.allowsGroupOpacity = YES; 93 | self.gradientLayer.opaque = YES; 94 | self.gradientLayer.opacity = 0.35; 95 | self.gradientLayer.compositingFilter = @"colorDodgeBlendMode"; 96 | 97 | CGColorRef lowPointColor = CGColorCreate(CGColorSpaceCreateDeviceGray(), (CGFloat []){0.05f, 1.0f}); 98 | CGColorRef highPointColor = CGColorCreate(CGColorSpaceCreateDeviceGray(), (CGFloat []){0.95f, 1.0f}); 99 | self.gradientLayer.colors = @[(__bridge id)lowPointColor, (__bridge id)highPointColor, (__bridge id)lowPointColor]; 100 | self.gradientLayer.frame = (CGRect){CGPointZero, {646, 46}}; 101 | [self addSublayer:self.gradientLayer]; 102 | 103 | self.reflectionLayer = [[CALayer alloc] init]; 104 | self.reflectionLayer.opaque = NO; 105 | self.reflectionLayer.frame = (CGRect){CGPointZero, {646, 46}}; 106 | self.reflectionLayer.opacity = 0.75f; 107 | self.reflectionLayer.contents = (__bridge id)reflectionImage; 108 | self.reflectionLayer.allowsGroupOpacity = YES; 109 | self.reflectionLayer.compositingFilter = @"multiplyBlendMode"; 110 | [self addSublayer:self.reflectionLayer]; 111 | } 112 | 113 | - (void)setFrame:(CGRect)frame 114 | { 115 | [super setFrame:frame]; 116 | 117 | frame = self.gradientLayer.frame; 118 | frame.origin.y = (CGRectGetHeight(self.frame) - CGRectGetHeight(frame)) * 0.5f; 119 | frame.origin.y += 2; 120 | self.gradientLayer.frame = frame; 121 | } 122 | 123 | - (void)startAnimation 124 | { 125 | [self.reflectionLayer addAnimation:[self reflectionAnimation] forKey:@"reflectionAnimation"]; 126 | [self.shimmerLayer addAnimation:[self shimmerAnimation] forKey:@"shimmerAnimation"]; 127 | } 128 | 129 | - (void)stopAnimation 130 | { 131 | [self.reflectionLayer removeAllAnimations]; 132 | [self.shimmerLayer removeAllAnimations]; 133 | } 134 | 135 | - (CAKeyframeAnimation *)shimmerAnimation 136 | { 137 | CAKeyframeAnimation *shimmerAnimation = [[CAKeyframeAnimation alloc] init]; 138 | shimmerAnimation.values = @[@(0), @(320)]; 139 | shimmerAnimation.keyTimes = @[@(0), @(1)]; 140 | shimmerAnimation.repeatCount = HUGE_VALF; 141 | shimmerAnimation.removedOnCompletion = NO; 142 | shimmerAnimation.duration = 2; 143 | shimmerAnimation.fillMode = @"both"; 144 | shimmerAnimation.keyPath = @"position.x"; 145 | return shimmerAnimation; 146 | } 147 | 148 | - (CAKeyframeAnimation *)reflectionAnimation 149 | { 150 | CAKeyframeAnimation *reflectionAnimation = [[CAKeyframeAnimation alloc] init]; 151 | reflectionAnimation.values = @[@(0), @(320)]; 152 | reflectionAnimation.keyTimes = @[@(0), @(1)]; 153 | reflectionAnimation.repeatCount = HUGE_VALF; 154 | reflectionAnimation.removedOnCompletion = NO; 155 | reflectionAnimation.fillMode = @"both"; 156 | reflectionAnimation.duration = 2; 157 | reflectionAnimation.keyPath = @"position.x"; 158 | return reflectionAnimation; 159 | } 160 | 161 | @end 162 | -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyShapeLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // TOGlintyShapeLayer.h 3 | // 4 | // Copyright 2016 Timothy Oliver. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to 8 | // deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | // sell copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface TOGlintyShapeLayer : CAShapeLayer 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyShapeLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // TOGlintyShapeLayer.m 3 | // 4 | // Copyright 2016 Timothy Oliver. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to 8 | // deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | // sell copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | #import "TOGlintyShapeLayer.h" 24 | 25 | @interface CAFilter : NSObject 26 | 27 | @property (copy) NSString *name; 28 | 29 | + (instancetype)filterWithName:(id)name; 30 | - (instancetype)initWithName:(id)name; 31 | 32 | @end 33 | 34 | @implementation TOGlintyShapeLayer 35 | 36 | - (instancetype)init 37 | { 38 | if (self = [super init]) { 39 | self.opaque = NO; 40 | self.shouldRasterize = YES; 41 | [self setValue:@(NO) forKey:@"allowsGroupBlending"]; 42 | self.lineDashPattern = @[@(10), @(10)]; 43 | self.lineJoin = @"round"; 44 | self.lineCap = @"round"; 45 | self.miterLimit = 5; 46 | self.lineWidth = 1.5f; 47 | self.allowsGroupOpacity = NO; 48 | self.strokeColor = CGColorCreate(CGColorSpaceCreateDeviceGray(), (CGFloat []){1.0f, 1.0f}); 49 | self.backgroundColor = CGColorCreate(CGColorSpaceCreateDeviceGray(), (CGFloat []){0.0f, 1.0f}); 50 | self.transform = CATransform3DScale(CATransform3DIdentity, 1.0f, -1.0f, 1.0f); 51 | 52 | // WARNING: This is exposing a private API and will absolutely 53 | // not be accepted on the App Store. Use at your own risk! 54 | CAFilter *blurFilter = [CAFilter filterWithName:@"gaussianBlur"]; 55 | [blurFilter setValue:@(1) forKey:@"inputRadius"]; 56 | self.filters = @[blurFilter]; 57 | } 58 | 59 | return self; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TOGlintyStringView.h 3 | // 4 | // Copyright 2016 Timothy Oliver. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to 8 | // deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | // sell copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | #import 24 | 25 | #if TARGET_OS_IPHONE 26 | #define TO_VIEW_FOR_PLATFORM UIView 27 | #elif TARGET_OS_MAC 28 | #define TOGLINTY_CROSSPLATFORM_VIEW NSView 29 | #endif 30 | 31 | #if TARGET_OS_IPHONE 32 | #define TO_FONT_FOR_PLATFORM UIFont 33 | #elif TARGET_OS_MAC 34 | #define TO_FONT_FOR_PLATFORM NSFont 35 | #endif 36 | 37 | #if TARGET_OS_IPHONE 38 | #define TO_IMAGE_FOR_PLATFORM UIImage 39 | #elif TARGET_OS_MAC 40 | #define TO_IMAGE_FOR_PLATFORM NSImage 41 | #endif 42 | 43 | @interface TOGlintyStringView : TO_VIEW_FOR_PLATFORM 44 | 45 | @property (nonatomic, copy) NSString *text; 46 | @property (nonatomic, assign) CGFloat chevronSpacing; 47 | 48 | @property (nonatomic, strong) TO_FONT_FOR_PLATFORM *font; 49 | @property (nonatomic, strong) TO_IMAGE_FOR_PLATFORM *chevronImage; 50 | @property (nonatomic, readonly) TO_VIEW_FOR_PLATFORM *effectView; 51 | 52 | + (id)defaultChevronImage; 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TOGlintyStringView.m 3 | // 4 | // Copyright 2016 Timothy Oliver. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to 8 | // deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | // sell copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | #import 24 | 25 | #import "TOGlintyStringView.h" 26 | #import "TOGlintyShapeLayer.h" 27 | #import "TOGlintyGradientLayer.h" 28 | #import "ARCGPathFromString.h" 29 | 30 | @interface TOGlintyStringView () 31 | 32 | @property (nonatomic, strong) TOGlintyGradientLayer *gradientLayer; 33 | @property (nonatomic, strong) TOGlintyShapeLayer *shapeLayer; 34 | @property (nonatomic, strong) CALayer *fillLayer; 35 | @property (nonatomic, strong) CALayer *textLayer; 36 | 37 | @property (nonatomic, assign) CGPathRef textPath; 38 | 39 | #if TARGET_OS_IPHONE 40 | @property (nonatomic, strong, readwrite) UIView *effectView; 41 | #elif TARGET_OS_MAC 42 | @property (nonatomic, strong, readwrite) NSView *effectView; 43 | #endif 44 | 45 | - (CGPathRef)pathForCurrentTextAndFont; 46 | - (CGPathRef)offsetForChevronWithTextPath:(CGPathRef)path offset:(CGPoint *)offset; 47 | 48 | - (void)setUp; 49 | - (void)resetContent; 50 | - (void)sizeLayersForContent; 51 | - (void)renderContent; 52 | 53 | @end 54 | 55 | @implementation TOGlintyStringView 56 | 57 | #pragma mark - View Creation - 58 | - (instancetype)initWithFrame:(CGRect)frame 59 | { 60 | if (self = [super initWithFrame:frame]) { 61 | [self setUp]; 62 | } 63 | 64 | return self; 65 | } 66 | 67 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 68 | { 69 | if (self = [super initWithCoder:aDecoder]) { 70 | [self setUp]; 71 | } 72 | 73 | return self; 74 | } 75 | 76 | #pragma mark - Set-up - 77 | - (void)setUp 78 | { 79 | self.text = NSLocalizedString(@"slide to unlock", @""); 80 | self.chevronSpacing = 10.0f; 81 | 82 | #if TARGET_OS_IPHONE 83 | self.font = [UIFont systemFontOfSize:24.0f]; 84 | #elif TARGET_OS_MAC 85 | self.font = [NSFont systemFontOfSize:24.0f]; 86 | #endif 87 | 88 | //Configure this view 89 | #if TARGET_OS_IPHONE 90 | #elif TARGET_OS_MAC 91 | self.wantsLayer = YES; 92 | #endif 93 | 94 | self.layer.allowsGroupOpacity = YES; 95 | self.layer.opaque = NO; 96 | self.opaque = NO; 97 | 98 | // TECHNICALLY a private API, but necessary to allow discrete blend modes on layers 99 | [self.layer setValue:@(NO) forKey:@"allowsGroupBlending"]; 100 | 101 | //Create the effect view 102 | if (self.effectView == nil) { 103 | #if TARGET_OS_IPHONE 104 | self.effectView = [[UIView alloc] initWithFrame:CGRectZero]; 105 | self.effectView.opaque = NO;; 106 | #elif TARGET_OS_MAC 107 | self.effectView = [[NSView alloc] initWithFrame:CGRectZero]; 108 | self.effectView.wantsLayer = YES; 109 | #endif 110 | [self.effectView.layer setValue:@(NO) forKey:@"allowsGroupBlending"]; 111 | self.effectView.layer.compositingFilter = @"linearLightBlendMode"; 112 | self.effectView.layer.opaque = NO; 113 | [self addSubview:self.effectView]; 114 | } 115 | 116 | if (self.shapeLayer == nil) { 117 | self.shapeLayer = [[TOGlintyShapeLayer alloc] init]; 118 | [self.effectView.layer addSublayer:self.shapeLayer]; 119 | } 120 | 121 | if (self.gradientLayer == nil) { 122 | self.gradientLayer = [[TOGlintyGradientLayer alloc] init]; 123 | [self.effectView.layer addSublayer:self.gradientLayer]; 124 | } 125 | 126 | if (self.fillLayer == nil) { 127 | self.fillLayer = [[CALayer alloc] init]; 128 | self.fillLayer.compositingFilter = @"screenBlendMode"; 129 | self.fillLayer.allowsGroupOpacity = NO; 130 | [self.fillLayer setValue:@(NO) forKey:@"allowsGroupBlending"]; 131 | self.fillLayer.backgroundColor = CGColorCreate(CGColorSpaceCreateDeviceGray(), (CGFloat []){0.39f, 1.0f}); 132 | [self.effectView.layer addSublayer:self.fillLayer]; 133 | } 134 | 135 | if (self.textLayer == nil) { 136 | self.textLayer = [[CALayer alloc] init]; 137 | self.textLayer.compositingFilter = @"destIn"; 138 | self.textLayer.rasterizationScale = 2.0f; 139 | self.textLayer.contentsScale = 2.0f; 140 | self.textLayer.allowsGroupOpacity = YES; 141 | [self.effectView.layer addSublayer:self.textLayer]; 142 | } 143 | } 144 | 145 | - (void)didMoveToSuperview 146 | { 147 | [super didMoveToSuperview]; 148 | [self resetContent]; 149 | [self.gradientLayer startAnimation]; 150 | } 151 | 152 | - (void)layoutSubviews 153 | { 154 | CGRect frame = self.effectView.frame; 155 | frame.origin.x = (CGRectGetWidth(self.bounds) - CGRectGetWidth(frame)) * 0.5f; 156 | frame.origin.y = (CGRectGetHeight(self.bounds) - CGRectGetHeight(frame)) * 0.5f; 157 | self.effectView.frame = frame; 158 | } 159 | 160 | - (void)resetContent 161 | { 162 | [self sizeLayersForContent]; 163 | [self renderContent]; 164 | [self setNeedsLayout]; 165 | } 166 | 167 | - (void)sizeLayersForContent 168 | { 169 | //Release our reference to the previous path for now 170 | if (self.textPath) { 171 | CGPathRelease(self.textPath); 172 | self.textPath = nil; 173 | } 174 | 175 | CGPoint offset = CGPointZero; 176 | self.textPath = [self pathForCurrentTextAndFont]; 177 | 178 | CGRect bounds = CGPathGetPathBoundingBox(self.textPath); 179 | bounds.size.height = ceilf(bounds.size.height); 180 | bounds.size.width = ceilf(bounds.size.width); 181 | 182 | if (self.chevronImage) { 183 | self.textPath = [self offsetForChevronWithTextPath:self.textPath offset:&offset]; 184 | 185 | CGSize chevronSize = [self.chevronImage size]; 186 | bounds.size.height = MAX(offset.y + bounds.size.height, chevronSize.height); 187 | bounds.size.width += offset.x; 188 | } 189 | 190 | self.shapeLayer.path = self.textPath; 191 | 192 | bounds.origin = CGPointZero; 193 | self.shapeLayer.frame = bounds; 194 | self.textLayer.frame = bounds; 195 | self.fillLayer.frame = bounds; 196 | self.gradientLayer.frame = bounds; 197 | 198 | self.effectView.frame = bounds; 199 | } 200 | 201 | - (void)renderContent 202 | { 203 | CGImageRef image = nil; 204 | CGSize size = self.textLayer.frame.size; 205 | 206 | #if TARGET_OS_IPHONE 207 | UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f); 208 | CGContextRef context = UIGraphicsGetCurrentContext(); 209 | CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); 210 | CGContextTranslateCTM(context, 0, size.height); 211 | CGContextScaleCTM(context, 1.0f, -1.0f); 212 | [self.chevronImage drawAtPoint:CGPointZero]; 213 | CGContextAddPath(context, self.textPath); 214 | CGContextFillPath(context); 215 | image = [UIGraphicsGetImageFromCurrentImageContext() CGImage]; 216 | UIGraphicsEndImageContext(); 217 | #elif TARGET_OS_MAC 218 | 219 | NSImage *im = [[NSImage alloc] initWithSize:size]; 220 | NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] 221 | initWithBitmapDataPlanes:NULL 222 | pixelsWide:size.width 223 | pixelsHigh:size.height 224 | bitsPerSample:8 225 | samplesPerPixel:4 226 | hasAlpha:YES 227 | isPlanar:NO 228 | colorSpaceName:NSCalibratedRGBColorSpace 229 | bytesPerRow:0 230 | bitsPerPixel:0]; 231 | 232 | [im addRepresentation:rep]; 233 | [im lockFocus]; 234 | 235 | CGColorRef fillColor = CGColorCreate(CGColorSpaceCreateDeviceGray(), (CGFloat []){1.0f, 1.0f}); 236 | 237 | CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; 238 | CGContextTranslateCTM(context, 0, size.height); 239 | CGContextScaleCTM(context, 1.0f, -1.0f); 240 | CGContextAddPath(context, self.textPath); 241 | CGContextSetFillColorWithColor(context, fillColor); 242 | CGContextFillPath(context); 243 | 244 | [im unlock] 245 | 246 | image = [im CGImageForProposedRect:(CGRect){CGPointZero, size} context:NULL hints:nil]; 247 | 248 | #endif 249 | 250 | self.textLayer.contents = (__bridge id)image; 251 | } 252 | 253 | - (CGPathRef)pathForCurrentTextAndFont 254 | { 255 | NSDictionary *attributes = @{NSFontAttributeName : self.font}; 256 | NSAttributedString *string = [[NSAttributedString alloc] initWithString:self.text attributes:attributes]; 257 | CGPathRef path = CGPathCreateSingleLineStringWithAttributedString(string); 258 | 259 | //Normalize the path to make sure its bounds are properly at 0,0 260 | CGRect frame = CGPathGetPathBoundingBox(path); 261 | CGAffineTransform transform = CGAffineTransformMakeTranslation(-frame.origin.x, -frame.origin.y); 262 | CGPathRef newPath = CGPathCreateCopyByTransformingPath(path, &transform); 263 | CGPathRelease(path); 264 | 265 | return newPath; 266 | } 267 | 268 | - (CGPathRef)offsetForChevronWithTextPath:(CGPathRef)path offset:(CGPoint *)offset 269 | { 270 | if (self.chevronImage == nil) { 271 | return NULL; 272 | } 273 | 274 | CGSize chevronSize = [self.chevronImage size]; 275 | CGPoint _offset = CGPointZero; 276 | CGRect bounds = CGPathGetPathBoundingBox(path); 277 | 278 | // offset the text path horizontally by the chevron 279 | _offset.x = fabs(bounds.origin.x) + chevronSize.width + self.chevronSpacing; 280 | 281 | //if the chevron is bigger than the text, offset the text vertically 282 | if (bounds.size.height < chevronSize.height) { 283 | _offset.y = (chevronSize.height - CGRectGetHeight(bounds)) * 0.5f; 284 | } 285 | 286 | *offset = _offset; 287 | 288 | CGAffineTransform transform = CGAffineTransformMakeTranslation(_offset.x, _offset.y); 289 | CGPathRef translatedPath = CGPathCreateCopyByTransformingPath(path, &transform); 290 | CGPathRelease(path); 291 | 292 | return translatedPath; 293 | } 294 | 295 | #pragma mark - Property Methods - 296 | - (void)setChevronImage:(id)chevronImage 297 | { 298 | if (_chevronImage == chevronImage) { 299 | return; 300 | } 301 | 302 | _chevronImage = chevronImage; 303 | 304 | [self resetContent]; 305 | } 306 | 307 | #pragma mark - Class Methods - 308 | + (id)defaultChevronImage 309 | { 310 | return (id)[TO_IMAGE_FOR_PLATFORM imageNamed:@"TOGlintyStringViewChevron"]; 311 | } 312 | 313 | @end 314 | -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewChevron.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "UIGlintyStringViewChevron.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "UIGlintyStringViewChevron@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "UIGlintyStringViewChevron@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewChevron.imageset/UIGlintyStringViewChevron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimOliver/TOGlintyStringView/2ecba2c9b8c592ce5d8db06a4065103aa36ba12c/TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewChevron.imageset/UIGlintyStringViewChevron.png -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewChevron.imageset/UIGlintyStringViewChevron@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimOliver/TOGlintyStringView/2ecba2c9b8c592ce5d8db06a4065103aa36ba12c/TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewChevron.imageset/UIGlintyStringViewChevron@2x.png -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewChevron.imageset/UIGlintyStringViewChevron@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimOliver/TOGlintyStringView/2ecba2c9b8c592ce5d8db06a4065103aa36ba12c/TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewChevron.imageset/UIGlintyStringViewChevron@3x.png -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewReflectionMask.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "UIGlintyStringViewReflectionMask.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "UIGlintyStringViewReflectionMask@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "UIGlintyStringViewReflectionMask@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewReflectionMask.imageset/UIGlintyStringViewReflectionMask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimOliver/TOGlintyStringView/2ecba2c9b8c592ce5d8db06a4065103aa36ba12c/TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewReflectionMask.imageset/UIGlintyStringViewReflectionMask.png -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewReflectionMask.imageset/UIGlintyStringViewReflectionMask@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimOliver/TOGlintyStringView/2ecba2c9b8c592ce5d8db06a4065103aa36ba12c/TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewReflectionMask.imageset/UIGlintyStringViewReflectionMask@2x.png -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewReflectionMask.imageset/UIGlintyStringViewReflectionMask@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimOliver/TOGlintyStringView/2ecba2c9b8c592ce5d8db06a4065103aa36ba12c/TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewReflectionMask.imageset/UIGlintyStringViewReflectionMask@3x.png -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewShimmerMask.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "UIGlintyStringViewShimmerMask.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "UIGlintyStringViewShimmerMask@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "UIGlintyStringViewShimmerMask@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewShimmerMask.imageset/UIGlintyStringViewShimmerMask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimOliver/TOGlintyStringView/2ecba2c9b8c592ce5d8db06a4065103aa36ba12c/TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewShimmerMask.imageset/UIGlintyStringViewShimmerMask.png -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewShimmerMask.imageset/UIGlintyStringViewShimmerMask@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimOliver/TOGlintyStringView/2ecba2c9b8c592ce5d8db06a4065103aa36ba12c/TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewShimmerMask.imageset/UIGlintyStringViewShimmerMask@2x.png -------------------------------------------------------------------------------- /TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewShimmerMask.imageset/UIGlintyStringViewShimmerMask@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimOliver/TOGlintyStringView/2ecba2c9b8c592ce5d8db06a4065103aa36ba12c/TOGlintyStringView/TOGlintyStringView.xcassets/TOGlintyStringViewShimmerMask.imageset/UIGlintyStringViewShimmerMask@3x.png -------------------------------------------------------------------------------- /TOGlintyStringViewExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 220D4B2E1C5E4C20006426A5 /* TOGlintyStringView.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 220D4B2D1C5E4C20006426A5 /* TOGlintyStringView.xcassets */; }; 11 | 2215702D1C5DADAF00AE3649 /* IMG_2875.PNG in Resources */ = {isa = PBXBuildFile; fileRef = 2215702C1C5DADAF00AE3649 /* IMG_2875.PNG */; }; 12 | 228A2EE51C5C7537002D8852 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 228A2EDA1C5C7537002D8852 /* AppDelegate.m */; }; 13 | 228A2EE61C5C7537002D8852 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 228A2EDB1C5C7537002D8852 /* Assets.xcassets */; }; 14 | 228A2EE71C5C7537002D8852 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 228A2EDC1C5C7537002D8852 /* LaunchScreen.storyboard */; }; 15 | 228A2EE81C5C7537002D8852 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 228A2EDE1C5C7537002D8852 /* Main.storyboard */; }; 16 | 228A2EEB1C5C7537002D8852 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 228A2EE21C5C7537002D8852 /* main.m */; }; 17 | 228A2EEC1C5C7537002D8852 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 228A2EE41C5C7537002D8852 /* ViewController.m */; }; 18 | 228A2EF01C5C7655002D8852 /* TOGlintyStringView.m in Sources */ = {isa = PBXBuildFile; fileRef = 228A2EEF1C5C7655002D8852 /* TOGlintyStringView.m */; }; 19 | 228A2EF31C5C7726002D8852 /* TOGlintyShapeLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 228A2EF21C5C7726002D8852 /* TOGlintyShapeLayer.m */; }; 20 | 228A2EF61C5C77A9002D8852 /* TOGlintyGradientLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 228A2EF51C5C77A9002D8852 /* TOGlintyGradientLayer.m */; }; 21 | 228A2EFD1C5C8BAD002D8852 /* ARCGPathFromString.m in Sources */ = {isa = PBXBuildFile; fileRef = 228A2EFA1C5C8BAD002D8852 /* ARCGPathFromString.m */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXFileReference section */ 25 | 220D4B2D1C5E4C20006426A5 /* TOGlintyStringView.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = TOGlintyStringView.xcassets; path = TOGlintyStringView/TOGlintyStringView.xcassets; sourceTree = ""; }; 26 | 2215702C1C5DADAF00AE3649 /* IMG_2875.PNG */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = IMG_2875.PNG; sourceTree = ""; }; 27 | 228A2EBE1C5C69B2002D8852 /* TOGlintyStringViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TOGlintyStringViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 228A2ED91C5C7537002D8852 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 29 | 228A2EDA1C5C7537002D8852 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 30 | 228A2EDB1C5C7537002D8852 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 31 | 228A2EDD1C5C7537002D8852 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 32 | 228A2EDF1C5C7537002D8852 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 33 | 228A2EE01C5C7537002D8852 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | 228A2EE21C5C7537002D8852 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 35 | 228A2EE31C5C7537002D8852 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 36 | 228A2EE41C5C7537002D8852 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 37 | 228A2EEE1C5C7655002D8852 /* TOGlintyStringView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TOGlintyStringView.h; path = TOGlintyStringView/TOGlintyStringView.h; sourceTree = ""; }; 38 | 228A2EEF1C5C7655002D8852 /* TOGlintyStringView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TOGlintyStringView.m; path = TOGlintyStringView/TOGlintyStringView.m; sourceTree = ""; }; 39 | 228A2EF11C5C7726002D8852 /* TOGlintyShapeLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TOGlintyShapeLayer.h; path = TOGlintyStringView/TOGlintyShapeLayer.h; sourceTree = ""; }; 40 | 228A2EF21C5C7726002D8852 /* TOGlintyShapeLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TOGlintyShapeLayer.m; path = TOGlintyStringView/TOGlintyShapeLayer.m; sourceTree = ""; }; 41 | 228A2EF41C5C77A9002D8852 /* TOGlintyGradientLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TOGlintyGradientLayer.h; path = TOGlintyStringView/TOGlintyGradientLayer.h; sourceTree = ""; }; 42 | 228A2EF51C5C77A9002D8852 /* TOGlintyGradientLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TOGlintyGradientLayer.m; path = TOGlintyStringView/TOGlintyGradientLayer.m; sourceTree = ""; }; 43 | 228A2EF91C5C8BAD002D8852 /* ARCGPathFromString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ARCGPathFromString.h; path = TOGlintyStringView/ARCGPathFromString.h; sourceTree = ""; }; 44 | 228A2EFA1C5C8BAD002D8852 /* ARCGPathFromString.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ARCGPathFromString.m; path = TOGlintyStringView/ARCGPathFromString.m; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 228A2EBB1C5C69B2002D8852 /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 228A2EB51C5C69B2002D8852 = { 59 | isa = PBXGroup; 60 | children = ( 61 | 228A2EED1C5C7604002D8852 /* TOGlintyStringView */, 62 | 228A2ED81C5C7537002D8852 /* TOGlintyStringViewExample */, 63 | 228A2EBF1C5C69B2002D8852 /* Products */, 64 | ); 65 | sourceTree = ""; 66 | }; 67 | 228A2EBF1C5C69B2002D8852 /* Products */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 228A2EBE1C5C69B2002D8852 /* TOGlintyStringViewExample.app */, 71 | ); 72 | name = Products; 73 | sourceTree = ""; 74 | }; 75 | 228A2ED81C5C7537002D8852 /* TOGlintyStringViewExample */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 2215702C1C5DADAF00AE3649 /* IMG_2875.PNG */, 79 | 228A2ED91C5C7537002D8852 /* AppDelegate.h */, 80 | 228A2EDA1C5C7537002D8852 /* AppDelegate.m */, 81 | 228A2EDB1C5C7537002D8852 /* Assets.xcassets */, 82 | 228A2EDC1C5C7537002D8852 /* LaunchScreen.storyboard */, 83 | 228A2EDE1C5C7537002D8852 /* Main.storyboard */, 84 | 228A2EE01C5C7537002D8852 /* Info.plist */, 85 | 228A2EE21C5C7537002D8852 /* main.m */, 86 | 228A2EE31C5C7537002D8852 /* ViewController.h */, 87 | 228A2EE41C5C7537002D8852 /* ViewController.m */, 88 | ); 89 | path = TOGlintyStringViewExample; 90 | sourceTree = ""; 91 | }; 92 | 228A2EED1C5C7604002D8852 /* TOGlintyStringView */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 228A2EEE1C5C7655002D8852 /* TOGlintyStringView.h */, 96 | 228A2EEF1C5C7655002D8852 /* TOGlintyStringView.m */, 97 | 228A2EF11C5C7726002D8852 /* TOGlintyShapeLayer.h */, 98 | 228A2EF21C5C7726002D8852 /* TOGlintyShapeLayer.m */, 99 | 228A2EF41C5C77A9002D8852 /* TOGlintyGradientLayer.h */, 100 | 228A2EF51C5C77A9002D8852 /* TOGlintyGradientLayer.m */, 101 | 228A2EF91C5C8BAD002D8852 /* ARCGPathFromString.h */, 102 | 228A2EFA1C5C8BAD002D8852 /* ARCGPathFromString.m */, 103 | 220D4B2D1C5E4C20006426A5 /* TOGlintyStringView.xcassets */, 104 | ); 105 | name = TOGlintyStringView; 106 | sourceTree = ""; 107 | }; 108 | /* End PBXGroup section */ 109 | 110 | /* Begin PBXNativeTarget section */ 111 | 228A2EBD1C5C69B2002D8852 /* TOGlintyStringViewExample */ = { 112 | isa = PBXNativeTarget; 113 | buildConfigurationList = 228A2ED51C5C69B2002D8852 /* Build configuration list for PBXNativeTarget "TOGlintyStringViewExample" */; 114 | buildPhases = ( 115 | 228A2EBA1C5C69B2002D8852 /* Sources */, 116 | 228A2EBB1C5C69B2002D8852 /* Frameworks */, 117 | 228A2EBC1C5C69B2002D8852 /* Resources */, 118 | ); 119 | buildRules = ( 120 | ); 121 | dependencies = ( 122 | ); 123 | name = TOGlintyStringViewExample; 124 | productName = TOGlintyStringView; 125 | productReference = 228A2EBE1C5C69B2002D8852 /* TOGlintyStringViewExample.app */; 126 | productType = "com.apple.product-type.application"; 127 | }; 128 | /* End PBXNativeTarget section */ 129 | 130 | /* Begin PBXProject section */ 131 | 228A2EB61C5C69B2002D8852 /* Project object */ = { 132 | isa = PBXProject; 133 | attributes = { 134 | LastUpgradeCheck = 0730; 135 | ORGANIZATIONNAME = "Tim Oliver"; 136 | TargetAttributes = { 137 | 228A2EBD1C5C69B2002D8852 = { 138 | CreatedOnToolsVersion = 7.2; 139 | }; 140 | }; 141 | }; 142 | buildConfigurationList = 228A2EB91C5C69B2002D8852 /* Build configuration list for PBXProject "TOGlintyStringViewExample" */; 143 | compatibilityVersion = "Xcode 3.2"; 144 | developmentRegion = English; 145 | hasScannedForEncodings = 0; 146 | knownRegions = ( 147 | en, 148 | Base, 149 | ); 150 | mainGroup = 228A2EB51C5C69B2002D8852; 151 | productRefGroup = 228A2EBF1C5C69B2002D8852 /* Products */; 152 | projectDirPath = ""; 153 | projectRoot = ""; 154 | targets = ( 155 | 228A2EBD1C5C69B2002D8852 /* TOGlintyStringViewExample */, 156 | ); 157 | }; 158 | /* End PBXProject section */ 159 | 160 | /* Begin PBXResourcesBuildPhase section */ 161 | 228A2EBC1C5C69B2002D8852 /* Resources */ = { 162 | isa = PBXResourcesBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | 2215702D1C5DADAF00AE3649 /* IMG_2875.PNG in Resources */, 166 | 220D4B2E1C5E4C20006426A5 /* TOGlintyStringView.xcassets in Resources */, 167 | 228A2EE81C5C7537002D8852 /* Main.storyboard in Resources */, 168 | 228A2EE61C5C7537002D8852 /* Assets.xcassets in Resources */, 169 | 228A2EE71C5C7537002D8852 /* LaunchScreen.storyboard in Resources */, 170 | ); 171 | runOnlyForDeploymentPostprocessing = 0; 172 | }; 173 | /* End PBXResourcesBuildPhase section */ 174 | 175 | /* Begin PBXSourcesBuildPhase section */ 176 | 228A2EBA1C5C69B2002D8852 /* Sources */ = { 177 | isa = PBXSourcesBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | 228A2EEC1C5C7537002D8852 /* ViewController.m in Sources */, 181 | 228A2EF31C5C7726002D8852 /* TOGlintyShapeLayer.m in Sources */, 182 | 228A2EEB1C5C7537002D8852 /* main.m in Sources */, 183 | 228A2EFD1C5C8BAD002D8852 /* ARCGPathFromString.m in Sources */, 184 | 228A2EF61C5C77A9002D8852 /* TOGlintyGradientLayer.m in Sources */, 185 | 228A2EE51C5C7537002D8852 /* AppDelegate.m in Sources */, 186 | 228A2EF01C5C7655002D8852 /* TOGlintyStringView.m in Sources */, 187 | ); 188 | runOnlyForDeploymentPostprocessing = 0; 189 | }; 190 | /* End PBXSourcesBuildPhase section */ 191 | 192 | /* Begin PBXVariantGroup section */ 193 | 228A2EDC1C5C7537002D8852 /* LaunchScreen.storyboard */ = { 194 | isa = PBXVariantGroup; 195 | children = ( 196 | 228A2EDD1C5C7537002D8852 /* Base */, 197 | ); 198 | name = LaunchScreen.storyboard; 199 | sourceTree = ""; 200 | }; 201 | 228A2EDE1C5C7537002D8852 /* Main.storyboard */ = { 202 | isa = PBXVariantGroup; 203 | children = ( 204 | 228A2EDF1C5C7537002D8852 /* Base */, 205 | ); 206 | name = Main.storyboard; 207 | sourceTree = ""; 208 | }; 209 | /* End PBXVariantGroup section */ 210 | 211 | /* Begin XCBuildConfiguration section */ 212 | 228A2ED31C5C69B2002D8852 /* Debug */ = { 213 | isa = XCBuildConfiguration; 214 | buildSettings = { 215 | ALWAYS_SEARCH_USER_PATHS = NO; 216 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 217 | CLANG_CXX_LIBRARY = "libc++"; 218 | CLANG_ENABLE_MODULES = YES; 219 | CLANG_ENABLE_OBJC_ARC = YES; 220 | CLANG_WARN_BOOL_CONVERSION = YES; 221 | CLANG_WARN_CONSTANT_CONVERSION = YES; 222 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 223 | CLANG_WARN_EMPTY_BODY = YES; 224 | CLANG_WARN_ENUM_CONVERSION = YES; 225 | CLANG_WARN_INT_CONVERSION = YES; 226 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 227 | CLANG_WARN_UNREACHABLE_CODE = YES; 228 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 229 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 230 | COPY_PHASE_STRIP = NO; 231 | DEBUG_INFORMATION_FORMAT = dwarf; 232 | ENABLE_STRICT_OBJC_MSGSEND = YES; 233 | ENABLE_TESTABILITY = YES; 234 | GCC_C_LANGUAGE_STANDARD = gnu99; 235 | GCC_DYNAMIC_NO_PIC = NO; 236 | GCC_NO_COMMON_BLOCKS = YES; 237 | GCC_OPTIMIZATION_LEVEL = 0; 238 | GCC_PREPROCESSOR_DEFINITIONS = ( 239 | "DEBUG=1", 240 | "$(inherited)", 241 | ); 242 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 243 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 244 | GCC_WARN_UNDECLARED_SELECTOR = YES; 245 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 246 | GCC_WARN_UNUSED_FUNCTION = YES; 247 | GCC_WARN_UNUSED_VARIABLE = YES; 248 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 249 | MTL_ENABLE_DEBUG_INFO = YES; 250 | ONLY_ACTIVE_ARCH = YES; 251 | SDKROOT = iphoneos; 252 | TARGETED_DEVICE_FAMILY = "1,2"; 253 | }; 254 | name = Debug; 255 | }; 256 | 228A2ED41C5C69B2002D8852 /* Release */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | ALWAYS_SEARCH_USER_PATHS = NO; 260 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 261 | CLANG_CXX_LIBRARY = "libc++"; 262 | CLANG_ENABLE_MODULES = YES; 263 | CLANG_ENABLE_OBJC_ARC = YES; 264 | CLANG_WARN_BOOL_CONVERSION = YES; 265 | CLANG_WARN_CONSTANT_CONVERSION = YES; 266 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 267 | CLANG_WARN_EMPTY_BODY = YES; 268 | CLANG_WARN_ENUM_CONVERSION = YES; 269 | CLANG_WARN_INT_CONVERSION = YES; 270 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 271 | CLANG_WARN_UNREACHABLE_CODE = YES; 272 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 273 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 274 | COPY_PHASE_STRIP = NO; 275 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 276 | ENABLE_NS_ASSERTIONS = NO; 277 | ENABLE_STRICT_OBJC_MSGSEND = YES; 278 | GCC_C_LANGUAGE_STANDARD = gnu99; 279 | GCC_NO_COMMON_BLOCKS = YES; 280 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 281 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 282 | GCC_WARN_UNDECLARED_SELECTOR = YES; 283 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 284 | GCC_WARN_UNUSED_FUNCTION = YES; 285 | GCC_WARN_UNUSED_VARIABLE = YES; 286 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 287 | MTL_ENABLE_DEBUG_INFO = NO; 288 | SDKROOT = iphoneos; 289 | TARGETED_DEVICE_FAMILY = "1,2"; 290 | VALIDATE_PRODUCT = YES; 291 | }; 292 | name = Release; 293 | }; 294 | 228A2ED61C5C69B2002D8852 /* Debug */ = { 295 | isa = XCBuildConfiguration; 296 | buildSettings = { 297 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 298 | FRAMEWORK_SEARCH_PATHS = ( 299 | "$(inherited)", 300 | "$(PROJECT_DIR)/TOGlintyStringViewExample", 301 | "\"$(SYSTEM_APPS_DIR)/Reveal.app/Contents/SharedSupport/iOS-Libraries\"", 302 | ); 303 | INFOPLIST_FILE = TOGlintyStringViewExample/Info.plist; 304 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 305 | PRODUCT_BUNDLE_IDENTIFIER = co.timoliver.TOGlintyStringView; 306 | PRODUCT_NAME = TOGlintyStringViewExample; 307 | }; 308 | name = Debug; 309 | }; 310 | 228A2ED71C5C69B2002D8852 /* Release */ = { 311 | isa = XCBuildConfiguration; 312 | buildSettings = { 313 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 314 | FRAMEWORK_SEARCH_PATHS = ( 315 | "$(inherited)", 316 | "$(PROJECT_DIR)/TOGlintyStringViewExample", 317 | "\"$(SYSTEM_APPS_DIR)/Reveal.app/Contents/SharedSupport/iOS-Libraries\"", 318 | ); 319 | INFOPLIST_FILE = TOGlintyStringViewExample/Info.plist; 320 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 321 | PRODUCT_BUNDLE_IDENTIFIER = co.timoliver.TOGlintyStringView; 322 | PRODUCT_NAME = TOGlintyStringViewExample; 323 | }; 324 | name = Release; 325 | }; 326 | /* End XCBuildConfiguration section */ 327 | 328 | /* Begin XCConfigurationList section */ 329 | 228A2EB91C5C69B2002D8852 /* Build configuration list for PBXProject "TOGlintyStringViewExample" */ = { 330 | isa = XCConfigurationList; 331 | buildConfigurations = ( 332 | 228A2ED31C5C69B2002D8852 /* Debug */, 333 | 228A2ED41C5C69B2002D8852 /* Release */, 334 | ); 335 | defaultConfigurationIsVisible = 0; 336 | defaultConfigurationName = Release; 337 | }; 338 | 228A2ED51C5C69B2002D8852 /* Build configuration list for PBXNativeTarget "TOGlintyStringViewExample" */ = { 339 | isa = XCConfigurationList; 340 | buildConfigurations = ( 341 | 228A2ED61C5C69B2002D8852 /* Debug */, 342 | 228A2ED71C5C69B2002D8852 /* Release */, 343 | ); 344 | defaultConfigurationIsVisible = 0; 345 | defaultConfigurationName = Release; 346 | }; 347 | /* End XCConfigurationList section */ 348 | }; 349 | rootObject = 228A2EB61C5C69B2002D8852 /* Project object */; 350 | } 351 | -------------------------------------------------------------------------------- /TOGlintyStringViewExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TOGlintyStringViewExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TOGlintyStringView 4 | // 5 | // Created by Tim Oliver on 30/01/2016. 6 | // Copyright © 2016 Tim Oliver. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /TOGlintyStringViewExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // TOGlintyStringView 4 | // 5 | // Created by Tim Oliver on 30/01/2016. 6 | // Copyright © 2016 Tim Oliver. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /TOGlintyStringViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /TOGlintyStringViewExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /TOGlintyStringViewExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /TOGlintyStringViewExample/IMG_2875.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimOliver/TOGlintyStringView/2ecba2c9b8c592ce5d8db06a4065103aa36ba12c/TOGlintyStringViewExample/IMG_2875.PNG -------------------------------------------------------------------------------- /TOGlintyStringViewExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /TOGlintyStringViewExample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // TOGlintyStringView 4 | // 5 | // Created by Tim Oliver on 30/01/2016. 6 | // Copyright © 2016 Tim Oliver. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /TOGlintyStringViewExample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // TOGlintyStringView 4 | // 5 | // Created by Tim Oliver on 30/01/2016. 6 | // Copyright © 2016 Tim Oliver. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "TOGlintyStringView.h" 11 | 12 | @interface ViewController () 13 | 14 | @property (nonatomic, strong) IBOutlet TOGlintyStringView *stringView; 15 | 16 | @end 17 | 18 | @implementation ViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | self.stringView.chevronImage = [TOGlintyStringView defaultChevronImage]; 23 | } 24 | 25 | - (UIStatusBarStyle)preferredStatusBarStyle 26 | { 27 | return UIStatusBarStyleLightContent; 28 | } 29 | 30 | - (BOOL)prefersStatusBarHidden 31 | { 32 | return YES; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /TOGlintyStringViewExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TOGlintyStringView 4 | // 5 | // Created by Tim Oliver on 30/01/2016. 6 | // Copyright © 2016 Tim Oliver. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimOliver/TOGlintyStringView/2ecba2c9b8c592ce5d8db06a4065103aa36ba12c/screenshot.jpg --------------------------------------------------------------------------------