TOCropViewController

TimOliverMIT活跃维护

iOS 上用于裁剪 UIImage 的视图控制器,自带可拖拽的裁剪框和旋转缩放交互,省去自己实现图片裁剪界面的麻烦。

A view controller for iOS that allows users to crop portions of UIImage objects

3.3.0发布于 2026-11-08 17:04 UTC4,948star1,027fork同步于 2026-11-10 05:36 UTC

怎么上手

README 里没有找到可直接复制的安装命令

README 里没有找到可直接复制的安装命令,请到 README 或官方文档查看安装方式。

硬件要求

README 没有说明硬件要求。

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

这个项目的说明还在生成中,先参考下面「最近发版」和 README。

最近发版

5 个版本3.3.0
  1. 3.3.0v3.3.0最新正式版2026-11-08 · 2026-11-08 17:04 UTC

    Added

    • Full support for iPhone Duo, in all rotations and screen states. This is still early days, so any feedback on how or if it can be improved would be appreciated! (#660)
    • TOCropToolbar.reverseVerticalContentLayout, which anchors the Done button to the bottom of a vertical toolbar instead of the top.
    • -[TOCropView performRelayoutForBoundsChange], which lays the crop out again after its content bounds have shifted, carrying any crop the user has made across.

    Fixed

    • On iOS 26, apps built with Xcode 16 (the iOS 18 SDK) showed no Done or Cancel buttons, as the toolbar laid itself out for the Liquid Glass design that the older SDK had compiled out. The glass design is now only used when both the SDK and the device support it. (#655)
    • On iOS 26, the aspect ratio picker could appear away from its button: offset sideways on iPhone, and in the wrong place entirely on iPad. (#644, #657)
    • On visionOS, the toolbar used UICornerConfiguration without checking for visionOS 26, which it requires. (#658)
    • Dismissing the crop view controller removed the presenting view controller's view when that controller wasn't full screen (such as a sheet, form sheet or popover), or when the crop view controller itself was presented over full screen. (#661)
    • Clearing transitioningDelegate to opt out of the custom transition had no effect, as it was installed again when the view loaded. (#661)
    • Tapping Done without making any changes, or after resetting, returned an image a few pixels smaller than the original (eg, 4032×3019 for a 4032×3024 photo), and each 90° rotation shaved off more. Crops that reach an edge of the image now include it exactly. (#662)
    • With a locked aspect ratio, each 90° rotation zoomed further into the image, and rotating without animation (including setting angle) left the crop box in the wrong orientation. The crop box now keeps its shape while the image turns underneath it, zooming in only as far as it needs to, and rotating all the way around returns to the original crop. (#663)
    • Switching between aspect ratio presets could slide the crop towards the image's left or top edge instead of keeping it centred. (#663)
    • Very wide or tall locked aspect ratios (such as 1920:190) were distorted by the minimum crop box size, especially after rotating. (#663)
    • With aspectRatioLockDimensionSwapEnabled on, rotating after choosing an aspect ratio could jump to a different part of the image. (#663)
    • At some view sizes, an aspect ratio preset's crop box came out a point narrower than it should, so a square preset didn't produce a square image. (#664)
    • Crops locked to an aspect ratio could be up to 20 pixels away from that ratio in the final image. They now match it to the nearest pixel. (#664)
    • Cropping a photo from the camera (or any other wide color image) produced a 16-bit-per-channel image, using twice the memory it needed and crashing share extensions with large photos. Crops are now drawn in the photo's own 8-bit color format, and crops that aren't rotated or circular reuse the photo's pixels without copying them. ([#665](ht
  2. 3.2.0v3.2.02026-08-05 · 2026-08-05 09:11 UTC

    这个版本没有写更新说明。

  3. 3.1.2v3.1.22026-04-07 · 2026-04-07 14:33 UTC
    查看更新说明

    Enhancements

    • Added callback closure for when the user taps the 'Done' button. #643

    Fixed

    • Restored some pre-defined aspect ratio values when enabling aspect ratio clamping. #646)
    • Restored source compatibility with Xcode 16. #645
  4. 3.1.1v3.1.12025-11-20 · 2025-11-20 08:26 UTC
    查看更新说明

    Fixed

    • A crash occurring on (allegedly) certain older beta version of iOS 26.0. (#635)
    • A UI layout issue where the Done button would be obscured by the iPadOS title bar buttons when in windowed mode. (#631)
    • Fixed a rotation animation issue occuring on iOS 26. (#636)
  5. 3.1.0v3.1.02025-10-18 · 2025-10-18 04:17 UTC
    查看更新说明

    Fixed

    • The cropping region being scaled to its minimum size when the toolbar position was set to top. (#626)
    • Gated out the iOS 26 code so the code is still buildable in Xcode 16.0. (#620)
    • The sizing code of the middle toolbar so it doesn't look overstreteched with fewer icons. (#624)

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    TOCropViewController

    CI Version Carthage compatible GitHub license Platform

    TOCropViewController is an open-source UIViewController subclass to crop out sections of UIImage objects, as well as perform basic rotations. It is excellent for things like editing profile pictures, or sharing parts of a photo online. It has been designed with the iOS Photos app editor in mind, and as such, behaves in a way that should already feel familiar to users of iOS.

    For Swift developers, CropViewController is a Swift wrapper that completely encapsulates TOCropViewController and provides a much more native, Swiftier interface.

    Proudly powering apps by

    Looking for something more? If TOCropViewController doesn't meet your exact requirements, please consider IMG.LY with video editing and photo filter capabilities instead! (Disclaimer: Affiliate Link)

    Features

    • Crop images by dragging the edges of a grid overlay.
    • Optionally, crop circular copies of images.
    • Rotate images in 90-degree segments.
    • Clamp the crop box to a specific aspect ratio.
    • A reset button to completely undo all changes.
    • iOS 7/8 translucency to make it easier to view the cropped region.
    • The choice of having the controller return the cropped image to a delegate, or immediately pass it to a UIActivityViewController.
    • A custom animation and layout when the device is rotated to landscape mode.
    • Custom 'opening' and 'dismissal' animations.
    • Localized in 28 languages.

    System Requirements

    iOS 12.0 or above.

    Disclaimer: visionOS and Mac Catalyst modes are also enabled, however they are not officially supported. PRs are always welcome!

    Installation

    Add the following to your Podfile:

    pod 'TOCropViewController'
    

    Add the following to your Podfile:

    pod 'CropViewController'
    

    Add the following to your Package.swift:

    dependencies: [
      // ...
      .package(url: "https://github.com/TimOliver/TOCropViewController.git"),
    ],
    
    1. Add the following to your Cartfile:
    github "TimOliver/TOCropViewController"
    
    1. Run carthage update

    2. From the Carthage/Build folder, import one of the two frameworks into your Xcode project. For Objective-C projects, import just TOCropViewController.framework and for Swift, import CropViewController.framework instead. Each framework is separate; you do not need to import both.

    3. Follow the remaining steps on Getting Started with Carthage to finish integrating the framework.

    All of the necessary source and resource files for TOCropViewController are in Objective-C/TOCropViewController, and all of the necessary Swift files are in Swift/CropViewController.

    For Objective-C projects, copy just the TOCropViewController directory to your Xcode project. For Swift projects, copy both TOCropViewController and CropViewController to your project.

    Examples

    Using TOCropViewController is very straightforward. Simply create a new instance passing the UIImage object you wish to crop, and then present it modally on the screen.

    While TOCropViewController prefers to be presented modally, it can also be pushed to a UINavigationController stack.

    For a complete working example, check out the sample apps included in this repo.

    Swift
    func presentCropViewController() {
      let image: UIImage = ... //Load an image
      
      let cropViewController = CropViewController(image: image)
      cropViewController.delegate = self
      self.present(cropViewController, animated: true, completion: nil)
    }
    
    func cropViewController(_ cropViewController: CropViewController, didCropToImage image: UIImage, withRect cropRect: CGRect, angle: Int) {
            // 'image' is the newly cropped version of the original image
        }
    
    Objective-C
    - (void)presentCropViewController
    {
      UIImage *image = ...; // Load an image
      
      TOCropViewController *cropViewController = [[TOCropViewController alloc] initWithImage:image];
      cropViewController.delegate = self;
      [self presentViewController:cropViewController animated:YES completion:nil];
    }
    
    - (void)cropViewController:(TOCropViewController *)cropViewController didCropToImage:(UIImage *)image withRect:(CGRect)cropRect angle:(NSInteger)angle
    {
      // 'image' is the newly cropped version of the original image
    }
    

    Similar to many UIKit UIViewController subclasses, like MFMailComposeViewController, the class responsible for presenting view controller should also take care of dismissing it upon cancellation. To dismiss TOCropViewController, implement the cropViewController:didFinishCancelled: delegate method, and call dismissViewController:animated: from there.

    Swift
    func presentCropViewController() {
        var image: UIImage? // Load an image
        let cropViewController = CropViewController(croppingStyle: .circular, image: image)
        cropViewController.delegate = self
        self.present(cropViewController, animated: true, completion: nil)
    }
    
    func cropViewController(_ cropViewController: TOCropViewController?, didCropToCircularImage image: UIImage?, with cropRect: CGRect, angle: Int) {
        // 'image' is the newly cropped, circular version of the original image
    }
    
    Objective-C
    - (void)presentCropViewController
    {
    UIImage *image = ...; // Load an image
    
    TOCropViewController *cropViewController = [[TOCropViewController alloc] initWithCroppingStyle:TOCropViewCroppingStyleCircular image:image];
    cropViewController.delegate = self;
    [self presentViewController:cropViewController animated:YES completion:nil];
    }
    
    - (void)cropViewController:(TOCropViewController *)cropViewController didCropToCircularImage:(UIImage *)image withRect:(CGRect)cropRect angle:(NSInteger)angle
    {
    // 'image' is the newly cropped, circular version of the original image
    }
    
    Swift
    func presentCropViewController() {
        var image: UIImage? // Load an image
        let cropViewController = CropViewController(image: image)
        cropViewController.showActivitySheetOnDone = true
        self.present(cropViewController, animated: true, completion: nil)
    }
    
    Objective-C
    - (void)presentCropViewController
    {
      UIImage *image = ...; // Load an image
      
      TOCropViewController *cropViewController = [[TOCropViewController alloc] initWithImage:image];
      cropViewController.showActivitySheetOnDone = YES;
      [self presentViewController:cropViewController animated:YES completion:nil];
    }
    

    Optionally, TOCropViewController also supports a custom presentation animation where an already-visible copy of the image will zoom in to fill the screen.

    Swift
    
    func presentCropViewController() {
        var image: UIImage? // Load an image
        var imageView = UIImageView(image: image)
        var frame: CGRect = view.convert(imageView.frame, to: view)
        
        let cropViewController = CropViewController(image: image)
        cropViewController.delegate = self
        self.present(cropViewController, animated: true, completion: nil)
        cropViewController.presentAnimated(fromParentViewController: self, fromFrame: frame, completion: nil)
    }
    
    Objective-C
    - (void)presentCropViewController
    {
      UIImage *image = ...;
      UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
      CGRect frame = [self.view convertRect:imageView.frame toView:self.view];
      
      TOCropViewController *cropViewController = [[TOCropViewController alloc] initWithImage:image];
      cropViewController.delegate = self;
      [self presentViewController:cropViewController animated:YES completion:nil];
      [cropViewController presentAnimatedFromParentViewController:self fromFrame:frame completion:nil];
    }
    

    Architecture of TOCropViewController

    While traditional cropping UI implementations will usually just have a dimming view with a square hole cut out of the middle, TOCropViewController goes about its implementation a little differently.

    Since there are two views that are overlaid over the image (A dimming view and a translucency view), trying to cut a hole open in both of them would be rather complex. Instead, an image view is placed in a scroll view in the background, and a copy of the image view is placed on top, inside a container view that is clipped to the designated cropping size. The size and position of the foreground image is then made to match the background view, creating the illusion that there is a hole in the dimming views, and minimising the number of views onscreen.

    Credits

    TOCropViewController was originally created by Tim Oliver as a component for iComics, a comic reader app for iOS.

    Thanks also goes to TOCropViewController's growing list of contributors!

    iOS Device mockups used in the screenshot created by Pixeden.

    License

    TOCropViewController is licensed under the MIT License, please see the LICENSE file.