@onekeyfe/react-native-image-crop-picker 3.0.147
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +83 -0
- package/ReactNativeImageCropPicker.podspec +40 -0
- package/android/CMakeLists.txt +24 -0
- package/android/build.gradle +144 -0
- package/android/gradle.properties +4 -0
- package/android/src/main/AndroidManifest.xml +24 -0
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/margelo/nitro/reactnativeimagecroppicker/ImageCropPickerException.kt +45 -0
- package/android/src/main/java/com/margelo/nitro/reactnativeimagecroppicker/ImageCropPickerImageProcessor.kt +308 -0
- package/android/src/main/java/com/margelo/nitro/reactnativeimagecroppicker/ImageCropPickerSession.kt +297 -0
- package/android/src/main/java/com/margelo/nitro/reactnativeimagecroppicker/ReactNativeImageCropPicker.kt +69 -0
- package/android/src/main/java/com/margelo/nitro/reactnativeimagecroppicker/ReactNativeImageCropPickerPackage.kt +24 -0
- package/ios/ImageCropPickerError.swift +57 -0
- package/ios/ImageCropPickerImageProcessor.swift +343 -0
- package/ios/ImageCropPickerSession.swift +407 -0
- package/ios/ReactNativeImageCropPicker.swift +65 -0
- package/ios/TOCropViewController/Categories/UIImage+CropRotate.h +39 -0
- package/ios/TOCropViewController/Categories/UIImage+CropRotate.m +94 -0
- package/ios/TOCropViewController/Constants/TOCropViewConstants.h +63 -0
- package/ios/TOCropViewController/LICENSE +21 -0
- package/ios/TOCropViewController/Models/TOActivityCroppedImageProvider.h +38 -0
- package/ios/TOCropViewController/Models/TOActivityCroppedImageProvider.m +73 -0
- package/ios/TOCropViewController/Models/TOCropViewControllerAspectRatioPreset.h +64 -0
- package/ios/TOCropViewController/Models/TOCropViewControllerAspectRatioPreset.m +121 -0
- package/ios/TOCropViewController/Models/TOCropViewControllerTransitioning.h +49 -0
- package/ios/TOCropViewController/Models/TOCropViewControllerTransitioning.m +125 -0
- package/ios/TOCropViewController/Models/TOCroppedImageAttributes.h +38 -0
- package/ios/TOCropViewController/Models/TOCroppedImageAttributes.m +45 -0
- package/ios/TOCropViewController/Resources/Base.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/PrivacyInfo.xcprivacy +14 -0
- package/ios/TOCropViewController/Resources/ar.lproj/TOCropViewControllerLocalizable.strings +9 -0
- package/ios/TOCropViewController/Resources/ca.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/cs.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/da-DK.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/de.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/en.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/es.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/fa-IR.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/fa.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/fi.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/fr.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/hu.lproj/TOCropViewControllerLocalizable.strings +9 -0
- package/ios/TOCropViewController/Resources/id.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/it.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/ja.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/ko.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/ms.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/nl.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/pl.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/pt-BR.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/pt.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/ro.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/ru.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/sk.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/tr.lproj/TOCropViewControllerLocalizable.strings +9 -0
- package/ios/TOCropViewController/Resources/uk.lproj/TOCropViewControllerLocalizable.strings +8 -0
- package/ios/TOCropViewController/Resources/vi.lproj/TOCropViewControllerLocalizable.strings +9 -0
- package/ios/TOCropViewController/Resources/zh-Hans.lproj/TOCropViewControllerLocalizable.strings +9 -0
- package/ios/TOCropViewController/Resources/zh-Hant.lproj/TOCropViewControllerLocalizable.strings +9 -0
- package/ios/TOCropViewController/TOCropViewController.h +479 -0
- package/ios/TOCropViewController/TOCropViewController.m +1355 -0
- package/ios/TOCropViewController/Views/TOCropOverlayView.h +43 -0
- package/ios/TOCropViewController/Views/TOCropOverlayView.m +240 -0
- package/ios/TOCropViewController/Views/TOCropScrollView.h +39 -0
- package/ios/TOCropViewController/Views/TOCropScrollView.m +48 -0
- package/ios/TOCropViewController/Views/TOCropToolbar.h +104 -0
- package/ios/TOCropViewController/Views/TOCropToolbar.m +783 -0
- package/ios/TOCropViewController/Views/TOCropView.h +311 -0
- package/ios/TOCropViewController/Views/TOCropView.m +1787 -0
- package/lib/module/ReactNativeImageCropPicker.nitro.js +4 -0
- package/lib/module/ReactNativeImageCropPicker.nitro.js.map +1 -0
- package/lib/module/index.js +83 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/ReactNativeImageCropPicker.nitro.d.ts +54 -0
- package/lib/typescript/src/ReactNativeImageCropPicker.nitro.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +31 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/nitro.json +23 -0
- package/nitrogen/generated/android/c++/JCropRect.hpp +69 -0
- package/nitrogen/generated/android/c++/JHybridReactNativeImageCropPickerSpec.cpp +125 -0
- package/nitrogen/generated/android/c++/JHybridReactNativeImageCropPickerSpec.hpp +66 -0
- package/nitrogen/generated/android/c++/JImageCropPickerOptions.hpp +154 -0
- package/nitrogen/generated/android/c++/JPickedImage.hpp +88 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativeimagecroppicker/CropRect.kt +66 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativeimagecroppicker/HybridReactNativeImageCropPickerSpec.kt +69 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativeimagecroppicker/ImageCropPickerOptions.kt +171 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativeimagecroppicker/PickedImage.kt +86 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativeimagecroppicker/reactnativeimagecroppickerOnLoad.kt +35 -0
- package/nitrogen/generated/android/reactnativeimagecroppicker+autolinking.cmake +81 -0
- package/nitrogen/generated/android/reactnativeimagecroppicker+autolinking.gradle +27 -0
- package/nitrogen/generated/android/reactnativeimagecroppickerOnLoad.cpp +54 -0
- package/nitrogen/generated/android/reactnativeimagecroppickerOnLoad.hpp +34 -0
- package/nitrogen/generated/ios/ReactNativeImageCropPicker+autolinking.rb +62 -0
- package/nitrogen/generated/ios/ReactNativeImageCropPicker-Swift-Cxx-Bridge.cpp +57 -0
- package/nitrogen/generated/ios/ReactNativeImageCropPicker-Swift-Cxx-Bridge.hpp +221 -0
- package/nitrogen/generated/ios/ReactNativeImageCropPicker-Swift-Cxx-Umbrella.hpp +55 -0
- package/nitrogen/generated/ios/ReactNativeImageCropPickerAutolinking.mm +33 -0
- package/nitrogen/generated/ios/ReactNativeImageCropPickerAutolinking.swift +26 -0
- package/nitrogen/generated/ios/c++/HybridReactNativeImageCropPickerSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridReactNativeImageCropPickerSpecSwift.hpp +116 -0
- package/nitrogen/generated/ios/swift/CropRect.swift +44 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_PickedImage.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridReactNativeImageCropPickerSpec.swift +58 -0
- package/nitrogen/generated/ios/swift/HybridReactNativeImageCropPickerSpec_cxx.swift +202 -0
- package/nitrogen/generated/ios/swift/ImageCropPickerOptions.swift +474 -0
- package/nitrogen/generated/ios/swift/PickedImage.swift +96 -0
- package/nitrogen/generated/shared/c++/CropRect.hpp +95 -0
- package/nitrogen/generated/shared/c++/HybridReactNativeImageCropPickerSpec.cpp +24 -0
- package/nitrogen/generated/shared/c++/HybridReactNativeImageCropPickerSpec.hpp +71 -0
- package/nitrogen/generated/shared/c++/ImageCropPickerOptions.hpp +180 -0
- package/nitrogen/generated/shared/c++/PickedImage.hpp +114 -0
- package/package.json +173 -0
- package/src/ReactNativeImageCropPicker.nitro.ts +72 -0
- package/src/index.tsx +134 -0
|
@@ -0,0 +1,783 @@
|
|
|
1
|
+
//
|
|
2
|
+
// TOCropToolbar.h
|
|
3
|
+
//
|
|
4
|
+
// Copyright 2015-2026 Timothy Oliver. All rights reserved.
|
|
5
|
+
//
|
|
6
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
// of this software and associated documentation files (the "Software"), to
|
|
8
|
+
// deal in the Software without restriction, including without limitation the
|
|
9
|
+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
10
|
+
// sell copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
// furnished to do so, subject to the following conditions:
|
|
12
|
+
//
|
|
13
|
+
// The above copyright notice and this permission notice shall be included in
|
|
14
|
+
// all copies or substantial portions of the Software.
|
|
15
|
+
//
|
|
16
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
17
|
+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
20
|
+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
21
|
+
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
#import "TOCropToolbar.h"
|
|
24
|
+
|
|
25
|
+
#define TOCROPTOOLBAR_DEBUG_SHOWING_BUTTONS_CONTAINER_RECT 0 // convenience debug toggle
|
|
26
|
+
|
|
27
|
+
@interface TOCropToolbar ()
|
|
28
|
+
|
|
29
|
+
@property (nonatomic, strong) UIView *backgroundView;
|
|
30
|
+
|
|
31
|
+
@property (nonatomic, strong, readwrite) UIButton *doneTextButton;
|
|
32
|
+
@property (nonatomic, strong, readwrite) UIButton *doneIconButton;
|
|
33
|
+
|
|
34
|
+
@property (nonatomic, strong, readwrite) UIButton *cancelTextButton;
|
|
35
|
+
@property (nonatomic, strong, readwrite) UIButton *cancelIconButton;
|
|
36
|
+
|
|
37
|
+
@property (nonatomic, strong) UIButton *resetButton;
|
|
38
|
+
@property (nonatomic, strong) UIButton *clampButton;
|
|
39
|
+
|
|
40
|
+
@property (nonatomic, strong) UIButton *rotateButton; // defaults to counterclockwise button for legacy compatibility
|
|
41
|
+
|
|
42
|
+
@property (nonatomic, strong) UIVisualEffectView *glassView;
|
|
43
|
+
|
|
44
|
+
@end
|
|
45
|
+
|
|
46
|
+
@implementation TOCropToolbar
|
|
47
|
+
|
|
48
|
+
- (instancetype)initWithFrame:(CGRect)frame {
|
|
49
|
+
if (self = [super initWithFrame:frame]) {
|
|
50
|
+
[self setup];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return self;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
- (void)setup {
|
|
57
|
+
self.backgroundView = [[UIView alloc] initWithFrame:self.bounds];
|
|
58
|
+
|
|
59
|
+
UIView *containerView = self;
|
|
60
|
+
#ifdef __IPHONE_26_0
|
|
61
|
+
if (@available(iOS 26.0, *)) {
|
|
62
|
+
UIVisualEffect *effect = nil;
|
|
63
|
+
#if !TARGET_OS_VISION
|
|
64
|
+
// We've been getting reports that some devices have been crashing with the error message
|
|
65
|
+
// that `effectWithStyle` was unrecognized. The ONLY way this seems to be possible
|
|
66
|
+
// is if there are users out there using the beta versions of iOS 26.0 before this method
|
|
67
|
+
// was introduced. Either way, we're going to need to manually confirm the selector exists
|
|
68
|
+
// in order to fix these crashes.
|
|
69
|
+
|
|
70
|
+
UIGlassEffect *glassEffect = nil;
|
|
71
|
+
SEL effectSelector = NSSelectorFromString(@"effectWithStyle:");
|
|
72
|
+
if ([[UIGlassEffect class] respondsToSelector:effectSelector]) {
|
|
73
|
+
glassEffect = [UIGlassEffect effectWithStyle:UIGlassEffectStyleClear];
|
|
74
|
+
} else {
|
|
75
|
+
glassEffect = [UIGlassEffect new];
|
|
76
|
+
}
|
|
77
|
+
glassEffect.interactive = YES;
|
|
78
|
+
effect = glassEffect;
|
|
79
|
+
#else
|
|
80
|
+
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleProminent];
|
|
81
|
+
effect = blurEffect;
|
|
82
|
+
#endif
|
|
83
|
+
_glassView = [[UIVisualEffectView alloc] initWithEffect:effect];
|
|
84
|
+
_glassView.cornerConfiguration = [UICornerConfiguration capsuleConfiguration];
|
|
85
|
+
_glassView.userInteractionEnabled = YES;
|
|
86
|
+
[self addSubview:_glassView];
|
|
87
|
+
|
|
88
|
+
containerView = _glassView.contentView;
|
|
89
|
+
_showOnlyIcons = YES;
|
|
90
|
+
} else {
|
|
91
|
+
self.backgroundView.backgroundColor = [UIColor colorWithWhite:0.12f alpha:1.0f];
|
|
92
|
+
[self addSubview:self.backgroundView];
|
|
93
|
+
}
|
|
94
|
+
#else
|
|
95
|
+
self.backgroundView.backgroundColor = [UIColor colorWithWhite:0.12f alpha:1.0f];
|
|
96
|
+
[self addSubview:self.backgroundView];
|
|
97
|
+
#endif
|
|
98
|
+
|
|
99
|
+
// On iOS 9 and up, we can use the new layout features to determine whether we're in an 'Arabic' style language mode
|
|
100
|
+
_reverseContentLayout = ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft);
|
|
101
|
+
|
|
102
|
+
// Get the resource bundle depending on the framework/dependency manager we're using
|
|
103
|
+
NSBundle *resourceBundle = TO_CROP_VIEW_RESOURCE_BUNDLE_FOR_OBJECT(self);
|
|
104
|
+
|
|
105
|
+
if (@available(iOS 26.0, *)) {
|
|
106
|
+
} else {
|
|
107
|
+
_doneTextButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
|
108
|
+
[_doneTextButton setTitle:_doneTextButtonTitle ? _doneTextButtonTitle : NSLocalizedStringFromTableInBundle(@"Done", @"TOCropViewControllerLocalizable", resourceBundle, nil)
|
|
109
|
+
forState:UIControlStateNormal];
|
|
110
|
+
[_doneTextButton setTitleColor:[UIColor colorWithRed:1.0f green:0.8f blue:0.0f alpha:1.0f] forState:UIControlStateNormal];
|
|
111
|
+
[_doneTextButton.titleLabel setFont:[UIFont systemFontOfSize:17.0f weight:UIFontWeightMedium]];
|
|
112
|
+
[_doneTextButton addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
|
|
113
|
+
[_doneTextButton sizeToFit];
|
|
114
|
+
[self addSubview:_doneTextButton];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
_doneIconButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
|
118
|
+
[_doneIconButton setImage:[TOCropToolbar doneImage] forState:UIControlStateNormal];
|
|
119
|
+
[_doneIconButton setTintColor:[UIColor colorWithRed:1.0f green:0.8f blue:0.0f alpha:1.0f]];
|
|
120
|
+
[_doneIconButton addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
|
|
121
|
+
#if defined(__IPHONE_26_0)
|
|
122
|
+
if (@available(iOS 26.0, *)) {
|
|
123
|
+
#if !TARGET_OS_VISION
|
|
124
|
+
UIButtonConfiguration *configuration = [UIButtonConfiguration prominentGlassButtonConfiguration];
|
|
125
|
+
configuration.baseForegroundColor = [UIColor labelColor];
|
|
126
|
+
#else
|
|
127
|
+
UIButtonConfiguration *configuration = [UIButtonConfiguration filledButtonConfiguration];
|
|
128
|
+
#endif
|
|
129
|
+
_doneIconButton.configuration = configuration;
|
|
130
|
+
}
|
|
131
|
+
#endif
|
|
132
|
+
[self addSubview:_doneIconButton];
|
|
133
|
+
|
|
134
|
+
// Set the default color for the done buttons
|
|
135
|
+
self.doneButtonColor = nil;
|
|
136
|
+
|
|
137
|
+
if (@available(iOS 26.0, *)) {
|
|
138
|
+
} else {
|
|
139
|
+
_cancelTextButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
|
140
|
+
|
|
141
|
+
[_cancelTextButton setTitle:_cancelTextButtonTitle ? _cancelTextButtonTitle : NSLocalizedStringFromTableInBundle(@"Cancel", @"TOCropViewControllerLocalizable", resourceBundle, nil)
|
|
142
|
+
forState:UIControlStateNormal];
|
|
143
|
+
[_cancelTextButton.titleLabel setFont:[UIFont systemFontOfSize:17.0f]];
|
|
144
|
+
[_cancelTextButton addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
|
|
145
|
+
[_cancelTextButton sizeToFit];
|
|
146
|
+
[self addSubview:_cancelTextButton];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
_cancelIconButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
|
150
|
+
[_cancelIconButton setImage:[TOCropToolbar cancelImage] forState:UIControlStateNormal];
|
|
151
|
+
[_cancelIconButton addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
|
|
152
|
+
#if defined(__IPHONE_26_0)
|
|
153
|
+
if (@available(iOS 26.0, *)) {
|
|
154
|
+
#if !TARGET_OS_VISION
|
|
155
|
+
UIButtonConfiguration *cancelConfiguration = [UIButtonConfiguration clearGlassButtonConfiguration];
|
|
156
|
+
cancelConfiguration.baseForegroundColor = [UIColor labelColor];
|
|
157
|
+
_cancelIconButton.configuration = cancelConfiguration;
|
|
158
|
+
#else
|
|
159
|
+
_cancelIconButton.configuration = [UIButtonConfiguration filledButtonConfiguration];
|
|
160
|
+
#endif
|
|
161
|
+
}
|
|
162
|
+
#endif
|
|
163
|
+
[self addSubview:_cancelIconButton];
|
|
164
|
+
|
|
165
|
+
_clampButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
|
166
|
+
_clampButton.contentMode = UIViewContentModeCenter;
|
|
167
|
+
_clampButton.tintColor = [UIColor whiteColor];
|
|
168
|
+
[_clampButton setImage:[TOCropToolbar clampImage] forState:UIControlStateNormal];
|
|
169
|
+
[_clampButton addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
|
|
170
|
+
[containerView addSubview:_clampButton];
|
|
171
|
+
|
|
172
|
+
_rotateCounterclockwiseButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
|
173
|
+
_rotateCounterclockwiseButton.contentMode = UIViewContentModeCenter;
|
|
174
|
+
_rotateCounterclockwiseButton.tintColor = [UIColor whiteColor];
|
|
175
|
+
[_rotateCounterclockwiseButton setImage:[TOCropToolbar rotateCCWImage] forState:UIControlStateNormal];
|
|
176
|
+
[_rotateCounterclockwiseButton addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
|
|
177
|
+
[containerView addSubview:_rotateCounterclockwiseButton];
|
|
178
|
+
|
|
179
|
+
_rotateClockwiseButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
|
180
|
+
_rotateClockwiseButton.contentMode = UIViewContentModeCenter;
|
|
181
|
+
_rotateClockwiseButton.tintColor = [UIColor whiteColor];
|
|
182
|
+
[_rotateClockwiseButton setImage:[TOCropToolbar rotateCWImage] forState:UIControlStateNormal];
|
|
183
|
+
[_rotateClockwiseButton addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
|
|
184
|
+
[containerView addSubview:_rotateClockwiseButton];
|
|
185
|
+
|
|
186
|
+
_resetButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
|
187
|
+
_resetButton.contentMode = UIViewContentModeCenter;
|
|
188
|
+
_resetButton.tintColor = [UIColor whiteColor];
|
|
189
|
+
_resetButton.enabled = NO;
|
|
190
|
+
[_resetButton setImage:[TOCropToolbar resetImage] forState:UIControlStateNormal];
|
|
191
|
+
[_resetButton addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
|
|
192
|
+
_resetButton.accessibilityLabel = NSLocalizedStringFromTableInBundle(@"Reset",
|
|
193
|
+
@"TOCropViewControllerLocalizable",
|
|
194
|
+
resourceBundle,
|
|
195
|
+
nil);
|
|
196
|
+
[containerView addSubview:_resetButton];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
- (void)layoutSubviews {
|
|
200
|
+
[super layoutSubviews];
|
|
201
|
+
|
|
202
|
+
BOOL verticalLayout = (CGRectGetWidth(self.bounds) < CGRectGetHeight(self.bounds));
|
|
203
|
+
CGSize boundsSize = self.bounds.size;
|
|
204
|
+
|
|
205
|
+
self.cancelIconButton.hidden = self.cancelButtonHidden || (_showOnlyIcons ? false : !verticalLayout);
|
|
206
|
+
self.cancelTextButton.hidden = self.cancelButtonHidden || (_showOnlyIcons ? true : verticalLayout);
|
|
207
|
+
self.doneIconButton.hidden = self.doneButtonHidden || (_showOnlyIcons ? false : !verticalLayout);
|
|
208
|
+
self.doneTextButton.hidden = self.doneButtonHidden || (_showOnlyIcons ? true : verticalLayout);
|
|
209
|
+
|
|
210
|
+
CGRect frame = self.bounds;
|
|
211
|
+
frame.origin.x -= self.backgroundViewOutsets.left;
|
|
212
|
+
frame.size.width += self.backgroundViewOutsets.left;
|
|
213
|
+
frame.size.width += self.backgroundViewOutsets.right;
|
|
214
|
+
frame.origin.y -= self.backgroundViewOutsets.top;
|
|
215
|
+
frame.size.height += self.backgroundViewOutsets.top;
|
|
216
|
+
frame.size.height += self.backgroundViewOutsets.bottom;
|
|
217
|
+
self.backgroundView.frame = frame;
|
|
218
|
+
|
|
219
|
+
#if TOCROPTOOLBAR_DEBUG_SHOWING_BUTTONS_CONTAINER_RECT
|
|
220
|
+
static UIView *containerView = nil;
|
|
221
|
+
if (!containerView) {
|
|
222
|
+
containerView = [[UIView alloc] initWithFrame:CGRectZero];
|
|
223
|
+
containerView.backgroundColor = [UIColor redColor];
|
|
224
|
+
containerView.alpha = 0.1;
|
|
225
|
+
[self addSubview:containerView];
|
|
226
|
+
}
|
|
227
|
+
#endif
|
|
228
|
+
|
|
229
|
+
if (verticalLayout == NO) {
|
|
230
|
+
CGFloat insetPadding = 10.0f;
|
|
231
|
+
if (@available(iOS 26.0, *)) {
|
|
232
|
+
insetPadding = 0.0f;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Work out the cancel button frame
|
|
236
|
+
CGRect frame = CGRectZero;
|
|
237
|
+
frame.size.height = 44.0f;
|
|
238
|
+
frame.size.width = _showOnlyIcons ? 44.0f : MIN(self.frame.size.width / 3.0, self.cancelTextButton.frame.size.width);
|
|
239
|
+
|
|
240
|
+
// If normal layout, place on the left side, else place on the right
|
|
241
|
+
if (self.reverseContentLayout == NO) {
|
|
242
|
+
frame.origin.x = insetPadding;
|
|
243
|
+
} else {
|
|
244
|
+
frame.origin.x = boundsSize.width - (frame.size.width + insetPadding);
|
|
245
|
+
}
|
|
246
|
+
(_showOnlyIcons ? self.cancelIconButton : self.cancelTextButton).frame = frame;
|
|
247
|
+
|
|
248
|
+
// Work out the Done button frame
|
|
249
|
+
frame.size.width = _showOnlyIcons ? 44.0f : MIN(self.frame.size.width / 3.0, self.doneTextButton.frame.size.width);
|
|
250
|
+
|
|
251
|
+
if (self.reverseContentLayout == NO) {
|
|
252
|
+
frame.origin.x = boundsSize.width - (frame.size.width + insetPadding);
|
|
253
|
+
} else {
|
|
254
|
+
frame.origin.x = insetPadding;
|
|
255
|
+
}
|
|
256
|
+
(_showOnlyIcons ? self.doneIconButton : self.doneTextButton).frame = frame;
|
|
257
|
+
|
|
258
|
+
// Work out the frame between the two buttons where we can layout our action buttons
|
|
259
|
+
CGFloat x = self.reverseContentLayout ? CGRectGetMaxX((_showOnlyIcons ? self.doneIconButton : self.doneTextButton).frame) : CGRectGetMaxX((_showOnlyIcons ? self.cancelIconButton : self.cancelTextButton).frame);
|
|
260
|
+
CGFloat width = 0.0f;
|
|
261
|
+
|
|
262
|
+
if (self.reverseContentLayout == NO) {
|
|
263
|
+
width = CGRectGetMinX((_showOnlyIcons ? self.doneIconButton : self.doneTextButton).frame) - CGRectGetMaxX((_showOnlyIcons ? self.cancelIconButton : self.cancelTextButton).frame);
|
|
264
|
+
} else {
|
|
265
|
+
width = CGRectGetMinX((_showOnlyIcons ? self.cancelIconButton : self.cancelTextButton).frame) - CGRectGetMaxX((_showOnlyIcons ? self.doneIconButton : self.doneTextButton).frame);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
CGRect containerRect = CGRectIntegral((CGRect){x, frame.origin.y, width, 44.0f});
|
|
269
|
+
|
|
270
|
+
#if TOCROPTOOLBAR_DEBUG_SHOWING_BUTTONS_CONTAINER_RECT
|
|
271
|
+
containerView.frame = containerRect;
|
|
272
|
+
#endif
|
|
273
|
+
|
|
274
|
+
CGSize buttonSize = (CGSize){44.0f, 44.0f};
|
|
275
|
+
|
|
276
|
+
NSMutableArray *buttonsInOrderHorizontally = [NSMutableArray new];
|
|
277
|
+
if (!self.rotateCounterclockwiseButtonHidden) {
|
|
278
|
+
[buttonsInOrderHorizontally addObject:self.rotateCounterclockwiseButton];
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (!self.resetButtonHidden) {
|
|
282
|
+
[buttonsInOrderHorizontally addObject:self.resetButton];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (!self.clampButtonHidden) {
|
|
286
|
+
[buttonsInOrderHorizontally addObject:self.clampButton];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (!self.rotateClockwiseButtonHidden) {
|
|
290
|
+
[buttonsInOrderHorizontally addObject:self.rotateClockwiseButton];
|
|
291
|
+
}
|
|
292
|
+
[self layoutToolbarButtons:buttonsInOrderHorizontally withSameButtonSize:buttonSize inContainerRect:containerRect horizontally:YES];
|
|
293
|
+
} else {
|
|
294
|
+
CGRect frame = CGRectZero;
|
|
295
|
+
frame.size.height = 44.0f;
|
|
296
|
+
frame.size.width = 44.0f;
|
|
297
|
+
frame.origin.y = CGRectGetHeight(self.bounds) - 44.0f;
|
|
298
|
+
self.cancelIconButton.frame = frame;
|
|
299
|
+
|
|
300
|
+
frame.origin.y = self.statusBarHeightInset;
|
|
301
|
+
frame.size.width = 44.0f;
|
|
302
|
+
frame.size.height = 44.0f;
|
|
303
|
+
self.doneIconButton.frame = frame;
|
|
304
|
+
|
|
305
|
+
CGRect containerRect = (CGRect){0, CGRectGetMaxY(self.doneIconButton.frame), 44.0f, CGRectGetMinY(self.cancelIconButton.frame) - CGRectGetMaxY(self.doneIconButton.frame)};
|
|
306
|
+
|
|
307
|
+
#if TOCROPTOOLBAR_DEBUG_SHOWING_BUTTONS_CONTAINER_RECT
|
|
308
|
+
containerView.frame = containerRect;
|
|
309
|
+
#endif
|
|
310
|
+
|
|
311
|
+
CGSize buttonSize = (CGSize){44.0f, 44.0f};
|
|
312
|
+
|
|
313
|
+
NSMutableArray *buttonsInOrderVertically = [NSMutableArray new];
|
|
314
|
+
if (!self.rotateCounterclockwiseButtonHidden) {
|
|
315
|
+
[buttonsInOrderVertically addObject:self.rotateCounterclockwiseButton];
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (!self.resetButtonHidden) {
|
|
319
|
+
[buttonsInOrderVertically addObject:self.resetButton];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (!self.clampButtonHidden) {
|
|
323
|
+
[buttonsInOrderVertically addObject:self.clampButton];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (!self.rotateClockwiseButtonHidden) {
|
|
327
|
+
[buttonsInOrderVertically addObject:self.rotateClockwiseButton];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
[self layoutToolbarButtons:buttonsInOrderVertically withSameButtonSize:buttonSize inContainerRect:containerRect horizontally:NO];
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// The convenience method for calculating button's frame inside of the container rect
|
|
335
|
+
- (void)layoutToolbarButtons:(NSArray<UIButton *> *)buttons withSameButtonSize:(CGSize)size inContainerRect:(CGRect)containerRect horizontally:(BOOL)horizontally {
|
|
336
|
+
// With no buttons to hold, collapse the glass container instead of leaving an
|
|
337
|
+
// empty capsule stranded at the frame it had when the last button was visible
|
|
338
|
+
if (@available(iOS 26.0, *)) {
|
|
339
|
+
_glassView.hidden = (buttons.count == 0);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (!buttons.count) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const CGFloat buttonSize = 44.0f;
|
|
347
|
+
|
|
348
|
+
if (@available(iOS 26.0, *)) {
|
|
349
|
+
CGFloat glassPadding = 6.0f;
|
|
350
|
+
CGFloat buttonPadding = 12.0f;
|
|
351
|
+
CGFloat maxExtent = buttons.count * buttonSize + (buttonPadding * (buttons.count - 1)) + (glassPadding * 2.0f);
|
|
352
|
+
|
|
353
|
+
CGRect glassFrame = CGRectZero;
|
|
354
|
+
glassFrame.size.width = horizontally ? maxExtent : buttonSize;
|
|
355
|
+
glassFrame.size.height = horizontally ? buttonSize : maxExtent;
|
|
356
|
+
glassFrame.origin.x = horizontally ? CGRectGetMidX(containerRect) - (glassFrame.size.width / 2.0f) : 0.0f;
|
|
357
|
+
glassFrame.origin.y = horizontally ? 0.0f : CGRectGetMidY(containerRect) - (glassFrame.size.height / 2.0f);
|
|
358
|
+
_glassView.frame = glassFrame;
|
|
359
|
+
|
|
360
|
+
CGFloat position = glassPadding;
|
|
361
|
+
for (UIButton *button in buttons) {
|
|
362
|
+
CGRect buttonFrame = CGRectMake(0.0, 0.0, buttonSize, buttonSize);
|
|
363
|
+
if (horizontally) {
|
|
364
|
+
buttonFrame.origin.x = position;
|
|
365
|
+
} else {
|
|
366
|
+
buttonFrame.origin.y = position;
|
|
367
|
+
}
|
|
368
|
+
button.frame = buttonFrame;
|
|
369
|
+
position += buttonSize + buttonPadding;
|
|
370
|
+
}
|
|
371
|
+
} else {
|
|
372
|
+
NSInteger count = buttons.count;
|
|
373
|
+
CGFloat fixedSize = horizontally ? size.width : size.height;
|
|
374
|
+
CGFloat maxLength = horizontally ? CGRectGetWidth(containerRect) : CGRectGetHeight(containerRect);
|
|
375
|
+
CGFloat padding = (maxLength - fixedSize * count) / (count + 1);
|
|
376
|
+
|
|
377
|
+
for (NSInteger i = 0; i < count; i++) {
|
|
378
|
+
UIButton *button = buttons[i];
|
|
379
|
+
CGFloat sameOffset = horizontally ? fabs(CGRectGetHeight(containerRect) - 44.0f) : fabs(CGRectGetWidth(containerRect) - size.width);
|
|
380
|
+
CGFloat diffOffset = padding + i * (fixedSize + padding);
|
|
381
|
+
CGPoint origin = horizontally ? CGPointMake(diffOffset, sameOffset) : CGPointMake(sameOffset, diffOffset);
|
|
382
|
+
if (horizontally) {
|
|
383
|
+
origin.x += CGRectGetMinX(containerRect);
|
|
384
|
+
if (@available(iOS 15.0, *)) {
|
|
385
|
+
// iOS 15+: Use UIButtonConfiguration
|
|
386
|
+
UIButtonConfiguration *config = button.configuration ?: [UIButtonConfiguration plainButtonConfiguration];
|
|
387
|
+
|
|
388
|
+
// Position image and text
|
|
389
|
+
config.imagePlacement = NSDirectionalRectEdgeLeading;
|
|
390
|
+
config.imagePadding = 8;
|
|
391
|
+
|
|
392
|
+
UIImage *image = button.imageView.image;
|
|
393
|
+
config.contentInsets = NSDirectionalEdgeInsetsMake(0, 0, image.baselineOffsetFromBottom, 0);
|
|
394
|
+
|
|
395
|
+
button.configuration = config;
|
|
396
|
+
|
|
397
|
+
} else if (@available(iOS 13.0, *)) {
|
|
398
|
+
UIImage *image = button.imageView.image;
|
|
399
|
+
button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, image.baselineOffsetFromBottom, 0);
|
|
400
|
+
}
|
|
401
|
+
} else {
|
|
402
|
+
origin.y += CGRectGetMinY(containerRect);
|
|
403
|
+
}
|
|
404
|
+
button.frame = (CGRect){origin, size};
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
- (void)buttonTapped:(id)button {
|
|
410
|
+
if (button == self.cancelTextButton || button == self.cancelIconButton) {
|
|
411
|
+
if (self.cancelButtonTapped)
|
|
412
|
+
self.cancelButtonTapped();
|
|
413
|
+
} else if (button == self.doneTextButton || button == self.doneIconButton) {
|
|
414
|
+
if (self.doneButtonTapped)
|
|
415
|
+
self.doneButtonTapped();
|
|
416
|
+
} else if (button == self.resetButton && self.resetButtonTapped) {
|
|
417
|
+
self.resetButtonTapped();
|
|
418
|
+
} else if (button == self.rotateCounterclockwiseButton && self.rotateCounterclockwiseButtonTapped) {
|
|
419
|
+
self.rotateCounterclockwiseButtonTapped();
|
|
420
|
+
} else if (button == self.rotateClockwiseButton && self.rotateClockwiseButtonTapped) {
|
|
421
|
+
self.rotateClockwiseButtonTapped();
|
|
422
|
+
} else if (button == self.clampButton && self.clampButtonTapped) {
|
|
423
|
+
self.clampButtonTapped();
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
- (CGRect)clampButtonFrame {
|
|
429
|
+
// Convert into the toolbar's space; on iOS 26 the button lives inside the glass container
|
|
430
|
+
return [self convertRect:self.clampButton.bounds fromView:self.clampButton];
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
- (void)setReverseContentLayout:(BOOL)reverseContentLayout {
|
|
434
|
+
if (_reverseContentLayout == reverseContentLayout)
|
|
435
|
+
return;
|
|
436
|
+
|
|
437
|
+
_reverseContentLayout = reverseContentLayout;
|
|
438
|
+
[self setNeedsLayout];
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
- (void)setClampButtonHidden:(BOOL)clampButtonHidden {
|
|
442
|
+
if (_clampButtonHidden == clampButtonHidden)
|
|
443
|
+
return;
|
|
444
|
+
|
|
445
|
+
_clampButtonHidden = clampButtonHidden;
|
|
446
|
+
[self setNeedsLayout];
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
- (void)setClampButtonGlowing:(BOOL)clampButtonGlowing {
|
|
450
|
+
if (_clampButtonGlowing == clampButtonGlowing)
|
|
451
|
+
return;
|
|
452
|
+
|
|
453
|
+
_clampButtonGlowing = clampButtonGlowing;
|
|
454
|
+
|
|
455
|
+
if (_clampButtonGlowing)
|
|
456
|
+
self.clampButton.tintColor = nil;
|
|
457
|
+
else
|
|
458
|
+
self.clampButton.tintColor = [UIColor whiteColor];
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
- (void)setRotateCounterclockwiseButtonHidden:(BOOL)rotateButtonHidden {
|
|
462
|
+
if (_rotateCounterclockwiseButtonHidden == rotateButtonHidden)
|
|
463
|
+
return;
|
|
464
|
+
|
|
465
|
+
_rotateCounterclockwiseButtonHidden = rotateButtonHidden;
|
|
466
|
+
[self setNeedsLayout];
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
- (BOOL)resetButtonEnabled {
|
|
470
|
+
return self.resetButton.enabled;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
- (void)setResetButtonEnabled:(BOOL)resetButtonEnabled {
|
|
474
|
+
self.resetButton.enabled = resetButtonEnabled;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
- (void)setDoneButtonHidden:(BOOL)doneButtonHidden {
|
|
478
|
+
if (_doneButtonHidden == doneButtonHidden)
|
|
479
|
+
return;
|
|
480
|
+
|
|
481
|
+
_doneButtonHidden = doneButtonHidden;
|
|
482
|
+
[self setNeedsLayout];
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
- (void)setCancelButtonHidden:(BOOL)cancelButtonHidden {
|
|
486
|
+
if (_cancelButtonHidden == cancelButtonHidden)
|
|
487
|
+
return;
|
|
488
|
+
|
|
489
|
+
_cancelButtonHidden = cancelButtonHidden;
|
|
490
|
+
[self setNeedsLayout];
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
- (CGRect)doneButtonFrame {
|
|
494
|
+
if (self.doneIconButton.hidden == NO)
|
|
495
|
+
return self.doneIconButton.frame;
|
|
496
|
+
|
|
497
|
+
return self.doneTextButton.frame;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
- (void)setShowOnlyIcons:(BOOL)showOnlyIcons {
|
|
501
|
+
// The text variants of the Done and Cancel buttons aren't created at all on iOS 26
|
|
502
|
+
// and up, where the toolbar is permanently icon-only. Honouring a NO in that case
|
|
503
|
+
// would hide the icon buttons with nothing to replace them, leaving no way to
|
|
504
|
+
// commit or cancel the crop at all.
|
|
505
|
+
if (_doneTextButton == nil || _cancelTextButton == nil) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (_showOnlyIcons == showOnlyIcons)
|
|
510
|
+
return;
|
|
511
|
+
|
|
512
|
+
_showOnlyIcons = showOnlyIcons;
|
|
513
|
+
[_doneIconButton sizeToFit];
|
|
514
|
+
[_cancelIconButton sizeToFit];
|
|
515
|
+
[self setNeedsLayout];
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
- (void)setCancelTextButtonTitle:(NSString *)cancelTextButtonTitle {
|
|
519
|
+
_cancelTextButtonTitle = cancelTextButtonTitle;
|
|
520
|
+
[_cancelTextButton setTitle:_cancelTextButtonTitle forState:UIControlStateNormal];
|
|
521
|
+
[_cancelTextButton sizeToFit];
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
- (void)setDoneTextButtonTitle:(NSString *)doneTextButtonTitle {
|
|
525
|
+
_doneTextButtonTitle = doneTextButtonTitle;
|
|
526
|
+
[_doneTextButton setTitle:_doneTextButtonTitle forState:UIControlStateNormal];
|
|
527
|
+
[_doneTextButton sizeToFit];
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
- (void)setCancelButtonColor:(UIColor *)cancelButtonColor {
|
|
531
|
+
// Default color is app tint color
|
|
532
|
+
if (cancelButtonColor == _cancelButtonColor) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
_cancelButtonColor = cancelButtonColor;
|
|
536
|
+
[_cancelTextButton setTitleColor:_cancelButtonColor forState:UIControlStateNormal];
|
|
537
|
+
[_cancelIconButton setTintColor:_cancelButtonColor];
|
|
538
|
+
[_cancelTextButton sizeToFit];
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
- (void)setDoneButtonColor:(UIColor *)doneButtonColor {
|
|
542
|
+
// Set the default color when nil is specified
|
|
543
|
+
if (doneButtonColor == nil) {
|
|
544
|
+
doneButtonColor = [UIColor colorWithRed:1.0f green:0.8f blue:0.0f alpha:1.0f];
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (doneButtonColor == _doneButtonColor) {
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
_doneButtonColor = doneButtonColor;
|
|
552
|
+
[_doneTextButton setTitleColor:_doneButtonColor forState:UIControlStateNormal];
|
|
553
|
+
[_doneIconButton setTintColor:_doneButtonColor];
|
|
554
|
+
[_doneTextButton sizeToFit];
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
#pragma mark - Image Generation -
|
|
558
|
+
+ (UIImage *)doneImage {
|
|
559
|
+
if (@available(iOS 13.0, *)) {
|
|
560
|
+
return [UIImage systemImageNamed:@"checkmark"
|
|
561
|
+
withConfiguration:[UIImageSymbolConfiguration configurationWithWeight:UIImageSymbolWeightSemibold]];
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:(CGSize){17, 14}];
|
|
565
|
+
UIImage *doneImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
|
|
566
|
+
UIBezierPath *rectanglePath = UIBezierPath.bezierPath;
|
|
567
|
+
[rectanglePath moveToPoint:CGPointMake(1, 7)];
|
|
568
|
+
[rectanglePath addLineToPoint:CGPointMake(6, 12)];
|
|
569
|
+
[rectanglePath addLineToPoint:CGPointMake(16, 1)];
|
|
570
|
+
[UIColor.whiteColor setStroke];
|
|
571
|
+
rectanglePath.lineWidth = 2;
|
|
572
|
+
[rectanglePath stroke];
|
|
573
|
+
}];
|
|
574
|
+
|
|
575
|
+
return doneImage;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
+ (UIImage *)cancelImage {
|
|
579
|
+
if (@available(iOS 13.0, *)) {
|
|
580
|
+
return [UIImage systemImageNamed:@"xmark"
|
|
581
|
+
withConfiguration:[UIImageSymbolConfiguration configurationWithWeight:UIImageSymbolWeightSemibold]];
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:(CGSize){16, 16}];
|
|
585
|
+
UIImage *cancelImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
|
|
586
|
+
UIBezierPath *bezierPath = UIBezierPath.bezierPath;
|
|
587
|
+
[bezierPath moveToPoint:CGPointMake(15, 15)];
|
|
588
|
+
[bezierPath addLineToPoint:CGPointMake(1, 1)];
|
|
589
|
+
[UIColor.whiteColor setStroke];
|
|
590
|
+
bezierPath.lineWidth = 2;
|
|
591
|
+
[bezierPath stroke];
|
|
592
|
+
|
|
593
|
+
UIBezierPath *bezier2Path = UIBezierPath.bezierPath;
|
|
594
|
+
[bezier2Path moveToPoint:CGPointMake(1, 15)];
|
|
595
|
+
[bezier2Path addLineToPoint:CGPointMake(15, 1)];
|
|
596
|
+
[UIColor.whiteColor setStroke];
|
|
597
|
+
bezier2Path.lineWidth = 2;
|
|
598
|
+
[bezier2Path stroke];
|
|
599
|
+
}];
|
|
600
|
+
|
|
601
|
+
return cancelImage;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
+ (UIImage *)rotateCCWImage {
|
|
605
|
+
if (@available(iOS 13.0, *)) {
|
|
606
|
+
return [[UIImage systemImageNamed:@"rotate.left.fill"
|
|
607
|
+
withConfiguration:[UIImageSymbolConfiguration configurationWithWeight:UIImageSymbolWeightSemibold]]
|
|
608
|
+
imageWithBaselineOffsetFromBottom:4];
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:(CGSize){18, 21}];
|
|
612
|
+
UIImage *rotateImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
|
|
613
|
+
UIBezierPath *rectangle2Path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 9, 12, 12)];
|
|
614
|
+
[UIColor.whiteColor setFill];
|
|
615
|
+
[rectangle2Path fill];
|
|
616
|
+
|
|
617
|
+
UIBezierPath *rectangle3Path = UIBezierPath.bezierPath;
|
|
618
|
+
[rectangle3Path moveToPoint:CGPointMake(5, 3)];
|
|
619
|
+
[rectangle3Path addLineToPoint:CGPointMake(10, 6)];
|
|
620
|
+
[rectangle3Path addLineToPoint:CGPointMake(10, 0)];
|
|
621
|
+
[rectangle3Path addLineToPoint:CGPointMake(5, 3)];
|
|
622
|
+
[rectangle3Path closePath];
|
|
623
|
+
[UIColor.whiteColor setFill];
|
|
624
|
+
[rectangle3Path fill];
|
|
625
|
+
|
|
626
|
+
UIBezierPath *bezierPath = UIBezierPath.bezierPath;
|
|
627
|
+
[bezierPath moveToPoint:CGPointMake(10, 3)];
|
|
628
|
+
[bezierPath addCurveToPoint:CGPointMake(17.5, 11) controlPoint1:CGPointMake(15, 3) controlPoint2:CGPointMake(17.5, 5.91)];
|
|
629
|
+
[UIColor.whiteColor setStroke];
|
|
630
|
+
bezierPath.lineWidth = 1;
|
|
631
|
+
[bezierPath stroke];
|
|
632
|
+
}];
|
|
633
|
+
|
|
634
|
+
return rotateImage;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
+ (UIImage *)rotateCWImage {
|
|
638
|
+
if (@available(iOS 13.0, *)) {
|
|
639
|
+
return [[UIImage systemImageNamed:@"rotate.right.fill"
|
|
640
|
+
withConfiguration:[UIImageSymbolConfiguration configurationWithWeight:UIImageSymbolWeightSemibold]]
|
|
641
|
+
imageWithBaselineOffsetFromBottom:4];
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
UIImage *rotateCCWImage = [self.class rotateCCWImage];
|
|
645
|
+
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:rotateCCWImage.size];
|
|
646
|
+
UIImage *rotateCWImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
|
|
647
|
+
CGContextRef context = rendererContext.CGContext;
|
|
648
|
+
CGContextTranslateCTM(context, rotateCCWImage.size.width, rotateCCWImage.size.height);
|
|
649
|
+
CGContextRotateCTM(context, M_PI);
|
|
650
|
+
CGContextDrawImage(context, CGRectMake(0, 0, rotateCCWImage.size.width, rotateCCWImage.size.height), rotateCCWImage.CGImage);
|
|
651
|
+
}];
|
|
652
|
+
|
|
653
|
+
return rotateCWImage;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
+ (UIImage *)resetImage {
|
|
657
|
+
if (@available(iOS 13.0, *)) {
|
|
658
|
+
return [[UIImage systemImageNamed:@"arrow.counterclockwise"
|
|
659
|
+
withConfiguration:[UIImageSymbolConfiguration configurationWithWeight:UIImageSymbolWeightSemibold]]
|
|
660
|
+
imageWithBaselineOffsetFromBottom:0];
|
|
661
|
+
;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:(CGSize){22, 18}];
|
|
665
|
+
UIImage *resetImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
|
|
666
|
+
UIBezierPath *bezier2Path = UIBezierPath.bezierPath;
|
|
667
|
+
[bezier2Path moveToPoint:CGPointMake(22, 9)];
|
|
668
|
+
[bezier2Path addCurveToPoint:CGPointMake(13, 18) controlPoint1:CGPointMake(22, 13.97) controlPoint2:CGPointMake(17.97, 18)];
|
|
669
|
+
[bezier2Path addCurveToPoint:CGPointMake(13, 16) controlPoint1:CGPointMake(13, 17.35) controlPoint2:CGPointMake(13, 16.68)];
|
|
670
|
+
[bezier2Path addCurveToPoint:CGPointMake(20, 9) controlPoint1:CGPointMake(16.87, 16) controlPoint2:CGPointMake(20, 12.87)];
|
|
671
|
+
[bezier2Path addCurveToPoint:CGPointMake(13, 2) controlPoint1:CGPointMake(20, 5.13) controlPoint2:CGPointMake(16.87, 2)];
|
|
672
|
+
[bezier2Path addCurveToPoint:CGPointMake(6.55, 6.27) controlPoint1:CGPointMake(10.1, 2) controlPoint2:CGPointMake(7.62, 3.76)];
|
|
673
|
+
[bezier2Path addCurveToPoint:CGPointMake(6, 9) controlPoint1:CGPointMake(6.2, 7.11) controlPoint2:CGPointMake(6, 8.03)];
|
|
674
|
+
[bezier2Path addLineToPoint:CGPointMake(4, 9)];
|
|
675
|
+
[bezier2Path addCurveToPoint:CGPointMake(4.65, 5.63) controlPoint1:CGPointMake(4, 7.81) controlPoint2:CGPointMake(4.23, 6.67)];
|
|
676
|
+
[bezier2Path addCurveToPoint:CGPointMake(7.65, 1.76) controlPoint1:CGPointMake(5.28, 4.08) controlPoint2:CGPointMake(6.32, 2.74)];
|
|
677
|
+
[bezier2Path addCurveToPoint:CGPointMake(13, 0) controlPoint1:CGPointMake(9.15, 0.65) controlPoint2:CGPointMake(11, 0)];
|
|
678
|
+
[bezier2Path addCurveToPoint:CGPointMake(22, 9) controlPoint1:CGPointMake(17.97, 0) controlPoint2:CGPointMake(22, 4.03)];
|
|
679
|
+
[bezier2Path closePath];
|
|
680
|
+
[UIColor.whiteColor setFill];
|
|
681
|
+
[bezier2Path fill];
|
|
682
|
+
|
|
683
|
+
UIBezierPath *polygonPath = UIBezierPath.bezierPath;
|
|
684
|
+
[polygonPath moveToPoint:CGPointMake(5, 15)];
|
|
685
|
+
[polygonPath addLineToPoint:CGPointMake(10, 9)];
|
|
686
|
+
[polygonPath addLineToPoint:CGPointMake(0, 9)];
|
|
687
|
+
[polygonPath addLineToPoint:CGPointMake(5, 15)];
|
|
688
|
+
[polygonPath closePath];
|
|
689
|
+
[UIColor.whiteColor setFill];
|
|
690
|
+
[polygonPath fill];
|
|
691
|
+
}];
|
|
692
|
+
|
|
693
|
+
return resetImage;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
+ (UIImage *)clampImage {
|
|
697
|
+
if (@available(iOS 13.0, *)) {
|
|
698
|
+
return [[UIImage systemImageNamed:@"aspectratio.fill"
|
|
699
|
+
withConfiguration:[UIImageSymbolConfiguration configurationWithWeight:UIImageSymbolWeightSemibold]]
|
|
700
|
+
imageWithBaselineOffsetFromBottom:0];
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:(CGSize){22, 16}];
|
|
704
|
+
UIImage *clampImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
|
|
705
|
+
//// Color Declarations
|
|
706
|
+
UIColor *outerBox = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.553];
|
|
707
|
+
UIColor *innerBox = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.773];
|
|
708
|
+
|
|
709
|
+
//// Rectangle Drawing
|
|
710
|
+
UIBezierPath *rectanglePath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 3, 13, 13)];
|
|
711
|
+
[UIColor.whiteColor setFill];
|
|
712
|
+
[rectanglePath fill];
|
|
713
|
+
|
|
714
|
+
//// Outer
|
|
715
|
+
{
|
|
716
|
+
//// Top Drawing
|
|
717
|
+
UIBezierPath *topPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 22, 2)];
|
|
718
|
+
[outerBox setFill];
|
|
719
|
+
[topPath fill];
|
|
720
|
+
|
|
721
|
+
//// Side Drawing
|
|
722
|
+
UIBezierPath *sidePath = [UIBezierPath bezierPathWithRect:CGRectMake(19, 2, 3, 14)];
|
|
723
|
+
[outerBox setFill];
|
|
724
|
+
[sidePath fill];
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
//// Rectangle 2 Drawing
|
|
728
|
+
UIBezierPath *rectangle2Path = [UIBezierPath bezierPathWithRect:CGRectMake(14, 3, 4, 13)];
|
|
729
|
+
[innerBox setFill];
|
|
730
|
+
[rectangle2Path fill];
|
|
731
|
+
}];
|
|
732
|
+
|
|
733
|
+
return clampImage;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
#pragma mark - Accessors -
|
|
737
|
+
|
|
738
|
+
- (void)setRotateClockwiseButtonHidden:(BOOL)rotateClockwiseButtonHidden {
|
|
739
|
+
if (_rotateClockwiseButtonHidden == rotateClockwiseButtonHidden) {
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
_rotateClockwiseButtonHidden = rotateClockwiseButtonHidden;
|
|
744
|
+
|
|
745
|
+
[self setNeedsLayout];
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
- (void)setResetButtonHidden:(BOOL)resetButtonHidden {
|
|
749
|
+
if (_resetButtonHidden == resetButtonHidden) {
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
_resetButtonHidden = resetButtonHidden;
|
|
754
|
+
|
|
755
|
+
[self setNeedsLayout];
|
|
756
|
+
}
|
|
757
|
+
- (UIButton *)rotateButton {
|
|
758
|
+
return self.rotateCounterclockwiseButton;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
- (void)setStatusBarHeightInset:(CGFloat)statusBarHeightInset {
|
|
762
|
+
_statusBarHeightInset = statusBarHeightInset;
|
|
763
|
+
[self setNeedsLayout];
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
- (UIView *)visibleCancelButton {
|
|
767
|
+
if (self.cancelIconButton.hidden == NO) {
|
|
768
|
+
return self.cancelIconButton;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
return self.cancelTextButton;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
- (void)setDisableRotationButtons:(BOOL)disableRotationButtons {
|
|
775
|
+
if (_disableRotationButtons == disableRotationButtons) {
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
_disableRotationButtons = disableRotationButtons;
|
|
779
|
+
_rotateClockwiseButton.enabled = !disableRotationButtons;
|
|
780
|
+
_rotateCounterclockwiseButton.enabled = !disableRotationButtons;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
@end
|