2016-11-23 4 views
1

Как молодой программист, мне сказали, что я узнаю, когда достиг уровня промежуточного уровня, когда мне удобно пользоваться библиотеками. Я столкнулся с проблемой позволить моему пользователю выбрать изображение профиля, а в случае, если он не квадрат, что позволяет моему пользователю перемещать квадрат вокруг изображения, чтобы обрезать его.Нет ли библиотеки для выбора изображения и выбора квадратного урожая из этого изображения?

Это такая общая особенность (tinder, GitHub, Facebook и т. Д.). Я думаю, что было бы быстрое 3-х совместимое решение, которое я мог бы найти в Интернете, но мне не повезло; Я взломал WDImagePicker только для того, чтобы найти, что он не совсем соответствует стандартам того, что пользователи привыкли видеть.

Если никакого решения не существует, я буду указывать свои собственные и, если удастся, поделиться им в Интернете, но мне трудно поверить, что это так. Кто-нибудь знает какое-либо решение?

Чтобы уточнить мое точное намерение: пользователь должен иметь возможность выбирать изображение для загрузки. Если это изображение не является квадратом, тогда должен появиться квадрат с размерами, равными min (imageWidth, imageHeight). Этот квадрат должен перекрывать изображение в виде прокрутки, чтобы пользователь мог перемещать изображение, чтобы он соответствовал квадрату, над тем, что он или она хочет, чтобы квадратное изображение было загружено. Затем эта часть изображения должна быть загружена. В сущности, я хочу, чтобы аспект был заполнен, когда пользователь решает, какая часть изображения отключена. Разве API нет, чтобы справиться с этим?

+1

Знаешь что? Мы даже не знаем, на какой платформе разработки вы работаете, глупый. –

+0

В итоге я написал свою собственную реализацию, так как UIImagePickerController явно не соответствует задаче. У меня нет кода со мной сейчас, но если к тому времени, когда я вернусь домой, вы все еще ищете, я добавлю его здесь. –

+0

Спасибо, Джейкоб, что было бы очень полезно – n00bie42

ответ

1

Право, позвольте мне начать с извинения за то, что вы не получили это раньше, но вот моя полностью настраиваемая реализация инструмента обработки изображений, который фактически работает (в отличие от Apple). Это довольно просто, но, к сожалению, я (глупо) не создавал его, чтобы его можно было легко использовать повторно, поэтому вам придется попытаться воссоздать то, что у меня есть в раскадровке. Я попытаюсь сделать это с помощью скриншотов.

Во-первых, обратите внимание, что это все Swift 2.3 код, поскольку это старый проект, если вы работаете в Swift 2.3, отлично, если нет, вам нужно его обновить.

Вот вид контроллера файл, который необходимо добавить в проект, обратите внимание на комментарии, которые объясняют, как работают различные части:

import Foundation 
import UIKit 
import AVFoundation 

class EditProfilePictureViewController : UIViewController { 

    var imageView: UIImageView? 
    var image : UIImage! 
    var center: CGPoint! 

    @IBOutlet var indicatorView: UIView! 
    @IBOutlet var spaceView: UIView! 

    // Set these to the desired width and height of your output image. 
    let desiredWidth:CGFloat = 75 
    let desiredHeight: CGFloat = 100 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     // Set up UI and gesture recognisers. 
     indicatorView.layer.borderColor = UIColor.whiteColor().CGColor 
     indicatorView.layer.borderWidth = 8 

     let pan = UIPanGestureRecognizer(target: self, action: "didPan:") 
     let pinch = UIPinchGestureRecognizer(target: self, action: "didPinch:") 
     indicatorView.addGestureRecognizer(pan) 
     indicatorView.addGestureRecognizer(pinch) 
    } 

    override func viewDidAppear(animated: Bool) { 
     super.viewDidAppear(animated) 

     // Set up the image view in relation to the storyboard views. 
     imageView = UIImageView() 
     imageView?.image = image 
     imageView!.frame = AVMakeRectWithAspectRatioInsideRect(image.size, indicatorView.frame) 
     center = imageView!.center 
     spaceView.insertSubview(imageView!, belowSubview: indicatorView) 
    } 


    // Invoked when the user pans accross the screen. The logic happening here basically just checks if the pan would move the image outside the cropping square and if so, don't allow the pan to happen. 
    func didPan(recognizer: UIPanGestureRecognizer) { 

     let translation = recognizer.translationInView(spaceView) 

     if (imageView!.frame.minX + translation.x >= indicatorView.frame.minX && imageView!.frame.maxX + translation.x <= indicatorView.frame.maxX) || ((imageView!.frame.size.width >= indicatorView.frame.size.width) && (imageView!.frame.minX + translation.x <= indicatorView.frame.minX && imageView!.frame.maxX + translation.x >= indicatorView.frame.maxX)) { 
      imageView!.center.x += translation.x 
     } 

     if (imageView!.frame.minY + translation.y >= indicatorView.frame.minY && imageView!.frame.maxY + translation.y <= indicatorView.frame.maxY) || ((imageView!.frame.size.height >= indicatorView.frame.size.height) && (imageView!.frame.minY + translation.y <= indicatorView.frame.minY && imageView!.frame.maxY + translation.y >= indicatorView.frame.maxY)) { 
      imageView!.center.y += translation.y 
     } 

     recognizer.setTranslation(CGPointZero, inView: spaceView) 
    } 

    // Invoked when the user pinches the screen. Again the logic here just ensures that zooming the image would not make it exceed the bounds of the cropping square and cancels the zoom if it does. 
    func didPinch(recognizer: UIPinchGestureRecognizer) { 

     let view = UIView(frame: imageView!.frame) 

     view.transform = CGAffineTransformScale(imageView!.transform, recognizer.scale, recognizer.scale) 

     if view.frame.size.width >= indicatorView.frame.size.width || view.frame.size.height >= indicatorView.frame.size.height { 

      imageView!.transform = CGAffineTransformScale(imageView!.transform, recognizer.scale, recognizer.scale) 
      recognizer.scale = 1 
     } 

     if recognizer.state == UIGestureRecognizerState.Ended { 

      if imageView!.frame.minX > indicatorView.frame.minX || imageView!.frame.maxX < indicatorView.frame.maxX { 

       UIView.animateWithDuration(0.3, animations: {() -> Void in 
        self.imageView!.center = self.indicatorView.center 
       }) 
      } 

      if imageView!.frame.size.height < indicatorView.frame.size.height && imageView!.frame.size.width < indicatorView.frame.size.width { 

       UIView.animateWithDuration(0.3, animations: {() -> Void in 
        self.imageView!.frame = AVMakeRectWithAspectRatioInsideRect(self.image.size, self.indicatorView.frame) 
       }) 
      } 
     } 
    } 

    // Outlet for the cancel button. 
    @IBAction func cancelButtonPressed(sender: AnyObject) { 
     dismissViewControllerAnimated(true, completion: nil) 
    } 

    // Outlet for the save button. The logic here scales the outputed image down to the desired size. 
    @IBAction func saveButtonPressed(sender: AnyObject) { 

     let croppedImage = grabIndicatedImage() 
     UIGraphicsBeginImageContext(CGSizeMake(desiredWidth, desiredHeight)) 
     CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), UIColor.blackColor().CGColor) 

     if desiredWidth/desiredHeight == croppedImage.size.width/croppedImage.size.height { 
      croppedImage.drawInRect(CGRect(x: 0, y: 0, width: desiredWidth, height: desiredHeight)) 
     } else { 
      let croppedImageSize : CGRect = AVMakeRectWithAspectRatioInsideRect(croppedImage.size, CGRectMake(0, 0, desiredWidth, desiredHeight)) 
      croppedImage.drawInRect(croppedImageSize) 
     } 

     let resizedCroppedImage = UIGraphicsGetImageFromCurrentImageContext() 
     UIGraphicsEndImageContext() 
     let data = UIImagePNGRepresentation(resizedCroppedImage) 
     // At this point you now have an image cropped to your desired size, as well as data representation of it should you want to send to an API. 
    } 

    // When you call this method it basically takes a screenshot of the crop area and gets the UIImage object from it. 
    func grabIndicatedImage() -> UIImage { 

     UIGraphicsBeginImageContext(self.view.layer.frame.size) 
     let context : CGContextRef = UIGraphicsGetCurrentContext(); 
     self.view.layer.renderInContext(context) 
     let screenshot : UIImage = UIGraphicsGetImageFromCurrentImageContext(); 

     let rectToCrop = CGRectMake(indicatorView.frame.minX + 8, indicatorView.frame.minY + 72, indicatorView.frame.width - 16, indicatorView.frame.height - 16) 

     let imageRef : CGImageRef = CGImageCreateWithImageInRect(screenshot.CGImage, rectToCrop) 
     let croppedImage = UIImage(CGImage: imageRef)! 


     UIGraphicsEndImageContext(); 
     return croppedImage 
    } 

    // MARK: The following methods relate to re-laying out the view if the user changes the device orientation. 

    override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) { 
     if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) 
     { 
      UIView.animateWithDuration(0.3, animations: {() -> Void in 
       self.imageView!.center = self.indicatorView.center 
       self.imageView!.frame = AVMakeRectWithAspectRatioInsideRect(self.image.size, self.indicatorView.frame) 
      }) 
     } 
    } 

    override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) { 

     coordinator.animateAlongsideTransition({ (context) -> Void in 

      if UIDevice.currentDevice().userInterfaceIdiom == .Pad 
      { 
       UIView.animateWithDuration(0.3, animations: {() -> Void in 
        self.imageView!.center = self.indicatorView.center 
        self.imageView!.frame = AVMakeRectWithAspectRatioInsideRect(self.image.size, self.indicatorView.frame) 
       }) 
      } 
     }, completion: { (context) -> Void in 
     }) 
    } 
} 

Далее вам необходимо настроить свой раскадровки/перо и подключите розетки. Пользовательский интерфейс в раскадровке выглядит следующим образом:

enter image description here

Не особенно полезно, я знаю. Вид иерархия является немного более проницательной и выглядит следующим образом:

enter image description here

Как вы можете видеть, есть не много, чтобы установить в раскадровке. Space View буквально просто подглядывает основной, корневой вид. Он закреплен ограничениями на всех четырех сторонах, поэтому он соответствует размеру корневого представления. Очень легко реплицировать. Он имеет черный фон, но это может быть любой цвет по вашему выбору.

Indicator View немного сложнее. Это подобие Space View, как видно из скриншота с изображением heirarchy. С точки зрения ограничений наиболее важным является соотношение сторон. Это должно быть соотношение сторон желаемого урожая. В моем случае это было 4: 3, но для вас это может быть 1: 1, если вы хотите квадратную культуру. Вы можете легко это изменить, но помните, что desiredHeight и desiredWidth должны быть настроены так, чтобы они отражали соотношение сторон.

enter image description here

Эти ограничения могут показаться сложным, но на самом деле они довольно просто, позвольте мне разбить их:

enter image description here

Как я уже упоминал, установить соотношение сторон. Следующий центр горизонтально и вертикально в пространстве. Затем создайте набор равных ширин, равных ограничений по высоте для представления пространства. Сделайте оба этих «меньше или равно». Затем создайте другой набор одинаковой ширины, равный ограничениям высоты. Установите оба их приоритета на 750.

Правильно, вот оно для выкладки интерфейса; теперь вам просто нужно подключить розетки. Довольно очевидно, что подключить выходы spaceView и indicatorView так, чтобы это произошло. Также не забудьте подключить кнопки отмены и сохранения к их действиям.

Наконец, я объясню, как его использовать. Создайте segue для этого нового контроллера представления и переопределите метод prepareForSegue на контроллере представления. Получите ссылку на контроллер вида с помощью любых средств, которые вы выбрали, и установите для свойства изображения изображение, которое вы хотите обрезать. Имейте в виду, что это не замена для UIImagePickerController, но больше для его дополнения. Вам все равно нужно будет использовать UIImagePickerController, чтобы получить изображение из рулона камеры или камеры, но это используется для обработки редактирования. Например:

override func prepareForSegue(segue: UIStoryboardSegue) { 
    if let editVC = segue.destinationViewController as? EditProfilePictureViewController { 
     editVC.image = self.pickedImage // Set image to the image you want to crop. 
    } 
} 

Редактор будет всплывать, и вы можете поместить изображение по своему вкусу, прежде чем нанести удар. Вы можете реализовать делегат, чтобы вернуть отрезанное изображение из редактора, но я оставлю это вам.

Снова извините за задержку в получении этого вам, но я надеюсь, что вы найдете его того стоит! Удачи с вашим приложением, пожалуйста, разместите ссылку, когда она будет жить, поэтому я могу проверить это!

+0

Я рассматриваю рефакторинг этого, чтобы сделать его легко повторно используемым и упаковать в рамку. Дайте мне знать, если вам это нравится, и подумайте, что это принесет пользу людям! –