├── LICENSE
├── README.md
├── SFImageMaker.podspec
├── SFImageMaker
├── SFImageMaker.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── Memerys.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ ├── xcshareddata
│ │ └── xcschemes
│ │ │ └── SFImageMaker.xcscheme
│ └── xcuserdata
│ │ ├── Memerys.xcuserdatad
│ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ │ ├── jiang.xcuserdatad
│ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ │ └── mac.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
├── SFImageMaker
│ ├── Info.plist
│ ├── SFBlockImageMaker.h
│ ├── SFBlockImageMaker.m
│ ├── SFBlurImageMaker.h
│ ├── SFBlurImageMaker.m
│ ├── SFBorderImageMaker.h
│ ├── SFBorderImageMaker.m
│ ├── SFCSBView.h
│ ├── SFCSBView.m
│ ├── SFColorImageMaker.h
│ ├── SFColorImageMaker.m
│ ├── SFCornerImageMaker.h
│ ├── SFCornerImageMaker.m
│ ├── SFGradientImageMaker.h
│ ├── SFGradientImageMaker.m
│ ├── SFImageFlow.h
│ ├── SFImageFlow.m
│ ├── SFImageMaker-Protocol.h
│ ├── SFImageMaker.h
│ ├── SFShadowImageMaker.h
│ ├── SFShadowImageMaker.m
│ ├── UIColor+SFIdentifier.h
│ ├── UIColor+SFIdentifier.m
│ ├── UIImageEffects
│ │ ├── UIImageEffects.h
│ │ └── UIImageEffects.m
│ ├── UIKit+SFImageFlow.h
│ └── UIKit+SFImageFlow.m
└── SFImageMakerTests
│ ├── Info.plist
│ ├── SFBorderImageMakerTests.swift
│ ├── SFImageMakerTests-Bridging-Header.h
│ ├── SFImageMakerTests.swift
│ ├── SFShadowImageMakerTests.swift
│ └── SFWebImageMakerHelperTest.swift
├── Space.xcworkspace
├── contents.xcworkspacedata
├── xcshareddata
│ └── IDEWorkspaceChecks.plist
└── xcuserdata
│ ├── Memerys.xcuserdatad
│ └── UserInterfaceState.xcuserstate
│ ├── apple.xcuserdatad
│ ├── UserInterfaceState.xcuserstate
│ └── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ ├── jiang.xcuserdatad
│ └── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ └── mac.xcuserdatad
│ └── UserInterfaceState.xcuserstate
├── UICornerShadowView.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── xcuserdata
│ │ ├── Memerys.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
│ │ ├── apple.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
│ │ └── mac.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
├── xcshareddata
│ └── xcschemes
│ │ └── UICornerShadowView.xcscheme
└── xcuserdata
│ ├── Memerys.xcuserdatad
│ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ │ └── xcschememanagement.plist
│ ├── jiang.xcuserdatad
│ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ │ └── xcschememanagement.plist
│ └── mac.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
├── UICornerShadowView
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Info.plist
├── README.md
├── SFImageFlowController.h
├── SFImageFlowController.m
├── SceneDelegate.swift
├── UICornerShadowView-Bridging-Header.h
├── UICornerShadowView.swift
├── UIImage+Extentions.h
├── UIImage+Extentions.m
├── ViewController.swift
├── ViewController1.swift
└── ViewController2.swift
├── UICornerShadowViewTests
├── Info.plist
└── UICornerShadowViewTests.swift
└── UICornerShadowViewUITests
├── Info.plist
└── UICornerShadowViewUITests.swift
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Silver
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 | [](https://travis-ci.org/389185764@qq.com/SDWebImage-SFImageMaker)
2 | [](https://cocoapods.org/pods/SFImageMaker)
3 | [](https://cocoapods.org/pods/SFImageMaker)
4 | [](https://cocoapods.org/pods/SFImageMaker)
5 |
6 | [掘金](https://juejin.im/post/5e1e08c36fb9a030080c9427)
7 |
8 | # SFCSBView
9 | iOS UIView Subclass: Use CoreGraphics to control the direction of Rect Corner、Shadow 、Border
10 | ## Example
11 | ```objective-c
12 | // UIAppearance
13 | [SFCSBView appearance].cornerRadius = 20;
14 | [SFCSBView appearance].rectCornner = UIRectCornerAllCorners;
15 | [SFCSBView appearance].shadowPosition = UIShadowPostionAll;
16 | [SFCSBView appearance].shadowRadius = 20;
17 | [SFCSBView appearance].borderColor = [UIColor systemBlueColor];
18 | [SFCSBView appearance].borderWidth = 5;
19 | [SFCSBView appearance].borderPosition = UIBorderPostionAll;
20 |
21 |
22 | SFCSBView * shadowView = [SFCSBView new];
23 | shadowView.cornerRadius = 20;
24 | shadowView.rectCornner = UIRectCornerAllCorners;
25 | shadowView.shadowPosition = UIShadowPostionAll;
26 | shadowView.shadowColor = [[UIColor blackColor]colorWithAlphaComponent:0.6];
27 | shadowView.shadowRadius = 20;
28 | shadowView.borderColor = UIColor.systemBlueColor;
29 | shadowView.borderWidth = 5;
30 | shadowView.borderPosition = UIBorderPostionAll;
31 | ```
32 |
33 | ## Features
34 | 1. Surpport xib / storyboard
35 |
36 |
37 |
38 |
39 | 2. Using them freedomly
40 |
41 |
42 |
43 | 3. Create and process image
44 |
45 | for objc:
46 | ```objc
47 | // Generate UIImage by color or gradient colors, then start UIImage process flow
48 | UIColor.lightGrayColor.sf_flow.corner(10, UIRectCornerAllCorners).border(0.5, UIColor.blackColor).image;
49 | @[UIColor.redColor,UIColor.purpleColor].sf_gradientFlow(YES,self.gradientButton.frame.size).corner(10, UIRectCornerTopLeft|UIRectCornerBottomRight).border(1, UIColor.blackColor).image
50 |
51 | // UIImage Process Flow
52 | [UIImage new].sf_flow.circle.resize(CGSizeMake(40, 40)).blur(SFBlurEffectLight).corner(10, UIRectEdgeAll).border(1, UIColor.redColor)
53 | ```
54 |
55 | for swift:
56 | ```swift
57 | let _ = UIColor.red
58 | .sf_flow(with: CGSize.init(width: 40, height: 40))
59 | .blur(with: SFBlurEffectLight)
60 | .border(withWidth: 1, color: .gray)
61 | .shadow(with: UIColor.black.withAlphaComponent(0.5), offset: .init(width: 10, height: 10), blurRadius: 10)
62 | .resize(with: .init(width: 20, height: 20))
63 | .circle
64 | .image();
65 | ```
66 |
67 |
68 | ## CocoaPods
69 | ```
70 | pod 'SFImageMaker'
71 | ```
72 |
73 | ## Preview
74 |
75 | **Compare with CALayer**
76 |
77 | 
78 |
79 | **Random Cells**
80 |
81 | 
82 |
83 |
84 |
85 |
86 |
87 | ## Performance
88 |
89 | 1000 shadow images only need 29MB and 1.01s
90 |
91 | In this condition, maximum cpu occupancy rates is 45% and 60FPS when the count of cell is 1000 and quick slide,use iPhone 7, iOS13.3.1.
92 |
93 | 
94 |
95 | 
96 |
97 | In extreme cases,no reuse, maximum cpu occupancy rates is 140% and 60FPS when the count of cell is 1000 and quick slide.
98 |
99 | 
100 |
101 | 
102 |
--------------------------------------------------------------------------------
/SFImageMaker.podspec:
--------------------------------------------------------------------------------
1 | #
2 | Pod::Spec.new do |spec|
3 |
4 | spec.name = "SFImageMaker"
5 | spec.version = "1.1.4"
6 | spec.summary = "Use CoreGraphics to control the direction of Rect Corner、Shadow 、Border"
7 | spec.homepage = "https://github.com/SilverFruity/UICornerShadowView"
8 | spec.license = { :type => "MIT", :file => "LICENSE" }
9 | spec.author = { "Jiang" => "15328044115@163.com" }
10 | spec.source = { :git => "https://github.com/SilverFruity/UICornerShadowView.git", :tag => "#{spec.version}" }
11 | spec.source_files = "SFImageMaker/SFImageMaker/*.{h,m}","SFImageMaker/SFImageMaker/**/*.{h,m}"
12 | spec.public_header_files = "SFImageMaker/SFImageMaker/*.h"
13 | spec.ios.deployment_target = '8.0'
14 | # spec.preserve_paths = "FilesToSave", "MoreFilesToSave"
15 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
16 | #
17 | # Link your library with frameworks, or libraries. Libraries do not include
18 | # the lib prefix of their name.
19 | #
20 |
21 | # spec.framework = "SomeFramework"
22 | # spec.frameworks = "SomeFramework", "AnotherFramework"
23 |
24 | # spec.library = "iconv"
25 | # spec.libraries = "iconv", "xml2"
26 |
27 |
28 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
29 | #
30 | # If your library depends on compiler flags you can set them in the xcconfig hash
31 | # where they will only apply to your library. If you depend on other Podspecs
32 | # you can include multiple dependencies to ensure it works.
33 |
34 | # spec.requires_arc = true
35 |
36 | # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }
37 | # spec.dependency "JSONKit", "~> 1.4"
38 |
39 | end
40 |
41 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker.xcodeproj/project.xcworkspace/xcuserdata/Memerys.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilverFruity/UICornerShadowView/97d23060a8bfc4beb3c8b57d900550f09c8fe91c/SFImageMaker/SFImageMaker.xcodeproj/project.xcworkspace/xcuserdata/Memerys.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker.xcodeproj/xcshareddata/xcschemes/SFImageMaker.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
43 |
44 |
54 |
55 |
61 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker.xcodeproj/xcuserdata/Memerys.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SFImageMaker.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 1
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker.xcodeproj/xcuserdata/jiang.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SFImageMaker.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 1B2CFBDF23FEF486008DBFC1
16 |
17 | primary
18 |
19 |
20 | 1B2CFBE823FEF486008DBFC1
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker.xcodeproj/xcuserdata/mac.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SFImageMaker.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFBlockImageMaker.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFBlockImageMaker.h
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/29.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 | @interface SFBlockImageMaker : NSObject
14 | @property(nonatomic, strong)NSMutableArray > *dependencies;
15 |
16 | + (instancetype)imageMakerWithProcessHandler:(UIImage * (^)(UIImage *image))processHandler
17 | isEnableHandler:(BOOL (^)(void))isEnableHandler
18 | identifierHandler:(NSString *(^)(void))identifierHandler;
19 |
20 | + (instancetype)centerRectWithAspectRatio:(CGFloat)aspectRatio;
21 | + (instancetype)centerSquare;
22 | + (instancetype)edgeInsets:(UIEdgeInsets)insets fillColor:(nullable UIColor *)color;
23 | + (instancetype)resizeWithSize:(CGSize)targetSize;
24 | + (instancetype)resizeWithMaxValue:(CGFloat)maxValue;
25 | + (instancetype)resizableCenterMode;
26 | @end
27 |
28 | NS_ASSUME_NONNULL_END
29 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFBlockImageMaker.m:
--------------------------------------------------------------------------------
1 | //
2 | // SFBlockImageMaker.m
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/29.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import "SFBlockImageMaker.h"
10 | #import "SFCornerImageMaker.h"
11 |
12 | @interface SFBlockImageMaker()
13 | @property (nonatomic,copy)UIImage *(^processHandler)(UIImage *image);
14 | @property (nonatomic,copy)BOOL (^isEnableHandler)(void);
15 | @property (nonatomic,copy)NSString *(^identifierHandler)(void);
16 | @end
17 | @implementation SFBlockImageMaker
18 | + (instancetype)imageMakerWithProcessHandler:(UIImage * (^)(UIImage *image))processHandler
19 | isEnableHandler:(BOOL (^)(void))isEnableHandler
20 | identifierHandler:(NSString *(^)(void))identifierHandler{
21 | SFBlockImageMaker *maker = [SFBlockImageMaker new];
22 | maker.processHandler = processHandler;
23 | maker.isEnableHandler = isEnableHandler;
24 | maker.identifierHandler = identifierHandler;
25 | return maker;
26 | }
27 | - (instancetype)init
28 | {
29 | self = [super init];
30 | self.dependencies = [NSMutableArray array];
31 | return self;
32 | }
33 | - (BOOL)isEnable{
34 | if (self.isEnableHandler) {
35 | return self.isEnableHandler();
36 | }
37 | return NO;
38 | }
39 | - (nonnull NSString *)identifier {
40 | if (self.identifierHandler && self.isEnable){
41 | return self.identifierHandler();
42 | }
43 | return @"";
44 | }
45 |
46 | - (nonnull UIImage *)process:(nullable UIImage *)target {
47 | if (self.processHandler && self.isEnable) {
48 | return self.processHandler(target);
49 | }
50 | return target;
51 | }
52 |
53 | + (instancetype)centerRectWithAspectRatio:(CGFloat)aspectRatio{
54 | return [SFBlockImageMaker imageMakerWithProcessHandler:^UIImage * _Nonnull(UIImage * _Nonnull image) {
55 | CGFloat width = floor(image.size.width);
56 | CGFloat height = floor(image.size.height);
57 | CGFloat imageAspectRatio = width / height;
58 | if(imageAspectRatio == aspectRatio) return image;
59 | CGFloat x = 0, y = 0;
60 | if (imageAspectRatio < aspectRatio) {
61 | y = floor((height - width/aspectRatio) * 0.5);
62 | height = width / aspectRatio;
63 | }else{
64 | x = floor((width - height*aspectRatio) * 0.5);
65 | width = height * aspectRatio;
66 | }
67 | UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 0);
68 | [image drawAtPoint:CGPointMake(-x, -y)];
69 | UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
70 | UIGraphicsEndImageContext();
71 | return newImage;
72 | } isEnableHandler:^BOOL{
73 | return aspectRatio != 0;
74 | } identifierHandler:^NSString * _Nonnull{
75 | return [NSString stringWithFormat:@"centerRect_%f",aspectRatio];
76 | }];
77 | }
78 | + (instancetype)centerSquare{
79 | return [self centerRectWithAspectRatio:1.0];
80 | }
81 | + (instancetype)edgeInsets:(UIEdgeInsets)insets fillColor:(UIColor *)color{
82 | color = color?:[UIColor clearColor];
83 | return [SFBlockImageMaker imageMakerWithProcessHandler:^UIImage * _Nonnull(UIImage * _Nonnull image) {
84 | CGRect imageRect = CGRectMake(insets.left, insets.top, image.size.width, image.size.height);
85 | CGSize newSize = CGSizeZero;
86 | newSize.height = imageRect.size.height + (insets.top + insets.bottom);
87 | newSize.width = imageRect.size.width + (insets.left + insets.right);
88 | UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
89 | CGContextRef context = UIGraphicsGetCurrentContext();
90 | [color setFill];
91 | CGContextFillRect(context, CGRectMake(0, 0, newSize.width, newSize.height));
92 | [image drawInRect:imageRect];
93 | UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
94 | UIGraphicsEndImageContext();
95 | return result;
96 | } isEnableHandler:^BOOL{
97 | return YES;
98 | } identifierHandler:^NSString * _Nonnull{
99 | return [NSString stringWithFormat:@"edgeInsets_%@_%@",[NSValue valueWithUIEdgeInsets:insets],color.sf_identifier];
100 | }];
101 | }
102 | + (instancetype)resizeWithSize:(CGSize)targetSize{
103 | return [SFBlockImageMaker imageMakerWithProcessHandler:^UIImage * _Nonnull(UIImage * _Nonnull image) {
104 | UIGraphicsBeginImageContextWithOptions(targetSize, NO, 0);
105 | CGContextRef ctx = UIGraphicsGetCurrentContext();
106 | CGContextSetFillColorWithColor(ctx, [UIColor clearColor].CGColor);
107 | CGRect rect = CGRectMake(0, 0, targetSize.width, targetSize.height);
108 | CGContextFillRect(ctx, rect);
109 | [image drawInRect:rect];
110 | UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
111 | UIGraphicsEndImageContext();
112 | return result;
113 | } isEnableHandler:^BOOL{
114 | return !CGSizeEqualToSize(targetSize, CGSizeZero);
115 | } identifierHandler:^NSString * _Nonnull{
116 | return [NSString stringWithFormat:@"resize_%@",[NSValue valueWithCGSize:targetSize]];
117 | }];
118 | }
119 | + (instancetype)resizeWithMaxValue:(CGFloat)maxValue{
120 | return [SFBlockImageMaker imageMakerWithProcessHandler:^UIImage * _Nonnull(UIImage * _Nonnull image) {
121 | CGFloat currentMax = MAX(image.size.width, image.size.height);
122 | if (maxValue > currentMax) return image;
123 | CGFloat ratio = image.size.width / image.size.height;
124 | CGSize size = ratio > 1 ? CGSizeMake(maxValue, maxValue/ratio) : CGSizeMake(maxValue*ratio, maxValue);
125 | return [[SFBlockImageMaker resizeWithSize:size] process:image];
126 | } isEnableHandler:^BOOL{
127 | return maxValue != 0;
128 | } identifierHandler:^NSString * _Nonnull{
129 | return [NSString stringWithFormat:@"resizeMax_%@",@(maxValue)];
130 | }];
131 | }
132 | + (instancetype)resizableCenterMode{
133 | return [SFBlockImageMaker imageMakerWithProcessHandler:^UIImage * _Nonnull(UIImage * _Nonnull image) {
134 | return [image resizableImageWithCapInsets:UIEdgeInsetsMake(image.size.height / 2, image.size.width / 2, image.size.height / 2, image.size.width / 2)];
135 | } isEnableHandler:^BOOL{
136 | return YES;
137 | } identifierHandler:^NSString * _Nonnull{
138 | return @"resizableCenterMode";
139 | }];
140 | }
141 | @end
142 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFBlurImageMaker.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFBlurImageMaker.h
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/29.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | NS_ASSUME_NONNULL_BEGIN
12 | typedef enum : NSUInteger {
13 | SFBlurEffectLight,
14 | SFBlurEffectExtraLight,
15 | SFBlurEffectDark,
16 | } SFBlurEffect;
17 | @interface SFBlurImageMaker : NSObject
18 | @property (nonatomic, assign)CGFloat blurRadius;
19 | @property (nonatomic, strong)UIColor * tintColor;
20 | @property (nonatomic, assign)CGFloat saturationDeltaFactor;
21 | @property (nonatomic, strong, nullable)UIImage *maskImage;
22 | @property(nonatomic, strong)NSMutableArray > *dependencies;
23 |
24 | + (instancetype)lightEffect;
25 | + (instancetype)extraLightEffect;
26 | + (instancetype)darkEffect;
27 | + (instancetype)tintEffectWithColor:(UIColor *)tintColor;
28 |
29 | @end
30 |
31 | NS_ASSUME_NONNULL_END
32 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFBlurImageMaker.m:
--------------------------------------------------------------------------------
1 | //
2 | // SFBlurImageMaker.m
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/29.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import "SFBlurImageMaker.h"
10 | #import "UIImageEffects.h"
11 | @implementation SFBlurImageMaker
12 | - (instancetype)init
13 | {
14 | self = [super init];
15 | self.dependencies = [NSMutableArray array];
16 | return self;
17 | }
18 | - (BOOL)isEnable{
19 | return self.blurRadius != 0 && self.tintColor != nil;
20 | }
21 | - (nonnull NSString *)identifier {
22 | return self.isEnable ? [NSString stringWithFormat:@"blureImage%.1f%@%.1f",self.blurRadius,self.tintColor.sf_identifier,self.saturationDeltaFactor] : @"";
23 | }
24 | + (instancetype)lightEffect{
25 | SFBlurImageMaker *maker = [SFBlurImageMaker new];
26 | maker.blurRadius = 60;
27 | maker.tintColor = [UIColor colorWithWhite:1.0 alpha:0.3];
28 | maker.saturationDeltaFactor = 1.8;
29 | return maker;
30 | }
31 | + (instancetype)extraLightEffect{
32 | SFBlurImageMaker *maker = [SFBlurImageMaker new];
33 | maker.blurRadius = 40;
34 | maker.tintColor = [UIColor colorWithWhite:0.97 alpha:0.82];
35 | maker.saturationDeltaFactor = 1.8;
36 | return maker;
37 | }
38 | + (instancetype)darkEffect{
39 | SFBlurImageMaker *maker = [SFBlurImageMaker new];
40 | maker.blurRadius = 40;
41 | maker.tintColor = [UIColor colorWithWhite:0.11 alpha:0.73];
42 | maker.saturationDeltaFactor = 1.8;
43 | return maker;
44 | }
45 | + (instancetype)tintEffectWithColor:(UIColor *)tintColor{
46 | SFBlurImageMaker *maker = [SFBlurImageMaker new];
47 | const CGFloat EffectColorAlpha = 0.6;
48 | UIColor *effectColor = tintColor;
49 | size_t componentCount = CGColorGetNumberOfComponents(tintColor.CGColor);
50 | if (componentCount == 2) {
51 | CGFloat b;
52 | if ([tintColor getWhite:&b alpha:NULL]) {
53 | effectColor = [UIColor colorWithWhite:b alpha:EffectColorAlpha];
54 | }
55 | }
56 | else {
57 | CGFloat r, g, b;
58 | if ([tintColor getRed:&r green:&g blue:&b alpha:NULL]) {
59 | effectColor = [UIColor colorWithRed:r green:g blue:b alpha:EffectColorAlpha];
60 | }
61 | }
62 | maker.blurRadius = 20;
63 | maker.saturationDeltaFactor = -1.0;
64 | maker.tintColor = effectColor;
65 | return maker;
66 | }
67 |
68 | - (nonnull UIImage *)process:(nullable UIImage *)target {
69 | if (self.isEnable) {
70 | return [UIImageEffects imageByApplyingBlurToImage:target withRadius:self.blurRadius tintColor:self.tintColor saturationDeltaFactor:self.saturationDeltaFactor maskImage:self.maskImage];
71 | }
72 | return target;
73 | }
74 | - (void)saveContext {
75 | _tintColor = [UIColor colorWithCGColor:_tintColor.CGColor];
76 | }
77 | @end
78 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFBorderImageMaker.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFBorderImageMaker.h
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @class SFCornerImageMaker;
15 | typedef NS_OPTIONS(NSUInteger, UIBorderPostion) {
16 | UIBorderPostionTop = 1 << 0,
17 | UIBorderPostionRight = 1 << 1,
18 | UIBorderPostionBottom = 1 << 2,
19 | UIBorderPostionLeft = 1 << 3,
20 | UIBorderPostionAll = ~0UL
21 | };
22 |
23 | @interface SFBorderImageMaker: NSObject
24 | @property (nonatomic, assign)CGFloat width;
25 | @property (nonatomic, strong)UIColor *color;
26 | @property (nonatomic, strong)UIColor *fillColor;
27 | @property (nonatomic, assign)UIBorderPostion position;
28 | @property (nonatomic, getter=isEnable)BOOL enable;
29 | @property (nonatomic, strong, nullable)SFCornerImageMaker *cornerMaker;
30 | @property (nonatomic, strong)NSMutableArray > *dependencies;
31 | - (CGRect)strokeRectWithSize:(CGSize)size;
32 | @end
33 |
34 | NS_ASSUME_NONNULL_END
35 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFBorderImageMaker.m:
--------------------------------------------------------------------------------
1 | //
2 | // SFBorderImageMaker.m
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import "SFBorderImageMaker.h"
10 | #import "SFCornerImageMaker.h"
11 |
12 |
13 | @implementation SFBorderImageMaker
14 | - (instancetype)init
15 | {
16 | self = [super init];
17 | self.fillColor = [UIColor clearColor];
18 | self.dependencies = [NSMutableArray array];
19 | return self;
20 | }
21 | - (BOOL)isEnable{
22 | return self.color != nil && self.color != [UIColor clearColor] && self.width != 0;
23 | }
24 | - (UIEdgeInsets)strokeEdgeInsets{
25 | CGFloat left=0, right = 0, top = 0, bottom = 0;
26 | left = self.position&UIBorderPostionLeft? self.width/2 : -self.width/2;
27 | right = self.position&UIBorderPostionRight? self.width/2 : -self.width/2;
28 | top = self.position&UIBorderPostionTop? self.width/2 : -self.width/2;
29 | bottom = self.position&UIBorderPostionBottom? self.width/2 : -self.width/2;
30 | return UIEdgeInsetsMake(top, left, bottom, right);
31 | }
32 | - (CGPoint)strokeOrigin{
33 | UIEdgeInsets insets = [self strokeEdgeInsets];
34 | return CGPointMake(insets.left, insets.top);
35 | }
36 | - (CGSize)strokeSizeWithSize:(CGSize)size{
37 | UIEdgeInsets insets = [self strokeEdgeInsets];
38 | return CGSizeMake(size.width - insets.left - insets.right,size.height - insets.top - insets.bottom);
39 | }
40 | - (CGRect)strokeRectWithSize:(CGSize)size{
41 | CGPoint origin = [self strokeOrigin];
42 | CGSize rectSize = [self strokeSizeWithSize:size];
43 | return CGRectMake(origin.x, origin.y, rectSize.width, rectSize.height);
44 | }
45 | - (nonnull UIImage *)process:(nullable UIImage *)target {
46 | if (!self.isEnable) return target;
47 | if (!target) return [UIImage new];
48 | CGRect rect = CGRectMake(0, 0, target.size.width, target.size.height);
49 | UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
50 | CGContextRef context = UIGraphicsGetCurrentContext();
51 | [self.color setStroke];
52 | [self.fillColor setFill];
53 | CGContextFillRect(context, rect);
54 | [target drawInRect:rect];
55 | SFCornerImageMaker *cornerMaker = self.cornerMaker;
56 | if (cornerMaker){
57 | UIBezierPath *clipPath = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:cornerMaker.position cornerRadii:CGSizeMake(cornerMaker.radius, cornerMaker.radius)];
58 | [clipPath addClip];
59 | }
60 |
61 | CGRect strokeRect = CGContextGetClipBoundingBox(context);
62 | strokeRect = [self strokeRectWithSize:target.size];
63 | UIBezierPath *linePath;
64 | if (!cornerMaker || self.width > cornerMaker.radius || cornerMaker.radius == 0){ // 当 width 40 radius 20的情况下
65 | linePath = [UIBezierPath bezierPathWithRect:strokeRect];
66 | }else{
67 | linePath = [UIBezierPath bezierPathWithRoundedRect:strokeRect byRoundingCorners:cornerMaker.position cornerRadii:CGSizeMake(cornerMaker.radius, cornerMaker.radius)];
68 | }
69 | linePath.lineWidth = self.width;
70 | if (cornerMaker.radius > 0){
71 | linePath.lineCapStyle = kCGLineCapRound;
72 | linePath.lineJoinStyle = kCGLineJoinRound;
73 | }
74 | [linePath stroke];
75 |
76 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
77 | UIGraphicsEndImageContext();
78 | return image;
79 | }
80 |
81 | - (nonnull NSString *)identifier {
82 | return self.isEnable ? [NSString stringWithFormat:@"border_%@_%@_%@",self.color.sf_identifier,@(self.width),@(self.position)] : @"";
83 | }
84 | - (void)saveContext {
85 | _fillColor = [UIColor colorWithCGColor:_fillColor.CGColor];
86 | _color = [UIColor colorWithCGColor:_color.CGColor];
87 | }
88 | @end
89 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFCSBView.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFCSBView.h
3 | // UICornerShadowView
4 | //
5 | // Created by Jiang on 2020/2/28.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface SFCSBView : UIView
14 | @property (nonatomic,assign)IBInspectable CGFloat cornerRadius UI_APPEARANCE_SELECTOR;
15 | @property (nonatomic,strong)IBInspectable UIColor *shadowColor UI_APPEARANCE_SELECTOR;
16 | @property (nonatomic,assign)IBInspectable CGSize shadowOffset UI_APPEARANCE_SELECTOR;
17 | @property (nonatomic,assign)IBInspectable CGFloat shadowRadius UI_APPEARANCE_SELECTOR;
18 | @property (nonatomic,assign)IBInspectable CGFloat borderWidth UI_APPEARANCE_SELECTOR;
19 | @property (nonatomic,strong)IBInspectable UIColor *borderColor UI_APPEARANCE_SELECTOR;
20 | @property (nonatomic,assign)IBInspectable UIRectCorner rectCornner UI_APPEARANCE_SELECTOR;
21 | @property (nonatomic,assign)IBInspectable UIShadowPostion shadowPosition UI_APPEARANCE_SELECTOR;
22 | @property (nonatomic,assign)IBInspectable UIBorderPostion borderPosition UI_APPEARANCE_SELECTOR;
23 | @property (nonatomic,nullable,copy)void(^handleMakers)(NSArray > * makers);
24 | @property (nonatomic,strong)Class defautlGeneratorClass;
25 | @property (nonatomic,readonly)id imageGenerator;
26 | @property (nonatomic,readonly)SFShadowImageMaker *shadowProcessor;
27 | @property (nonatomic,readonly)SFCornerImageMaker *cornerProcessor;
28 | @property (nonatomic,readonly)SFBorderImageMaker *borderProcessor;
29 | // 用于解决阴影衔接问题,通常不会用到,默认值为0
30 | // 需要解决时,可以将其设置为 -0.5 或者 -0.25
31 | @property (nonatomic,assign)CGFloat shadowFixValue;
32 | - (void)reloadBackGourndImage;
33 | @end
34 |
35 | NS_ASSUME_NONNULL_END
36 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFCSBView.m:
--------------------------------------------------------------------------------
1 | //
2 | // SFCSBView.m
3 | // UICornerShadowView
4 | //
5 | // Created by Jiang on 2020/2/28.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import "SFCSBView.h"
10 |
11 | @interface SFCSBViewImageCache: NSObject
12 | @property (nonatomic, strong)NSCache *cache;
13 | @end
14 |
15 | @implementation SFCSBViewImageCache
16 | + (instancetype)shared{
17 | static dispatch_once_t onceToken;
18 | static SFCSBViewImageCache *_instance = nil;
19 | dispatch_once(&onceToken, ^{
20 | _instance = [SFCSBViewImageCache new];
21 | });
22 | return _instance;
23 | }
24 | - (instancetype)init
25 | {
26 | self = [super init];
27 | self.cache = [[NSCache alloc] init];
28 | self.cache.totalCostLimit = 10 * 1024 * 1024;
29 | return self;
30 | }
31 | - (nullable UIImage *)objectForKey:(NSString *)key{
32 | return [self.cache objectForKey:key];
33 | }
34 | - (void)setObject:(nullable UIImage *)obj forKey:(NSString *)key{
35 | if (!obj) {
36 | return;
37 | }
38 | [self.cache setObject:obj forKey:key];
39 | }
40 | @end
41 |
42 | @interface SFCSBView()
43 | @property (nonatomic, strong)UIColor * initailBackGroundColor;
44 | @property (nonatomic, copy)NSString * lastBackGroundImageIdentifer;
45 | @property (nonatomic, strong)UIImageView * backGroundImageView;
46 | @property (nonatomic, strong)SFImageFlow * flow;
47 | @end
48 | @implementation SFCSBView
49 | - (SFShadowImageMaker *)shadowProcessor{
50 | SFShadowImageMaker *maker = [[SFShadowImageMaker alloc] init];
51 | maker.shadowColor = self.shadowColor;
52 | maker.shadowOffset = self.shadowOffset;
53 | maker.shadowBlurRadius = self.shadowRadius;
54 | maker.position = self.shadowPosition;
55 | return maker;
56 | }
57 | - (SFCornerImageMaker *)cornerProcessor{
58 | SFCornerImageMaker *maker = [[SFCornerImageMaker alloc] init];
59 | maker.radius = self.cornerRadius;
60 | maker.position = self.rectCornner;
61 | return maker;
62 | }
63 | - (SFBorderImageMaker *)borderProcessor{
64 | SFBorderImageMaker *maker = [[SFBorderImageMaker alloc] init];
65 | maker.width = self.borderWidth;
66 | maker.color = self.borderColor;
67 | maker.position = self.borderPosition;
68 | maker.cornerMaker = self.cornerProcessor;
69 | return maker;
70 | }
71 |
72 | - (Class)defautlGeneratorClass{
73 | if (!_defautlGeneratorClass) {
74 | _defautlGeneratorClass = [SFColorImageMaker class];
75 | }
76 | return _defautlGeneratorClass;
77 | }
78 | - (id )imageGenerator{
79 | id generator = [self.defautlGeneratorClass alloc];
80 | // 只有纯色背景色才能以中点为拉伸点
81 | if ([generator isKindOfClass:[SFColorImageMaker class]]){
82 | // Radius ShadowRadius BorderWidth 取最大值
83 | CGFloat maxValue = self.cornerProcessor.radius > (self.borderProcessor.width + 1) && self.cornerProcessor.isEnable ? self.cornerProcessor.radius : self.borderProcessor.width + 1;
84 | maxValue = self.shadowProcessor.shadowBlurRadius > maxValue ? self.shadowProcessor.shadowBlurRadius : maxValue;
85 | CGSize size = CGSizeMake(maxValue * 2, maxValue * 2);
86 | return [generator initWithSize:size];
87 | }else{
88 | return [generator initWithSize:self.bounds.size];
89 | }
90 |
91 | }
92 | - (UIImageView *)backGroundImageView{
93 | if (!_backGroundImageView){
94 | _backGroundImageView = [UIImageView new];
95 | _backGroundImageView.clipsToBounds = NO;
96 | [self addSubview:_backGroundImageView];
97 | }
98 | return _backGroundImageView;
99 | }
100 | - (instancetype)initWithCoder:(NSCoder *)coder
101 | {
102 | self = [super initWithCoder:coder];
103 | if (self) {
104 | [self setDefaultValues];
105 | }
106 | return self;
107 | }
108 | - (instancetype)initWithFrame:(CGRect)frame
109 | {
110 | self = [super initWithFrame:frame];
111 | if (self) {
112 | [self setDefaultValues];
113 | }
114 | return self;
115 | }
116 | - (void)setDefaultValues{
117 | self.shadowFixValue = 0;
118 | self.rectCornner = UIRectCornerAllCorners;
119 | self.cornerRadius = 8;
120 | self.shadowColor = [UIColor.blueColor colorWithAlphaComponent:0.08];
121 | self.shadowOffset = CGSizeZero;
122 | self.shadowRadius = 4;
123 | self.borderWidth = 0;
124 | self.borderColor = UIColor.clearColor;
125 | self.shadowPosition = UIShadowPostionAll;
126 | self.borderPosition = UIBorderPostionAll;
127 | self.clipsToBounds = NO;
128 | }
129 | - (void)layoutSubviews{
130 | [super layoutSubviews];
131 | [self reloadBackGourndImage];
132 | }
133 | - (void)reloadBackGourndImage{
134 | [self sendSubviewToBack:self.backGroundImageView];
135 | if (self.backgroundColor != nil) {
136 | self.initailBackGroundColor = self.backgroundColor;
137 | }
138 | //保证不会在子线程中调用self获取值
139 | //TODO: 增加网络图片的处理
140 | SFShadowImageMaker *shadowMaker = self.shadowProcessor;
141 | SFCornerImageMaker *cornerMaker = self.cornerProcessor;
142 | SFBorderImageMaker *borderMaker = self.borderProcessor;
143 | id imageGenerator = self.imageGenerator;
144 | if ([imageGenerator isKindOfClass:[SFColorImageMaker class]]) {
145 | SFColorImageMaker *colorMaker = imageGenerator;
146 | colorMaker.color = self.initailBackGroundColor;
147 | }
148 | if (self.handleMakers)
149 | self.handleMakers(@[imageGenerator,cornerMaker,borderMaker,shadowMaker]);
150 |
151 | self.flow = [SFImageFlow flowWithGenerator:imageGenerator];
152 | self.flow.processors = [@[cornerMaker,borderMaker,shadowMaker] mutableCopy];
153 |
154 | NSString *identifier = self.flow.identifier;
155 |
156 | CGRect backImageViewFrame = self.bounds;
157 | if (shadowMaker.isEnable){
158 | //FIXME: 外边框 border和shadow同时存在时,宽高的计算,一大一小。
159 | //FIXME: 外边框 border和shadow只有一者存在时,宽高的计算。
160 | backImageViewFrame = [shadowMaker viewRectForSize:self.bounds.size];
161 | // 解决tableView显示时,cell上下阴影衔接时会有一空缺的问题。
162 | // 2021.12.17 默认为0,解决子视图对齐时,白边的问题
163 | CGFloat insertValue = _shadowFixValue;
164 | // CGRect(2,2,2,2) -> CGRect(1,1,4,4)
165 | backImageViewFrame = CGRectInset(backImageViewFrame, insertValue, insertValue);
166 | }
167 | // 每修改一次subview的frame,view会调用layoutSubviews方法。
168 | // 目的:在高度重用UICornerShadowView的情况,并且每次都更新的情况下,减少frame更新。
169 | // 如果上一次的identifer相同说明是重用图片
170 | // 如果当前frame和需要的frame相同,也不用更新frame
171 | if (![identifier isEqualToString:self.lastBackGroundImageIdentifer] || !CGRectEqualToRect(self.backGroundImageView.frame, backImageViewFrame)) {
172 | self.backGroundImageView.frame = backImageViewFrame;
173 | }
174 | UIImage *cacheImage = [[SFCSBViewImageCache shared] objectForKey:identifier];
175 | if (cacheImage) {
176 | self.backGroundImageView.image = cacheImage;
177 | self.backgroundColor = nil;
178 | self.lastBackGroundImageIdentifer = identifier;
179 | return;
180 | }
181 | /*
182 | 新增 saveContext
183 | 解决当 App 直接退到后台时,会直接调用 layoutSubViews 两次,并且两次 backgroudColor 的 RGB 的值都不相同,
184 | 而我们此处是异步调用的执行图片的绘制,会出现 identifier 对应的图片颜色被改变的问题。
185 | 比如:此处我们生成 identfier 时是使用的白色RGB值,但当我们在其他线程绘制的时候,color 值却在其他线程被改变为黑色,导致我们绘制颜色的 RGB 值却是黑色,
186 | 从而导致使用白色图片的 identifier 获取到的图片却是黑色的问题。
187 | 为避免这种情况发生,我们需要在异步执行前,先将相关颜色RGB值进行一次复制,避免出现以上问题。
188 | */
189 | [self.flow saveContext];
190 | __weak typeof(self) weakSelf = self;
191 | dispatch_async(dispatch_get_global_queue(0, 0), ^{
192 | UIImage *image = [self.flow image];
193 | if (shadowMaker.isEnable) {
194 | UIEdgeInsets inset = shadowMaker.convasEdgeInsets;
195 | CGFloat x = (image.size.width - inset.left - inset.right) / 2;
196 | CGFloat y = (image.size.height - inset.top - inset.bottom) / 2;
197 | image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(y + inset.top, x + inset.left, y + inset.bottom, x + inset.right)];
198 | }else{
199 | image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(image.size.height / 2, image.size.width / 2, image.size.height / 2, image.size.width / 2)];
200 | }
201 | [SFCSBViewImageCache.shared setObject:image forKey:identifier];
202 | dispatch_async(dispatch_get_main_queue(), ^{
203 | if (!weakSelf) {
204 | return;
205 | }
206 | weakSelf.lastBackGroundImageIdentifer = identifier;
207 | weakSelf.backGroundImageView.image = image;
208 | });
209 | });
210 | self.backgroundColor = nil;
211 | }
212 | @end
213 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFColorImageMaker.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFColorImageMaker.h
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface SFColorImageMaker : NSObject
14 | @property (nonatomic, assign)CGSize size;
15 | @property (nonatomic, strong, nullable)UIColor *color;
16 | @property (nonatomic, getter=isEnable)BOOL enable;
17 | @property (nonatomic, strong)NSMutableArray > *dependencies;
18 | + (CGSize)defaultSize;
19 | + (instancetype)imageMakerWithColor:(UIColor *)color;
20 | + (instancetype)imageMakerWithColor:(UIColor *)color size:(CGSize)size;
21 | @end
22 |
23 | NS_ASSUME_NONNULL_END
24 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFColorImageMaker.m:
--------------------------------------------------------------------------------
1 | //
2 | // SFColorImageMaker.m
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import "SFColorImageMaker.h"
10 |
11 | @implementation SFColorImageMaker
12 | + (CGSize)defaultSize{
13 | return CGSizeMake(1, 1);
14 | }
15 | - (instancetype)init{
16 | self = [[SFColorImageMaker alloc] initWithSize:[[self class] defaultSize]];
17 | return self;
18 | }
19 | - (nonnull instancetype)initWithSize:(CGSize)size{
20 | self = [super init];
21 | self.size = size;
22 | self.dependencies = [NSMutableArray array];
23 | return self;
24 | }
25 | + (instancetype)imageMakerWithColor:(UIColor *)color{
26 | SFColorImageMaker *colorImage = [self imageMakerWithColor:color size:CGSizeMake(1, 1)];
27 | return colorImage;
28 | }
29 | + (instancetype)imageMakerWithColor:(UIColor *)color size:(CGSize)size{
30 | SFColorImageMaker *colorImage = [[SFColorImageMaker alloc] initWithSize:size];
31 | colorImage.color = color;
32 | return colorImage;
33 | }
34 | - (BOOL)isEnable{
35 | return self.color != [UIColor clearColor] && !CGSizeEqualToSize(self.size, CGSizeZero);
36 | }
37 | - (nonnull UIImage *)generate {
38 | if (!self.isEnable) return [UIImage new];
39 | CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
40 | UIGraphicsBeginImageContextWithOptions(self.size, NO, 0);
41 | CGContextRef context = UIGraphicsGetCurrentContext();
42 | CGContextSetFillColorWithColor(context, self.color.CGColor);
43 | CGContextSetAlpha(context, CGColorGetAlpha(self.color.CGColor));
44 | CGContextFillRect(context, rect);
45 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
46 | UIGraphicsEndImageContext();
47 | return image;
48 | }
49 | - (nonnull NSString *)identifier {
50 | return self.isEnable ? [NSString stringWithFormat:@"%@%@",self.color.sf_identifier,[NSValue valueWithCGSize:self.size]] : @"";
51 | }
52 | - (void)saveContext {
53 | _color = [UIColor colorWithCGColor:_color.CGColor];
54 | }
55 | @end
56 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFCornerImageMaker.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFRectCornerMaker.h
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface SFCornerImageMaker : NSObject
14 | @property (nonatomic, assign)CGFloat radius;
15 | @property (nonatomic, strong)UIColor *fillColor;
16 | @property (nonatomic, assign)UIRectCorner position;
17 | @property (nonatomic, getter=isEnable)BOOL enable;
18 | @property (nonatomic, strong)NSMutableArray > *dependencies;
19 | @end
20 |
21 | @interface SFCircleImageMaker: SFCornerImageMaker
22 | @end
23 | NS_ASSUME_NONNULL_END
24 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFCornerImageMaker.m:
--------------------------------------------------------------------------------
1 | //
2 | // SFRectCornerMaker.m
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import "SFCornerImageMaker.h"
10 | #import "SFBlockImageMaker.h"
11 | @implementation SFCornerImageMaker
12 | - (instancetype)init
13 | {
14 | self = [super init];
15 | self.fillColor = [UIColor clearColor];
16 | self.dependencies = [NSMutableArray array];
17 | return self;
18 | }
19 | - (BOOL)isEnable{
20 | return self.position != 0 && self.radius != 0;
21 | }
22 | - (UIImage *)process:(UIImage *)target{
23 | if (!self.isEnable) return target;
24 | UIGraphicsBeginImageContextWithOptions(target.size, NO, 0);
25 | CGContextRef context = UIGraphicsGetCurrentContext();
26 | CGRect rect = CGRectMake(0, 0, target.size.width, target.size.height);
27 | [self.fillColor setFill];
28 | CGContextFillRect(context, rect);
29 | //利用贝塞尔曲线裁剪矩形
30 | if (self.radius != 0) {
31 | UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:self.position cornerRadii:CGSizeMake(self.radius, self.radius)];
32 | [path addClip];
33 | }
34 | //绘制图像
35 | [target drawInRect:rect];
36 | //获取图像
37 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
38 | UIGraphicsEndImageContext();
39 |
40 | return image;
41 | }
42 | - (nonnull NSString *)identifier {
43 | return self.isEnable ? [NSString stringWithFormat:@"rectCorner_%@_%@",@(self.radius),@(self.position)]:@"";
44 | }
45 | - (void)saveContext {
46 | _fillColor = [UIColor colorWithCGColor:_fillColor.CGColor];
47 | }
48 | @end
49 |
50 | @implementation SFCircleImageMaker
51 | - (BOOL)isEnable{
52 | return YES;
53 | }
54 | - (UIImage *)process:(UIImage *)target{
55 | if (ceil(target.size.width) != ceil(target.size.height)) {
56 | target = [[SFBlockImageMaker centerSquare] process:target];
57 | }
58 | self.position = UIRectCornerAllCorners;
59 | self.radius = target.size.width * 0.5;
60 | return [super process:target];
61 | }
62 | - (NSString *)identifier{
63 | return @"circle";
64 | }
65 | @end
66 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFGradientImageMaker.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFGradientImageMaker.h
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface SFGradientImageMaker : NSObject
14 | @property (nonatomic, assign)BOOL isHorizontal;
15 | @property (nonatomic, assign)CGSize size;
16 | @property (nonatomic, copy)NSArray *locations;
17 | @property (nonatomic, copy)NSArray *colors;
18 | @property (nonatomic, strong)NSMutableArray > *dependencies;
19 |
20 | + (instancetype)isHorizontal:(BOOL)isHorizontal startColor:(UIColor *)startColor endColor:(UIColor *)endColor;
21 | @end
22 |
23 | NS_ASSUME_NONNULL_END
24 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFGradientImageMaker.m:
--------------------------------------------------------------------------------
1 | //
2 | // SFGradientImageMaker.m
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import "SFGradientImageMaker.h"
10 | #import
11 |
12 | @implementation SFGradientImageMaker
13 | - (instancetype)init{
14 | self = [[SFGradientImageMaker alloc] initWithSize:CGSizeMake(1, 1)];
15 | return self;
16 | }
17 | - (nonnull instancetype)initWithSize:(CGSize)size{
18 | self = [super init];
19 | self.size = size;
20 | self.dependencies = [NSMutableArray array];
21 | return self;
22 | }
23 | + (instancetype)isHorizontal:(BOOL)isHorizontal startColor:(UIColor *)startColor endColor:(UIColor *)endColor{
24 | SFGradientImageMaker *gradient = [[self class] new];
25 | gradient.isHorizontal = isHorizontal;
26 | gradient.colors = @[startColor, endColor];
27 | gradient.locations = @[@(0),@(1)];
28 | gradient.size = isHorizontal ? CGSizeMake(UIScreen.mainScreen.bounds.size.width, 1) : CGSizeMake(1, UIScreen.mainScreen.bounds.size.height);
29 | return gradient;
30 | }
31 | - (BOOL)isEnable{
32 | return self.colors.count > 0 && self.locations.count > 0 && !CGSizeEqualToSize(self.size, CGSizeZero);
33 | }
34 | - (nonnull UIImage *)generate {
35 | UIGraphicsBeginImageContext(self.size);
36 | CGContextRef ctx = UIGraphicsGetCurrentContext();
37 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
38 |
39 | CGFloat cLocations[2];
40 | cLocations[0] = self.locations.firstObject.doubleValue;
41 | cLocations[1] = self.locations.lastObject.doubleValue;
42 |
43 | NSMutableArray *colors = [NSMutableArray array];
44 | for (UIColor *color in self.colors) {
45 | [colors addObject:(__bridge id)color.CGColor];
46 | }
47 | CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, cLocations);
48 |
49 | CGPathRef path = CGPathCreateWithRect(CGRectMake(0, 0, self.size.width, self.size.height), &CGAffineTransformIdentity);
50 | CGRect pathRect = CGPathGetBoundingBox(path);
51 |
52 | CGPoint startPoint = CGPointMake(CGRectGetMaxX(pathRect) * cLocations[0], CGRectGetMidY(pathRect));
53 | CGPoint endPoint = CGPointMake(CGRectGetMaxX(pathRect) * cLocations[1], CGRectGetMidY(pathRect));
54 | if (!self.isHorizontal) {
55 | startPoint = CGPointMake(CGRectGetMidX(pathRect), CGRectGetMaxY(pathRect) * cLocations[0]);
56 | endPoint = CGPointMake(CGRectGetMidX(pathRect), CGRectGetMaxY(pathRect) * cLocations[1]);
57 | }
58 | CGContextSaveGState(ctx);
59 | CGContextAddPath(ctx, path);
60 | CGContextClip(ctx);
61 | CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint, 0);
62 | CGContextRestoreGState(ctx);
63 | CGGradientRelease(gradient);
64 | CGColorSpaceRelease(colorSpace);
65 | CGPathRelease(path);
66 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
67 | UIGraphicsEndImageContext();
68 | return image;
69 | }
70 | - (nonnull NSString *)identifier {
71 | return self.isEnable ? [NSString stringWithFormat:@"gradient_%@_%@_%@",[NSValue valueWithCGSize:self.size],[self.colors componentsJoinedByString:@","],[self.locations componentsJoinedByString:@","]] : @"";
72 | }
73 | - (void)saveContext {
74 | NSMutableArray *container = [@[] mutableCopy];
75 | for (UIColor *color in container) {
76 | [container addObject:[UIColor colorWithCGColor:color.CGColor]];
77 | }
78 | _colors = [container copy];
79 | }
80 | @end
81 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFImageFlow.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFImageManager.h
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/25.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "SFImageMaker-Protocol.h"
11 | #import "SFBlurImageMaker.h"
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface SFImageFlow : NSObject
15 | @property (nonatomic, nullable, strong)UIImage *targetImage;
16 | @property (nonatomic, nullable, strong)id generator;
17 | @property (nonatomic, nullable, strong)NSMutableArray *processors;
18 | @property (nonatomic, nullable, strong)NSMutableArray *finals;
19 | + (instancetype)flow;
20 | + (instancetype)flowWithImage:(UIImage *)targetImage;
21 | + (instancetype)flowWithGenerator:(id )generator;
22 | - (void)appendProcessor:(id)processor;
23 | - (void)saveContext;
24 | - (UIImage *)image;
25 | - (NSString *)identifier;
26 | @end
27 | NS_ASSUME_NONNULL_END
28 |
29 | #import
30 | NS_ASSUME_NONNULL_BEGIN
31 | @interface SFImageFlow (Processor)
32 | @property (nonatomic, readonly)SFImageFlow* (^corner)(CGFloat radius, UIRectCorner rectCorner);
33 | @property (nonatomic, readonly)SFImageFlow* (^border)(CGFloat width, UIColor *color);
34 | @property (nonatomic, readonly)SFImageFlow* (^borderWithPosition)(UIBorderPostion position, CGFloat width, UIColor *color);
35 | @property (nonatomic, readonly)SFImageFlow* (^shadow)(UIColor *color, CGSize offset, CGFloat blurRadius);
36 | @property (nonatomic, readonly)SFImageFlow* (^blur)(SFBlurEffect effect);;
37 | @property (nonatomic, readonly)SFImageFlow* (^centerRect)(CGFloat aspectRatio);
38 | @property (nonatomic, readonly)SFImageFlow* (^edgeInsets)(UIEdgeInsets insets, UIColor *fillColor);
39 | @property (nonatomic, readonly)SFImageFlow* (^resize)(CGSize size);
40 | @property (nonatomic, readonly)SFImageFlow* (^resizeWithMax)(CGFloat max);
41 | @end
42 |
43 |
44 | @interface SFImageFlow (SwiftProcessor)
45 | - (SFImageFlow *)cornerWithRadius:(CGFloat)radius rectCorner: (UIRectCorner) rectCorner;
46 | - (SFImageFlow *)borderWithWidth:(CGFloat)width color:(UIColor *)color;
47 | - (SFImageFlow *)borderWithPosition:(UIBorderPostion)position width:(CGFloat)width color:(UIColor *)color;
48 | - (SFImageFlow *)shadowWithColor:(UIColor *)color offset:(CGSize)offset blurRadius:(CGFloat) blurRadius;
49 | - (SFImageFlow *)blurWithEffect:(SFBlurEffect)effect;
50 | - (SFImageFlow *)centerRectWithAspectRatio:(CGFloat)aspectRatio;
51 | - (SFImageFlow *)edgeInsetsWithInsets:(UIEdgeInsets)insets fillColor:(UIColor *)fillColor;
52 | - (SFImageFlow *)resizeWithSize:(CGSize)size;
53 | - (SFImageFlow *)resizeWithMax:(CGFloat)max;
54 | @property (nonatomic, readonly)SFImageFlow* circle;
55 | @property (nonatomic, readonly)SFImageFlow* centerSquare;
56 | @end
57 | NS_ASSUME_NONNULL_END
58 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFImageFlow.m:
--------------------------------------------------------------------------------
1 | //
2 | // SFImageManager.m
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/25.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import "SFImageFlow.h"
10 | static id visitProcessor(id processor,
11 | id result,
12 | id (^handler)(id, id)) {
13 | if (handler) {
14 | result = handler(result, processor);
15 | }
16 | id cur = processor;
17 | for (id subProcessor in cur.dependencies) {
18 | result = visitProcessor(subProcessor, result, handler);
19 | }
20 | return result;
21 | }
22 |
23 | static id visitProcessors(NSArray *processors,
24 | id result,
25 | id (^handler)(id result, id curProcessor)) {
26 | for (id processor in processors) {
27 | result = visitProcessor(processor, result, handler);
28 | }
29 | return result;
30 | }
31 |
32 | NSString *sf_identifierWithProcessors(NSArray *processors) {
33 | return visitProcessors(processors, [@"" mutableCopy], ^id(id result, id curProcessor) {
34 | return [NSString stringWithFormat:@"%@%@", result, curProcessor.identifier];
35 | });
36 | }
37 |
38 | @implementation SFImageFlow
39 | + (instancetype)flow{
40 | return [SFImageFlow new];
41 | }
42 | + (instancetype)flowWithImage:(UIImage *)targetImage{
43 | SFImageFlow *proc = [SFImageFlow new];
44 | proc.targetImage = targetImage;
45 | return proc;
46 | }
47 | + (instancetype)flowWithGenerator:(id )generator{
48 | SFImageFlow *proc = [SFImageFlow new];
49 | proc.generator = generator;
50 | return proc;
51 | }
52 | - (instancetype)init
53 | {
54 | self = [super init];
55 | self.processors = [NSMutableArray array];
56 | self.finals = [NSMutableArray array];
57 | return self;
58 | }
59 | - (void)appendProcessor:(id)processor{
60 | [self.processors addObject:processor];
61 | }
62 | - (UIImage *)image{
63 | UIImage *result = nil;
64 | if (self.generator) {
65 | result = [self startWithGenerator:self.generator processors:self.processors];
66 | }else{
67 | result = [self startWithImage:self.targetImage processors:self.processors];
68 | }
69 | result = [self startWithImage:result processors:self.finals];
70 | return result;
71 | }
72 |
73 | - (NSString *)identifier {
74 | NSString *result = nil;
75 | if (self.generator) {
76 | NSMutableArray *procssors = [@[ self.generator ] mutableCopy];
77 | [procssors addObjectsFromArray:self.processors];
78 | result = sf_identifierWithProcessors(procssors);
79 | } else {
80 | result = sf_identifierWithProcessors(self.processors);
81 | }
82 | return [result stringByAppendingString:sf_identifierWithProcessors(self.finals)];
83 | }
84 |
85 | - (UIImage *)startWithGenerator:(id)generator processors:(NSArray> *)processors{
86 | UIImage *image = [generator generate];
87 | image = [self startWithImage:image processors:(id)generator.dependencies];
88 | return [self startWithImage:image processors:processors];
89 | }
90 |
91 | - (UIImage *)startWithImage:(UIImage *)image processors:(NSArray> *)processors {
92 | return visitProcessors(processors, image, ^id(id result, id curProcessor) {
93 | return [curProcessor process:result];
94 | });
95 | }
96 |
97 | - (void)saveContext {
98 | NSMutableArray *processors = [@[ self.generator ] mutableCopy];
99 | [processors addObjectsFromArray:self.processors];
100 | visitProcessors(processors, nil, ^id(id result, id curProcessor) {
101 | if ([curProcessor respondsToSelector:@selector(saveContext)]) {
102 | [curProcessor saveContext];
103 | }
104 | return nil;
105 | });
106 | }
107 | @end
108 |
109 | #import "SFImageMaker.h"
110 |
111 | @implementation SFImageFlow (Processor)
112 | - (SFImageFlow * _Nonnull (^)(CGFloat, UIRectCorner))corner{
113 | return ^SFImageFlow* (CGFloat radius, UIRectCorner rectCorner){
114 | return [self cornerWithRadius:radius rectCorner:rectCorner];
115 | };
116 | }
117 | - (SFImageFlow * _Nonnull (^)(CGFloat, UIColor * _Nonnull))border{
118 | return ^SFImageFlow* (CGFloat width, UIColor *color){
119 | return [self borderWithWidth:width color:color];
120 | };
121 | }
122 | - (SFImageFlow * _Nonnull (^)(UIBorderPostion, CGFloat, UIColor * _Nonnull))borderWithPosition{
123 | return ^SFImageFlow* (UIBorderPostion position, CGFloat width, UIColor *color){
124 | return [self borderWithPosition:position width:width color:color];
125 | };
126 | }
127 |
128 | - (SFImageFlow * _Nonnull (^)(UIColor * _Nonnull, CGSize, CGFloat))shadow{
129 | return ^SFImageFlow* (UIColor *color, CGSize offset, CGFloat blurRadius){
130 | return [self shadowWithColor:color offset:offset blurRadius:blurRadius];
131 | };
132 | }
133 | - (SFImageFlow * _Nonnull (^)(SFBlurEffect))blur{
134 | return ^SFImageFlow* (SFBlurEffect effect){
135 | return [self blurWithEffect:effect];
136 | };
137 | }
138 | - (SFImageFlow * _Nonnull (^)(CGFloat))centerRect{
139 | return ^SFImageFlow* (CGFloat aspectRatio){
140 | return [self centerRectWithAspectRatio:aspectRatio];
141 | };
142 | }
143 | - (SFImageFlow * _Nonnull (^)(UIEdgeInsets, UIColor * _Nonnull))edgeInsets{
144 | return ^SFImageFlow* (UIEdgeInsets insets, UIColor *fillColor){
145 | return [self edgeInsetsWithInsets:insets fillColor:fillColor];
146 | };
147 | }
148 | - (SFImageFlow * _Nonnull (^)(CGSize))resize{
149 | return ^SFImageFlow* (CGSize size){
150 | return [self resizeWithSize:size];
151 | };
152 | }
153 | - (SFImageFlow * _Nonnull (^)(CGFloat))resizeWithMax{
154 | return ^SFImageFlow* (CGFloat max){
155 | return [self resizeWithMax:max];
156 | };
157 | }
158 | @end
159 |
160 |
161 |
162 | @implementation SFImageFlow (SwiftProcessor)
163 | - (SFImageFlow *)cornerWithRadius:(CGFloat)radius rectCorner: (UIRectCorner) rectCorner{
164 | SFCornerImageMaker *maker = [SFCornerImageMaker new];
165 | maker.radius = radius;
166 | maker.position = rectCorner;
167 | if ([self.generator conformsToProtocol:@protocol(SFImageGenerator)]) {
168 | SFColorImageMaker *generator = (SFColorImageMaker *)self.generator;
169 | CGSize size = generator.size;
170 | if (size.height < radius * 2) size.height = radius * 2;
171 | if (size.width < radius * 2) size.width = radius * 2;
172 | if (!CGSizeEqualToSize(generator.size, size)) {
173 | generator.size = size;
174 | [self.finals addObject:[SFBlockImageMaker resizableCenterMode]];
175 | }
176 | }
177 | [self appendProcessor:maker];
178 | return self;
179 | }
180 | - (SFImageFlow *)borderWithWidth:(CGFloat)width color:(UIColor *)color{
181 | return [self borderWithPosition:UIBorderPostionAll width:width color:color];
182 | }
183 | - (SFImageFlow *)borderWithPosition:(UIBorderPostion)position width:(CGFloat)width color:(UIColor *)color{
184 | SFBorderImageMaker *maker = [SFBorderImageMaker new];
185 | maker.width = width;
186 | maker.position = position;
187 | maker.color = color;
188 | for (id processor in self.processors) {
189 | if ([processor isKindOfClass:[SFCornerImageMaker class]]) {
190 | maker.cornerMaker = processor;
191 | break;
192 | }
193 | }
194 | [self appendProcessor:maker];
195 | return self;
196 | }
197 | - (SFImageFlow *)shadowWithColor:(UIColor *)color offset:(CGSize)offset blurRadius:(CGFloat) blurRadius{
198 | SFShadowImageMaker *maker = [SFShadowImageMaker new];
199 | maker.shadowColor = color;
200 | maker.shadowOffset = offset;
201 | maker.shadowBlurRadius = blurRadius;
202 | maker.position = UIShadowPostionAll;
203 | [self appendProcessor:maker];
204 | return self;
205 | }
206 | - (SFImageFlow *)blurWithEffect:(SFBlurEffect)effect{
207 | switch (effect) {
208 | case SFBlurEffectLight:
209 | [self appendProcessor:[SFBlurImageMaker lightEffect]];
210 | break;
211 | case SFBlurEffectDark:
212 | [self appendProcessor:[SFBlurImageMaker darkEffect]];
213 | break;
214 | case SFBlurEffectExtraLight:
215 | [self appendProcessor:[SFBlurImageMaker extraLightEffect]];
216 | break;
217 | }
218 | return self;
219 | }
220 | - (SFImageFlow *)centerRectWithAspectRatio:(CGFloat)aspectRatio{
221 | [self appendProcessor:[SFBlockImageMaker centerRectWithAspectRatio:aspectRatio]];
222 | return self;
223 | }
224 | - (SFImageFlow *)edgeInsetsWithInsets:(UIEdgeInsets)insets fillColor:(UIColor *)fillColor{
225 | [self appendProcessor:[SFBlockImageMaker edgeInsets:insets fillColor:fillColor]];
226 | return self;
227 | }
228 | - (SFImageFlow *)resizeWithSize:(CGSize)size{
229 | [self appendProcessor:[SFBlockImageMaker resizeWithSize:size]];
230 | return self;
231 | }
232 | - (SFImageFlow *)resizeWithMax:(CGFloat)max{
233 | [self appendProcessor:[SFBlockImageMaker resizeWithMaxValue:max]];
234 | return self;
235 | }
236 | - (SFImageFlow *)circle{
237 | [self appendProcessor:[SFCircleImageMaker new]];
238 | return self;
239 | }
240 | - (SFImageFlow *)centerSquare{
241 | [self appendProcessor:[SFBlockImageMaker centerSquare]];
242 | return self;
243 | }
244 | @end
245 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFImageMaker-Protocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFImageMaker-Protocol.h
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | NS_ASSUME_NONNULL_BEGIN
12 | @protocol SFImageIdentifier
13 | @property (nonatomic, readonly, getter=isEnable)BOOL enable;
14 | - (NSString *)identifier;
15 | @end
16 |
17 | @protocol SFImageProcessor;
18 | @protocol SFImageDependencies
19 | @property(nonatomic, strong)NSMutableArray > *dependencies;
20 | @end
21 |
22 | @protocol SFImageSaveContext
23 | @optional
24 | - (void)saveContext;
25 | @end
26 |
27 | @protocol SFImageProcessor
28 | @required
29 | - (UIImage *)process:(nullable UIImage *)target;
30 | @end
31 |
32 | @protocol SFImageGenerator
33 | @required
34 | @property (nonatomic,assign)CGSize size;
35 | - (instancetype)initWithSize:(CGSize)size;
36 | - (UIImage *)generate;
37 | @end
38 |
39 |
40 |
41 | NS_ASSUME_NONNULL_END
42 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFImageMaker.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFImageMaker.h
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for SFImageMaker.
12 | FOUNDATION_EXPORT double SFImageMakerVersionNumber;
13 |
14 | //! Project version string for SFImageMaker.
15 | FOUNDATION_EXPORT const unsigned char SFImageMakerVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 | #import
19 | #import
20 | #import
21 | #import
22 | #import
23 | #import
24 | #import
25 | #import
26 | #import
27 | #import
28 | #import
29 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFShadowImageMaker.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFShadowImageMaker.h
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | typedef NS_OPTIONS(NSUInteger, UIShadowPostion) {
14 | UIShadowPostionTop = 1 << 0,
15 | UIShadowPostionRight = 1 << 1,
16 | UIShadowPostionBottom = 1 << 2,
17 | UIShadowPostionLeft = 1 << 3,
18 | UIShadowPostionAll = ~0UL
19 | };
20 |
21 | @interface SFShadowImageMaker : NSObject
22 | @property (nonatomic, assign)CGSize shadowOffset;
23 | @property (nonatomic, assign)CGFloat shadowBlurRadius;
24 | @property (nonatomic, strong)UIColor *shadowColor;
25 | @property (nonatomic, strong)UIColor *fillColor;
26 | @property (nonatomic, assign)UIShadowPostion position;
27 | @property (nonatomic, getter=isEnable)BOOL enable;
28 | @property(nonatomic, strong)NSMutableArray > *dependencies;
29 |
30 | - (CGRect)viewRectForSize:(CGSize)size;
31 | - (UIEdgeInsets)convasEdgeInsets;
32 | @end
33 |
34 | NS_ASSUME_NONNULL_END
35 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/SFShadowImageMaker.m:
--------------------------------------------------------------------------------
1 | //
2 | // SFShadowImageMaker.m
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import "SFShadowImageMaker.h"
10 |
11 | @implementation SFShadowImageMaker
12 | - (instancetype)init
13 | {
14 | self = [super init];
15 | self.fillColor = [UIColor clearColor];
16 | self.dependencies = [NSMutableArray array];
17 | return self;
18 | }
19 | - (BOOL)isEnable{
20 | return self.shadowColor != nil && self.shadowColor != [UIColor clearColor] && self.shadowBlurRadius != 0;
21 | }
22 | - (CGRect)viewRectForSize:(CGSize)size{
23 | UIEdgeInsets insets = [self convasEdgeInsets];
24 | CGSize convasSize = [self convasSizeWithSize:size];
25 | return CGRectMake(-insets.left, -insets.top, convasSize.width, convasSize.height);
26 | }
27 | - (CGSize)convasSizeWithSize:(CGSize)size{
28 | UIEdgeInsets insets = [self convasEdgeInsets];
29 | return CGSizeMake(size.width + insets.left + insets.right, size.height + insets.top + insets.bottom);
30 | }
31 | - (UIEdgeInsets)convasEdgeInsets{
32 | CGFloat left=0, right = 0, top = 0, bottom = 0;
33 | if (self.position&UIShadowPostionRight&&self.shadowOffset.width>0) {
34 | right += fabs(self.shadowOffset.width);
35 | }else if (self.position&UIShadowPostionLeft&&self.shadowOffset.width<0) {
36 | left += fabs(self.shadowOffset.width);
37 | }
38 | if (self.position&UIShadowPostionBottom&&self.shadowOffset.height>0) {
39 | bottom += fabs(self.shadowOffset.height);
40 | }else if (self.position&UIShadowPostionTop&&self.shadowOffset.height<0) {
41 | top += fabs(self.shadowOffset.height);
42 | }
43 | top += self.position&UIShadowPostionTop?self.shadowBlurRadius:0;
44 | bottom += self.position&UIShadowPostionBottom?self.shadowBlurRadius:0;
45 | left += self.position&UIShadowPostionLeft?self.shadowBlurRadius:0;
46 | right += self.position&UIShadowPostionRight?self.shadowBlurRadius:0;
47 | return UIEdgeInsetsMake(top, left, bottom, right);
48 | }
49 | - (UIImage *)process:(UIImage *)target{
50 | if (!self.isEnable) return target;
51 | UIEdgeInsets insets = [self convasEdgeInsets];
52 | CGSize canvasSize = [self convasSizeWithSize:target.size];
53 | CGRect canvasRect = CGRectMake(0, 0, canvasSize.width, canvasSize.height);
54 | UIGraphicsBeginImageContextWithOptions(canvasRect.size, NO, 0);
55 | CGContextRef context = UIGraphicsGetCurrentContext();
56 | [self.fillColor setFill];
57 | CGContextFillRect(context,canvasRect);
58 | CGContextSetShadowWithColor(context, self.shadowOffset, self.shadowBlurRadius, self.shadowColor.CGColor);
59 | [target drawInRect:CGRectMake(insets.left, insets.top, target.size.width, target.size.height)];
60 | //获取图像
61 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
62 | UIGraphicsEndImageContext();
63 | return image;
64 | }
65 | - (nonnull NSString *)identifier {
66 | return self.isEnable ? [NSString stringWithFormat:@"shadow_%@_%@_%@_%@",[NSValue valueWithCGSize:self.shadowOffset],@(self.shadowBlurRadius),self.shadowColor.sf_identifier,@(self.position)] : @"";
67 | }
68 | @end
69 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/UIColor+SFIdentifier.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+SFIdentifier.h
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2021/1/20.
6 | // Copyright © 2021 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface UIColor (SFIdentifier)
14 | @property (nonatomic, readonly)NSString *sf_identifier;
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/UIColor+SFIdentifier.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+SFIdentifier.m
3 | // SFImageMaker
4 | //
5 | // Created by Jiang on 2021/1/20.
6 | // Copyright © 2021 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import "UIColor+SFIdentifier.h"
10 |
11 | @implementation UIColor (SFIdentifier)
12 | - (NSString *)sf_identifier{
13 | CGFloat red, green, blue, alpha;
14 | [self getRed:&red green:&green blue:&blue alpha:&alpha];
15 | return [NSString stringWithFormat:@"color_%.2f%.2f%.2f%.2f",red,green,blue,alpha];
16 | }
17 | @end
18 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/UIImageEffects/UIImageEffects.h:
--------------------------------------------------------------------------------
1 | /*
2 | File: UIImageEffects.h
3 | Abstract: This class contains methods to apply blur and tint effects to an image.
4 | This is the code you’ll want to look out to find out how to use vImage to
5 | efficiently calculate a blur.
6 | Version: 1.1
7 |
8 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
9 | Inc. ("Apple") in consideration of your agreement to the following
10 | terms, and your use, installation, modification or redistribution of
11 | this Apple software constitutes acceptance of these terms. If you do
12 | not agree with these terms, please do not use, install, modify or
13 | redistribute this Apple software.
14 |
15 | In consideration of your agreement to abide by the following terms, and
16 | subject to these terms, Apple grants you a personal, non-exclusive
17 | license, under Apple's copyrights in this original Apple software (the
18 | "Apple Software"), to use, reproduce, modify and redistribute the Apple
19 | Software, with or without modifications, in source and/or binary forms;
20 | provided that if you redistribute the Apple Software in its entirety and
21 | without modifications, you must retain this notice and the following
22 | text and disclaimers in all such redistributions of the Apple Software.
23 | Neither the name, trademarks, service marks or logos of Apple Inc. may
24 | be used to endorse or promote products derived from the Apple Software
25 | without specific prior written permission from Apple. Except as
26 | expressly stated in this notice, no other rights or licenses, express or
27 | implied, are granted by Apple herein, including but not limited to any
28 | patent rights that may be infringed by your derivative works or by other
29 | works in which the Apple Software may be incorporated.
30 |
31 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE
32 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
33 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
34 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
35 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
36 |
37 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
38 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
39 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
40 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
41 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
42 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
43 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
44 | POSSIBILITY OF SUCH DAMAGE.
45 |
46 | Copyright (C) 2014 Apple Inc. All Rights Reserved.
47 |
48 | */
49 |
50 | #import
51 |
52 | @interface UIImageEffects : NSObject
53 |
54 | + (UIImage*)imageByApplyingLightEffectToImage:(UIImage*)inputImage;
55 | + (UIImage*)imageByApplyingExtraLightEffectToImage:(UIImage*)inputImage;
56 | + (UIImage*)imageByApplyingDarkEffectToImage:(UIImage*)inputImage;
57 | + (UIImage*)imageByApplyingTintEffectWithColor:(UIColor *)tintColor toImage:(UIImage*)inputImage;
58 |
59 | //| ----------------------------------------------------------------------------
60 | //! Applies a blur, tint color, and saturation adjustment to @a inputImage,
61 | //! optionally within the area specified by @a maskImage.
62 | //!
63 | //! @param inputImage
64 | //! The source image. A modified copy of this image will be returned.
65 | //! @param blurRadius
66 | //! The radius of the blur in points.
67 | //! @param tintColor
68 | //! An optional UIColor object that is uniformly blended with the
69 | //! result of the blur and saturation operations. The alpha channel
70 | //! of this color determines how strong the tint is.
71 | //! @param saturationDeltaFactor
72 | //! A value of 1.0 produces no change in the resulting image. Values
73 | //! less than 1.0 will desaturation the resulting image while values
74 | //! greater than 1.0 will have the opposite effect.
75 | //! @param maskImage
76 | //! If specified, @a inputImage is only modified in the area(s) defined
77 | //! by this mask. This must be an image mask or it must meet the
78 | //! requirements of the mask parameter of CGContextClipToMask.
79 | + (UIImage *)imageByApplyingBlurToImage:(UIImage*)inputImage withRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage;
80 |
81 | @end
82 |
83 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/UIImageEffects/UIImageEffects.m:
--------------------------------------------------------------------------------
1 | /*
2 | File: UIImageEffects.m
3 | Abstract: This class contains methods to apply blur and tint effects to an image.
4 | This is the code you’ll want to look out to find out how to use vImage to
5 | efficiently calculate a blur.
6 | Version: 1.1
7 |
8 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
9 | Inc. ("Apple") in consideration of your agreement to the following
10 | terms, and your use, installation, modification or redistribution of
11 | this Apple software constitutes acceptance of these terms. If you do
12 | not agree with these terms, please do not use, install, modify or
13 | redistribute this Apple software.
14 |
15 | In consideration of your agreement to abide by the following terms, and
16 | subject to these terms, Apple grants you a personal, non-exclusive
17 | license, under Apple's copyrights in this original Apple software (the
18 | "Apple Software"), to use, reproduce, modify and redistribute the Apple
19 | Software, with or without modifications, in source and/or binary forms;
20 | provided that if you redistribute the Apple Software in its entirety and
21 | without modifications, you must retain this notice and the following
22 | text and disclaimers in all such redistributions of the Apple Software.
23 | Neither the name, trademarks, service marks or logos of Apple Inc. may
24 | be used to endorse or promote products derived from the Apple Software
25 | without specific prior written permission from Apple. Except as
26 | expressly stated in this notice, no other rights or licenses, express or
27 | implied, are granted by Apple herein, including but not limited to any
28 | patent rights that may be infringed by your derivative works or by other
29 | works in which the Apple Software may be incorporated.
30 |
31 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE
32 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
33 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
34 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
35 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
36 |
37 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
38 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
39 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
40 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
41 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
42 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
43 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
44 | POSSIBILITY OF SUCH DAMAGE.
45 |
46 | Copyright (C) 2014 Apple Inc. All Rights Reserved.
47 |
48 | */
49 |
50 | #import "UIImageEffects.h"
51 | #import
52 |
53 | @implementation UIImageEffects
54 |
55 | #pragma mark -
56 | #pragma mark - Effects
57 |
58 | //| ----------------------------------------------------------------------------
59 | + (UIImage *)imageByApplyingLightEffectToImage:(UIImage*)inputImage
60 | {
61 | UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:0.3];
62 | return [self imageByApplyingBlurToImage:inputImage withRadius:60 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
63 | }
64 |
65 |
66 | //| ----------------------------------------------------------------------------
67 | + (UIImage *)imageByApplyingExtraLightEffectToImage:(UIImage*)inputImage
68 | {
69 | UIColor *tintColor = [UIColor colorWithWhite:0.97 alpha:0.82];
70 | return [self imageByApplyingBlurToImage:inputImage withRadius:40 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
71 | }
72 |
73 |
74 | //| ----------------------------------------------------------------------------
75 | + (UIImage *)imageByApplyingDarkEffectToImage:(UIImage*)inputImage
76 | {
77 | UIColor *tintColor = [UIColor colorWithWhite:0.11 alpha:0.73];
78 | return [self imageByApplyingBlurToImage:inputImage withRadius:40 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
79 | }
80 |
81 |
82 | //| ----------------------------------------------------------------------------
83 | + (UIImage *)imageByApplyingTintEffectWithColor:(UIColor *)tintColor toImage:(UIImage*)inputImage
84 | {
85 | const CGFloat EffectColorAlpha = 0.6;
86 | UIColor *effectColor = tintColor;
87 | size_t componentCount = CGColorGetNumberOfComponents(tintColor.CGColor);
88 | if (componentCount == 2) {
89 | CGFloat b;
90 | if ([tintColor getWhite:&b alpha:NULL]) {
91 | effectColor = [UIColor colorWithWhite:b alpha:EffectColorAlpha];
92 | }
93 | }
94 | else {
95 | CGFloat r, g, b;
96 | if ([tintColor getRed:&r green:&g blue:&b alpha:NULL]) {
97 | effectColor = [UIColor colorWithRed:r green:g blue:b alpha:EffectColorAlpha];
98 | }
99 | }
100 | return [self imageByApplyingBlurToImage:inputImage withRadius:20 tintColor:effectColor saturationDeltaFactor:-1.0 maskImage:nil];
101 | }
102 |
103 | #pragma mark -
104 | #pragma mark - Implementation
105 |
106 | //| ----------------------------------------------------------------------------
107 | + (UIImage*)imageByApplyingBlurToImage:(UIImage*)inputImage withRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage
108 | {
109 | #define ENABLE_BLUR 1
110 | #define ENABLE_SATURATION_ADJUSTMENT 1
111 | #define ENABLE_TINT 1
112 |
113 | // Check pre-conditions.
114 | if (inputImage.size.width < 1 || inputImage.size.height < 1)
115 | {
116 | NSLog(@"*** error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", inputImage.size.width, inputImage.size.height, inputImage);
117 | return nil;
118 | }
119 | if (!inputImage.CGImage)
120 | {
121 | NSLog(@"*** error: inputImage must be backed by a CGImage: %@", inputImage);
122 | return nil;
123 | }
124 | if (maskImage && !maskImage.CGImage)
125 | {
126 | NSLog(@"*** error: effectMaskImage must be backed by a CGImage: %@", maskImage);
127 | return nil;
128 | }
129 |
130 | BOOL hasBlur = blurRadius > __FLT_EPSILON__;
131 | BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__;
132 |
133 | CGImageRef inputCGImage = inputImage.CGImage;
134 | CGFloat inputImageScale = inputImage.scale;
135 | CGBitmapInfo inputImageBitmapInfo = CGImageGetBitmapInfo(inputCGImage);
136 | CGImageAlphaInfo inputImageAlphaInfo = (inputImageBitmapInfo & kCGBitmapAlphaInfoMask);
137 |
138 | CGSize outputImageSizeInPoints = inputImage.size;
139 | CGRect outputImageRectInPoints = { CGPointZero, outputImageSizeInPoints };
140 |
141 | // Set up output context.
142 | BOOL useOpaqueContext;
143 | if (inputImageAlphaInfo == kCGImageAlphaNone || inputImageAlphaInfo == kCGImageAlphaNoneSkipLast || inputImageAlphaInfo == kCGImageAlphaNoneSkipFirst)
144 | useOpaqueContext = YES;
145 | else
146 | useOpaqueContext = NO;
147 | UIGraphicsBeginImageContextWithOptions(outputImageRectInPoints.size, useOpaqueContext, inputImageScale);
148 | CGContextRef outputContext = UIGraphicsGetCurrentContext();
149 | CGContextScaleCTM(outputContext, 1.0, -1.0);
150 | CGContextTranslateCTM(outputContext, 0, -outputImageRectInPoints.size.height);
151 |
152 | if (hasBlur || hasSaturationChange)
153 | {
154 | vImage_Buffer effectInBuffer;
155 | vImage_Buffer scratchBuffer1;
156 |
157 | vImage_Buffer *inputBuffer;
158 | vImage_Buffer *outputBuffer;
159 |
160 | vImage_CGImageFormat format = {
161 | .bitsPerComponent = 8,
162 | .bitsPerPixel = 32,
163 | .colorSpace = NULL,
164 | // (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little)
165 | // requests a BGRA buffer.
166 | .bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little,
167 | .version = 0,
168 | .decode = NULL,
169 | .renderingIntent = kCGRenderingIntentDefault
170 | };
171 |
172 | vImage_Error e = vImageBuffer_InitWithCGImage(&effectInBuffer, &format, NULL, inputImage.CGImage, kvImagePrintDiagnosticsToConsole);
173 | if (e != kvImageNoError)
174 | {
175 | NSLog(@"*** error: vImageBuffer_InitWithCGImage returned error code %zi for inputImage: %@", e, inputImage);
176 | UIGraphicsEndImageContext();
177 | return nil;
178 | }
179 |
180 | vImageBuffer_Init(&scratchBuffer1, effectInBuffer.height, effectInBuffer.width, format.bitsPerPixel, kvImageNoFlags);
181 | inputBuffer = &effectInBuffer;
182 | outputBuffer = &scratchBuffer1;
183 |
184 | #if ENABLE_BLUR
185 | if (hasBlur)
186 | {
187 | // A description of how to compute the box kernel width from the Gaussian
188 | // radius (aka standard deviation) appears in the SVG spec:
189 | // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
190 | //
191 | // For larger values of 's' (s >= 2.0), an approximation can be used: Three
192 | // successive box-blurs build a piece-wise quadratic convolution kernel, which
193 | // approximates the Gaussian kernel to within roughly 3%.
194 | //
195 | // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
196 | //
197 | // ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
198 | //
199 | CGFloat inputRadius = blurRadius * inputImageScale;
200 | if (inputRadius - 2. < __FLT_EPSILON__)
201 | inputRadius = 2.;
202 | uint32_t radius = floor((inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5) / 2);
203 |
204 | radius |= 1; // force radius to be odd so that the three box-blur methodology works.
205 |
206 | NSInteger tempBufferSize = vImageBoxConvolve_ARGB8888(inputBuffer, outputBuffer, NULL, 0, 0, radius, radius, NULL, kvImageGetTempBufferSize | kvImageEdgeExtend);
207 | void *tempBuffer = malloc(tempBufferSize);
208 |
209 | vImageBoxConvolve_ARGB8888(inputBuffer, outputBuffer, tempBuffer, 0, 0, radius, radius, NULL, kvImageEdgeExtend);
210 | vImageBoxConvolve_ARGB8888(outputBuffer, inputBuffer, tempBuffer, 0, 0, radius, radius, NULL, kvImageEdgeExtend);
211 | vImageBoxConvolve_ARGB8888(inputBuffer, outputBuffer, tempBuffer, 0, 0, radius, radius, NULL, kvImageEdgeExtend);
212 |
213 | free(tempBuffer);
214 |
215 | vImage_Buffer *temp = inputBuffer;
216 | inputBuffer = outputBuffer;
217 | outputBuffer = temp;
218 | }
219 | #endif
220 |
221 | #if ENABLE_SATURATION_ADJUSTMENT
222 | if (hasSaturationChange)
223 | {
224 | CGFloat s = saturationDeltaFactor;
225 | // These values appear in the W3C Filter Effects spec:
226 | // https://dvcs.w3.org/hg/FXTF/raw-file/default/filters/index.html#grayscaleEquivalent
227 | //
228 | CGFloat floatingPointSaturationMatrix[] = {
229 | 0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0,
230 | 0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0,
231 | 0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0,
232 | 0, 0, 0, 1,
233 | };
234 | const int32_t divisor = 256;
235 | NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix)/sizeof(floatingPointSaturationMatrix[0]);
236 | int16_t saturationMatrix[matrixSize];
237 | for (NSUInteger i = 0; i < matrixSize; ++i) {
238 | saturationMatrix[i] = (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor);
239 | }
240 | vImageMatrixMultiply_ARGB8888(inputBuffer, outputBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
241 |
242 | vImage_Buffer *temp = inputBuffer;
243 | inputBuffer = outputBuffer;
244 | outputBuffer = temp;
245 | }
246 | #endif
247 |
248 | CGImageRef effectCGImage;
249 | if ( (effectCGImage = vImageCreateCGImageFromBuffer(inputBuffer, &format, &cleanupBuffer, NULL, kvImageNoAllocate, NULL)) == NULL ) {
250 | effectCGImage = vImageCreateCGImageFromBuffer(inputBuffer, &format, NULL, NULL, kvImageNoFlags, NULL);
251 | free(inputBuffer->data);
252 | }
253 | if (maskImage) {
254 | // Only need to draw the base image if the effect image will be masked.
255 | CGContextDrawImage(outputContext, outputImageRectInPoints, inputCGImage);
256 | }
257 |
258 | // draw effect image
259 | CGContextSaveGState(outputContext);
260 | if (maskImage)
261 | CGContextClipToMask(outputContext, outputImageRectInPoints, maskImage.CGImage);
262 | CGContextDrawImage(outputContext, outputImageRectInPoints, effectCGImage);
263 | CGContextRestoreGState(outputContext);
264 |
265 | // Cleanup
266 | CGImageRelease(effectCGImage);
267 | free(outputBuffer->data);
268 | }
269 | else
270 | {
271 | // draw base image
272 | CGContextDrawImage(outputContext, outputImageRectInPoints, inputCGImage);
273 | }
274 |
275 | #if ENABLE_TINT
276 | // Add in color tint.
277 | if (tintColor)
278 | {
279 | CGContextSaveGState(outputContext);
280 | CGContextSetFillColorWithColor(outputContext, tintColor.CGColor);
281 | CGContextFillRect(outputContext, outputImageRectInPoints);
282 | CGContextRestoreGState(outputContext);
283 | }
284 | #endif
285 |
286 | // Output image is ready.
287 | UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
288 | UIGraphicsEndImageContext();
289 |
290 | return outputImage;
291 | #undef ENABLE_BLUR
292 | #undef ENABLE_SATURATION_ADJUSTMENT
293 | #undef ENABLE_TINT
294 | }
295 |
296 |
297 | //| ----------------------------------------------------------------------------
298 | // Helper function to handle deferred cleanup of a buffer.
299 | //
300 | void cleanupBuffer(void *userData, void *buf_data)
301 | { free(buf_data); }
302 |
303 | @end
304 |
305 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/UIKit+SFImageFlow.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIKit+SFImageFlow.h
3 | // SFImageMaker
4 | //
5 | // Created by APPLE on 2021/5/21.
6 | // Copyright © 2021 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 | @class SFImageFlow;
13 | @interface UIColor (SFImageFlow)
14 | @property (nonatomic, readonly)SFImageFlow *sf_flow;
15 | @property (nonatomic, readonly)SFImageFlow *(^sf_flowWithSize)(CGSize size);
16 | - (SFImageFlow *)sf_flowWithSize:(CGSize)size;
17 | @end
18 |
19 | @class SFImageFlow;
20 | @interface UIImage (SFImageFlow)
21 | @property (nonatomic, readonly)SFImageFlow *sf_flow;
22 | @end
23 |
24 | @class SFImageFlow;
25 | @interface NSArray (SFImageFlow)
26 | @property (nonatomic, readonly)SFImageFlow *(^sf_gradientFlow)(BOOL isHorizontal, CGSize size);
27 | - (SFImageFlow *)sf_gradientFlow:(BOOL)isHorizontal size:(CGSize)size;
28 | @end
29 |
30 | NS_ASSUME_NONNULL_END
31 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMaker/UIKit+SFImageFlow.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIKit+SFImageFlow.m
3 | // SFImageMaker
4 | //
5 | // Created by APPLE on 2021/5/21.
6 | // Copyright © 2021 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import "UIKit+SFImageFlow.h"
10 | #import "SFImageMaker.h"
11 | @implementation UIColor (SFImageFlow)
12 | - (SFImageFlow *)sf_flow{
13 | SFColorImageMaker *maker = [SFColorImageMaker imageMakerWithColor:self];
14 | return [SFImageFlow flowWithGenerator:maker];;
15 | }
16 | - (SFImageFlow * _Nonnull (^)(CGSize))sf_flowWithSize{
17 | return ^SFImageFlow* (CGSize size){
18 | return [self sf_flowWithSize:size];
19 | };
20 | }
21 | - (SFImageFlow *)sf_flowWithSize:(CGSize)size{
22 | SFColorImageMaker *maker = [SFColorImageMaker imageMakerWithColor:self size:size];
23 | return [SFImageFlow flowWithGenerator:maker];;
24 | }
25 | @end
26 |
27 |
28 | @implementation UIImage (SFImageFlow)
29 | - (SFImageFlow *)sf_flow{
30 | return [SFImageFlow flowWithImage:self];;
31 | }
32 | @end
33 |
34 |
35 | @implementation NSArray (SFImageFlow)
36 | - (SFImageFlow * _Nonnull (^)(BOOL, CGSize))sf_gradientFlow{
37 | return ^SFImageFlow* (BOOL isHorizontal, CGSize size){
38 | return [self sf_gradientFlow:isHorizontal size:size];
39 | };
40 | }
41 | - (SFImageFlow *)sf_gradientFlow:(BOOL)isHorizontal size:(CGSize)size{
42 | NSAssert(self.count == 2, @"");
43 | SFGradientImageMaker *maker = [SFGradientImageMaker isHorizontal:isHorizontal startColor:self[0] endColor:self[1]];
44 | maker.size = size;
45 | return [SFImageFlow flowWithGenerator:maker];
46 | }
47 | @end
48 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMakerTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMakerTests/SFBorderImageMakerTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SFBorderImageMakerTests.swift
3 | // SFImageMakerTests
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class SFBorderImageMakerTests: XCTestCase {
12 | let border = SFBorderImageMaker.init()
13 | override func setUp() {
14 | border.width = 20
15 | border.color = UIColor.black
16 | }
17 |
18 | override func tearDown() {
19 | // Put teardown code here. This method is called after the invocation of each test method in the class.
20 | }
21 |
22 | func testAllBorder(){
23 | border.position = [.left,.right,.top,.bottom]
24 | let rect = border.strokeRect(with: CGSize.init(width: 200, height: 200))
25 | XCTAssertTrue(rect == CGRect(x: 10, y: 10, width: 180, height: 180),rect.debugDescription)
26 | }
27 | func testHideLeftBorder(){
28 | border.position = [.right,.top,.bottom]
29 | let rect = border.strokeRect(with: CGSize.init(width: 200, height: 200))
30 | XCTAssertTrue(rect == CGRect(x: -10, y: 10, width: 200, height: 180),rect.debugDescription)
31 | }
32 | func testHideRightBorder(){
33 | border.position = [.left,.top,.bottom]
34 | let rect = border.strokeRect(with: CGSize.init(width: 200, height: 200))
35 | XCTAssertTrue(rect == CGRect(x: 10, y: 10, width: 200, height: 180),rect.debugDescription)
36 | }
37 |
38 | func testHideTopBorder(){
39 | border.position = [.left,.right,.bottom]
40 | let rect = border.strokeRect(with: CGSize.init(width: 200, height: 200))
41 | XCTAssertTrue(rect == CGRect(x: 10, y: -10, width: 180, height: 200),rect.debugDescription)
42 | }
43 | func testHideBottomBorder(){
44 | border.position = [.left,.right,.top]
45 | let rect = border.strokeRect(with: CGSize.init(width: 200, height: 200))
46 | XCTAssertTrue(rect == CGRect(x: 10, y: 10, width: 180, height: 200),rect.debugDescription)
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMakerTests/SFImageMakerTests-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import
6 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMakerTests/SFImageMakerTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SFImageMakerTests.swift
3 | // SFImageMakerTests
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import SFImageMaker
11 |
12 | class SFImageMakerTests: XCTestCase {
13 | let shadow = SFShadowImageMaker.init()
14 | let border = SFBorderImageMaker.init()
15 | let rectCorner = SFCornerImageMaker.init()
16 | override func setUp() {
17 | rectCorner.radius = 20
18 | rectCorner.position = .allCorners
19 | shadow.position = .all
20 | shadow.shadowColor = UIColor.black
21 | shadow.shadowBlurRadius = 20
22 | border.color = UIColor.black
23 | border.width = 5
24 | border.cornerMaker = rectCorner
25 | }
26 | override func tearDown() {
27 |
28 | }
29 | func testDependenciesIdentifier(){
30 | let noDependencies = SFColorImageMaker.init(color: UIColor.white)
31 | let colorMaker = SFColorImageMaker.init(color: UIColor.white)
32 | colorMaker.dependencies.addObjects(from: [rectCorner,border,shadow])
33 | var result = noDependencies.identifier() + rectCorner.identifier() + border.identifier() + shadow.identifier()
34 | XCTAssert(SFImageMakerManager.shared().identifier(with: colorMaker, processors: nil) == result, result)
35 |
36 | colorMaker.dependencies = NSMutableArray.init()
37 | colorMaker.dependencies.add(SFBlurImageMaker.lightEffect())
38 | let processors:[SFImageProcessor] = [rectCorner,border,shadow]
39 | result = noDependencies.identifier() + SFBlurImageMaker.lightEffect().identifier() + processors.reduce("", {$0 + $1.identifier()})
40 | XCTAssert(SFImageMakerManager.shared().identifier(with: colorMaker, processors: processors) == result, result)
41 | }
42 | func testPerformanceExample() {
43 | self.measure {
44 | var cost: Int = 0
45 | for _ in (0...1000){
46 | rectCorner.radius = CGFloat(Int.random(in: 0..<10))
47 | let cornerPositions: [UIRectCorner] = [.topLeft,.topRight,.bottomLeft,.bottomRight]
48 | var cornerResult = [UIRectCorner]()
49 | var cornerCount = Int.random(in: 0..<4)
50 | while cornerCount >= 0 {
51 | cornerResult.append(cornerPositions[Int.random(in: 0..<4)])
52 | cornerCount -= 1
53 | }
54 | rectCorner.position = UIRectCorner.init(cornerResult)
55 |
56 | let shadowPositions: [UIShadowPostion] = [.left,.top,.right,.bottom]
57 | var shadowResult = [UIShadowPostion]()
58 | var shadowResultCount = Int.random(in: 0..<4)
59 | while shadowResultCount >= 0 {
60 | shadowResult.append(shadowPositions[Int.random(in: 0..<4)])
61 | shadowResultCount -= 1
62 | }
63 | shadow.position = UIShadowPostion.init(shadowResult)
64 | shadow.shadowColor = UIColor.init(red: CGFloat(Int.random(in: 0...10)) / 10, green: CGFloat(Int.random(in: 0...10)) / 10, blue: CGFloat(Int.random(in: 0...10)) / 10, alpha: 1)
65 | shadow.shadowBlurRadius = CGFloat(Int.random(in: 8..<20))
66 | border.color = UIColor.init(red: CGFloat(Int.random(in: 0...10)) / 10, green: CGFloat(Int.random(in: 0...10)) / 10, blue: CGFloat(Int.random(in: 0...10)) / 10, alpha: 1)
67 | border.width = CGFloat(Int.random(in: 0..<20))
68 |
69 | let borderPositions: [UIBorderPostion] = [.left,.top,.right,.bottom]
70 | var borderResult = [UIBorderPostion]()
71 | var borderCount = Int.random(in: 0..<4)
72 | while borderCount >= 0 {
73 | borderResult.append(borderPositions[Int.random(in: 0..<4)])
74 | borderCount -= 1
75 | }
76 | border.position = UIBorderPostion.init(borderResult)
77 | border.cornerMaker = rectCorner
78 | var maxValue = rectCorner.radius > (border.width + 1) && rectCorner.isEnable ? rectCorner.radius : border.width + 1
79 | maxValue = shadow.shadowBlurRadius > maxValue ? shadow.shadowBlurRadius : maxValue
80 | let size = CGSize.init(width: maxValue * 2, height: maxValue * 2)
81 | // let size = CGSize.init(width: 400, height: 100)
82 | let image = SFImageMakerManager.shared().start(with: SFColorImageMaker(color: UIColor.white, size: size), processors: [rectCorner,border,shadow])
83 | if let cgimg = image.cgImage{
84 | cost += cgimg.height * cgimg.width * (cgimg.bitsPerPixel / cgimg.bitsPerComponent)
85 | }
86 | }
87 | // width:400 height:100 1000次 耗时 7.5s 缓存大小715MB
88 | // 仅使用圆角大小、阴影、边框的大小计算宽高时 1000次 耗时 1.01s 缓存大小29MB
89 | print("\(cost / (1024 * 1024))MB")
90 | cost = 0
91 | }
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMakerTests/SFShadowImageMakerTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SFShadowImageMakerTests.swift
3 | // SFImageMakerTests
4 | //
5 | // Created by Jiang on 2020/2/21.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class SFShadowImageMakerTests: XCTestCase {
12 | let shadow = SFShadowImageMaker.init()
13 | override func setUp() {
14 | shadow.shadowBlurRadius = 20
15 | shadow.shadowOffset = CGSize.zero
16 | shadow.shadowColor = UIColor.black
17 | // Put setup code here. This method is called before the invocation of each test method in the class.
18 | }
19 |
20 | override func tearDown() {
21 | // Put teardown code here. This method is called after the invocation of each test method in the class.
22 | }
23 |
24 | func testAllShadowWithNegativeOffset(){
25 | shadow.position = .all
26 | let rect = shadow.viewRect(for: CGSize(width: 200, height: 200))
27 | XCTAssertTrue(rect == CGRect(x: -20, y: -20, width: 240, height: 240),rect.debugDescription)
28 | }
29 | func testAllShadowWithPositiveOffset(){
30 | shadow.position = .all
31 | let rect = shadow.viewRect(for: CGSize(width: 200, height: 200))
32 | XCTAssertTrue(rect == CGRect(x: -20, y: -20, width: 240, height: 240),rect.debugDescription)
33 | }
34 | func testHideLeftShadow(){
35 | shadow.position = [.top,.bottom,.right]
36 | let rect = shadow.viewRect(for: CGSize(width: 200, height: 200))
37 | XCTAssertTrue(rect == CGRect(x: 0, y: -20, width: 220, height: 240),rect.debugDescription)
38 | }
39 | func testHideRightShadow(){
40 | shadow.position = [.top,.bottom,.left]
41 | let rect = shadow.viewRect(for: CGSize(width: 200, height: 200))
42 | XCTAssertTrue(rect == CGRect(x: -20, y: -20, width: 220, height: 240),rect.debugDescription)
43 | }
44 |
45 | func testHideVerticalShadow(){
46 | shadow.position = [.left,.right]
47 | let rect = shadow.viewRect(for: CGSize(width: 200, height: 200))
48 | XCTAssertTrue(rect == CGRect(x: -20, y: 0, width: 240, height: 200),rect.debugDescription)
49 | }
50 |
51 | func testHideTopShadow(){
52 | shadow.position = [.left,.right,.bottom]
53 | let rect = shadow.viewRect(for: CGSize(width: 200, height: 200))
54 | XCTAssertTrue(rect == CGRect(x: -20, y: 0, width: 240, height: 220),rect.debugDescription)
55 | }
56 | func testHideBottomShadow(){
57 | shadow.position = [.left,.right,.top]
58 | let rect = shadow.viewRect(for: CGSize(width: 200, height: 200))
59 | XCTAssertTrue(rect == CGRect(x: -20, y: -20, width: 240, height: 220),rect.debugDescription)
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/SFImageMaker/SFImageMakerTests/SFWebImageMakerHelperTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SFWebImageMakerHelperTest.swift
3 | // SFImageMakerTests
4 | //
5 | // Created by Jiang on 2020/3/14.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class WebImageFakeMananger:NSObject,SFWebImageManagerDelegate{
12 | var memCache = [String:UIImage]()
13 | var diskCache = [String:UIImage]()
14 | func download(for url: URL, completed: @escaping SFWebImageCompleteHandler) {
15 | DispatchQueue.global().async{
16 | Thread.sleep(forTimeInterval: 0.5)
17 | completed(UIImage.init(), url, nil)
18 | }
19 | }
20 |
21 | func key(for url: URL, identifier: String?) -> String {
22 | return identifier == nil ? url.absoluteString : url.absoluteString + identifier!
23 | }
24 |
25 | func memeryCache(forKey key: String) -> UIImage? {
26 | return self.memCache[key]
27 | }
28 |
29 | func saveMemeryCache(_ image: UIImage, forKey key: String) {
30 | DispatchQueue.main.async {
31 | self.memCache[key] = image
32 | }
33 | }
34 |
35 | func saveDiskCache(_ image: UIImage, forKey key: String, completed: ((Error?) -> Void)? = nil) {
36 | DispatchQueue.global().async{
37 | Thread.sleep(forTimeInterval: 0.5)
38 | DispatchQueue.main.async {
39 | self.diskCache[key] = image
40 | completed?(nil)
41 | }
42 | }
43 | }
44 | func diskCache(forKey key: String, completed: ((UIImage?, Error?) -> Void)? = nil) {
45 | DispatchQueue.global().async{
46 | Thread.sleep(forTimeInterval: 0.5)
47 | DispatchQueue.main.async {
48 | if let image = self.diskCache[key]{
49 | completed?(image,nil)
50 | }else{
51 | completed?(nil,NSError.init(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey:"no disk image"]))
52 | }
53 | }
54 | }
55 | }
56 | }
57 | class SFWebImageMakerHelperTest: XCTestCase {
58 | let fakeManager = WebImageFakeMananger()
59 | var helper: SFWebImageMakerHelper!
60 | var noIdentifierKey: String!
61 | var identifierKey: String!
62 | override func setUp() {
63 | let url = URL.init(string: "https://www.test.com/image.png")
64 | let shadow = SFShadowImageMaker.init()
65 | shadow.position = .all
66 | shadow.shadowColor = UIColor.black
67 | shadow.shadowBlurRadius = 20
68 | helper = SFWebImageMakerHelper.init(url: url, processors: [SFBlockImageMaker.circle(),shadow])
69 | helper.delegate = fakeManager
70 | noIdentifierKey = fakeManager.key(for: helper.url, identifier: nil)
71 | identifierKey = fakeManager.key(for: helper.url, identifier: helper.identifier)
72 | }
73 |
74 | override func tearDown() {
75 | fakeManager.memCache = [:]
76 | fakeManager.diskCache = [:]
77 | }
78 |
79 | func testCacheOnlyStoreResultImage() {
80 | helper.saveOption = [.resultMemery,.resultDisk]
81 | var exception = self.expectation(description: "")
82 | helper.prcoess { (image, url, error) in
83 | if let image = image{
84 | XCTAssert(image.size != CGSize.zero)
85 | }
86 | exception.fulfill()
87 | }
88 | self.waitForExpectations(timeout: 2, handler: nil)
89 | // wait for disk save
90 | exception = self.expectation(description: "")
91 | DispatchQueue.global().async {
92 | Thread.sleep(forTimeInterval: 1)
93 | exception.fulfill()
94 | }
95 | self.waitForExpectations(timeout: 2, handler: nil)
96 | XCTAssert(fakeManager.memeryCache(forKey: noIdentifierKey) == nil)
97 | XCTAssert(fakeManager.diskCache[noIdentifierKey] == nil)
98 |
99 | XCTAssert(fakeManager.memeryCache(forKey: identifierKey) != nil)
100 | XCTAssert(fakeManager.diskCache[identifierKey] != nil)
101 | }
102 |
103 | func testCacheOnlyStoreOriginalImage() {
104 | helper.saveOption = [.originalMemery,.originalDisk]
105 | var exception = self.expectation(description: "")
106 | helper.prcoess { (image, url, error) in
107 | if let image = image{
108 | XCTAssert(image.size != CGSize.zero)
109 | }
110 | exception.fulfill()
111 | }
112 | self.waitForExpectations(timeout: 2, handler: nil)
113 | // wait for disk save
114 | exception = self.expectation(description: "")
115 | DispatchQueue.global().async {
116 | Thread.sleep(forTimeInterval: 1)
117 | exception.fulfill()
118 | }
119 | self.waitForExpectations(timeout: 2, handler: nil)
120 | XCTAssert(fakeManager.diskCache[noIdentifierKey] != nil)
121 | XCTAssert(fakeManager.memeryCache(forKey: noIdentifierKey) != nil)
122 | XCTAssert(fakeManager.diskCache[identifierKey] == nil)
123 | XCTAssert(fakeManager.memeryCache(forKey: identifierKey) == nil)
124 | }
125 | func testCacheStoreInAll() {
126 | helper.saveOption = .all
127 | var exception = self.expectation(description: "")
128 | helper.prcoess { (image, url, error) in
129 | if let image = image{
130 | XCTAssert(image.size != CGSize.zero)
131 | }
132 | exception.fulfill()
133 | }
134 | self.waitForExpectations(timeout: 3, handler: nil)
135 | // wait for disk save
136 | exception = self.expectation(description: "")
137 | DispatchQueue.global().async {
138 | Thread.sleep(forTimeInterval: 1)
139 | exception.fulfill()
140 | }
141 | self.waitForExpectations(timeout: 2, handler: nil)
142 | XCTAssert(fakeManager.diskCache[noIdentifierKey] != nil)
143 | XCTAssert(fakeManager.diskCache[identifierKey] != nil)
144 |
145 | XCTAssert(fakeManager.memeryCache(forKey: noIdentifierKey) != nil)
146 | XCTAssert(fakeManager.memeryCache(forKey: identifierKey) != nil)
147 | }
148 | func testWhileOriginalInDiskCache(){
149 | helper.saveOption = .all
150 | self.fakeManager.diskCache[noIdentifierKey] = UIImage.init()
151 | var exception = self.expectation(description: "")
152 | helper.prcoess { (image, url, error) in
153 | if let image = image{
154 | XCTAssert(image.size != CGSize.zero)
155 | }
156 | exception.fulfill()
157 | }
158 | self.waitForExpectations(timeout: 3, handler: nil)
159 | // wait for disk save
160 | exception = self.expectation(description: "")
161 | DispatchQueue.global().async {
162 | Thread.sleep(forTimeInterval: 1)
163 | exception.fulfill()
164 | }
165 | self.waitForExpectations(timeout: 2, handler: nil)
166 | XCTAssert(fakeManager.diskCache[noIdentifierKey] != nil)
167 | XCTAssert(fakeManager.diskCache[identifierKey] != nil)
168 |
169 | XCTAssert(fakeManager.memeryCache(forKey: noIdentifierKey) != nil)
170 | XCTAssert(fakeManager.memeryCache(forKey: identifierKey) != nil)
171 | }
172 |
173 | func testWhileResultInDiskCache(){
174 | helper.saveOption = .all
175 | self.fakeManager.diskCache[identifierKey] = UIImage.init()
176 | var exception = self.expectation(description: "")
177 | helper.prcoess { (image, url, error) in
178 | if let image = image{
179 | XCTAssert(image.size == CGSize.zero)
180 | }
181 | exception.fulfill()
182 | }
183 | self.waitForExpectations(timeout: 3, handler: nil)
184 | // wait for disk save
185 | exception = self.expectation(description: "")
186 | DispatchQueue.global().async {
187 | Thread.sleep(forTimeInterval: 1)
188 | exception.fulfill()
189 | }
190 | self.waitForExpectations(timeout: 2, handler: nil)
191 |
192 | XCTAssert(fakeManager.diskCache[identifierKey] != nil)
193 | XCTAssert(fakeManager.memeryCache(forKey: identifierKey) != nil)
194 | }
195 |
196 | func testWhileOriginalInMemeryCache(){
197 | helper.saveOption = .all
198 | self.fakeManager.memCache[noIdentifierKey] = UIImage.init()
199 | var exception = self.expectation(description: "")
200 | helper.prcoess { (image, url, error) in
201 | if let image = image{
202 | XCTAssert(image.size != CGSize.zero)
203 | }
204 | exception.fulfill()
205 | }
206 | self.waitForExpectations(timeout: 3, handler: nil)
207 | // wait for disk save
208 | exception = self.expectation(description: "")
209 | DispatchQueue.global().async {
210 | Thread.sleep(forTimeInterval: 1)
211 | exception.fulfill()
212 | }
213 | self.waitForExpectations(timeout: 2, handler: nil)
214 | XCTAssert(fakeManager.memeryCache(forKey: identifierKey) != nil)
215 | }
216 |
217 | func testWhileResultInMemeryCache(){
218 | helper.saveOption = .all
219 | self.fakeManager.memCache[identifierKey] = UIImage.init()
220 | var exception = self.expectation(description: "")
221 | helper.prcoess { (image, url, error) in
222 | if let image = image{
223 | XCTAssert(image.size == CGSize.zero)
224 | }
225 | exception.fulfill()
226 | }
227 | self.waitForExpectations(timeout: 3, handler: nil)
228 | // wait for disk save
229 | exception = self.expectation(description: "")
230 | DispatchQueue.global().async {
231 | Thread.sleep(forTimeInterval: 1)
232 | exception.fulfill()
233 | }
234 | self.waitForExpectations(timeout: 2, handler: nil)
235 | XCTAssert(fakeManager.memeryCache(forKey: identifierKey) != nil)
236 | }
237 |
238 | func testPerformanceExample() {
239 | // This is an example of a performance test case.
240 | self.measure {
241 | // Put the code you want to measure the time of here.
242 | }
243 | }
244 |
245 | }
246 |
--------------------------------------------------------------------------------
/Space.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Space.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Space.xcworkspace/xcuserdata/Memerys.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilverFruity/UICornerShadowView/97d23060a8bfc4beb3c8b57d900550f09c8fe91c/Space.xcworkspace/xcuserdata/Memerys.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Space.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilverFruity/UICornerShadowView/97d23060a8bfc4beb3c8b57d900550f09c8fe91c/Space.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Space.xcworkspace/xcuserdata/apple.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Space.xcworkspace/xcuserdata/jiang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Space.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilverFruity/UICornerShadowView/97d23060a8bfc4beb3c8b57d900550f09c8fe91c/Space.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/UICornerShadowView.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 18F13929265CB622006E7730 /* SFImageFlowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18F13927265CB622006E7730 /* SFImageFlowController.m */; };
11 | 18F1392D265CBA82006E7730 /* UIImage+Extentions.m in Sources */ = {isa = PBXBuildFile; fileRef = 18F1392C265CBA82006E7730 /* UIImage+Extentions.m */; };
12 | 1B55706D23F3000A00B20FD5 /* ViewController1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B55706C23F3000900B20FD5 /* ViewController1.swift */; };
13 | 1B65C53923F53B62007540F6 /* ViewController2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B65C53823F53B62007540F6 /* ViewController2.swift */; };
14 | 1B6CC3132403740E00189336 /* SFImageMaker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B6CC3122403740E00189336 /* SFImageMaker.framework */; };
15 | 1B6CC3142403740E00189336 /* SFImageMaker.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 1B6CC3122403740E00189336 /* SFImageMaker.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
16 | 1B9FB04323E73A2400AA7D9B /* UICornerShadowViewUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE731EC123CE3A89002ABFB5 /* UICornerShadowViewUITests.swift */; };
17 | EE731EA023CE3A87002ABFB5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE731E9F23CE3A87002ABFB5 /* AppDelegate.swift */; };
18 | EE731EA223CE3A87002ABFB5 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE731EA123CE3A87002ABFB5 /* SceneDelegate.swift */; };
19 | EE731EA423CE3A87002ABFB5 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE731EA323CE3A87002ABFB5 /* ViewController.swift */; };
20 | EE731EA723CE3A87002ABFB5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EE731EA523CE3A87002ABFB5 /* Main.storyboard */; };
21 | EE731EA923CE3A89002ABFB5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EE731EA823CE3A89002ABFB5 /* Assets.xcassets */; };
22 | EE731EAC23CE3A89002ABFB5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EE731EAA23CE3A89002ABFB5 /* LaunchScreen.storyboard */; };
23 | EE731EB723CE3A89002ABFB5 /* UICornerShadowViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE731EB623CE3A89002ABFB5 /* UICornerShadowViewTests.swift */; };
24 | /* End PBXBuildFile section */
25 |
26 | /* Begin PBXContainerItemProxy section */
27 | EE731EB323CE3A89002ABFB5 /* PBXContainerItemProxy */ = {
28 | isa = PBXContainerItemProxy;
29 | containerPortal = EE731E9423CE3A87002ABFB5 /* Project object */;
30 | proxyType = 1;
31 | remoteGlobalIDString = EE731E9B23CE3A87002ABFB5;
32 | remoteInfo = UICornerShadowView;
33 | };
34 | EE731EBE23CE3A89002ABFB5 /* PBXContainerItemProxy */ = {
35 | isa = PBXContainerItemProxy;
36 | containerPortal = EE731E9423CE3A87002ABFB5 /* Project object */;
37 | proxyType = 1;
38 | remoteGlobalIDString = EE731E9B23CE3A87002ABFB5;
39 | remoteInfo = UICornerShadowView;
40 | };
41 | /* End PBXContainerItemProxy section */
42 |
43 | /* Begin PBXCopyFilesBuildPhase section */
44 | 1B6CC3152403740E00189336 /* Embed Frameworks */ = {
45 | isa = PBXCopyFilesBuildPhase;
46 | buildActionMask = 2147483647;
47 | dstPath = "";
48 | dstSubfolderSpec = 10;
49 | files = (
50 | 1B6CC3142403740E00189336 /* SFImageMaker.framework in Embed Frameworks */,
51 | );
52 | name = "Embed Frameworks";
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | /* End PBXCopyFilesBuildPhase section */
56 |
57 | /* Begin PBXFileReference section */
58 | 18F13926265CB622006E7730 /* SFImageFlowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SFImageFlowController.h; sourceTree = ""; };
59 | 18F13927265CB622006E7730 /* SFImageFlowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFImageFlowController.m; sourceTree = ""; };
60 | 18F1392B265CBA82006E7730 /* UIImage+Extentions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+Extentions.h"; sourceTree = ""; };
61 | 18F1392C265CBA82006E7730 /* UIImage+Extentions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+Extentions.m"; sourceTree = ""; };
62 | 1B2CFBA823FEF28D008DBFC1 /* SFImageProcess.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SFImageProcess.framework; sourceTree = BUILT_PRODUCTS_DIR; };
63 | 1B55706C23F3000900B20FD5 /* ViewController1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController1.swift; sourceTree = ""; };
64 | 1B65C53823F53B62007540F6 /* ViewController2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController2.swift; sourceTree = ""; };
65 | 1B6CC30B240373EB00189336 /* SFImageMaker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SFImageMaker.framework; sourceTree = BUILT_PRODUCTS_DIR; };
66 | 1B6CC3122403740E00189336 /* SFImageMaker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SFImageMaker.framework; sourceTree = BUILT_PRODUCTS_DIR; };
67 | EE731E9C23CE3A87002ABFB5 /* UICornerShadowView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UICornerShadowView.app; sourceTree = BUILT_PRODUCTS_DIR; };
68 | EE731E9F23CE3A87002ABFB5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
69 | EE731EA123CE3A87002ABFB5 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
70 | EE731EA323CE3A87002ABFB5 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
71 | EE731EA623CE3A87002ABFB5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
72 | EE731EA823CE3A89002ABFB5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
73 | EE731EAB23CE3A89002ABFB5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
74 | EE731EAD23CE3A89002ABFB5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
75 | EE731EB223CE3A89002ABFB5 /* UICornerShadowViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UICornerShadowViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
76 | EE731EB623CE3A89002ABFB5 /* UICornerShadowViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UICornerShadowViewTests.swift; sourceTree = ""; };
77 | EE731EB823CE3A89002ABFB5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
78 | EE731EBD23CE3A89002ABFB5 /* UICornerShadowViewUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UICornerShadowViewUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
79 | EE731EC123CE3A89002ABFB5 /* UICornerShadowViewUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UICornerShadowViewUITests.swift; sourceTree = ""; };
80 | EE731EC323CE3A89002ABFB5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
81 | EE731ECF23CE3AA6002ABFB5 /* UICornerShadowView-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UICornerShadowView-Bridging-Header.h"; sourceTree = ""; };
82 | /* End PBXFileReference section */
83 |
84 | /* Begin PBXFrameworksBuildPhase section */
85 | EE731E9923CE3A87002ABFB5 /* Frameworks */ = {
86 | isa = PBXFrameworksBuildPhase;
87 | buildActionMask = 2147483647;
88 | files = (
89 | 1B6CC3132403740E00189336 /* SFImageMaker.framework in Frameworks */,
90 | );
91 | runOnlyForDeploymentPostprocessing = 0;
92 | };
93 | EE731EAF23CE3A89002ABFB5 /* Frameworks */ = {
94 | isa = PBXFrameworksBuildPhase;
95 | buildActionMask = 2147483647;
96 | files = (
97 | );
98 | runOnlyForDeploymentPostprocessing = 0;
99 | };
100 | EE731EBA23CE3A89002ABFB5 /* Frameworks */ = {
101 | isa = PBXFrameworksBuildPhase;
102 | buildActionMask = 2147483647;
103 | files = (
104 | );
105 | runOnlyForDeploymentPostprocessing = 0;
106 | };
107 | /* End PBXFrameworksBuildPhase section */
108 |
109 | /* Begin PBXGroup section */
110 | 1B2CFBA723FEF28D008DBFC1 /* Frameworks */ = {
111 | isa = PBXGroup;
112 | children = (
113 | 1B6CC3122403740E00189336 /* SFImageMaker.framework */,
114 | 1B6CC30B240373EB00189336 /* SFImageMaker.framework */,
115 | 1B2CFBA823FEF28D008DBFC1 /* SFImageProcess.framework */,
116 | );
117 | name = Frameworks;
118 | sourceTree = "";
119 | };
120 | EE731E9323CE3A87002ABFB5 = {
121 | isa = PBXGroup;
122 | children = (
123 | EE731E9E23CE3A87002ABFB5 /* UICornerShadowView */,
124 | EE731EB523CE3A89002ABFB5 /* UICornerShadowViewTests */,
125 | EE731EC023CE3A89002ABFB5 /* UICornerShadowViewUITests */,
126 | EE731E9D23CE3A87002ABFB5 /* Products */,
127 | 1B2CFBA723FEF28D008DBFC1 /* Frameworks */,
128 | );
129 | sourceTree = "";
130 | };
131 | EE731E9D23CE3A87002ABFB5 /* Products */ = {
132 | isa = PBXGroup;
133 | children = (
134 | EE731E9C23CE3A87002ABFB5 /* UICornerShadowView.app */,
135 | EE731EB223CE3A89002ABFB5 /* UICornerShadowViewTests.xctest */,
136 | EE731EBD23CE3A89002ABFB5 /* UICornerShadowViewUITests.xctest */,
137 | );
138 | name = Products;
139 | sourceTree = "";
140 | };
141 | EE731E9E23CE3A87002ABFB5 /* UICornerShadowView */ = {
142 | isa = PBXGroup;
143 | children = (
144 | EE731E9F23CE3A87002ABFB5 /* AppDelegate.swift */,
145 | EE731EA123CE3A87002ABFB5 /* SceneDelegate.swift */,
146 | EE731EA323CE3A87002ABFB5 /* ViewController.swift */,
147 | 1B55706C23F3000900B20FD5 /* ViewController1.swift */,
148 | 1B65C53823F53B62007540F6 /* ViewController2.swift */,
149 | 18F13926265CB622006E7730 /* SFImageFlowController.h */,
150 | 18F13927265CB622006E7730 /* SFImageFlowController.m */,
151 | 18F1392B265CBA82006E7730 /* UIImage+Extentions.h */,
152 | 18F1392C265CBA82006E7730 /* UIImage+Extentions.m */,
153 | EE731EA523CE3A87002ABFB5 /* Main.storyboard */,
154 | EE731EA823CE3A89002ABFB5 /* Assets.xcassets */,
155 | EE731EAA23CE3A89002ABFB5 /* LaunchScreen.storyboard */,
156 | EE731EAD23CE3A89002ABFB5 /* Info.plist */,
157 | EE731ECF23CE3AA6002ABFB5 /* UICornerShadowView-Bridging-Header.h */,
158 | );
159 | path = UICornerShadowView;
160 | sourceTree = "";
161 | };
162 | EE731EB523CE3A89002ABFB5 /* UICornerShadowViewTests */ = {
163 | isa = PBXGroup;
164 | children = (
165 | EE731EB623CE3A89002ABFB5 /* UICornerShadowViewTests.swift */,
166 | EE731EB823CE3A89002ABFB5 /* Info.plist */,
167 | );
168 | path = UICornerShadowViewTests;
169 | sourceTree = "";
170 | };
171 | EE731EC023CE3A89002ABFB5 /* UICornerShadowViewUITests */ = {
172 | isa = PBXGroup;
173 | children = (
174 | EE731EC123CE3A89002ABFB5 /* UICornerShadowViewUITests.swift */,
175 | EE731EC323CE3A89002ABFB5 /* Info.plist */,
176 | );
177 | path = UICornerShadowViewUITests;
178 | sourceTree = "";
179 | };
180 | /* End PBXGroup section */
181 |
182 | /* Begin PBXNativeTarget section */
183 | EE731E9B23CE3A87002ABFB5 /* UICornerShadowView */ = {
184 | isa = PBXNativeTarget;
185 | buildConfigurationList = EE731EC623CE3A89002ABFB5 /* Build configuration list for PBXNativeTarget "UICornerShadowView" */;
186 | buildPhases = (
187 | EE731E9823CE3A87002ABFB5 /* Sources */,
188 | EE731E9923CE3A87002ABFB5 /* Frameworks */,
189 | EE731E9A23CE3A87002ABFB5 /* Resources */,
190 | 1B6CC3152403740E00189336 /* Embed Frameworks */,
191 | );
192 | buildRules = (
193 | );
194 | dependencies = (
195 | );
196 | name = UICornerShadowView;
197 | productName = UICornerShadowView;
198 | productReference = EE731E9C23CE3A87002ABFB5 /* UICornerShadowView.app */;
199 | productType = "com.apple.product-type.application";
200 | };
201 | EE731EB123CE3A89002ABFB5 /* UICornerShadowViewTests */ = {
202 | isa = PBXNativeTarget;
203 | buildConfigurationList = EE731EC923CE3A89002ABFB5 /* Build configuration list for PBXNativeTarget "UICornerShadowViewTests" */;
204 | buildPhases = (
205 | EE731EAE23CE3A89002ABFB5 /* Sources */,
206 | EE731EAF23CE3A89002ABFB5 /* Frameworks */,
207 | EE731EB023CE3A89002ABFB5 /* Resources */,
208 | );
209 | buildRules = (
210 | );
211 | dependencies = (
212 | EE731EB423CE3A89002ABFB5 /* PBXTargetDependency */,
213 | );
214 | name = UICornerShadowViewTests;
215 | productName = UICornerShadowViewTests;
216 | productReference = EE731EB223CE3A89002ABFB5 /* UICornerShadowViewTests.xctest */;
217 | productType = "com.apple.product-type.bundle.unit-test";
218 | };
219 | EE731EBC23CE3A89002ABFB5 /* UICornerShadowViewUITests */ = {
220 | isa = PBXNativeTarget;
221 | buildConfigurationList = EE731ECC23CE3A89002ABFB5 /* Build configuration list for PBXNativeTarget "UICornerShadowViewUITests" */;
222 | buildPhases = (
223 | EE731EB923CE3A89002ABFB5 /* Sources */,
224 | EE731EBA23CE3A89002ABFB5 /* Frameworks */,
225 | EE731EBB23CE3A89002ABFB5 /* Resources */,
226 | );
227 | buildRules = (
228 | );
229 | dependencies = (
230 | EE731EBF23CE3A89002ABFB5 /* PBXTargetDependency */,
231 | );
232 | name = UICornerShadowViewUITests;
233 | productName = UICornerShadowViewUITests;
234 | productReference = EE731EBD23CE3A89002ABFB5 /* UICornerShadowViewUITests.xctest */;
235 | productType = "com.apple.product-type.bundle.ui-testing";
236 | };
237 | /* End PBXNativeTarget section */
238 |
239 | /* Begin PBXProject section */
240 | EE731E9423CE3A87002ABFB5 /* Project object */ = {
241 | isa = PBXProject;
242 | attributes = {
243 | LastSwiftUpdateCheck = 1130;
244 | LastUpgradeCheck = 1130;
245 | ORGANIZATIONNAME = SilverFruity;
246 | TargetAttributes = {
247 | EE731E9B23CE3A87002ABFB5 = {
248 | CreatedOnToolsVersion = 11.3;
249 | LastSwiftMigration = 1130;
250 | };
251 | EE731EB123CE3A89002ABFB5 = {
252 | CreatedOnToolsVersion = 11.3;
253 | TestTargetID = EE731E9B23CE3A87002ABFB5;
254 | };
255 | EE731EBC23CE3A89002ABFB5 = {
256 | CreatedOnToolsVersion = 11.3;
257 | TestTargetID = EE731E9B23CE3A87002ABFB5;
258 | };
259 | };
260 | };
261 | buildConfigurationList = EE731E9723CE3A87002ABFB5 /* Build configuration list for PBXProject "UICornerShadowView" */;
262 | compatibilityVersion = "Xcode 9.3";
263 | developmentRegion = en;
264 | hasScannedForEncodings = 0;
265 | knownRegions = (
266 | en,
267 | Base,
268 | );
269 | mainGroup = EE731E9323CE3A87002ABFB5;
270 | productRefGroup = EE731E9D23CE3A87002ABFB5 /* Products */;
271 | projectDirPath = "";
272 | projectRoot = "";
273 | targets = (
274 | EE731E9B23CE3A87002ABFB5 /* UICornerShadowView */,
275 | EE731EB123CE3A89002ABFB5 /* UICornerShadowViewTests */,
276 | EE731EBC23CE3A89002ABFB5 /* UICornerShadowViewUITests */,
277 | );
278 | };
279 | /* End PBXProject section */
280 |
281 | /* Begin PBXResourcesBuildPhase section */
282 | EE731E9A23CE3A87002ABFB5 /* Resources */ = {
283 | isa = PBXResourcesBuildPhase;
284 | buildActionMask = 2147483647;
285 | files = (
286 | EE731EAC23CE3A89002ABFB5 /* LaunchScreen.storyboard in Resources */,
287 | EE731EA923CE3A89002ABFB5 /* Assets.xcassets in Resources */,
288 | EE731EA723CE3A87002ABFB5 /* Main.storyboard in Resources */,
289 | );
290 | runOnlyForDeploymentPostprocessing = 0;
291 | };
292 | EE731EB023CE3A89002ABFB5 /* Resources */ = {
293 | isa = PBXResourcesBuildPhase;
294 | buildActionMask = 2147483647;
295 | files = (
296 | );
297 | runOnlyForDeploymentPostprocessing = 0;
298 | };
299 | EE731EBB23CE3A89002ABFB5 /* Resources */ = {
300 | isa = PBXResourcesBuildPhase;
301 | buildActionMask = 2147483647;
302 | files = (
303 | );
304 | runOnlyForDeploymentPostprocessing = 0;
305 | };
306 | /* End PBXResourcesBuildPhase section */
307 |
308 | /* Begin PBXSourcesBuildPhase section */
309 | EE731E9823CE3A87002ABFB5 /* Sources */ = {
310 | isa = PBXSourcesBuildPhase;
311 | buildActionMask = 2147483647;
312 | files = (
313 | 18F13929265CB622006E7730 /* SFImageFlowController.m in Sources */,
314 | 1B65C53923F53B62007540F6 /* ViewController2.swift in Sources */,
315 | 1B55706D23F3000A00B20FD5 /* ViewController1.swift in Sources */,
316 | EE731EA423CE3A87002ABFB5 /* ViewController.swift in Sources */,
317 | 18F1392D265CBA82006E7730 /* UIImage+Extentions.m in Sources */,
318 | EE731EA023CE3A87002ABFB5 /* AppDelegate.swift in Sources */,
319 | EE731EA223CE3A87002ABFB5 /* SceneDelegate.swift in Sources */,
320 | );
321 | runOnlyForDeploymentPostprocessing = 0;
322 | };
323 | EE731EAE23CE3A89002ABFB5 /* Sources */ = {
324 | isa = PBXSourcesBuildPhase;
325 | buildActionMask = 2147483647;
326 | files = (
327 | EE731EB723CE3A89002ABFB5 /* UICornerShadowViewTests.swift in Sources */,
328 | );
329 | runOnlyForDeploymentPostprocessing = 0;
330 | };
331 | EE731EB923CE3A89002ABFB5 /* Sources */ = {
332 | isa = PBXSourcesBuildPhase;
333 | buildActionMask = 2147483647;
334 | files = (
335 | 1B9FB04323E73A2400AA7D9B /* UICornerShadowViewUITests.swift in Sources */,
336 | );
337 | runOnlyForDeploymentPostprocessing = 0;
338 | };
339 | /* End PBXSourcesBuildPhase section */
340 |
341 | /* Begin PBXTargetDependency section */
342 | EE731EB423CE3A89002ABFB5 /* PBXTargetDependency */ = {
343 | isa = PBXTargetDependency;
344 | target = EE731E9B23CE3A87002ABFB5 /* UICornerShadowView */;
345 | targetProxy = EE731EB323CE3A89002ABFB5 /* PBXContainerItemProxy */;
346 | };
347 | EE731EBF23CE3A89002ABFB5 /* PBXTargetDependency */ = {
348 | isa = PBXTargetDependency;
349 | target = EE731E9B23CE3A87002ABFB5 /* UICornerShadowView */;
350 | targetProxy = EE731EBE23CE3A89002ABFB5 /* PBXContainerItemProxy */;
351 | };
352 | /* End PBXTargetDependency section */
353 |
354 | /* Begin PBXVariantGroup section */
355 | EE731EA523CE3A87002ABFB5 /* Main.storyboard */ = {
356 | isa = PBXVariantGroup;
357 | children = (
358 | EE731EA623CE3A87002ABFB5 /* Base */,
359 | );
360 | name = Main.storyboard;
361 | sourceTree = "";
362 | };
363 | EE731EAA23CE3A89002ABFB5 /* LaunchScreen.storyboard */ = {
364 | isa = PBXVariantGroup;
365 | children = (
366 | EE731EAB23CE3A89002ABFB5 /* Base */,
367 | );
368 | name = LaunchScreen.storyboard;
369 | sourceTree = "";
370 | };
371 | /* End PBXVariantGroup section */
372 |
373 | /* Begin XCBuildConfiguration section */
374 | EE731EC423CE3A89002ABFB5 /* Debug */ = {
375 | isa = XCBuildConfiguration;
376 | buildSettings = {
377 | ALWAYS_SEARCH_USER_PATHS = NO;
378 | CLANG_ANALYZER_NONNULL = YES;
379 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
380 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
381 | CLANG_CXX_LIBRARY = "libc++";
382 | CLANG_ENABLE_MODULES = YES;
383 | CLANG_ENABLE_OBJC_ARC = YES;
384 | CLANG_ENABLE_OBJC_WEAK = YES;
385 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
386 | CLANG_WARN_BOOL_CONVERSION = YES;
387 | CLANG_WARN_COMMA = YES;
388 | CLANG_WARN_CONSTANT_CONVERSION = YES;
389 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
390 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
391 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
392 | CLANG_WARN_EMPTY_BODY = YES;
393 | CLANG_WARN_ENUM_CONVERSION = YES;
394 | CLANG_WARN_INFINITE_RECURSION = YES;
395 | CLANG_WARN_INT_CONVERSION = YES;
396 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
397 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
398 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
399 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
400 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
401 | CLANG_WARN_STRICT_PROTOTYPES = YES;
402 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
403 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
404 | CLANG_WARN_UNREACHABLE_CODE = YES;
405 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
406 | COPY_PHASE_STRIP = NO;
407 | DEBUG_INFORMATION_FORMAT = dwarf;
408 | ENABLE_STRICT_OBJC_MSGSEND = YES;
409 | ENABLE_TESTABILITY = YES;
410 | GCC_C_LANGUAGE_STANDARD = gnu11;
411 | GCC_DYNAMIC_NO_PIC = NO;
412 | GCC_NO_COMMON_BLOCKS = YES;
413 | GCC_OPTIMIZATION_LEVEL = 0;
414 | GCC_PREPROCESSOR_DEFINITIONS = (
415 | "DEBUG=1",
416 | "$(inherited)",
417 | );
418 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
419 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
420 | GCC_WARN_UNDECLARED_SELECTOR = YES;
421 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
422 | GCC_WARN_UNUSED_FUNCTION = YES;
423 | GCC_WARN_UNUSED_VARIABLE = YES;
424 | IPHONEOS_DEPLOYMENT_TARGET = 13.2;
425 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
426 | MTL_FAST_MATH = YES;
427 | ONLY_ACTIVE_ARCH = YES;
428 | SDKROOT = iphoneos;
429 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
430 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
431 | };
432 | name = Debug;
433 | };
434 | EE731EC523CE3A89002ABFB5 /* Release */ = {
435 | isa = XCBuildConfiguration;
436 | buildSettings = {
437 | ALWAYS_SEARCH_USER_PATHS = NO;
438 | CLANG_ANALYZER_NONNULL = YES;
439 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
440 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
441 | CLANG_CXX_LIBRARY = "libc++";
442 | CLANG_ENABLE_MODULES = YES;
443 | CLANG_ENABLE_OBJC_ARC = YES;
444 | CLANG_ENABLE_OBJC_WEAK = YES;
445 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
446 | CLANG_WARN_BOOL_CONVERSION = YES;
447 | CLANG_WARN_COMMA = YES;
448 | CLANG_WARN_CONSTANT_CONVERSION = YES;
449 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
450 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
451 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
452 | CLANG_WARN_EMPTY_BODY = YES;
453 | CLANG_WARN_ENUM_CONVERSION = YES;
454 | CLANG_WARN_INFINITE_RECURSION = YES;
455 | CLANG_WARN_INT_CONVERSION = YES;
456 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
457 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
458 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
459 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
460 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
461 | CLANG_WARN_STRICT_PROTOTYPES = YES;
462 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
463 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
464 | CLANG_WARN_UNREACHABLE_CODE = YES;
465 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
466 | COPY_PHASE_STRIP = NO;
467 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
468 | ENABLE_NS_ASSERTIONS = NO;
469 | ENABLE_STRICT_OBJC_MSGSEND = YES;
470 | GCC_C_LANGUAGE_STANDARD = gnu11;
471 | GCC_NO_COMMON_BLOCKS = YES;
472 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
473 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
474 | GCC_WARN_UNDECLARED_SELECTOR = YES;
475 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
476 | GCC_WARN_UNUSED_FUNCTION = YES;
477 | GCC_WARN_UNUSED_VARIABLE = YES;
478 | IPHONEOS_DEPLOYMENT_TARGET = 13.2;
479 | MTL_ENABLE_DEBUG_INFO = NO;
480 | MTL_FAST_MATH = YES;
481 | SDKROOT = iphoneos;
482 | SWIFT_COMPILATION_MODE = wholemodule;
483 | SWIFT_OPTIMIZATION_LEVEL = "-O";
484 | VALIDATE_PRODUCT = YES;
485 | };
486 | name = Release;
487 | };
488 | EE731EC723CE3A89002ABFB5 /* Debug */ = {
489 | isa = XCBuildConfiguration;
490 | buildSettings = {
491 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
492 | CLANG_ENABLE_MODULES = YES;
493 | CODE_SIGN_IDENTITY = "Apple Development";
494 | CODE_SIGN_STYLE = Automatic;
495 | DEVELOPMENT_TEAM = SH4YVD7HDL;
496 | INFOPLIST_FILE = UICornerShadowView/Info.plist;
497 | LD_RUNPATH_SEARCH_PATHS = (
498 | "$(inherited)",
499 | "@executable_path/Frameworks",
500 | );
501 | PRODUCT_BUNDLE_IDENTIFIER = silverfruity.UICornerShadowView;
502 | PRODUCT_NAME = "$(TARGET_NAME)";
503 | PROVISIONING_PROFILE_SPECIFIER = "";
504 | SWIFT_OBJC_BRIDGING_HEADER = "UICornerShadowView/UICornerShadowView-Bridging-Header.h";
505 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
506 | SWIFT_VERSION = 5.0;
507 | TARGETED_DEVICE_FAMILY = "1,2";
508 | };
509 | name = Debug;
510 | };
511 | EE731EC823CE3A89002ABFB5 /* Release */ = {
512 | isa = XCBuildConfiguration;
513 | buildSettings = {
514 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
515 | CLANG_ENABLE_MODULES = YES;
516 | CODE_SIGN_IDENTITY = "Apple Development";
517 | CODE_SIGN_STYLE = Automatic;
518 | DEVELOPMENT_TEAM = SH4YVD7HDL;
519 | INFOPLIST_FILE = UICornerShadowView/Info.plist;
520 | LD_RUNPATH_SEARCH_PATHS = (
521 | "$(inherited)",
522 | "@executable_path/Frameworks",
523 | );
524 | PRODUCT_BUNDLE_IDENTIFIER = silverfruity.UICornerShadowView;
525 | PRODUCT_NAME = "$(TARGET_NAME)";
526 | PROVISIONING_PROFILE_SPECIFIER = "";
527 | SWIFT_OBJC_BRIDGING_HEADER = "UICornerShadowView/UICornerShadowView-Bridging-Header.h";
528 | SWIFT_VERSION = 5.0;
529 | TARGETED_DEVICE_FAMILY = "1,2";
530 | };
531 | name = Release;
532 | };
533 | EE731ECA23CE3A89002ABFB5 /* Debug */ = {
534 | isa = XCBuildConfiguration;
535 | buildSettings = {
536 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
537 | BUNDLE_LOADER = "$(TEST_HOST)";
538 | CODE_SIGN_STYLE = Automatic;
539 | DEVELOPMENT_TEAM = SH4YVD7HDL;
540 | INFOPLIST_FILE = UICornerShadowViewTests/Info.plist;
541 | IPHONEOS_DEPLOYMENT_TARGET = 13.2;
542 | LD_RUNPATH_SEARCH_PATHS = (
543 | "$(inherited)",
544 | "@executable_path/Frameworks",
545 | "@loader_path/Frameworks",
546 | );
547 | PRODUCT_BUNDLE_IDENTIFIER = silverfruity.UICornerShadowViewTests;
548 | PRODUCT_NAME = "$(TARGET_NAME)";
549 | SWIFT_VERSION = 5.0;
550 | TARGETED_DEVICE_FAMILY = "1,2";
551 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/UICornerShadowView.app/UICornerShadowView";
552 | };
553 | name = Debug;
554 | };
555 | EE731ECB23CE3A89002ABFB5 /* Release */ = {
556 | isa = XCBuildConfiguration;
557 | buildSettings = {
558 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
559 | BUNDLE_LOADER = "$(TEST_HOST)";
560 | CODE_SIGN_STYLE = Automatic;
561 | DEVELOPMENT_TEAM = SH4YVD7HDL;
562 | INFOPLIST_FILE = UICornerShadowViewTests/Info.plist;
563 | IPHONEOS_DEPLOYMENT_TARGET = 13.2;
564 | LD_RUNPATH_SEARCH_PATHS = (
565 | "$(inherited)",
566 | "@executable_path/Frameworks",
567 | "@loader_path/Frameworks",
568 | );
569 | PRODUCT_BUNDLE_IDENTIFIER = silverfruity.UICornerShadowViewTests;
570 | PRODUCT_NAME = "$(TARGET_NAME)";
571 | SWIFT_VERSION = 5.0;
572 | TARGETED_DEVICE_FAMILY = "1,2";
573 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/UICornerShadowView.app/UICornerShadowView";
574 | };
575 | name = Release;
576 | };
577 | EE731ECD23CE3A89002ABFB5 /* Debug */ = {
578 | isa = XCBuildConfiguration;
579 | buildSettings = {
580 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
581 | CODE_SIGN_STYLE = Automatic;
582 | DEVELOPMENT_TEAM = SH4YVD7HDL;
583 | INFOPLIST_FILE = UICornerShadowViewUITests/Info.plist;
584 | LD_RUNPATH_SEARCH_PATHS = (
585 | "$(inherited)",
586 | "@executable_path/Frameworks",
587 | "@loader_path/Frameworks",
588 | );
589 | PRODUCT_BUNDLE_IDENTIFIER = silverfruity.UICornerShadowViewUITests;
590 | PRODUCT_NAME = "$(TARGET_NAME)";
591 | SWIFT_VERSION = 5.0;
592 | TARGETED_DEVICE_FAMILY = "1,2";
593 | TEST_TARGET_NAME = UICornerShadowView;
594 | };
595 | name = Debug;
596 | };
597 | EE731ECE23CE3A89002ABFB5 /* Release */ = {
598 | isa = XCBuildConfiguration;
599 | buildSettings = {
600 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
601 | CODE_SIGN_STYLE = Automatic;
602 | DEVELOPMENT_TEAM = SH4YVD7HDL;
603 | INFOPLIST_FILE = UICornerShadowViewUITests/Info.plist;
604 | LD_RUNPATH_SEARCH_PATHS = (
605 | "$(inherited)",
606 | "@executable_path/Frameworks",
607 | "@loader_path/Frameworks",
608 | );
609 | PRODUCT_BUNDLE_IDENTIFIER = silverfruity.UICornerShadowViewUITests;
610 | PRODUCT_NAME = "$(TARGET_NAME)";
611 | SWIFT_VERSION = 5.0;
612 | TARGETED_DEVICE_FAMILY = "1,2";
613 | TEST_TARGET_NAME = UICornerShadowView;
614 | };
615 | name = Release;
616 | };
617 | /* End XCBuildConfiguration section */
618 |
619 | /* Begin XCConfigurationList section */
620 | EE731E9723CE3A87002ABFB5 /* Build configuration list for PBXProject "UICornerShadowView" */ = {
621 | isa = XCConfigurationList;
622 | buildConfigurations = (
623 | EE731EC423CE3A89002ABFB5 /* Debug */,
624 | EE731EC523CE3A89002ABFB5 /* Release */,
625 | );
626 | defaultConfigurationIsVisible = 0;
627 | defaultConfigurationName = Release;
628 | };
629 | EE731EC623CE3A89002ABFB5 /* Build configuration list for PBXNativeTarget "UICornerShadowView" */ = {
630 | isa = XCConfigurationList;
631 | buildConfigurations = (
632 | EE731EC723CE3A89002ABFB5 /* Debug */,
633 | EE731EC823CE3A89002ABFB5 /* Release */,
634 | );
635 | defaultConfigurationIsVisible = 0;
636 | defaultConfigurationName = Release;
637 | };
638 | EE731EC923CE3A89002ABFB5 /* Build configuration list for PBXNativeTarget "UICornerShadowViewTests" */ = {
639 | isa = XCConfigurationList;
640 | buildConfigurations = (
641 | EE731ECA23CE3A89002ABFB5 /* Debug */,
642 | EE731ECB23CE3A89002ABFB5 /* Release */,
643 | );
644 | defaultConfigurationIsVisible = 0;
645 | defaultConfigurationName = Release;
646 | };
647 | EE731ECC23CE3A89002ABFB5 /* Build configuration list for PBXNativeTarget "UICornerShadowViewUITests" */ = {
648 | isa = XCConfigurationList;
649 | buildConfigurations = (
650 | EE731ECD23CE3A89002ABFB5 /* Debug */,
651 | EE731ECE23CE3A89002ABFB5 /* Release */,
652 | );
653 | defaultConfigurationIsVisible = 0;
654 | defaultConfigurationName = Release;
655 | };
656 | /* End XCConfigurationList section */
657 | };
658 | rootObject = EE731E9423CE3A87002ABFB5 /* Project object */;
659 | }
660 |
--------------------------------------------------------------------------------
/UICornerShadowView.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/UICornerShadowView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/UICornerShadowView.xcodeproj/project.xcworkspace/xcuserdata/Memerys.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilverFruity/UICornerShadowView/97d23060a8bfc4beb3c8b57d900550f09c8fe91c/UICornerShadowView.xcodeproj/project.xcworkspace/xcuserdata/Memerys.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/UICornerShadowView.xcodeproj/project.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilverFruity/UICornerShadowView/97d23060a8bfc4beb3c8b57d900550f09c8fe91c/UICornerShadowView.xcodeproj/project.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/UICornerShadowView.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilverFruity/UICornerShadowView/97d23060a8bfc4beb3c8b57d900550f09c8fe91c/UICornerShadowView.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/UICornerShadowView.xcodeproj/xcshareddata/xcschemes/UICornerShadowView.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
43 |
49 |
50 |
51 |
52 |
53 |
63 |
65 |
71 |
72 |
73 |
74 |
80 |
82 |
88 |
89 |
90 |
91 |
93 |
94 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/UICornerShadowView.xcodeproj/xcuserdata/Memerys.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/UICornerShadowView.xcodeproj/xcuserdata/Memerys.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | UICornerShadowView.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/UICornerShadowView.xcodeproj/xcuserdata/jiang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/UICornerShadowView.xcodeproj/xcuserdata/jiang.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | UICornerShadowView.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 1
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | EE731E9B23CE3A87002ABFB5
16 |
17 | primary
18 |
19 |
20 | EE731EB123CE3A89002ABFB5
21 |
22 | primary
23 |
24 |
25 | EE731EBC23CE3A89002ABFB5
26 |
27 | primary
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/UICornerShadowView.xcodeproj/xcuserdata/mac.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | UICornerShadowView.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 1
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/UICornerShadowView/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // UICornerShadowView
4 | //
5 | // Created by Jiang on 2020/1/15.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SFImageMaker
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 |
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | let appearance = SFCSBView.appearance()
19 | appearance.cornerRadius = 20
20 | appearance.rectCornner = [.topLeft,.topRight,.bottomLeft,.bottomRight]
21 | appearance.shadowPosition = [.left,.top,.right,.bottom]
22 | appearance.shadowColor = UIColor.black.withAlphaComponent(0.6)
23 | appearance.shadowRadius = 20
24 | appearance.borderColor = UIColor.systemBlue
25 | appearance.borderWidth = 5
26 | appearance.borderPosition = .all
27 | return true
28 | }
29 |
30 | // MARK: UISceneSession Lifecycle
31 |
32 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
33 | // Called when a new scene session is being created.
34 | // Use this method to select a configuration to create the new scene with.
35 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
36 | }
37 |
38 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
39 | // Called when the user discards a scene session.
40 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
41 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/UICornerShadowView/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/UICornerShadowView/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/UICornerShadowView/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 |
--------------------------------------------------------------------------------
/UICornerShadowView/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 | UISceneStoryboardFile
37 | Main
38 |
39 |
40 |
41 |
42 | UILaunchStoryboardName
43 | LaunchScreen
44 | UIMainStoryboardFile
45 | Main
46 | UIRequiredDeviceCapabilities
47 |
48 | armv7
49 |
50 | UISupportedInterfaceOrientations
51 |
52 | UIInterfaceOrientationPortrait
53 | UIInterfaceOrientationLandscapeLeft
54 | UIInterfaceOrientationLandscapeRight
55 |
56 | UISupportedInterfaceOrientations~ipad
57 |
58 | UIInterfaceOrientationPortrait
59 | UIInterfaceOrientationPortraitUpsideDown
60 | UIInterfaceOrientationLandscapeLeft
61 | UIInterfaceOrientationLandscapeRight
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/UICornerShadowView/README.md:
--------------------------------------------------------------------------------
1 | # UICornerShadowView
2 |
--------------------------------------------------------------------------------
/UICornerShadowView/SFImageFlowController.h:
--------------------------------------------------------------------------------
1 | //
2 | // SFImageFlowController.h
3 | // UICornerShadowView
4 | //
5 | // Created by APPLE on 2021/5/25.
6 | // Copyright © 2021 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface SFImageFlowController : UIViewController
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/UICornerShadowView/SFImageFlowController.m:
--------------------------------------------------------------------------------
1 | //
2 | // SFImageFlowController.m
3 | // UICornerShadowView
4 | //
5 | // Created by APPLE on 2021/5/25.
6 | // Copyright © 2021 SilverFruity. All rights reserved.
7 | //
8 |
9 | #import "SFImageFlowController.h"
10 | #import
11 | #import "UIImage+Extentions.h"
12 | @interface SFImageFlowController ()
13 | @property (weak, nonatomic) IBOutlet UIButton *cornerBorderButton;
14 | @property (weak, nonatomic) IBOutlet UIButton *gradientButton;
15 | @property (weak, nonatomic) IBOutlet UIButton *avatarButton;
16 | @property (weak, nonatomic) IBOutlet SFCSBView *testView;
17 |
18 | @property (weak, nonatomic) IBOutlet SFCSBView *topView;
19 | @property (weak, nonatomic) IBOutlet SFCSBView *bottomView;
20 | @end
21 |
22 | @implementation SFImageFlowController
23 |
24 | - (void)viewDidLoad {
25 | [super viewDidLoad];
26 | [self.cornerBorderButton setBackgroundImage:UIColor.lightGrayColor.sf_flow.corner(10, UIRectCornerAllCorners).border(0.5, UIColor.blackColor).image forState:UIControlStateNormal];
27 | UIImage *image = @[UIColor.redColor,UIColor.purpleColor].sf_gradientFlow(YES,self.gradientButton.frame.size).corner(10, UIRectCornerTopLeft|UIRectCornerBottomRight).border(1, UIColor.blackColor).image;
28 | [self.gradientButton setBackgroundImage:image forState:UIControlStateNormal];
29 | // image = @[UIColor.redColor,UIColor.greenColor].sf_gradientFlow(YES,CGSizeMake(40, 40)).blur(SFBlurEffectLight).circle.image;
30 | image = @[UIColor.redColor,UIColor.greenColor].sf_gradientFlow(YES,CGSizeMake(40, 40)).blur(SFBlurEffectLight).circle.border(1, UIColor.blackColor).shadow([UIColor.blackColor colorWithAlphaComponent:0.5], CGSizeMake(10, 10), 10).image;
31 | [self.avatarButton setBackgroundImage:image forState:UIControlStateNormal];
32 |
33 | image = [UIImage new].sf_flow.corner(10, UIRectCornerAllCorners).border(1, UIColor.redColor).image;
34 |
35 | self.testView.cornerRadius = 8;
36 | self.testView.shadowRadius = 20;
37 | self.testView.shadowColor = UIColor.blackColor;
38 | self.testView.borderWidth = 0;
39 | self.testView.borderColor = [UIColor clearColor];
40 |
41 |
42 | self.topView.cornerRadius = 0;
43 | self.topView.shadowRadius = 20;
44 | self.topView.shadowColor = UIColor.blackColor;
45 | self.topView.rectCornner = UIRectCornerTopLeft | UIRectCornerBottomRight;
46 | self.topView.shadowPosition = UIShadowPostionLeft | UIShadowPostionRight;
47 | self.topView.shadowFixValue = -0.2455;
48 | self.topView.borderWidth = 0;
49 | self.topView.borderColor = [UIColor clearColor];
50 |
51 | self.bottomView.cornerRadius = 10;
52 | self.bottomView.shadowRadius = 20;
53 | self.bottomView.shadowFixValue = -0.2455;
54 | self.bottomView.shadowPosition = UIShadowPostionLeft | UIShadowPostionRight;
55 | self.bottomView.rectCornner = UIRectCornerBottomLeft | UIRectCornerTopRight;
56 | self.bottomView.shadowColor = UIColor.blackColor;
57 | self.bottomView.borderWidth = 0;
58 | self.bottomView.borderColor = [UIColor clearColor];
59 |
60 | }
61 |
62 | /*
63 | #pragma mark - Navigation
64 |
65 | // In a storyboard-based application, you will often want to do a little preparation before navigation
66 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
67 | // Get the new view controller using [segue destinationViewController].
68 | // Pass the selected object to the new view controller.
69 | }
70 | */
71 |
72 | @end
73 |
--------------------------------------------------------------------------------
/UICornerShadowView/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // UICornerShadowView
4 | //
5 | // Created by Jiang on 2020/1/15.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
12 |
13 | var window: UIWindow?
14 |
15 |
16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
17 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
18 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
19 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
20 | guard let _ = (scene as? UIWindowScene) else { return }
21 | }
22 |
23 | func sceneDidDisconnect(_ scene: UIScene) {
24 | // Called as the scene is being released by the system.
25 | // This occurs shortly after the scene enters the background, or when its session is discarded.
26 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
27 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
28 | }
29 |
30 | func sceneDidBecomeActive(_ scene: UIScene) {
31 | // Called when the scene has moved from an inactive state to an active state.
32 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
33 | }
34 |
35 | func sceneWillResignActive(_ scene: UIScene) {
36 | // Called when the scene will move from an active state to an inactive state.
37 | // This may occur due to temporary interruptions (ex. an incoming phone call).
38 | }
39 |
40 | func sceneWillEnterForeground(_ scene: UIScene) {
41 | // Called as the scene transitions from the background to the foreground.
42 | // Use this method to undo the changes made on entering the background.
43 | }
44 |
45 | func sceneDidEnterBackground(_ scene: UIScene) {
46 | // Called as the scene transitions from the foreground to the background.
47 | // Use this method to save data, release shared resources, and store enough scene-specific state information
48 | // to restore the scene back to its current state.
49 | }
50 |
51 |
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/UICornerShadowView/UICornerShadowView-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "SFImageFlowController.h"
6 |
--------------------------------------------------------------------------------
/UICornerShadowView/UICornerShadowView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UICornerShadowView.swift
3 | //
4 | // Created by Jiang on 2019/12/5.
5 | // Copyright © 2019 goute. All rights reserved.
6 | //
7 |
8 | import UIKit
9 | import CoreGraphics
10 | import SFImageMaker
11 |
12 | class CustomRenderCache{
13 | static let `default` = CustomRenderCache()
14 | private let cache: NSCache = NSCache.init()
15 | private var hasCost: Int = 0
16 | init() {
17 | //10MB
18 | self.cache.totalCostLimit = 10 * 1024 * 1024
19 | }
20 | open func object(forKey key: String) -> UIImage?{
21 | return self.cache.object(forKey: NSString.init(string: key))
22 | }
23 |
24 | open func setObject(_ obj: UIImage?, forKey key: String){
25 | guard let obj = obj else{
26 | return
27 | }
28 | // 32: RGBA
29 | var bytes = Int(obj.size.width * obj.size.height * 4 * UIScreen.main.scale * UIScreen.main.scale)
30 | if let cgimg = obj.cgImage{
31 | bytes = cgimg.height * cgimg.width * (cgimg.bitsPerPixel / cgimg.bitsPerComponent)
32 | }
33 | hasCost += bytes
34 | self.cache.setObject(obj, forKey: NSString.init(string: key), cost: bytes)
35 | }
36 |
37 | open func removeObject(forKey key: String){
38 | self.cache.removeObject(forKey: NSString.init(string: key))
39 | }
40 | func clear(){
41 | self.cache.removeAllObjects()
42 | }
43 | func cacheCost()->Int{
44 | return hasCost
45 | }
46 | }
47 |
48 | class UICornerShadowView: UIView {
49 | var backGroundImageView: UIImageView!
50 | // 加下划线的原因是与SwifterSwift库有冲突
51 | // FIXME: 后续添加UIAppearance 全局设置默认值
52 | @IBInspectable public var _rectCornner: UIRectCorner = .allCorners
53 | @IBInspectable public var _cornerRadius: CGFloat = 8
54 | @IBInspectable public var _shadowColor: UIColor = UIColor.black.withAlphaComponent(0.08)
55 | @IBInspectable public var _shadowOffset: CGSize = CGSize.init(width: 0, height: 0)
56 | @IBInspectable public var _shadowRadius: CGFloat = 0
57 | @IBInspectable public var _borderWidth: CGFloat = 0
58 | @IBInspectable public var _borderColor: UIColor = UIColor.clear
59 | var _shadowPosition:UIShadowPostion = .all
60 | var _borderPosition:UIBorderPostion = .all
61 |
62 | var shadowProcesser: SFShadowImageMaker{
63 | let shadow = SFShadowImageMaker.init()
64 | shadow.shadowColor = self._shadowColor
65 | shadow.shadowOffset = self._shadowOffset
66 | shadow.shadowBlurRadius = self._shadowRadius
67 | shadow.position = self._shadowPosition
68 | return shadow;
69 | }
70 | var rectCornerProcesser: SFCornerImageMaker{
71 | let rectCorner = SFCornerImageMaker.init()
72 | rectCorner.position = self._rectCornner
73 | rectCorner.radius = self._cornerRadius
74 | return rectCorner
75 | }
76 | var borderProcesser: SFBorderImageMaker{
77 | let border = SFBorderImageMaker.init()
78 | border.width = self._borderWidth
79 | border.color = self._borderColor
80 | border.position = self._borderPosition
81 | border.cornerMaker = self.rectCornerProcesser
82 | return border;
83 | }
84 | var generalImageProcesser: SFColorImageMaker{
85 | // Radius ShadowRadius BorderWidth 取最大值
86 | var maxValue = self.rectCornerProcesser.radius > (self.borderProcesser.width + 1) && self.rectCornerProcesser.isEnable ? self.rectCornerProcesser.radius : self.borderProcesser.width + 1
87 | maxValue = self.shadowProcesser.shadowBlurRadius > maxValue ? self.shadowProcesser.shadowBlurRadius : maxValue
88 | let size = CGSize.init(width: maxValue * 2, height: maxValue * 2)
89 | return SFColorImageMaker.init(color: self.initailBackGroundColor, size: size)
90 | }
91 | private var initailBackGroundColor = UIColor.white
92 | // 针对上一次图片的identifier的
93 | private var lastBackGroundImageIdentifer = ""
94 | required init?(coder: NSCoder) {
95 | super.init(coder: coder)
96 | self.backGroundImageView = UIImageView.init()
97 | self.addSubview(self.backGroundImageView)
98 | self.backGroundImageView.clipsToBounds = false
99 | self.clipsToBounds = false
100 | }
101 | override init(frame: CGRect) {
102 | super.init(frame: frame)
103 | self.backGroundImageView = UIImageView.init()
104 | self.addSubview(self.backGroundImageView)
105 | self.backGroundImageView.clipsToBounds = false
106 | self.clipsToBounds = false
107 | }
108 | override func layoutSubviews() {
109 | super.layoutSubviews()
110 | self.reloadBackGourndImage()
111 | }
112 | func reloadBackGourndImage(){
113 | self.sendSubviewToBack(self.backGroundImageView)
114 | if self.backgroundColor != UIColor.clear && self.backgroundColor != nil{
115 | self.initailBackGroundColor = self.backgroundColor!
116 | }
117 | //保证不会在子线程中调用self获取值
118 | let generalImage = self.generalImageProcesser
119 | let shadow = self.shadowProcesser
120 | let rectCorner = self.rectCornerProcesser
121 | let border = self.borderProcesser
122 | let imageIdentifier = self.identifier()
123 |
124 | //FIXME: 外边框 border和shadow同时存在时,宽高的计算,一大一小。
125 | //FIXME: 外边框 border和shadow只有一者存在时,宽高的计算。
126 | var viewFrame = shadow.viewRect(for: self.bounds.size)
127 | //insetValue: 特意增加的误差 0.o。解决tableView显示时,cell上下阴影衔接时会有一空缺的问题。
128 | let insetValue :CGFloat = -1
129 | // CGRect(2,2,2,2) -> CGRect(1,1,4,4)
130 | viewFrame = viewFrame.inset(by: UIEdgeInsets(top: insetValue, left: insetValue, bottom: insetValue, right: insetValue))
131 | // 每修改一次subview的frame,view会调用layoutSubviews方法。
132 | // 目的:在高度重用UICornerShadowView的情况,并且每次都更新的情况下,减少frame更新。
133 | // 如果上一次的identifer相同说明是重用图片
134 | // 如果当前frame和需要的frame相同,也不用更新frame
135 | if imageIdentifier != lastBackGroundImageIdentifer || self.backGroundImageView.frame != viewFrame{
136 | self.backGroundImageView.frame = viewFrame
137 | }
138 | if let cacheImage = CustomRenderCache.default.object(forKey: imageIdentifier){
139 | self.backGroundImageView.image = cacheImage
140 | self.backgroundColor = UIColor.clear
141 | self.lastBackGroundImageIdentifer = imageIdentifier
142 | return
143 | }
144 | DispatchQueue.global().async { [unowned self] in
145 | let flow = SFImageFlow.init(generator: generalImage)
146 | flow.processors = [rectCorner,border,shadow];
147 | var image = flow.process()
148 | if shadow.isEnable{
149 | let insets = shadow.convasEdgeInsets()
150 | image = image.resizableImageCenter(withInset: insets)
151 | }else{
152 | image = image.resizableImageCenterMode()
153 | }
154 | CustomRenderCache.default.setObject(image, forKey: imageIdentifier)
155 | DispatchQueue.main.async { [weak self] in
156 | if self == nil{
157 | return
158 | }
159 | self?.lastBackGroundImageIdentifer = imageIdentifier
160 | self?.backGroundImageView.image = image
161 | }
162 | }
163 | self.backgroundColor = UIColor.clear
164 | }
165 | func identifier()->String{
166 | return self.generalImageProcesser.identifier() + self.rectCornerProcesser.identifier() + self.borderProcesser.identifier() + self.shadowProcesser.identifier()
167 | }
168 |
169 | #if DEBUG
170 | func debugInfo()->String{
171 | return """
172 | initailBackGroundColor=\(initailBackGroundColor);\n
173 | cornerRadius=\(_cornerRadius);\n
174 | rectCornner=\(_rectCornner);\n
175 | shadowOffset=\(_shadowOffset);\n
176 | shadowRadius=\(_shadowRadius);\n
177 | shadowColor=\(_shadowColor);\n
178 | shadowPosition=\(_shadowPosition);\n
179 | borderColor=\(_borderColor);\n
180 | borderWidth=\(_borderWidth);\n
181 | borderPosition=\(_borderPosition);
182 | """
183 | }
184 | #endif
185 | }
186 |
--------------------------------------------------------------------------------
/UICornerShadowView/UIImage+Extentions.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+imageExtention.h
3 | // SharedGym
4 | //
5 | // Created by Jiang on 2017/4/18.
6 | //
7 | //
8 |
9 | #import
10 | NS_ASSUME_NONNULL_BEGIN
11 | @interface UIImage (Radius)
12 | ///设置中点为拉伸区域
13 | - (UIImage *)resizableImageCenterMode;
14 | - (UIImage *)resizableImageCenterWithInset:(UIEdgeInsets)inset;
15 | @end
16 |
17 |
18 | NS_ASSUME_NONNULL_END
19 |
--------------------------------------------------------------------------------
/UICornerShadowView/UIImage+Extentions.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+imageExtention.m
3 | //
4 | // Created by Jiang on 2017/4/18.
5 | //
6 | //
7 |
8 | #import "UIImage+Extentions.h"
9 | #import
10 | #import
11 |
12 | #ifndef SCREEN_WIDTH
13 | #define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
14 | #endif
15 | #ifndef SCREEN_HEIGHT
16 | #define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
17 | #endif
18 |
19 | #ifndef SCREEN_SCALE
20 | #define SCREEN_SCALE [UIScreen mainScreen].scale
21 | #endif
22 | @implementation UIImage (Radius)
23 | - (UIImage *)resizableImageCenterMode{
24 | return [self resizableImageWithCapInsets:UIEdgeInsetsMake(self.size.height / 2, self.size.width / 2, self.size.height / 2, self.size.width / 2)];
25 | }
26 | - (UIImage *)resizableImageCenterWithInset:(UIEdgeInsets)inset{
27 | CGFloat x = (self.size.width - inset.left - inset.right) / 2;
28 | CGFloat y = (self.size.height - inset.top - inset.bottom) / 2;
29 | return [self resizableImageWithCapInsets:UIEdgeInsetsMake(y + inset.top, x + inset.left, y + inset.bottom, x + inset.right)];
30 | }
31 | @end
32 |
--------------------------------------------------------------------------------
/UICornerShadowView/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // UICornerShadowView
4 | //
5 | // Created by Jiang on 2020/1/15.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SFImageMaker
11 | class ViewController: UIViewController {
12 | @IBOutlet weak var shadowView: SFCSBView!
13 |
14 | @IBOutlet weak var layerCorner: UIView!
15 | @IBOutlet weak var layerShadow: UIView!
16 |
17 | @IBOutlet weak var leftRightShadowView: SFCSBView!
18 | @IBOutlet weak var topBottomShadowView: SFCSBView!
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 | self.shadowView.cornerRadius = 20
23 | self.shadowView.rectCornner = [.topLeft,.topRight,.bottomLeft,.bottomRight]
24 | self.shadowView.shadowPosition = [.left,.top,.right,.bottom]
25 | self.shadowView.shadowColor = UIColor.black.withAlphaComponent(0.6)
26 | self.shadowView.shadowRadius = 20
27 | self.shadowView.borderColor = UIColor.systemBlue
28 | self.shadowView.borderWidth = 5
29 | self.shadowView.borderPosition = .all
30 |
31 | self.layerCorner.layer.borderWidth = self.shadowView.borderWidth
32 | self.layerCorner.layer.borderColor = self.shadowView.borderColor.cgColor
33 | self.layerCorner.layer.cornerRadius = self.shadowView.cornerRadius
34 | self.layerCorner.layer.masksToBounds = true
35 |
36 | self.layerShadow.backgroundColor = UIColor.clear
37 | self.layerShadow.layer.shadowOpacity = 1.0
38 | self.layerShadow.layer.shadowOffset = CGSize.zero
39 | //FIXME: 和系统相同需要 /2
40 | self.layerShadow.layer.shadowRadius = self.shadowView.shadowRadius / 2
41 | self.layerShadow.layer.shadowColor = self.shadowView.shadowColor.cgColor
42 |
43 | self.leftRightShadowView.cornerRadius = 0
44 | self.leftRightShadowView.shadowPosition = [.left,.right]
45 | self.leftRightShadowView.shadowColor = UIColor.black.withAlphaComponent(0.6)
46 | self.leftRightShadowView.shadowRadius = 20
47 | self.leftRightShadowView.borderWidth = 5
48 | self.leftRightShadowView.borderColor = UIColor.systemBlue
49 | self.leftRightShadowView.borderPosition = [.left,.right]
50 |
51 | self.topBottomShadowView.cornerRadius = 0
52 | self.topBottomShadowView.shadowPosition = [.top,.bottom]
53 | self.topBottomShadowView.shadowColor = UIColor.black.withAlphaComponent(0.6)
54 | self.topBottomShadowView.shadowRadius = 20
55 | self.topBottomShadowView.borderWidth = 5
56 | self.topBottomShadowView.borderColor = UIColor.systemBlue
57 | self.topBottomShadowView.borderPosition = [.top,.bottom]
58 |
59 | }
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/UICornerShadowView/ViewController1.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController1.swift
3 | // UICornerShadowView
4 | //
5 | // Created by Jiang on 2020/2/11.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SFImageMaker
11 | class RandomCell: UITableViewCell{
12 | @IBOutlet weak var container: SFCSBView!
13 | override func awakeFromNib() {
14 | super.awakeFromNib()
15 |
16 | }
17 | }
18 | class ViewController1: UIViewController,UITableViewDataSource,UITableViewDelegate{
19 |
20 | @IBOutlet weak var tableView: UITableView!
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 |
24 | tableView.rowHeight = 100
25 | }
26 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
27 | return 1000;
28 | }
29 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
30 | let cell = tableView.dequeueReusableCell(withIdentifier: "RandomCell") as! RandomCell
31 | cell.container.backgroundColor = UIColor.init(red: CGFloat.random(in: 0...10) / 10, green: CGFloat.random(in: 0...10) / 10, blue: CGFloat.random(in: 0...10) / 10, alpha: 1)
32 | cell.container.cornerRadius = CGFloat(Int.random(in: 10..<20))
33 | let cornerPositions: [UIRectCorner] = [.topLeft,.topRight,.bottomLeft,.bottomRight]
34 | var cornerResult = [UIRectCorner]()
35 | var cornerCount = Int.random(in: 0..<4)
36 | while cornerCount >= 0 {
37 | cornerResult.append(cornerPositions[Int.random(in: 0..<4)])
38 | cornerCount -= 1
39 | }
40 | cell.container.rectCornner = UIRectCorner.init(cornerResult)
41 |
42 | let shadowPositions: [UIShadowPostion] = [.left,.top,.right,.bottom]
43 | var shadowResult = [UIShadowPostion]()
44 | var shadowResultCount = Int.random(in: 0..<4)
45 | while shadowResultCount >= 0 {
46 | shadowResult.append(shadowPositions[Int.random(in: 0..<4)])
47 | shadowResultCount -= 1
48 | }
49 | cell.container.shadowPosition = UIShadowPostion.init(shadowResult)
50 | cell.container.shadowColor = UIColor.black.withAlphaComponent(CGFloat(Int.random(in: 4..<10)) / 10)
51 | cell.container.shadowRadius = CGFloat(Int.random(in: 8..<20))
52 | cell.container.borderColor = UIColor.init(red: CGFloat(Int.random(in: 0...10)) / 10, green: CGFloat(Int.random(in: 0...10)) / 10, blue: CGFloat(Int.random(in: 0...10)) / 10, alpha: 1)
53 | cell.container.borderWidth = CGFloat(Int.random(in: 0..<8))
54 |
55 | let borderPositions: [UIBorderPostion] = [.left,.top,.right,.bottom]
56 | var borderResult = [UIBorderPostion]()
57 | var borderCount = Int.random(in: 0..<4)
58 | while borderCount >= 0 {
59 | borderResult.append(borderPositions[Int.random(in: 0..<4)])
60 | borderCount -= 1
61 | }
62 | if Int.random(in: 0..<5) / 2 == 0{
63 | cell.container.defautlGeneratorClass = SFGradientImageMaker.self
64 | }else{
65 | cell.container.defautlGeneratorClass = SFColorImageMaker.self
66 | }
67 | cell.container.handleMakers = { makers in
68 | if let gradientGenerator = makers.filter({ $0 is SFGradientImageMaker }).first as? SFGradientImageMaker{
69 | let startColor = UIColor.init(red: CGFloat(Int.random(in: 0...10)) / 10, green: CGFloat(Int.random(in: 0...10)) / 10, blue: CGFloat(Int.random(in: 0...10)) / 10, alpha: 1)
70 | let endColor = UIColor.init(red: CGFloat(Int.random(in: 0...10)) / 10, green: CGFloat(Int.random(in: 0...10)) / 10, blue: CGFloat(Int.random(in: 0...10)) / 10, alpha: 1)
71 | gradientGenerator.colors = [startColor, endColor]
72 | gradientGenerator.locations = [NSNumber(0), NSNumber(1)];
73 | gradientGenerator.isHorizontal = true
74 | gradientGenerator.dependencies.add(SFBlurImageMaker.lightEffect())
75 | }
76 | if let gradientGenerator = makers.filter({ $0 is SFColorImageMaker }).first as? SFColorImageMaker{
77 | gradientGenerator.dependencies.add(SFBlurImageMaker.lightEffect())
78 | }
79 | }
80 | cell.container.borderPosition = UIBorderPostion.init(borderResult)
81 | cell.container.reloadBackGourndImage()
82 | return cell
83 | }
84 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
85 | let cell = tableView.cellForRow(at: indexPath) as! RandomCell
86 | // let alert = UIAlertController.init(title: "DebugInfo", message: cell.container.debugInfo(), preferredStyle: .alert)
87 | // alert.addAction(UIAlertAction.init(title: "cancel", style: .cancel, handler: nil))
88 | // self.present(alert, animated: true, completion: nil)
89 | }
90 |
91 | /*
92 | // MARK: - Navigation
93 |
94 | // In a storyboard-based application, you will often want to do a little preparation before navigation
95 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
96 | // Get the new view controller using segue.destination.
97 | // Pass the selected object to the new view controller.
98 | }
99 | */
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/UICornerShadowView/ViewController2.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController2.swift
3 | // UICornerShadowView
4 | //
5 | // Created by Jiang on 2020/2/13.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SFImageMaker
11 | class ReuseImageCell: UITableViewCell{
12 | @IBOutlet weak var container: SFCSBView!
13 | override func awakeFromNib() {
14 | super.awakeFromNib()
15 | self.container.shadowColor = UIColor.black.withAlphaComponent(0.6)
16 | self.container.shadowRadius = 10
17 | self.container.cornerRadius = 10
18 | self.container.borderWidth = 0
19 | self.container.shadowFixValue = -0.5;
20 | }
21 | func topStyle(){
22 | self.container.shadowPosition = [.left,.right,.top]
23 | self.container.rectCornner = [.topLeft,.topRight]
24 | }
25 | func middleStyle(){
26 | self.container.shadowPosition = [.left,.right]
27 | self.container.rectCornner = UIRectCorner.init(rawValue: 0)
28 | }
29 | func bottomStyle(){
30 | self.container.shadowPosition = [.left,.right,.bottom]
31 | self.container.rectCornner = [.bottomRight,.bottomLeft]
32 | }
33 | }
34 | class ViewController2: UIViewController,UITableViewDataSource,UITableViewDelegate{
35 | @IBOutlet weak var tableView: UITableView!
36 | override func viewDidLoad() {
37 | super.viewDidLoad()
38 | let _ = UIColor.red
39 | .sf_flow(with: CGSize.init(width: 40, height: 40))
40 | .blur(with: SFBlurEffectLight)
41 | .border(withWidth: 1, color: .gray)
42 | .shadow(with: UIColor.black.withAlphaComponent(0.5), offset: .init(width: 10, height: 10), blurRadius: 10)
43 | .resize(with: .init(width: 20, height: 20))
44 | .circle
45 | .image();
46 |
47 | tableView.rowHeight = 80
48 | tableView.tableHeaderView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 20))
49 | tableView.tableFooterView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 20))
50 | }
51 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
52 | return 1000;
53 | }
54 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
55 | let cell = tableView.dequeueReusableCell(withIdentifier: "ReuseImageCell") as! ReuseImageCell
56 | let totalCount = tableView.numberOfRows(inSection: indexPath.section)
57 | if indexPath.row == 0{
58 | cell.topStyle()
59 | }
60 | if indexPath.row == totalCount - 1{
61 | cell.bottomStyle()
62 | }
63 | if indexPath.row > 0 && indexPath.row < totalCount - 1{
64 | cell.middleStyle()
65 | }
66 | cell.container.reloadBackGourndImage()
67 | return cell
68 | }
69 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
70 | let cell = tableView.cellForRow(at: indexPath) as! ReuseImageCell
71 | // let alert = UIAlertController.init(title: "DebugInfo", message: cell.container.debugInfo(), preferredStyle: .alert)
72 | // alert.addAction(UIAlertAction.init(title: "cancel", style: .cancel, handler: nil))
73 | // self.present(alert, animated: true, completion: nil)
74 | }
75 |
76 | /*
77 | // MARK: - Navigation
78 |
79 | // In a storyboard-based application, you will often want to do a little preparation before navigation
80 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
81 | // Get the new view controller using segue.destination.
82 | // Pass the selected object to the new view controller.
83 | }
84 | */
85 |
86 | }
87 |
88 |
--------------------------------------------------------------------------------
/UICornerShadowViewTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/UICornerShadowViewTests/UICornerShadowViewTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UICornerShadowViewTests.swift
3 | // UICornerShadowViewTests
4 | //
5 | // Created by Jiang on 2020/1/15.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import UICornerShadowView
11 |
12 | class UICornerShadowViewTests: XCTestCase {
13 |
14 |
15 | override func setUp() {
16 |
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | }
22 |
23 |
24 | func testPerformanceExample() {
25 | // This is an example of a performance test case.
26 |
27 | self.measure {
28 |
29 | }
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/UICornerShadowViewUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/UICornerShadowViewUITests/UICornerShadowViewUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UICornerShadowViewUITests.swift
3 | // UICornerShadowViewUITests
4 | //
5 | // Created by Jiang on 2020/1/15.
6 | // Copyright © 2020 SilverFruity. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class UICornerShadowViewUITests: XCTestCase {
12 |
13 | override func setUp() {
14 | // Put setup code here. This method is called before the invocation of each test method in the class.
15 |
16 | // In UI tests it is usually best to stop immediately when a failure occurs.
17 | continueAfterFailure = false
18 |
19 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
20 | }
21 |
22 | override func tearDown() {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | }
25 |
26 | func testExample() {
27 | // UI tests must launch the application that they test.
28 | let app = XCUIApplication()
29 | app.launch()
30 |
31 | // Use recording to get started writing UI tests.
32 | // Use XCTAssert and related functions to verify your tests produce the correct results.
33 | }
34 |
35 | // func testLaunchPerformance() {
36 | // if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) {
37 | // // This measures how long it takes to launch your application.
38 | // measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) {
39 | // XCUIApplication().launch()
40 | // }
41 | // }
42 | // }
43 | }
44 |
--------------------------------------------------------------------------------