Skip to content

Move TOCropViewControllerAspectRatioPreset to a class and make it configurable #608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions Objective-C/TOCropViewController/Constants/TOCropViewConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,6 @@ typedef NS_ENUM(NSInteger, TOCropViewCroppingStyle) {
TOCropViewCroppingStyleCircular // A fixed, circular crop box
};

/**
Preset values of the most common aspect ratios that can be used to quickly configure
the crop view controller.
*/
typedef NS_ENUM(NSInteger, TOCropViewControllerAspectRatioPreset) {
TOCropViewControllerAspectRatioPresetOriginal,
TOCropViewControllerAspectRatioPresetSquare,
TOCropViewControllerAspectRatioPreset3x2,
TOCropViewControllerAspectRatioPreset5x3,
TOCropViewControllerAspectRatioPreset4x3,
TOCropViewControllerAspectRatioPreset5x4,
TOCropViewControllerAspectRatioPreset7x5,
TOCropViewControllerAspectRatioPreset16x9,
TOCropViewControllerAspectRatioPresetCustom
};

/**
Whether the control toolbar is placed at the bottom or the top
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// TOCropViewControllerAspectRatioPreset.h
//
// Copyright 2015-2024 Timothy Oliver. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface TOCropViewControllerAspectRatioPreset : NSObject

@property (nonatomic, readonly) CGSize size;
@property (nonatomic, readonly) NSString *title;

+ (NSArray<TOCropViewControllerAspectRatioPreset *> *)portraitPresets;
+ (NSArray<TOCropViewControllerAspectRatioPreset *> *)landscapePresets;

- (nonnull instancetype)initWithSize:(CGSize)size title:(NSString *)title;

@end

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//
// TOCropViewControllerAspectRatioPreset.m
//
// Copyright 2015-2024 Timothy Oliver. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#import "TOCropViewControllerAspectRatioPreset.h"
#if !__has_include(<TOCropViewController/TOCropViewConstants.h>)
#import "TOCropViewConstants.h"
#else
#import <TOCropViewController/TOCropViewConstants.h>
#endif

@interface TOCropViewControllerAspectRatioPreset ()

@property (nonatomic, assign, readwrite) CGFloat width;
@property (nonatomic, assign, readwrite) CGFloat height;
@property (nonatomic, strong, readwrite) NSString *title;

@end

@implementation TOCropViewControllerAspectRatioPreset

- (instancetype)initWithSize:(CGSize)size title:(NSString *)title
{
self = [super init];
if (self) {
_size = size;
_title = title;
}
return self;
}

- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[TOCropViewControllerAspectRatioPreset class]]) {
return NO;
}
TOCropViewControllerAspectRatioPreset *other = (TOCropViewControllerAspectRatioPreset *)object;
return CGSizeEqualToSize(self.size, other.size) && [self.title isEqualToString:other.title];
}

+ (NSArray<TOCropViewControllerAspectRatioPreset *> *)portraitPresets
{
TOCropViewControllerAspectRatioPreset *object = [[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeZero title:@"Original"];
NSBundle *resourceBundle = TO_CROP_VIEW_RESOURCE_BUNDLE_FOR_OBJECT(object);
return @[
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeZero title:NSLocalizedStringFromTableInBundle(@"Original", @"TOCropViewControllerLocalizable", resourceBundle, nil)],
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeMake(1.0f, 1.0f) title:NSLocalizedStringFromTableInBundle(@"Square", @"TOCropViewControllerLocalizable", resourceBundle, nil)],
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeMake(2.0f, 3.0f) title:@"2:3"],
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeMake(3.0f, 5.0f) title:@"3:5"],
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeMake(3.0f, 4.0f) title:@"3:4"],
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeMake(4.0f, 5.0f) title:@"4:5"],
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeMake(5.0f, 7.0f) title:@"5:7"],
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeMake(9.0f, 16.0f) title:@"9:16"],
];
}

+ (NSArray<TOCropViewControllerAspectRatioPreset *> *)landscapePresets
{
TOCropViewControllerAspectRatioPreset *object = [[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeZero title:@"Original"];
NSBundle *resourceBundle = TO_CROP_VIEW_RESOURCE_BUNDLE_FOR_OBJECT(object);
return @[
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeZero title:NSLocalizedStringFromTableInBundle(@"Original", @"TOCropViewControllerLocalizable", resourceBundle, nil)],
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeMake(1.0f, 1.0f) title:NSLocalizedStringFromTableInBundle(@"Square", @"TOCropViewControllerLocalizable", resourceBundle, nil)],
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeMake(3.0f, 2.0f) title:@"3:2"],
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeMake(5.0f, 3.0f) title:@"5:3"],
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeMake(4.0f, 3.0f) title:@"4:3"],
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeMake(5.0f, 4.0f) title:@"5:4"],
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeMake(7.0f, 5.0f) title:@"7:5"],
[[TOCropViewControllerAspectRatioPreset alloc] initWithSize:CGSizeMake(16.0f, 9.0f) title:@"16:9"],
];
}
@end


20 changes: 5 additions & 15 deletions Objective-C/TOCropViewController/TOCropViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@

#if !__has_include(<TOCropViewController/TOCropViewConstants.h>)
#import "TOCropViewConstants.h"
#import "TOCropViewControllerAspectRatioPreset.h"
#import "TOCropView.h"
#import "TOCropToolbar.h"
#else
#import <TOCropViewController/TOCropViewConstants.h>
#import <TOCropViewController/TOCropViewControllerAspectRatioPreset.h>
#import <TOCropViewController/TOCropView.h>
#import <TOCropViewController/TOCropToolbar.h>
#endif
Expand Down Expand Up @@ -150,19 +152,7 @@
/**
A choice from one of the pre-defined aspect ratio presets
*/
@property (nonatomic, assign) TOCropViewControllerAspectRatioPreset aspectRatioPreset;

/**
A CGSize value representing a custom aspect ratio, not listed in the presets.
E.g. A ratio of 4:3 would be represented as (CGSize){4.0f, 3.0f}
*/
@property (nonatomic, assign) CGSize customAspectRatio;

/**
If this is set alongside `customAspectRatio`, the custom aspect ratio
will be shown as a selectable choice in the list of aspect ratios. (Default is `nil`)
*/
@property (nullable, nonatomic, copy) NSString *customAspectRatioName;
@property (nonatomic, assign) CGSize aspectRatioPreset;

/**
Title label which can be used to show instruction on the top of the crop view controller
Expand Down Expand Up @@ -324,7 +314,7 @@
An array of `TOCropViewControllerAspectRatioPreset` enum values denoting which
aspect ratios the crop view controller may display (Default is nil. All are shown)
*/
@property (nullable, nonatomic, strong) NSArray<NSNumber *> *allowedAspectRatios;
@property (nullable, nonatomic, strong) NSArray<TOCropViewControllerAspectRatioPreset *> *allowedAspectRatios;

/**
When the user hits cancel, or completes a
Expand Down Expand Up @@ -402,7 +392,7 @@
@param aspectRatioPreset The aspect ratio preset
@param animated Whether the transition to the aspect ratio is animated
*/
- (void)setAspectRatioPreset:(TOCropViewControllerAspectRatioPreset)aspectRatioPreset animated:(BOOL)animated NS_SWIFT_NAME(setAspectRatioPreset(_:animated:));
- (void)setAspectRatioPreset:(CGSize)aspectRatioPreset animated:(BOOL)animated NS_SWIFT_NAME(setAspectRatioPreset(_:animated:));

/**
Play a custom animation of the target image zooming to its position in
Expand Down
96 changes: 10 additions & 86 deletions Objective-C/TOCropViewController/TOCropViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ - (instancetype)initWithCroppingStyle:(TOCropViewCroppingStyle)style image:(UIIm
_transitionController = [[TOCropViewControllerTransitioning alloc] init];

// Default initial behaviour
_aspectRatioPreset = TOCropViewControllerAspectRatioPresetOriginal;
_aspectRatioPreset = CGSizeZero;

#if TARGET_OS_MACCATALYST
_toolbarPosition = TOCropViewControllerToolbarPositionTop;
Expand Down Expand Up @@ -170,7 +170,7 @@ - (void)viewWillAppear:(BOOL)animated

// If an initial aspect ratio was set before presentation, set it now once the rest of
// the setup will have been done
if (self.aspectRatioPreset != TOCropViewControllerAspectRatioPresetOriginal) {
if (!CGSizeEqualToSize(self.aspectRatioPreset, CGSizeZero)) {
[self setAspectRatioPreset:self.aspectRatioPreset animated:NO];
}
}
Expand Down Expand Up @@ -586,50 +586,28 @@ - (void)showAspectRatioDialog

//Prepare the localized options
NSString *cancelButtonTitle = NSLocalizedStringFromTableInBundle(@"Cancel", @"TOCropViewControllerLocalizable", resourceBundle, nil);
NSString *originalButtonTitle = NSLocalizedStringFromTableInBundle(@"Original", @"TOCropViewControllerLocalizable", resourceBundle, nil);
NSString *squareButtonTitle = NSLocalizedStringFromTableInBundle(@"Square", @"TOCropViewControllerLocalizable", resourceBundle, nil);

//Prepare the list that will be fed to the alert view/controller

// Ratio titles according to the order of enum TOCropViewControllerAspectRatioPreset
NSArray<NSString *> *portraitRatioTitles = @[originalButtonTitle, squareButtonTitle, @"2:3", @"3:5", @"3:4", @"4:5", @"5:7", @"9:16"];
NSArray<NSString *> *landscapeRatioTitles = @[originalButtonTitle, squareButtonTitle, @"3:2", @"5:3", @"4:3", @"5:4", @"7:5", @"16:9"];

NSMutableArray *ratioValues = [NSMutableArray array];
NSMutableArray *itemStrings = [NSMutableArray array];
NSArray<TOCropViewControllerAspectRatioPreset *> *presets;

if (self.allowedAspectRatios == nil) {
for (NSInteger i = 0; i < TOCropViewControllerAspectRatioPresetCustom; i++) {
NSString *itemTitle = verticalCropBox ? portraitRatioTitles[i] : landscapeRatioTitles[i];
[itemStrings addObject:itemTitle];
[ratioValues addObject:@(i)];
}
presets = verticalCropBox ? [TOCropViewControllerAspectRatioPreset portraitPresets] : [TOCropViewControllerAspectRatioPreset landscapePresets];
}
else {
for (NSNumber *allowedRatio in self.allowedAspectRatios) {
TOCropViewControllerAspectRatioPreset ratio = allowedRatio.integerValue;
NSString *itemTitle = verticalCropBox ? portraitRatioTitles[ratio] : landscapeRatioTitles[ratio];
[itemStrings addObject:itemTitle];
[ratioValues addObject:allowedRatio];
}
}

// If a custom aspect ratio is provided, and a custom name has been given to it, add it as a visible choice
if (self.customAspectRatioName.length > 0 && !CGSizeEqualToSize(CGSizeZero, self.customAspectRatio)) {
[itemStrings addObject:self.customAspectRatioName];
[ratioValues addObject:@(TOCropViewControllerAspectRatioPresetCustom)];
presets = self.allowedAspectRatios;
}

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[alertController addAction:[UIAlertAction actionWithTitle:cancelButtonTitle style:UIAlertActionStyleCancel handler:nil]];

//Add each item to the alert controller
for (NSInteger i = 0; i < itemStrings.count; i++) {
for (NSInteger i = 0; i < presets.count; i++) {
id handlerBlock = ^(UIAlertAction *action) {
[self setAspectRatioPreset:[ratioValues[i] integerValue] animated:YES];
[self setAspectRatioPreset:presets[i].size animated:YES];
self.aspectRatioLockEnabled = YES;
};
UIAlertAction *action = [UIAlertAction actionWithTitle:itemStrings[i] style:UIAlertActionStyleDefault handler:handlerBlock];
UIAlertAction *action = [UIAlertAction actionWithTitle:presets[i].title style:UIAlertActionStyleDefault handler:handlerBlock];
[alertController addAction:action];
}

Expand All @@ -640,58 +618,10 @@ - (void)showAspectRatioDialog
[self presentViewController:alertController animated:YES completion:nil];
}

- (void)setAspectRatioPreset:(TOCropViewControllerAspectRatioPreset)aspectRatioPreset animated:(BOOL)animated
- (void)setAspectRatioPreset:(CGSize)aspectRatioPreset animated:(BOOL)animated
{
CGSize aspectRatio = CGSizeZero;

_aspectRatioPreset = aspectRatioPreset;

switch (aspectRatioPreset) {
case TOCropViewControllerAspectRatioPresetOriginal:
aspectRatio = CGSizeZero;
break;
case TOCropViewControllerAspectRatioPresetSquare:
aspectRatio = CGSizeMake(1.0f, 1.0f);
break;
case TOCropViewControllerAspectRatioPreset3x2:
aspectRatio = CGSizeMake(3.0f, 2.0f);
break;
case TOCropViewControllerAspectRatioPreset5x3:
aspectRatio = CGSizeMake(5.0f, 3.0f);
break;
case TOCropViewControllerAspectRatioPreset4x3:
aspectRatio = CGSizeMake(4.0f, 3.0f);
break;
case TOCropViewControllerAspectRatioPreset5x4:
aspectRatio = CGSizeMake(5.0f, 4.0f);
break;
case TOCropViewControllerAspectRatioPreset7x5:
aspectRatio = CGSizeMake(7.0f, 5.0f);
break;
case TOCropViewControllerAspectRatioPreset16x9:
aspectRatio = CGSizeMake(16.0f, 9.0f);
break;
case TOCropViewControllerAspectRatioPresetCustom:
aspectRatio = self.customAspectRatio;
break;
}

// If the aspect ratio lock is not enabled, allow a swap
// If the aspect ratio lock is on, allow a aspect ratio swap
// only if the allowDimensionSwap option is specified.
BOOL aspectRatioCanSwapDimensions = !self.aspectRatioLockEnabled ||
(self.aspectRatioLockEnabled && self.aspectRatioLockDimensionSwapEnabled);

//If the image is a portrait shape, flip the aspect ratio to match
if (self.cropView.cropBoxAspectRatioIsPortrait &&
aspectRatioCanSwapDimensions)
{
CGFloat width = aspectRatio.width;
aspectRatio.width = aspectRatio.height;
aspectRatio.height = width;
}

[self.cropView setAspectRatio:aspectRatio animated:animated];
[self.cropView setAspectRatio:aspectRatioPreset animated:animated];
}

- (void)rotateCropViewClockwise
Expand Down Expand Up @@ -1215,12 +1145,6 @@ - (void)setResetAspectRatioEnabled:(BOOL)resetAspectRatioEnabled
}
}

- (void)setCustomAspectRatio:(CGSize)customAspectRatio
{
_customAspectRatio = customAspectRatio;
[self setAspectRatioPreset:TOCropViewControllerAspectRatioPresetCustom animated:NO];
}

- (BOOL)resetAspectRatioEnabled
{
return self.cropView.resetAspectRatioEnabled;
Expand Down
1 change: 1 addition & 0 deletions Swift/CropViewController/CropViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#import "TOCropView.h"
#import "TOCropToolbar.h"
#import "TOCropViewConstants.h"
#import "TOCropViewControllerAspectRatioPreset.h"
#import "UIImage+CropRotate.h"

FOUNDATION_EXPORT double CropViewControllerVersionNumber;
Expand Down
Loading