Repository: hhunaid/react-native-image-crop-tools Branch: master Commit: f1fafefaafb1 Files: 24 Total size: 32.0 KB Directory structure: gitextract_oass29u0/ ├── .gitattributes ├── .gitignore ├── .npmignore ├── .prettierrc ├── .watchmanconfig ├── README.md ├── android/ │ ├── README.md │ ├── build.gradle │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── com/ │ └── parsempo/ │ └── ImageCropTools/ │ ├── ImageCropToolsModule.kt │ ├── ImageCropToolsPackage.kt │ └── ImageCropViewManager.kt ├── ios/ │ ├── CropViewManager.h │ ├── CropViewManager.m │ ├── RCTCropView.h │ ├── RCTCropView.m │ └── react-native-image-crop-tools.xcodeproj/ │ ├── project.pbxproj │ └── project.xcworkspace/ │ └── xcshareddata/ │ └── IDEWorkspaceChecks.plist ├── package.json ├── react-native-image-crop-tools.podspec ├── src/ │ ├── crop-view.component.tsx │ └── index.ts ├── tsconfig.json └── tslint.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ *.pbxproj -text ================================================ FILE: .gitignore ================================================ node_modules .idea .DS_Store dist/ build/ /android/react-native-image-crop-tools.iml /android/.gradle contents.xcworkspacedata xcuserdata .vscode ================================================ FILE: .npmignore ================================================ src/ Example/ previews/ README.md tsconfig.json tslint.json node_modules .prettierrc .watchmanconfig .idea /android/build/ /android/.gradle .vscode README.md ================================================ FILE: .prettierrc ================================================ { "printWidth": 120, "trailingComma": "es5", "singleQuote": true } ================================================ FILE: .watchmanconfig ================================================ { "ignore_dirs": [ "node_modules" ] } ================================================ FILE: README.md ================================================ # react-native-image-crop-tools ## Previews

## Getting started `$ yarn add react-native-image-crop-tools` ### Automatic installation Only RN > 0.61.x is supported. - Android: Installation is automatic. - iOS: Run `pod install`in `ios` folder. ### Why another cropping library? Most cropping tools available for RN are usually wrappers over popular native tools which itself isn't a bad thing. But this means you are stuck with their UI and feature set. The ones made in RN are not the most optimized and correct tools. ## Features 1. Native views. Which means performance even on low end devices. 2. You can embed the view into you own UI. It's not very customizable (yet) 3. Change and lock/unlock aspect ratio on the fly (This is the main reason I am making this library) # NOTE This library is not supposed to work directly with remote images. There are very few usecases for that. You need to provide a sourceUrl string for a local file which you can obtain from image pickers or by downloading a remote file with rn-fetch-blob ## Usage ```javascript import { CropView } from 'react-native-image-crop-tools'; console.warn(res)} keepAspectRatio aspectRatio={{width: 16, height: 9}} /> ``` Two methods are exposed on the ref you can use them as follows ```javascript this.cropViewRef.saveImage(true, 90 // image quality percentage) this.cropViewRef.rotateImage(true // true for clockwise, false for counterclockwise) ``` ### Props | Name | Description | Default | | ---- | ----------- | ------- | | sourceUrl | URL of the source image | `null` | | aspectRatio | Aspect ratio of the cropped image | `null` | | keepAspectRatio | Locks the aspect ratio to given aspect ratio | `false` | | iosDimensionSwapEnabled | (iOS Only) Swaps the width and height of the crop rectangle upon rotation | `false` | #### TODO: - [x] Add screenshots - [x] Support transparency - [ ] Add access to prebuilt UI for those who want to use it. ================================================ FILE: android/README.md ================================================ README ====== If you want to publish the lib as a maven dependency, follow these steps before publishing a new version to npm: 1. Be sure to have the Android [SDK](https://developer.android.com/studio/index.html) and [NDK](https://developer.android.com/ndk/guides/index.html) installed 2. Be sure to have a `local.properties` file in this folder that points to the Android SDK and NDK ``` ndk.dir=/Users/{username}/Library/Android/sdk/ndk-bundle sdk.dir=/Users/{username}/Library/Android/sdk ``` 3. Delete the `maven` folder 4. Run `./gradlew installArchives` 5. Verify that latest set of generated files is in the maven folder with the correct version number ================================================ FILE: android/build.gradle ================================================ def safeExtGet(prop, fallback) { rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback } buildscript { ext.kotlin_version = '1.7.20' repositories { google() mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:7.3.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { google() mavenCentral() flatDir { dirs 'libs' } } } apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { compileSdkVersion safeExtGet('compileSdkVersion', 31) defaultConfig { minSdkVersion safeExtGet('minSdkVersion', 17) targetSdkVersion safeExtGet('targetSdkVersion', 31) versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { api "com.facebook.react:react-native:+" implementation 'com.github.CanHub:Android-Image-Cropper:4.1.0' implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } ================================================ FILE: android/src/main/AndroidManifest.xml ================================================ ================================================ FILE: android/src/main/java/com/parsempo/ImageCropTools/ImageCropToolsModule.kt ================================================ package com.parsempo.ImageCropTools import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule class ImageCropToolsModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { override fun getName(): String { return "ImageCropTools" } } ================================================ FILE: android/src/main/java/com/parsempo/ImageCropTools/ImageCropToolsPackage.kt ================================================ package com.parsempo.ImageCropTools import com.facebook.react.ReactPackage import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.uimanager.ViewManager class ImageCropToolsPackage : ReactPackage { override fun createNativeModules(reactContext: ReactApplicationContext): List { return mutableListOf(ImageCropToolsModule(reactContext)) } override fun createViewManagers(reactContext: ReactApplicationContext): List> { return mutableListOf(ImageCropViewManager()) } } ================================================ FILE: android/src/main/java/com/parsempo/ImageCropTools/ImageCropViewManager.kt ================================================ package com.parsempo.ImageCropTools import android.graphics.Bitmap import android.net.Uri import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap import com.facebook.react.common.MapBuilder import com.facebook.react.uimanager.SimpleViewManager import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.annotations.ReactProp import com.canhub.cropper.CropImageView import com.facebook.react.uimanager.events.RCTEventEmitter import java.io.File import java.util.* class ImageCropViewManager: SimpleViewManager() { companion object { const val REACT_CLASS = "CropView" const val ON_IMAGE_SAVED = "onImageSaved" const val SOURCE_URL_PROP = "sourceUrl" const val KEEP_ASPECT_RATIO_PROP = "keepAspectRatio" const val ASPECT_RATIO_PROP = "cropAspectRatio" const val SAVE_IMAGE_COMMAND = 1 const val ROTATE_IMAGE_COMMAND = 2 const val SAVE_IMAGE_COMMAND_NAME = "saveImage" const val ROTATE_IMAGE_COMMAND_NAME = "rotateImage" } override fun createViewInstance(reactContext: ThemedReactContext): CropImageView { val view = CropImageView(reactContext) view.setOnCropImageCompleteListener { _, result -> if (result.isSuccessful) { val map = Arguments.createMap() map.putString("uri", result.getUriFilePath(reactContext, true).toString()) map.putInt("x", result.cropRect!!.left) map.putInt("y", result.cropRect!!.top) map.putInt("width", result.cropRect!!.width()) map.putInt("height", result.cropRect!!.height()) reactContext.getJSModule(RCTEventEmitter::class.java)?.receiveEvent( view.id, ON_IMAGE_SAVED, map ) } } return view } override fun getName(): String { return REACT_CLASS } override fun getExportedCustomDirectEventTypeConstants(): MutableMap { return MapBuilder.of( ON_IMAGE_SAVED, MapBuilder.of("registrationName", ON_IMAGE_SAVED) ) } override fun getCommandsMap(): MutableMap { return MapBuilder.of( SAVE_IMAGE_COMMAND_NAME, SAVE_IMAGE_COMMAND, ROTATE_IMAGE_COMMAND_NAME, ROTATE_IMAGE_COMMAND ) } override fun receiveCommand(root: CropImageView, commandId: Int, args: ReadableArray?) { when (commandId) { SAVE_IMAGE_COMMAND -> { val preserveTransparency = args?.getBoolean(0) ?: false var extension = "jpg" var format = Bitmap.CompressFormat.JPEG if (preserveTransparency && root.croppedImage!!.hasAlpha()) { extension = "png" format = Bitmap.CompressFormat.PNG } val path = File(root.context.cacheDir, "${UUID.randomUUID()}.$extension").toURI().toString() val quality = args?.getInt(1) ?: 100 root.croppedImageAsync(format, quality, customOutputUri = Uri.parse(path)) } ROTATE_IMAGE_COMMAND -> { val clockwise = args?.getBoolean(0) ?: true root.rotateImage(if (clockwise) 90 else -90) } } } @ReactProp(name = SOURCE_URL_PROP) fun setSourceUrl(view: CropImageView, url: String?) { url?.let { view.setImageUriAsync(Uri.parse(it)) } } @ReactProp(name = KEEP_ASPECT_RATIO_PROP) fun setFixedAspectRatio(view: CropImageView, fixed: Boolean) { view.setFixedAspectRatio(fixed) } @ReactProp(name = ASPECT_RATIO_PROP) fun setAspectRatio(view: CropImageView, aspectRatio: ReadableMap?) { if (aspectRatio != null) { view.setAspectRatio(aspectRatio.getInt("width"), aspectRatio.getInt("height")) }else { view.clearAspectRatio() } } } ================================================ FILE: ios/CropViewManager.h ================================================ // // Header.h // react-native-image-crop-tools // // Created by Hunaid Hassan on 06/01/2020. // #import NS_ASSUME_NONNULL_BEGIN @interface CropViewManager : RCTViewManager @end NS_ASSUME_NONNULL_END ================================================ FILE: ios/CropViewManager.m ================================================ // // CropViewManager.m // react-native-image-crop-tools // // Created by Hunaid Hassan on 31/12/2019. // #import "CropViewManager.h" #import "RCTCropView.h" #import @implementation CropViewManager RCT_EXPORT_MODULE() -(UIView *)view { return [[RCTCropView alloc] init]; } RCT_EXPORT_VIEW_PROPERTY(sourceUrl, NSString) RCT_EXPORT_VIEW_PROPERTY(onImageSaved, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(keepAspectRatio, BOOL) RCT_EXPORT_VIEW_PROPERTY(cropAspectRatio, CGSize) RCT_EXPORT_VIEW_PROPERTY(iosDimensionSwapEnabled, BOOL) RCT_EXPORT_METHOD(saveImage:(nonnull NSNumber*) reactTag preserveTransparency:(BOOL) preserveTransparency quality:(nonnull NSNumber *) quality) { [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { RCTCropView *cropView = (RCTCropView *)viewRegistry[reactTag]; CGRect cropFrame = [cropView getCropFrame]; UIImage *image = [cropView getCroppedImage]; NSString *extension = @"jpg"; if ([[image valueForKey:@"hasAlpha"] boolValue] && preserveTransparency) { extension = @"png"; } NSArray *paths = [[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask]; NSURL *url = [[paths firstObject] URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", [[NSUUID UUID] UUIDString], extension]]; if ([[image valueForKey:@"hasAlpha"] boolValue] && preserveTransparency) { [UIImagePNGRepresentation(image) writeToURL:url atomically:YES]; }else { [UIImageJPEGRepresentation(image, [quality floatValue] / 100.0f) writeToURL:url atomically:YES]; } cropView.onImageSaved(@{ @"uri": url.absoluteString, @"width": [NSNumber numberWithDouble:cropFrame.size.width], @"height": [NSNumber numberWithDouble:cropFrame.size.height], @"x": [NSNumber numberWithDouble:cropFrame.origin.x], @"y": [NSNumber numberWithDouble:cropFrame.origin.y] }); }]; } RCT_EXPORT_METHOD(rotateImage:(nonnull NSNumber*) reactTag clockwise:(BOOL) clockwise) { [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { RCTCropView *cropView = (RCTCropView *)viewRegistry[reactTag]; [cropView rotateImage:clockwise]; }]; } @end ================================================ FILE: ios/RCTCropView.h ================================================ // // RCTCropView.h // react-native-image-crop-tools // // Created by Hunaid Hassan on 06/01/2020. // #import #import NS_ASSUME_NONNULL_BEGIN @interface RCTCropView : RCTView @property (nonatomic, strong) NSString * sourceUrl; @property (atomic, assign) BOOL keepAspectRatio; @property (atomic, assign) BOOL iosDimensionSwapEnabled; @property (nonatomic, assign) CGSize cropAspectRatio; @property (nonatomic, strong) RCTDirectEventBlock onImageSaved; - (UIImage *)getCroppedImage; - (CGRect)getCropFrame; - (void)rotateImage:(BOOL)clockwise; @end NS_ASSUME_NONNULL_END ================================================ FILE: ios/RCTCropView.m ================================================ // // RCTCropView.m // react-native-image-crop-tools // // Created by Hunaid Hassan on 06/01/2020. // #import "RCTCropView.h" #import #import #import @implementation RCTCropView { TOCropView * _inlineCropView; } @synthesize sourceUrl, keepAspectRatio, cropAspectRatio, iosDimensionSwapEnabled; - (void)layoutSubviews { if (_inlineCropView == nil) { if([sourceUrl rangeOfString:@"ph://"].location == 0) { NSString *url = [sourceUrl stringByReplacingOccurrencesOfString:@"ph://" withString:@""]; PHImageRequestOptions * requestOptions = [[PHImageRequestOptions alloc] init]; requestOptions.resizeMode = PHImageRequestOptionsResizeModeExact; requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat; requestOptions.synchronous = YES; PHAsset *photosAsset = [PHAsset fetchAssetsWithLocalIdentifiers:@[url] options: nil].lastObject; PHImageManager *manager = [PHImageManager defaultManager]; __block UIImage *blockImage; [manager requestImageForAsset:photosAsset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:requestOptions resultHandler:^void(UIImage *image, NSDictionary *info) { blockImage = image; }]; _inlineCropView = [[TOCropView alloc] initWithImage:blockImage]; } else { UIImage * image =[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:sourceUrl]]]; _inlineCropView = [[TOCropView alloc] initWithImage:image]; } _inlineCropView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; _inlineCropView.frame = self.bounds; if (self->keepAspectRatio) { _inlineCropView.aspectRatioLockEnabled = keepAspectRatio; } if (self->iosDimensionSwapEnabled) { _inlineCropView.aspectRatioLockDimensionSwapEnabled = iosDimensionSwapEnabled; } if (!CGSizeEqualToSize(self -> cropAspectRatio, CGSizeZero)) { _inlineCropView.aspectRatio = self->cropAspectRatio; } [_inlineCropView moveCroppedContentToCenterAnimated:NO]; [_inlineCropView performInitialSetup]; [self addSubview:_inlineCropView]; } } - (UIImage *)getCroppedImage { return [_inlineCropView.image croppedImageWithFrame:_inlineCropView.imageCropFrame angle:_inlineCropView.angle circularClip:NO]; } - (CGRect)getCropFrame { return _inlineCropView.imageCropFrame; } - (void)setCropAspectRatio:(CGSize)aspectRatio { if (_inlineCropView) { _inlineCropView.aspectRatio = aspectRatio; } self->cropAspectRatio = aspectRatio; } -(CGSize)cropAspectRatio { return _inlineCropView.aspectRatio; } - (void)setKeepAspectRatio:(BOOL)keepAspectRatio { if (_inlineCropView) { _inlineCropView.aspectRatioLockEnabled = keepAspectRatio; } self->keepAspectRatio = keepAspectRatio; } - (BOOL)keepAspectRatio { return _inlineCropView.aspectRatioLockEnabled; } - (void)setIosDimensionSwapEnabled:(BOOL)iosDimensionSwapEnabled { if (_inlineCropView) { _inlineCropView.aspectRatioLockDimensionSwapEnabled = iosDimensionSwapEnabled; } self->iosDimensionSwapEnabled = iosDimensionSwapEnabled; } - (BOOL)iosDimensionSwapEnabled { return _inlineCropView.aspectRatioLockDimensionSwapEnabled; } - (void)rotateImage:(BOOL)clockwise { [_inlineCropView rotateImageNinetyDegreesAnimated:YES clockwise:clockwise]; } @end ================================================ FILE: ios/react-native-image-crop-tools.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 55; objects = { /* Begin PBXBuildFile section */ 20E26B5827222734001DE3D2 /* CropViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 20E26B5527222734001DE3D2 /* CropViewManager.m */; }; 20E26B5927222734001DE3D2 /* RCTCropView.m in Sources */ = {isa = PBXBuildFile; fileRef = 20E26B5727222734001DE3D2 /* RCTCropView.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 20E26B46272226EF001DE3D2 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = "include/$(PRODUCT_NAME)"; dstSubfolderSpec = 16; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 20E26B48272226EF001DE3D2 /* libreact-native-image-crop-tools.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libreact-native-image-crop-tools.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 20E26B5427222734001DE3D2 /* CropViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CropViewManager.h; sourceTree = ""; }; 20E26B5527222734001DE3D2 /* CropViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CropViewManager.m; sourceTree = ""; }; 20E26B5627222734001DE3D2 /* RCTCropView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCropView.h; sourceTree = ""; }; 20E26B5727222734001DE3D2 /* RCTCropView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCropView.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 20E26B45272226EF001DE3D2 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 20E26B3F272226EF001DE3D2 = { isa = PBXGroup; children = ( 20E26B5427222734001DE3D2 /* CropViewManager.h */, 20E26B5527222734001DE3D2 /* CropViewManager.m */, 20E26B5627222734001DE3D2 /* RCTCropView.h */, 20E26B5727222734001DE3D2 /* RCTCropView.m */, 20E26B49272226EF001DE3D2 /* Products */, ); sourceTree = ""; }; 20E26B49272226EF001DE3D2 /* Products */ = { isa = PBXGroup; children = ( 20E26B48272226EF001DE3D2 /* libreact-native-image-crop-tools.a */, ); name = Products; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 20E26B47272226EF001DE3D2 /* react-native-image-crop-tools */ = { isa = PBXNativeTarget; buildConfigurationList = 20E26B51272226EF001DE3D2 /* Build configuration list for PBXNativeTarget "react-native-image-crop-tools" */; buildPhases = ( 20E26B44272226EF001DE3D2 /* Sources */, 20E26B45272226EF001DE3D2 /* Frameworks */, 20E26B46272226EF001DE3D2 /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = "react-native-image-crop-tools"; productName = "react-native-image-crop-tools"; productReference = 20E26B48272226EF001DE3D2 /* libreact-native-image-crop-tools.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 20E26B40272226EF001DE3D2 /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; LastUpgradeCheck = 1300; TargetAttributes = { 20E26B47272226EF001DE3D2 = { CreatedOnToolsVersion = 13.0; }; }; }; buildConfigurationList = 20E26B43272226EF001DE3D2 /* Build configuration list for PBXProject "react-native-image-crop-tools" */; compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 20E26B3F272226EF001DE3D2; productRefGroup = 20E26B49272226EF001DE3D2 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 20E26B47272226EF001DE3D2 /* react-native-image-crop-tools */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ 20E26B44272226EF001DE3D2 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 20E26B5927222734001DE3D2 /* RCTCropView.m in Sources */, 20E26B5827222734001DE3D2 /* CropViewManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 20E26B4F272226EF001DE3D2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; }; 20E26B50272226EF001DE3D2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; name = Release; }; 20E26B52272226EF001DE3D2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 5NJK8TB2FJ; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 20E26B53272226EF001DE3D2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 5NJK8TB2FJ; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 20E26B43272226EF001DE3D2 /* Build configuration list for PBXProject "react-native-image-crop-tools" */ = { isa = XCConfigurationList; buildConfigurations = ( 20E26B4F272226EF001DE3D2 /* Debug */, 20E26B50272226EF001DE3D2 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 20E26B51272226EF001DE3D2 /* Build configuration list for PBXNativeTarget "react-native-image-crop-tools" */ = { isa = XCConfigurationList; buildConfigurations = ( 20E26B52272226EF001DE3D2 /* Debug */, 20E26B53272226EF001DE3D2 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 20E26B40272226EF001DE3D2 /* Project object */; } ================================================ FILE: ios/react-native-image-crop-tools.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: package.json ================================================ { "name": "react-native-image-crop-tools", "title": "React Native Image Crop Tools", "version": "1.8.0", "description": "Native image crop tools with embeddable UI and on the fly aspect ratio switchin", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "tsc", "format": "prettier --write \"src/**/*.ts\" \"src/**/*.tsx\" \"src/**/*.js\"", "lint": "tslint -p tsconfig.json --fix", "prepublishOnly": "npm run lint", "preversion": "npm run lint", "version": "npm run format && git add -A src", "postversion": "git push && git push --tags" }, "main": "dist/index.js", "types": "dist/index.d.ts", "repository": { "type": "git", "url": "git+https://github.com/hhunaid/react-native-image-crop-tools.git", "baseUrl": "https://github.com/hhunaid/react-native-image-crop-tools" }, "keywords": [ "react-native", "crop", "cropping", "image", "rotate", "native", "react", "typescript" ], "author": { "name": "Hunaid Hassan", "email": "hhunaid@gmail.com" }, "license": "MIT", "readmeFilename": "README.md", "peerDependencies": { "react": ">=16.0.0", "react-native": ">=0.59.0-rc.0 <1.0.x" }, "devDependencies": { "react": "^16.9.0", "react-native": "^0.61.0", "tslint": "^5.17.0", "tslint-config-prettier": "^1.18.0", "tslint-react": "^4.0.0", "typescript": "^3.7.2", "prettier": "^1.19.1", "@types/react-native": "^0.60.23" } } ================================================ FILE: react-native-image-crop-tools.podspec ================================================ require "json" package = JSON.parse(File.read(File.join(__dir__, "package.json"))) Pod::Spec.new do |s| s.name = "react-native-image-crop-tools" s.version = package["version"] s.summary = package["description"] s.description = <<-DESC react-native-image-crop-tools DESC s.homepage = "https://github.com/hhunaid/react-native-image-crop-tools" s.license = "MIT" s.authors = { "Hunaid Hassan" => "hhunaid@gmail.com" } s.platforms = { :ios => "9.0" } s.source = { :git => "https://github.com/hhunaid/react-native-image-crop-tools.git", :tag => "#{s.version}" } s.source_files = "ios/**/*.{h,m}" s.requires_arc = true s.dependency "React-Core" s.dependency 'TOCropViewController', '2.5.3' end ================================================ FILE: src/crop-view.component.tsx ================================================ import React, { createRef } from 'react'; import { findNodeHandle, NativeSyntheticEvent, requireNativeComponent, StyleProp, UIManager, ViewStyle, } from 'react-native'; const RCTCropView = requireNativeComponent('CropView'); type Response = { uri: string; width: number; height: number; x: number; y: number; }; type Props = { sourceUrl: string; style?: StyleProp; onImageCrop?: (res: Response) => void; keepAspectRatio?: boolean; aspectRatio?: { width: number; height: number }; iosDimensionSwapEnabled?: boolean; }; class CropView extends React.PureComponent { public static defaultProps = { keepAspectRatio: false, iosDimensionSwapEnabled: false, }; private viewRef = createRef(); public saveImage = (preserveTransparency: boolean = true, quality: number = 90) => { UIManager.dispatchViewManagerCommand( findNodeHandle(this.viewRef.current!), UIManager.getViewManagerConfig('CropView').Commands.saveImage, [preserveTransparency, quality] ); }; public rotateImage = (clockwise: boolean = true) => { UIManager.dispatchViewManagerCommand( findNodeHandle(this.viewRef.current!), UIManager.getViewManagerConfig('CropView').Commands.rotateImage, [clockwise] ); }; public render() { const { onImageCrop, aspectRatio, ...rest } = this.props; return ( ) => { onImageCrop!(event.nativeEvent); }} {...rest} /> ); } } export default CropView; ================================================ FILE: src/index.ts ================================================ import CropView from './crop-view.component'; export { CropView }; ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "experimentalDecorators": true, "declaration": true, "outDir": "./dist", "strict": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "jsx": "react", "typeRoots": [ "./node_modules/@types" ], "lib": ["es6"], "moduleResolution": "node", "target": "esnext" }, "include": [ "src/**/*" ], "files": ["src/index.ts"], "exclude": [ "node_modules", "**/__tests__/*", "dist" ] } ================================================ FILE: tslint.json ================================================ { "extends": [ "tslint:recommended", "tslint-config-prettier" ], "rules": { "interface-over-type-literal": false, "object-literal-sort-keys": false, "variable-name": { "options": [ "check-format", "allow-leading-underscore", "allow-pascal-case" ] }, "no-unused-variable": true, "no-shadowed-variable": false } }