Эх сурвалжийг харах

Merge pull request #386 from deltachat/avatar_image_picker

avatar image picker
cyBerta 5 жил өмнө
parent
commit
b1bd735611
33 өөрчлөгдсөн 973 нэмэгдсэн , 797 устгасан
  1. 1 1
      Podfile
  2. 5 13
      Podfile.lock
  3. 2 0
      Pods/ALCameraViewController/ALCameraViewController/Utilities/CameraShot.swift
  4. 2 0
      Pods/ALCameraViewController/ALCameraViewController/Utilities/PhotoLibraryAuthorizer.swift
  5. 4 0
      Pods/ALCameraViewController/ALCameraViewController/Utilities/Utilities.swift
  6. 3 25
      Pods/ALCameraViewController/ALCameraViewController/ViewController/CameraViewController.swift
  7. 1 88
      Pods/ALCameraViewController/ALCameraViewController/ViewController/CameraViewControllerConstraint.swift
  8. 175 132
      Pods/ALCameraViewController/ALCameraViewController/ViewController/ConfirmViewController.swift
  9. 14 62
      Pods/ALCameraViewController/ALCameraViewController/ViewController/ConfirmViewController.xib
  10. 1 57
      Pods/ALCameraViewController/ALCameraViewController/Views/CameraView.swift
  11. 249 178
      Pods/ALCameraViewController/ALCameraViewController/Views/CropOverlay.swift
  12. 1 1
      Pods/ALCameraViewController/ALCameraViewController/Views/PermissionsView.swift
  13. 0 27
      Pods/Local Podspecs/ALCameraViewController.podspec.json
  14. 5 13
      Pods/Manifest.lock
  15. 25 23
      Pods/Pods.xcodeproj/project.pbxproj
  16. 1 1
      Pods/Target Support Files/ALCameraViewController/ALCameraViewController-Info.plist
  17. 0 1
      Pods/Target Support Files/ALCameraViewController/ALCameraViewController.xcconfig
  18. 12 4
      deltachat-ios.xcodeproj/project.pbxproj
  19. 31 9
      deltachat-ios/Controller/EditGroupViewController.swift
  20. 86 26
      deltachat-ios/Controller/EditSettingsController.swift
  21. 41 10
      deltachat-ios/Controller/GroupNameController.swift
  22. 7 0
      deltachat-ios/Controller/QrViewController.swift
  23. 24 0
      deltachat-ios/Coordinator/AppCoordinator.swift
  24. 15 0
      deltachat-ios/DC/Wrapper.swift
  25. 25 37
      deltachat-ios/Extensions/UIImage+Extension.swift
  26. 16 1
      deltachat-ios/Extensions/UIView+Extensions.swift
  27. 37 0
      deltachat-ios/Helper/AvatarHelper.swift
  28. 2 0
      deltachat-ios/Helper/Constants.swift
  29. 62 0
      deltachat-ios/Helper/MediaPicker.swift
  30. 107 0
      deltachat-ios/View/AvatarSelectionCell.swift
  31. 2 2
      deltachat-ios/View/ContactCell.swift
  32. 0 80
      deltachat-ios/View/GroupNameCell.swift
  33. 17 6
      deltachat-ios/View/InitialsBadge.swift

+ 1 - 1
Podfile

@@ -8,7 +8,7 @@ target 'deltachat-ios' do
 
   pod 'SwiftLint'
   pod 'SwiftFormat/CLI'
-  pod 'ALCameraViewController', :git => 'https://github.com/dignifiedquire/ALCameraViewController'
+  pod 'ALCameraViewController'
   # pod 'openssl-ios-bitcode'
   pod 'ReachabilitySwift'
   pod 'QuickTableViewController'

+ 5 - 13
Podfile.lock

@@ -1,5 +1,5 @@
 PODS:
-  - ALCameraViewController (3.0.3)
+  - ALCameraViewController (3.1)
   - DBDebugToolkit (0.5.0)
   - InputBarAccessoryView (4.3.1):
     - InputBarAccessoryView/Core (= 4.3.1)
@@ -13,7 +13,7 @@ PODS:
   - UICircularProgressRing (6.2.0)
 
 DEPENDENCIES:
-  - ALCameraViewController (from `https://github.com/dignifiedquire/ALCameraViewController`)
+  - ALCameraViewController
   - DBDebugToolkit
   - InputBarAccessoryView
   - JGProgressHUD
@@ -26,6 +26,7 @@ DEPENDENCIES:
 
 SPEC REPOS:
   trunk:
+    - ALCameraViewController
     - DBDebugToolkit
     - InputBarAccessoryView
     - JGProgressHUD
@@ -36,17 +37,8 @@ SPEC REPOS:
     - SwiftyBeaver
     - UICircularProgressRing
 
-EXTERNAL SOURCES:
-  ALCameraViewController:
-    :git: https://github.com/dignifiedquire/ALCameraViewController
-
-CHECKOUT OPTIONS:
-  ALCameraViewController:
-    :commit: e0fcc4338276e4fc6240e8726ecc77aa39ce1385
-    :git: https://github.com/dignifiedquire/ALCameraViewController
-
 SPEC CHECKSUMS:
-  ALCameraViewController: dd13cf0a5b44a4d542c73bbcbebd02bc09e929c7
+  ALCameraViewController: 144dea45fd749a623aafd8cb7b10cea88499fcc3
   DBDebugToolkit: c04bb6f618051d3de447a4b4323f37826116cfed
   InputBarAccessoryView: 58a348be7ea2736c7eec60e5c315511c2dbb39fd
   JGProgressHUD: 12b20a8f4ffe05258f8635c1ab92816e451f904d
@@ -57,6 +49,6 @@ SPEC CHECKSUMS:
   SwiftyBeaver: 4cc0080d2e23f980652e28978db11a5c9da39165
   UICircularProgressRing: 0ff679b05a17f15ad6301a7886686837b8c301a9
 
-PODFILE CHECKSUM: 4ccd5fdf02ac6afab3d4113bde3777593b2ff412
+PODFILE CHECKSUM: 37ab88c96ba5352ae1d3f997dc2615856fbd7e4c
 
 COCOAPODS: 1.8.4

+ 2 - 0
Pods/ALCameraViewController/ALCameraViewController/Utilities/CameraShot.swift

@@ -48,6 +48,8 @@ public func takePhoto(_ stillImageOutput: AVCaptureStillImageOutput, videoOrient
                 image = UIImage(cgImage: cgImage, scale: image.scale, orientation: .downMirrored)
             case .downMirrored:
                 image = UIImage(cgImage: cgImage, scale: image.scale, orientation: .down)
+            @unknown default:
+                break
             }
         }
         

+ 2 - 0
Pods/ALCameraViewController/ALCameraViewController/Utilities/PhotoLibraryAuthorizer.swift

@@ -42,6 +42,8 @@ class PhotoLibraryAuthorizer {
                 self.onDeniedOrRestricted(completion: self.completion)
             }
             break
+        @unknown default:
+            break
         }
     }
 }

+ 4 - 0
Pods/ALCameraViewController/ALCameraViewController/Utilities/Utilities.swift

@@ -86,6 +86,8 @@ internal func normalizedRect(_ rect: CGRect, orientation: UIImage.Orientation) -
         normalizedRect = CGRect(x: 1-normalizedY-normalizedHeight, y: normalizedX, width: normalizedHeight, height: normalizedWidth)
     case .right, .rightMirrored:
         normalizedRect = CGRect(x: normalizedY, y: 1-normalizedX-normalizedWidth, width: normalizedHeight, height: normalizedWidth)
+    @unknown default:
+        normalizedRect = .zero
     }
     
     return normalizedRect
@@ -100,6 +102,8 @@ internal func flashImage(_ mode: AVCaptureDevice.FlashMode) -> String {
         image = "flashOnIcon"
     case .off:
         image = "flashOffIcon"
+    @unknown default:
+        image = "flashOffIcon"
     }
     return image
 }

+ 3 - 25
Pods/ALCameraViewController/ALCameraViewController/ViewController/CameraViewController.swift

@@ -14,7 +14,7 @@ public typealias CameraViewCompletion = (UIImage?, PHAsset?) -> Void
 
 public extension CameraViewController {
     /// Provides an image picker wrapped inside a UINavigationController instance
-    public class func imagePickerViewController(croppingParameters: CroppingParameters, completion: @escaping CameraViewCompletion) -> UINavigationController {
+    class func imagePickerViewController(croppingParameters: CroppingParameters, completion: @escaping CameraViewCompletion) -> UINavigationController {
         let imagePicker = PhotoLibraryViewController()
         let navigationController = UINavigationController(rootViewController: imagePicker)
         
@@ -78,23 +78,12 @@ open class CameraViewController: UIViewController {
     
     var flashButtonEdgeConstraint: NSLayoutConstraint?
     var flashButtonGravityConstraint: NSLayoutConstraint?
-    
-    var cameraOverlayEdgeOneConstraint: NSLayoutConstraint?
-    var cameraOverlayEdgeTwoConstraint: NSLayoutConstraint?
-    var cameraOverlayWidthConstraint: NSLayoutConstraint?
-    var cameraOverlayCenterConstraint: NSLayoutConstraint?
-    
+
     let cameraView : CameraView = {
         let cameraView = CameraView()
         cameraView.translatesAutoresizingMaskIntoConstraints = false
         return cameraView
     }()
-
-    let cameraOverlay : CropOverlay = {
-        let cameraOverlay = CropOverlay()
-        cameraOverlay.translatesAutoresizingMaskIntoConstraints = false
-        return cameraOverlay
-    }()
     
     let cameraButton : UIButton = {
         let button = UIButton(frame: CGRect(x: 0, y: 0, width: 64, height: 64))
@@ -170,8 +159,6 @@ open class CameraViewController: UIViewController {
         self.allowVolumeButtonCapture = allowVolumeButtonCapture
         super.init(nibName: nil, bundle: nil)
         onCompletion = completion
-        cameraOverlay.isHidden = !croppingParameters.isEnabled
-        cameraOverlay.isUserInteractionEnabled = false
         libraryButton.isEnabled = allowsLibraryAccess
         libraryButton.isHidden = !allowsLibraryAccess
 		swapButton.isEnabled = allowsSwapCameraOrientation
@@ -199,7 +186,6 @@ open class CameraViewController: UIViewController {
         super.loadView()
         view.backgroundColor = UIColor.black
         [cameraView,
-            cameraOverlay,
             cameraButton,
             closeButton,
             flashButton,
@@ -247,14 +233,7 @@ open class CameraViewController: UIViewController {
         
         configFlashEdgeButtonConstraint(statusBarOrientation)
         configFlashGravityButtonConstraint(statusBarOrientation)
-        
-        let padding : CGFloat = portrait ? 16.0 : -16.0
-        removeCameraOverlayEdgesConstraints()
-        configCameraOverlayEdgeOneContraint(portrait, padding: padding)
-        configCameraOverlayEdgeTwoConstraint(portrait, padding: padding)
-        configCameraOverlayWidthConstraint(portrait)
-        configCameraOverlayCenterConstraint(portrait)
-        
+
         rotate(actualInterfaceOrientation: statusBarOrientation)
         
         super.updateViewConstraints()
@@ -273,7 +252,6 @@ open class CameraViewController: UIViewController {
         super.viewDidLoad()
         setupActions()
         checkPermissions()
-        cameraView.configureFocus()
         cameraView.configureZoom()
     }
 

+ 1 - 88
Pods/ALCameraViewController/ALCameraViewController/ViewController/CameraViewControllerConstraint.swift

@@ -437,92 +437,5 @@ extension CameraViewController {
             constant: constraintRight ? -8 : 8)
         view.addConstraint(flashButtonGravityConstraint!)
     }
-    
-    /**
-     * Used to create a perfect square for CameraOverlay.
-     * This method will determinate the size of CameraOverlay,
-     * if portrait, it will use the width of superview to
-     * determinate the height of the view. Else if landscape,
-     * it uses the height of the superview to create the width
-     * of the CameraOverlay.
-     */
-    func configCameraOverlayWidthConstraint(_ portrait: Bool) {
-        view.autoRemoveConstraint(cameraOverlayWidthConstraint)
-        cameraOverlayWidthConstraint = NSLayoutConstraint(
-            item: cameraOverlay,
-            attribute: portrait ? .height : .width,
-            relatedBy: .equal,
-            toItem: cameraOverlay,
-            attribute: portrait ? .width : .height,
-            multiplier: 1.0,
-            constant: 0)
-        view.addConstraint(cameraOverlayWidthConstraint!)
-    }
-    
-    /**
-     * This method will center the relative position of
-     * CameraOverlay, based on the biggest size of the
-     * superview.
-     */
-    func configCameraOverlayCenterConstraint(_ portrait: Bool) {
-        view.autoRemoveConstraint(cameraOverlayCenterConstraint)
-        let attribute : NSLayoutConstraint.Attribute = portrait ? .centerY : .centerX
-        cameraOverlayCenterConstraint = NSLayoutConstraint(
-            item: cameraOverlay,
-            attribute: attribute,
-            relatedBy: .equal,
-            toItem: view,
-            attribute: attribute,
-            multiplier: 1.0,
-            constant: 0)
-        view.addConstraint(cameraOverlayCenterConstraint!)
-    }
-    
-    /**
-     * Remove the CameraOverlay constraints to be updated when
-     * the device was rotated.
-     */
-    func removeCameraOverlayEdgesConstraints() {
-        view.autoRemoveConstraint(cameraOverlayEdgeOneConstraint)
-        view.autoRemoveConstraint(cameraOverlayEdgeTwoConstraint)
-    }
-    
-    /**
-     * It needs to get a determined smallest size of the screen
-     to create the smallest size to be used on CameraOverlay.
-     It uses the orientation of the screen to determinate where
-     the view will be pinned.
-     */
-    func configCameraOverlayEdgeOneContraint(_ portrait: Bool, padding: CGFloat) {
-        let attribute : NSLayoutConstraint.Attribute = portrait ? .left : .bottom
-        cameraOverlayEdgeOneConstraint = NSLayoutConstraint(
-            item: cameraOverlay,
-            attribute: attribute,
-            relatedBy: .equal,
-            toItem: view,
-            attribute: attribute,
-            multiplier: 1.0,
-            constant: padding)
-        view.addConstraint(cameraOverlayEdgeOneConstraint!)
-    }
-    
-    /**
-     * It needs to get a determined smallest size of the screen
-     to create the smallest size to be used on CameraOverlay.
-     It uses the orientation of the screen to determinate where
-     the view will be pinned.
-     */
-    func configCameraOverlayEdgeTwoConstraint(_ portrait: Bool, padding: CGFloat) {
-        let attributeTwo : NSLayoutConstraint.Attribute = portrait ? .right : .top
-        cameraOverlayEdgeTwoConstraint = NSLayoutConstraint(
-            item: cameraOverlay,
-            attribute: attributeTwo,
-            relatedBy: .equal,
-            toItem: view,
-            attribute: attributeTwo,
-            multiplier: 1.0,
-            constant: -padding)
-        view.addConstraint(cameraOverlayEdgeTwoConstraint!)
-    }
-    
+
 }

+ 175 - 132
Pods/ALCameraViewController/ALCameraViewController/ViewController/ConfirmViewController.swift

@@ -9,14 +9,21 @@
 import UIKit
 import Photos
 
-public class ConfirmViewController: UIViewController, UIScrollViewDelegate {
+public class ConfirmViewController: UIViewController {
 	
 	let imageView = UIImageView()
 	@IBOutlet weak var scrollView: UIScrollView!
-	@IBOutlet weak var cropOverlay: CropOverlay!
 	@IBOutlet weak var cancelButton: UIButton!
 	@IBOutlet weak var confirmButton: UIButton!
-	@IBOutlet weak var centeringView: UIView!
+	@IBOutlet weak var centeredView: UIView!
+
+    private let cropOverlay = CropOverlay()
+    private var spinner: UIActivityIndicatorView? = nil
+    private var cropOverlayLeftConstraint = NSLayoutConstraint()
+    private var cropOverlayTopConstraint = NSLayoutConstraint()
+    private var cropOverlayWidthConstraint = NSLayoutConstraint()
+    private var cropOverlayHeightConstraint = NSLayoutConstraint()
+    private var isFirstLayout = true
 	
     var croppingParameters: CroppingParameters {
         didSet {
@@ -25,8 +32,45 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate {
         }
     }
 
-	var verticalPadding: CGFloat = 30
-	var horizontalPadding: CGFloat = 30
+    private var scrollViewVisibleSize: CGSize {
+        let contentInset = scrollView.contentInset
+        let scrollViewSize = scrollView.bounds.standardized.size
+        let width = scrollViewSize.width - contentInset.left - contentInset.right
+        let height = scrollViewSize.height - contentInset.top - contentInset.bottom
+        return CGSize(width:width, height:height)
+    }
+
+    private var scrollViewCenter: CGPoint {
+        let scrollViewSize = scrollViewVisibleSize
+        return CGPoint(x: scrollViewSize.width / 2.0,
+                       y: scrollViewSize.height / 2.0)
+    }
+
+    private let cropOverlayDefaultPadding: CGFloat = 20
+    private var cropOverlayDefaultFrame: CGRect {
+        let buttonsViewGap: CGFloat = 20 * 2 + 64
+        let centeredViewBounds: CGRect
+        if view.bounds.size.height > view.bounds.size.width {
+            centeredViewBounds = CGRect(x: 0,
+                                        y: 0,
+                                        width: view.bounds.size.width,
+                                        height: view.bounds.size.height - buttonsViewGap)
+        } else {
+            centeredViewBounds = CGRect(x: 0,
+                                        y: 0,
+                                        width: view.bounds.size.width - buttonsViewGap,
+                                        height: view.bounds.size.height)
+        }
+        
+        let cropOverlayWidth = min(centeredViewBounds.size.width, centeredViewBounds.size.height) - 2 * cropOverlayDefaultPadding
+        let cropOverlayX = centeredViewBounds.size.width / 2 - cropOverlayWidth / 2
+        let cropOverlayY = centeredViewBounds.size.height / 2 - cropOverlayWidth / 2
+
+        return CGRect(x: cropOverlayX,
+                      y: cropOverlayY,
+                      width: cropOverlayWidth,
+                      height: cropOverlayWidth)
+    }
 	
 	public var onComplete: CameraViewCompletion?
 
@@ -63,17 +107,11 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate {
 		super.viewDidLoad()
 
 		view.backgroundColor = UIColor.black
-		
-		scrollView.addSubview(imageView)
-		scrollView.delegate = self
-		scrollView.maximumZoomScale = 1
-		
-        cropOverlay.isHidden = true
-        cropOverlay.isResizable = croppingParameters.allowResizing
-        cropOverlay.isMovable = croppingParameters.allowMoving
-        cropOverlay.minimumSize = croppingParameters.minimumSize
 
-		let spinner = showSpinner()
+        loadScrollView()
+        loadCropOverlay()
+
+		showSpinner()
 		
 		disable()
 		
@@ -83,82 +121,86 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate {
 				.setTargetSize(largestPhotoSize())
 				.onSuccess { [weak self] image in
 					self?.configureWithImage(image)
-					self?.hideSpinner(spinner)
+					self?.hideSpinner()
 					self?.enable()
 				}
 				.onFailure { [weak self] error in
-					self?.hideSpinner(spinner)
+					self?.hideSpinner()
 				}
 				.fetch()
 		} else if let image = image {
 			configureWithImage(image)
-			hideSpinner(spinner)
+			hideSpinner()
 			enable()
 		}
 	}
-	
-	public override func viewWillLayoutSubviews() {
-		super.viewWillLayoutSubviews()
-		let scale = calculateMinimumScale(view.frame.size)
-		let frame = croppingParameters.isEnabled ? cropOverlay.frame : view.bounds
-		
-		scrollView.contentInset = calculateScrollViewInsets(frame)
-		scrollView.minimumZoomScale = scale
-		scrollView.zoomScale = scale
-		centerScrollViewContents()
-	}
-	
-	public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
-		super.viewWillTransition(to: size, with: coordinator)
-		
-		let scale = calculateMinimumScale(size)
-		var frame = view.bounds
-		
-		if croppingParameters.isEnabled {
-			frame = cropOverlay.frame
-			let centeringFrame = centeringView.frame
-			var origin: CGPoint
-			
-			if size.width > size.height { // landscape
-				let offset = (size.width - centeringFrame.height)
-				let expectedX = (centeringFrame.height/2 - frame.height/2) + offset
-				origin = CGPoint(x: expectedX, y: frame.origin.x)
-			} else {
-				let expectedY = (centeringFrame.width/2 - frame.width/2)
-				origin = CGPoint(x: frame.origin.y, y: expectedY)
-			}
-			
-			frame.origin = origin
-		} else {
-			frame.size = size
-		}
-		
-		let insets = calculateScrollViewInsets(frame)
-		
-		coordinator.animate(alongsideTransition: { [weak self] context in
-			self?.scrollView.contentInset = insets
-			self?.scrollView.minimumZoomScale = scale
-			self?.scrollView.zoomScale = scale
-			self?.centerScrollViewContents()
-			self?.centerImageViewOnRotate()
-			}, completion: nil)
-	}
+
+    public override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+
+        if isFirstLayout {
+            isFirstLayout = false
+            activateCropOverlayConstraint()
+            spinner?.center = centeredView.center
+        }
+    }
+
+    private func activateCropOverlayConstraint() {
+        cropOverlayLeftConstraint.constant = cropOverlayDefaultFrame.origin.x
+        cropOverlayTopConstraint.constant = cropOverlayDefaultFrame.origin.y
+        cropOverlayWidthConstraint.constant = cropOverlayDefaultFrame.size.width
+        cropOverlayHeightConstraint.constant = cropOverlayDefaultFrame.size.height
+
+        cropOverlayLeftConstraint.isActive = true
+        cropOverlayTopConstraint.isActive = true
+        cropOverlayWidthConstraint.isActive = true
+        cropOverlayHeightConstraint.isActive = true
+    }
+
+    private func loadScrollView() {
+        scrollView.addSubview(imageView)
+        scrollView.delegate = self
+        scrollView.maximumZoomScale = 1
+    }
+
+    private func prepareScrollView() {
+        let scale = calculateMinimumScale(view.bounds.size)
+
+        scrollView.minimumZoomScale = scale
+        scrollView.zoomScale = scale
+
+        centerScrollViewContent()
+    }
+
+    private func loadCropOverlay() {
+        cropOverlay.translatesAutoresizingMaskIntoConstraints = false
+        view.addSubview(cropOverlay)
+
+        cropOverlayLeftConstraint = cropOverlay.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0)
+        cropOverlayTopConstraint = cropOverlay.topAnchor.constraint(equalTo: view.topAnchor, constant: 0)
+        cropOverlayWidthConstraint = cropOverlay.widthAnchor.constraint(equalToConstant: 0)
+        cropOverlayHeightConstraint = cropOverlay.heightAnchor.constraint(equalToConstant: 0)
+
+        cropOverlay.delegate = self
+        cropOverlay.isHidden = !croppingParameters.isEnabled
+        cropOverlay.isResizable = croppingParameters.allowResizing
+        cropOverlay.isMovable = croppingParameters.allowMoving
+        cropOverlay.minimumSize = croppingParameters.minimumSize
+    }
 	
 	private func configureWithImage(_ image: UIImage) {
-		cropOverlay.isHidden = !croppingParameters.isEnabled
-		
 		buttonActions()
 		
 		imageView.image = image
 		imageView.sizeToFit()
-		view.setNeedsLayout()
+        prepareScrollView()
 	}
 	
 	private func calculateMinimumScale(_ size: CGSize) -> CGFloat {
 		var _size = size
 		
 		if croppingParameters.isEnabled {
-			_size = cropOverlay.frame.size
+            _size = cropOverlayDefaultFrame.size
 		}
 		
 		guard let image = imageView.image else {
@@ -171,39 +213,32 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate {
 		return min(scaleWidth, scaleHeight)
 	}
 	
-	private func calculateScrollViewInsets(_ frame: CGRect) -> UIEdgeInsets {
-		let bottom = view.frame.height - (frame.origin.y + frame.height)
-		let right = view.frame.width - (frame.origin.x + frame.width)
-		let insets = UIEdgeInsets(top: frame.origin.y, left: frame.origin.x, bottom: bottom, right: right)
-		return insets
-	}
-	
-	private func centerImageViewOnRotate() {
-		if croppingParameters.isEnabled {
-			let size = cropOverlay.frame.size
-			let scrollInsets = scrollView.contentInset
-			let imageSize = imageView.frame.size
-			var contentOffset = CGPoint(x: -scrollInsets.left, y: -scrollInsets.top)
-			contentOffset.x -= (size.width - imageSize.width) / 2
-			contentOffset.y -= (size.height - imageSize.height) / 2
-			scrollView.contentOffset = contentOffset
-		}
-	}
-	
-	private func centerScrollViewContents() {
-		let size = croppingParameters.isEnabled ? cropOverlay.frame.size : scrollView.frame.size
-		let imageSize = imageView.frame.size
-		var imageOrigin = CGPoint.zero
-		
-		if imageSize.width < size.width {
-			imageOrigin.x = (size.width - imageSize.width) / 2
-		}
-		
-		if imageSize.height < size.height {
-			imageOrigin.y = (size.height - imageSize.height) / 2
-		}
-		
-		imageView.frame.origin = imageOrigin
+	private func centerScrollViewContent() {
+        guard let image = imageView.image else {
+            return
+        }
+
+        let imgViewSize = imageView.frame.size
+        let imageSize = image.size
+
+        var realImgSize: CGSize
+        if imageSize.width / imageSize.height > imgViewSize.width / imgViewSize.height {
+            realImgSize = CGSize(width: imgViewSize.width,height: imgViewSize.width / imageSize.width * imageSize.height)
+        } else {
+            realImgSize = CGSize(width: imgViewSize.height / imageSize.height * imageSize.width, height: imgViewSize.height)
+        }
+
+        var frame = CGRect.zero
+        frame.size = realImgSize
+        imageView.frame = frame
+
+        let screenSize  = scrollView.frame.size
+        let offx = screenSize.width > realImgSize.width ? (screenSize.width - realImgSize.width) / 2 : 0
+        let offy = screenSize.height > realImgSize.height ? (screenSize.height - realImgSize.height) / 2 : 0
+        scrollView.contentInset = UIEdgeInsets(top: offy,
+                                               left: offx,
+                                               bottom: offy,
+                                               right: offx)
 	}
 	
 	private func buttonActions() {
@@ -225,17 +260,17 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate {
 		
 		imageView.isHidden = true
 		
-		let spinner = showSpinner()
+		showSpinner()
 		
 		if let asset = asset {
 			var fetcher = SingleImageFetcher()
 				.onSuccess { [weak self] image in
 					self?.onComplete?(image, self?.asset)
-					self?.hideSpinner(spinner)
+					self?.hideSpinner()
 					self?.enable()
 				}
 				.onFailure { [weak self] error in
-					self?.hideSpinner(spinner)
+					self?.hideSpinner()
 					self?.showNoImageScreen(error)
 				}
 				.setAsset(asset)
@@ -258,34 +293,24 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate {
 			}
 			
 			onComplete?(newImage, nil)
-			hideSpinner(spinner)
+			hideSpinner()
 			enable()
 		}
 	}
 	
-	public func viewForZooming(in scrollView: UIScrollView) -> UIView? {
-		return imageView
-	}
-	
-	public func scrollViewDidZoom(_ scrollView: UIScrollView) {
-		centerScrollViewContents()
-	}
-	
-	func showSpinner() -> UIActivityIndicatorView {
-		let spinner = UIActivityIndicatorView()
-        spinner.style = .white
-		spinner.center = view.center
-		spinner.startAnimating()
-		
-		view.addSubview(spinner)
-        view.bringSubviewToFront(spinner)
-		
-		return spinner
-	}
+	func showSpinner() {
+		spinner = UIActivityIndicatorView()
+        spinner!.style = .white
+        spinner!.center = centeredView.center
+		spinner!.startAnimating()
+		
+		view.addSubview(spinner!)
+        view.bringSubviewToFront(spinner!)
+    }
 	
-	func hideSpinner(_ spinner: UIActivityIndicatorView) {
-		spinner.stopAnimating()
-		spinner.removeFromSuperview()
+	func hideSpinner() {
+		spinner?.stopAnimating()
+		spinner?.removeFromSuperview()
 	}
 	
 	func disable() {
@@ -305,10 +330,7 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate {
 	}
 	
 	private func makeProportionalCropRect() -> CGRect {
-		var cropRect = CGRect(x: cropOverlay.frame.origin.x + cropOverlay.outterGap,
-		                      y: cropOverlay.frame.origin.y + cropOverlay.outterGap,
-		                      width: cropOverlay.frame.size.width - 2 * cropOverlay.outterGap,
-		                      height: cropOverlay.frame.size.height - 2 * cropOverlay.outterGap)
+        var cropRect = cropOverlay.croppedRect
         cropRect.origin.x += scrollView.contentOffset.x - imageView.frame.origin.x
         cropRect.origin.y += scrollView.contentOffset.y - imageView.frame.origin.y
 
@@ -326,6 +348,27 @@ public class ConfirmViewController: UIViewController, UIScrollViewDelegate {
 	
 }
 
+extension ConfirmViewController: UIScrollViewDelegate {
+
+    public func viewForZooming(in scrollView: UIScrollView) -> UIView? {
+        return imageView
+    }
+
+    public func scrollViewDidZoom(_ scrollView: UIScrollView) {
+        centerScrollViewContent()
+    }
+}
+
+extension ConfirmViewController: CropOverlayDelegate {
+
+    func didMoveCropOverlay(newFrame: CGRect) {
+        cropOverlayLeftConstraint.constant = newFrame.origin.x
+        cropOverlayTopConstraint.constant = newFrame.origin.y
+        cropOverlayWidthConstraint.constant = newFrame.size.width
+        cropOverlayHeightConstraint.constant = newFrame.size.height
+    }
+}
+
 extension UIImage {
 	func crop(rect: CGRect) -> UIImage {
 

+ 14 - 62
Pods/ALCameraViewController/ALCameraViewController/ViewController/ConfirmViewController.xib

@@ -1,21 +1,16 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
-    <device id="retina4_0" orientation="portrait">
-        <adaptation id="fullscreen"/>
-    </device>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15400" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina4_0" orientation="portrait" appearance="light"/>
     <dependencies>
-        <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
-        <capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <objects>
         <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ConfirmViewController" customModule="ALCameraViewController" customModuleProvider="target">
             <connections>
                 <outlet property="cancelButton" destination="yRi-ES-LfN" id="PUm-mc-H7R"/>
-                <outlet property="centeringView" destination="KYd-D9-K5d" id="CmX-BA-VtZ"/>
+                <outlet property="centeredView" destination="KYd-D9-K5d" id="CmX-BA-VtZ"/>
                 <outlet property="confirmButton" destination="ASf-ZD-cIs" id="mpH-Eg-IbA"/>
-                <outlet property="cropOverlay" destination="lnA-tb-Sap" id="34E-zk-Axd"/>
                 <outlet property="scrollView" destination="oUR-U3-uEM" id="rF0-ZM-RmA"/>
                 <outlet property="view" destination="iN0-l3-epB" id="FcS-Dy-kqF"/>
             </connections>
@@ -28,19 +23,11 @@
                 <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oUR-U3-uEM">
                     <rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
                 </scrollView>
-                <view hidden="YES" userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KYd-D9-K5d">
-                    <rect key="frame" x="0.0" y="0.0" width="320" height="474"/>
-                    <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
-                </view>
-                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lnA-tb-Sap" customClass="CropOverlay" customModule="ALCameraViewController" customModuleProvider="target">
-                    <rect key="frame" x="15" y="92" width="290" height="290"/>
-                    <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
-                    <constraints>
-                        <constraint firstAttribute="width" secondItem="lnA-tb-Sap" secondAttribute="height" multiplier="1:1" id="lv8-l9-lJq"/>
-                    </constraints>
+                <view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KYd-D9-K5d">
+                    <rect key="frame" x="0.0" y="0.0" width="320" height="464"/>
                 </view>
                 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ASf-ZD-cIs">
-                    <rect key="frame" x="74" y="474" width="64" height="64"/>
+                    <rect key="frame" x="20" y="484" width="64" height="64"/>
                     <constraints>
                         <constraint firstAttribute="height" constant="64" id="J7n-mn-Ebe"/>
                         <constraint firstAttribute="width" constant="64" id="YWt-e6-Bvy"/>
@@ -48,7 +35,7 @@
                     <state key="normal" image="confirmButton"/>
                 </button>
                 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yRi-ES-LfN">
-                    <rect key="frame" x="198" y="474" width="64" height="64"/>
+                    <rect key="frame" x="236" y="484" width="64" height="64"/>
                     <constraints>
                         <constraint firstAttribute="height" constant="64" id="FdJ-mW-Tx6"/>
                         <constraint firstAttribute="width" constant="64" id="urS-JS-i1S"/>
@@ -58,41 +45,31 @@
             </subviews>
             <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
             <constraints>
-                <constraint firstItem="lnA-tb-Sap" firstAttribute="centerY" secondItem="KYd-D9-K5d" secondAttribute="centerY" id="1uu-Bd-bz3"/>
+                <constraint firstItem="ASf-ZD-cIs" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" id="1AT-7k-2QI"/>
                 <constraint firstAttribute="trailing" secondItem="oUR-U3-uEM" secondAttribute="trailing" id="7A6-HH-MEu"/>
-                <constraint firstAttribute="bottom" secondItem="yRi-ES-LfN" secondAttribute="bottom" constant="30" id="7jo-lC-34t"/>
+                <constraint firstAttribute="bottom" secondItem="yRi-ES-LfN" secondAttribute="bottom" constant="20" id="7jo-lC-34t"/>
                 <constraint firstItem="ASf-ZD-cIs" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" constant="-45" id="89L-rE-rmd"/>
                 <constraint firstAttribute="bottom" secondItem="oUR-U3-uEM" secondAttribute="bottom" id="8mU-82-Hf6"/>
                 <constraint firstItem="oUR-U3-uEM" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="92s-g3-wfK"/>
                 <constraint firstItem="KYd-D9-K5d" firstAttribute="leading" secondItem="yRi-ES-LfN" secondAttribute="trailing" id="CcA-mG-BLU"/>
-                <constraint firstItem="yRi-ES-LfN" firstAttribute="top" secondItem="KYd-D9-K5d" secondAttribute="bottom" id="Ck2-xk-HbG"/>
+                <constraint firstItem="yRi-ES-LfN" firstAttribute="top" secondItem="KYd-D9-K5d" secondAttribute="bottom" constant="20" id="Ck2-xk-HbG"/>
                 <constraint firstItem="oUR-U3-uEM" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="Cuy-cw-jOk"/>
-                <constraint firstAttribute="bottom" secondItem="ASf-ZD-cIs" secondAttribute="bottom" constant="30" id="DZ5-WA-3ZQ"/>
-                <constraint firstItem="lnA-tb-Sap" firstAttribute="top" relation="greaterThanOrEqual" secondItem="oUR-U3-uEM" secondAttribute="top" constant="100" id="GAL-ck-d0B"/>
+                <constraint firstAttribute="bottom" secondItem="ASf-ZD-cIs" secondAttribute="bottom" constant="20" id="DZ5-WA-3ZQ"/>
                 <constraint firstAttribute="bottom" secondItem="yRi-ES-LfN" secondAttribute="bottom" constant="60" id="H4a-3r-r5r"/>
                 <constraint firstItem="oUR-U3-uEM" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="J3N-Vr-euu"/>
                 <constraint firstAttribute="trailing" secondItem="KYd-D9-K5d" secondAttribute="trailing" id="JLk-PT-5kn"/>
-                <constraint firstItem="lnA-tb-Sap" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="15" id="P5N-Xh-C1z"/>
+                <constraint firstAttribute="trailing" secondItem="yRi-ES-LfN" secondAttribute="trailing" constant="20" id="M7P-T7-qoy"/>
                 <constraint firstItem="ASf-ZD-cIs" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="30" id="Rwi-4K-QRb"/>
                 <constraint firstItem="ASf-ZD-cIs" firstAttribute="bottom" secondItem="yRi-ES-LfN" secondAttribute="top" constant="-60" id="TLl-L0-yLb"/>
-                <constraint firstItem="ASf-ZD-cIs" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" constant="-54" id="WSF-pa-4wb"/>
                 <constraint firstAttribute="trailing" secondItem="KYd-D9-K5d" secondAttribute="trailing" id="YBv-2a-KdQ"/>
                 <constraint firstItem="KYd-D9-K5d" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="Yhb-Y3-ngX"/>
                 <constraint firstItem="KYd-D9-K5d" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="aj9-Xh-WHL"/>
-                <constraint firstItem="lnA-tb-Sap" firstAttribute="leading" secondItem="oUR-U3-uEM" secondAttribute="leading" priority="999" constant="100" id="bRR-fN-fEb"/>
-                <constraint firstItem="lnA-tb-Sap" firstAttribute="centerY" secondItem="KYd-D9-K5d" secondAttribute="centerY" id="dbm-T2-OgB"/>
-                <constraint firstAttribute="bottom" secondItem="lnA-tb-Sap" secondAttribute="bottom" constant="15" id="eMF-sY-QhQ"/>
-                <constraint firstAttribute="trailing" secondItem="lnA-tb-Sap" secondAttribute="trailing" constant="15" id="efC-ff-52c"/>
-                <constraint firstItem="lnA-tb-Sap" firstAttribute="centerX" secondItem="KYd-D9-K5d" secondAttribute="centerX" id="fPc-8D-E0O"/>
                 <constraint firstItem="KYd-D9-K5d" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="hPp-8q-A1p"/>
-                <constraint firstItem="lnA-tb-Sap" firstAttribute="centerX" secondItem="KYd-D9-K5d" secondAttribute="centerX" id="hpa-Qn-uzT"/>
                 <constraint firstItem="ASf-ZD-cIs" firstAttribute="top" secondItem="KYd-D9-K5d" secondAttribute="bottom" id="iar-0y-gBe"/>
                 <constraint firstAttribute="bottom" secondItem="oUR-U3-uEM" secondAttribute="bottom" id="iw9-Cg-Kkk"/>
                 <constraint firstItem="oUR-U3-uEM" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="kDN-mM-6KE"/>
-                <constraint firstItem="yRi-ES-LfN" firstAttribute="leading" secondItem="ASf-ZD-cIs" secondAttribute="trailing" constant="60" id="m1F-XR-hgU"/>
                 <constraint firstAttribute="trailing" secondItem="oUR-U3-uEM" secondAttribute="trailing" id="mL3-PM-TEd"/>
                 <constraint firstAttribute="bottom" secondItem="KYd-D9-K5d" secondAttribute="bottom" id="o6j-Qo-hB5"/>
-                <constraint firstItem="lnA-tb-Sap" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="15" id="ohf-4f-yKs"/>
                 <constraint firstItem="ASf-ZD-cIs" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" constant="-54" id="pNU-uB-Igt"/>
                 <constraint firstItem="yRi-ES-LfN" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="30" id="v0q-tU-ywG"/>
                 <constraint firstItem="KYd-D9-K5d" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="vnU-ey-3GX"/>
@@ -113,16 +90,6 @@
                     <exclude reference="vnU-ey-3GX"/>
                     <exclude reference="wTn-DN-uQO"/>
                     <exclude reference="wn3-We-BBK"/>
-                    <exclude reference="1uu-Bd-bz3"/>
-                    <exclude reference="GAL-ck-d0B"/>
-                    <exclude reference="P5N-Xh-C1z"/>
-                    <exclude reference="bRR-fN-fEb"/>
-                    <exclude reference="dbm-T2-OgB"/>
-                    <exclude reference="eMF-sY-QhQ"/>
-                    <exclude reference="efC-ff-52c"/>
-                    <exclude reference="fPc-8D-E0O"/>
-                    <exclude reference="hpa-Qn-uzT"/>
-                    <exclude reference="ohf-4f-yKs"/>
                     <exclude reference="7A6-HH-MEu"/>
                     <exclude reference="8mU-82-Hf6"/>
                     <exclude reference="92s-g3-wfK"/>
@@ -131,13 +98,11 @@
                     <exclude reference="DZ5-WA-3ZQ"/>
                     <exclude reference="Rwi-4K-QRb"/>
                     <exclude reference="TLl-L0-yLb"/>
-                    <exclude reference="WSF-pa-4wb"/>
                     <exclude reference="iar-0y-gBe"/>
                     <exclude reference="pNU-uB-Igt"/>
                     <exclude reference="7jo-lC-34t"/>
                     <exclude reference="Ck2-xk-HbG"/>
                     <exclude reference="H4a-3r-r5r"/>
-                    <exclude reference="m1F-XR-hgU"/>
                     <exclude reference="v0q-tU-ywG"/>
                     <exclude reference="xFS-1L-oa4"/>
                     <exclude reference="xFc-F1-V7I"/>
@@ -146,16 +111,12 @@
             <variation key="heightClass=compact">
                 <mask key="subviews">
                     <include reference="KYd-D9-K5d"/>
-                    <include reference="lnA-tb-Sap"/>
                 </mask>
                 <mask key="constraints">
                     <include reference="CcA-mG-BLU"/>
                     <include reference="Yhb-Y3-ngX"/>
                     <include reference="o6j-Qo-hB5"/>
                     <include reference="wTn-DN-uQO"/>
-                    <include reference="P5N-Xh-C1z"/>
-                    <include reference="eMF-sY-QhQ"/>
-                    <include reference="hpa-Qn-uzT"/>
                     <include reference="Rwi-4K-QRb"/>
                     <include reference="TLl-L0-yLb"/>
                     <include reference="pNU-uB-Igt"/>
@@ -165,20 +126,14 @@
             <variation key="heightClass=regular-widthClass=compact">
                 <mask key="subviews">
                     <include reference="KYd-D9-K5d"/>
-                    <include reference="lnA-tb-Sap"/>
                 </mask>
                 <mask key="constraints">
                     <include reference="JLk-PT-5kn"/>
                     <include reference="aj9-Xh-WHL"/>
                     <include reference="wn3-We-BBK"/>
-                    <include reference="1uu-Bd-bz3"/>
-                    <include reference="efC-ff-52c"/>
-                    <include reference="ohf-4f-yKs"/>
                     <include reference="DZ5-WA-3ZQ"/>
-                    <include reference="WSF-pa-4wb"/>
                     <include reference="7jo-lC-34t"/>
                     <include reference="Ck2-xk-HbG"/>
-                    <include reference="m1F-XR-hgU"/>
                 </mask>
             </variation>
             <variation key="heightClass=regular-widthClass=regular">
@@ -186,10 +141,6 @@
                     <include reference="YBv-2a-KdQ"/>
                     <include reference="hPp-8q-A1p"/>
                     <include reference="vnU-ey-3GX"/>
-                    <include reference="GAL-ck-d0B"/>
-                    <include reference="bRR-fN-fEb"/>
-                    <include reference="dbm-T2-OgB"/>
-                    <include reference="fPc-8D-E0O"/>
                     <include reference="7A6-HH-MEu"/>
                     <include reference="8mU-82-Hf6"/>
                     <include reference="92s-g3-wfK"/>
@@ -201,6 +152,7 @@
                     <include reference="xFc-F1-V7I"/>
                 </mask>
             </variation>
+            <point key="canvasLocation" x="131" y="154"/>
         </view>
     </objects>
     <resources>

+ 1 - 57
Pods/ALCameraViewController/ALCameraViewController/Views/CameraView.swift

@@ -18,9 +18,7 @@ public class CameraView: UIView {
     var preview: AVCaptureVideoPreviewLayer!
     
     let cameraQueue = DispatchQueue(label: "com.zero.ALCameraViewController.Queue")
-    
-    let focusView = CropOverlay(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
-    
+
     public var currentPosition = CameraGlobals.shared.defaultCameraPosition
     
     public func startSession() {
@@ -81,65 +79,11 @@ public class CameraView: UIView {
         super.layoutSubviews()
         preview?.frame = bounds
     }
-    
-    public func configureFocus() {
-        
-        if let gestureRecognizers = gestureRecognizers {
-            gestureRecognizers.forEach({ removeGestureRecognizer($0) })
-        }
-        
-        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(focus(gesture:)))
-        addGestureRecognizer(tapGesture)
-        isUserInteractionEnabled = true
-        addSubview(focusView)
-        
-        focusView.isHidden = true
-        
-        let lines = focusView.horizontalLines + focusView.verticalLines + focusView.outerLines
-        
-        lines.forEach { line in
-            line.alpha = 0
-        }
-    }
 
     public func configureZoom() {
         let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(pinch(gesture:)))
         addGestureRecognizer(pinchGesture)
     }
-    
-    @objc internal func focus(gesture: UITapGestureRecognizer) {
-        let point = gesture.location(in: self)
-        
-        guard focusCamera(toPoint: point) else {
-            return
-        }
-        
-        focusView.isHidden = false
-        focusView.center = point
-        focusView.alpha = 0
-        focusView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
-        
-        bringSubviewToFront(focusView)
-        
-        UIView.animateKeyframes(withDuration: 1.5, delay: 0, options: UIView.KeyframeAnimationOptions(), animations: {
-            
-            UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.15, animations: { [weak self] in
-                self?.focusView.alpha = 1
-                self?.focusView.transform = CGAffineTransform.identity
-            })
-            
-            UIView.addKeyframe(withRelativeStartTime: 0.80, relativeDuration: 0.20, animations: { [weak self] in
-                self?.focusView.alpha = 0
-                self?.focusView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
-            })
-            
-            
-            }, completion: { [weak self] finished in
-                if finished {
-                    self?.focusView.isHidden = true
-                }
-        })
-    }
 
     @objc internal func pinch(gesture: UIPinchGestureRecognizer) {
         guard let device = device else { return }

+ 249 - 178
Pods/ALCameraViewController/ALCameraViewController/Views/CropOverlay.swift

@@ -1,210 +1,66 @@
 //
-//  CropOverlay.swift
+//  NewCropOverlay.swift
 //  ALCameraViewController
 //
-//  Created by Alex Littlejohn on 2015/06/30.
-//  Copyright (c) 2015 zero. All rights reserved.
+//  Created by Guillaume Bellut on 12/10/2019.
+//  Copyright © 2019 zero. All rights reserved.
 //
 
 import UIKit
 
+protocol CropOverlayDelegate: class {
+    func didMoveCropOverlay(newFrame: CGRect)
+}
+
 internal class CropOverlay: UIView {
 
-    var outerLines = [UIView]()
-    var horizontalLines = [UIView]()
-    var verticalLines = [UIView]()
-    
-    var topLeftCornerLines = [UIView]()
-    var topRightCornerLines = [UIView]()
-    var bottomLeftCornerLines = [UIView]()
-    var bottomRightCornerLines = [UIView]()
-
-    var cornerButtons = [UIButton]()
-
-    let cornerLineDepth: CGFloat = 3
-    let cornerLineWidth: CGFloat = 22.5
-    var cornerButtonWidth: CGFloat {
-        return self.cornerLineWidth * 2
+    private let buttons = [UIButton(),  // top left
+                           UIButton(),  // top right
+                           UIButton(),  // bottom left
+                           UIButton()]  // bottom right
+    private let precisionView = UIView()    // view containing lines
+
+    private var cornerButtonWidth: CGFloat = 45
+
+    private let cornerLineDepth: CGFloat = 3
+    private var cornerLineLength: CGFloat {
+        return cornerButtonWidth / 2
     }
 
-    let lineWidth: CGFloat = 1
+    private let lineDepth: CGFloat = 1
 
-    let outterGapRatio: CGFloat = 1/3
-    var outterGap: CGFloat {
-        return self.cornerButtonWidth * self.outterGapRatio
+    private let outterGapRatio: CGFloat = 1/3
+    private var outterGap: CGFloat {
+        return cornerButtonWidth * self.outterGapRatio
     }
 
     var isResizable: Bool = false
     var isMovable: Bool = false
     var minimumSize: CGSize = CGSize.zero
+    weak var delegate: CropOverlayDelegate?
+
+    var croppedRect: CGRect {
+        return CGRect(x: frame.origin.x + outterGap,
+                      y: frame.origin.y + outterGap,
+                      width: frame.size.width - 2 * outterGap,
+                      height: frame.size.height - 2 * outterGap)
+    }
 
     internal override init(frame: CGRect) {
         super.init(frame: frame)
-        createLines()
+        setup()
     }
 
     internal required init?(coder aDecoder: NSCoder) {
         super.init(coder: aDecoder)
-        createLines()
-    }
-    
-    override func layoutSubviews() {
-        
-        for i in 0..<outerLines.count {
-            let line = outerLines[i]
-            var lineFrame: CGRect
-            switch (i) {
-            case 0:
-                lineFrame = CGRect(x: outterGap, y: outterGap, width: bounds.width - outterGap * 2, height: lineWidth)
-                break
-            case 1:
-                lineFrame = CGRect(x: bounds.width - lineWidth - outterGap, y: outterGap, width: lineWidth, height: bounds.height - outterGap * 2)
-                break
-            case 2:
-                lineFrame = CGRect(x: outterGap, y: bounds.height - lineWidth - outterGap, width: bounds.width - outterGap * 2, height: lineWidth)
-                break
-            case 3:
-                lineFrame = CGRect(x: outterGap, y: outterGap, width: lineWidth, height: bounds.height - outterGap * 2)
-                break
-            default:
-                lineFrame = CGRect.zero
-                break
-            }
-            
-            line.frame = lineFrame
-        }
-        
-        let corners = [topLeftCornerLines, topRightCornerLines, bottomLeftCornerLines, bottomRightCornerLines]
-        for i in 0..<corners.count {
-            let corner = corners[i]
-			
-			var horizontalFrame: CGRect
-			var verticalFrame: CGRect
-			var buttonFrame: CGRect
-			let buttonSize = CGSize(width: cornerButtonWidth, height: cornerButtonWidth)
-			
-            switch (i) {
-			case 0:	// Top Left
-				verticalFrame = CGRect(x: outterGap, y: outterGap, width: cornerLineDepth, height: cornerLineWidth)
-				horizontalFrame = CGRect(x: outterGap, y: outterGap, width: cornerLineWidth, height: cornerLineDepth)
-				buttonFrame = CGRect(origin: CGPoint(x: 0, y: 0), size: buttonSize)
-			case 1:	// Top Right
-				verticalFrame = CGRect(x: bounds.width - cornerLineDepth - outterGap, y: outterGap, width: cornerLineDepth, height: cornerLineWidth)
-				horizontalFrame = CGRect(x: bounds.width - cornerLineWidth - outterGap, y: outterGap, width: cornerLineWidth, height: cornerLineDepth)
-				buttonFrame = CGRect(origin: CGPoint(x: bounds.width - cornerButtonWidth, y: 0), size: buttonSize)
-			case 2:	// Bottom Left
-				verticalFrame = CGRect(x: outterGap, y:  bounds.height - cornerLineWidth - outterGap, width: cornerLineDepth, height: cornerLineWidth)
-				horizontalFrame = CGRect(x: outterGap, y:  bounds.height - cornerLineDepth - outterGap, width: cornerLineWidth, height: cornerLineDepth)
-				buttonFrame = CGRect(origin: CGPoint(x: 0, y: bounds.height - cornerButtonWidth), size: buttonSize)
-			case 3:	// Bottom Right
-				verticalFrame = CGRect(x: bounds.width - cornerLineDepth - outterGap, y: bounds.height - cornerLineWidth - outterGap, width: cornerLineDepth, height: cornerLineWidth)
-				horizontalFrame = CGRect(x: bounds.width - cornerLineWidth - outterGap, y: bounds.height - cornerLineDepth - outterGap, width: cornerLineWidth, height: cornerLineDepth)
-				buttonFrame = CGRect(origin: CGPoint(x: bounds.width - cornerButtonWidth, y: bounds.height - cornerButtonWidth), size: buttonSize)
-
-            default:
-                verticalFrame = CGRect.zero
-                horizontalFrame = CGRect.zero
-				buttonFrame = CGRect.zero
-            }
-			
-            corner[0].frame = verticalFrame
-            corner[1].frame = horizontalFrame
-			cornerButtons[i].frame = buttonFrame
-        }
-		
-		let lineThickness = lineWidth / UIScreen.main.scale
-		let vPadding = (bounds.height - outterGap * 2 - (lineThickness * CGFloat(horizontalLines.count))) / CGFloat(horizontalLines.count + 1)
-		let hPadding = (bounds.width - outterGap * 2 - (lineThickness * CGFloat(verticalLines.count))) / CGFloat(verticalLines.count + 1)
-		
-        for i in 0..<horizontalLines.count {
-            let hLine = horizontalLines[i]
-            let vLine = verticalLines[i]
-			
-			let vSpacing = (vPadding * CGFloat(i + 1)) + (lineThickness * CGFloat(i))
-			let hSpacing = (hPadding * CGFloat(i + 1)) + (lineThickness * CGFloat(i))
-			
-			hLine.frame = CGRect(x: outterGap, y: vSpacing + outterGap, width: bounds.width - outterGap * 2, height:  lineThickness)
-			vLine.frame = CGRect(x: hSpacing + outterGap, y: outterGap, width: lineThickness, height: bounds.height - outterGap * 2)
-        }
-		
-    }
-	
-    func createLines() {
-        
-        outerLines = [createLine(), createLine(), createLine(), createLine()]
-        horizontalLines = [createLine(), createLine()]
-        verticalLines = [createLine(), createLine()]
-        
-        topLeftCornerLines = [createLine(), createLine()]
-        topRightCornerLines = [createLine(), createLine()]
-        bottomLeftCornerLines = [createLine(), createLine()]
-        bottomRightCornerLines = [createLine(), createLine()]
-        
-		cornerButtons = [createButton(), createButton(), createButton(), createButton()]
-		
-		let dragGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(moveCropOverlay))
-		addGestureRecognizer(dragGestureRecognizer)
-    }
-    
-    func createLine() -> UIView {
-        let line = UIView()
-        line.backgroundColor = UIColor.white
-        addSubview(line)
-        return line
+        setup()
     }
-	
-	func createButton() -> UIButton {
-		let button = UIButton()
-		button.backgroundColor = UIColor.clear
-		
-		let dragGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(moveCropOverlay))
-		button.addGestureRecognizer(dragGestureRecognizer)
-
-		addSubview(button)
-		return button
-	}
-	
-	@objc func moveCropOverlay(gestureRecognizer: UIPanGestureRecognizer) {
-		if isResizable, let button = gestureRecognizer.view as? UIButton {
-			if gestureRecognizer.state == .began || gestureRecognizer.state == .changed {
-				let translation = gestureRecognizer.translation(in: self)
-				
-				var newFrame: CGRect
-				
-				switch button {
-				case cornerButtons[0]:	// Top Left
-                    newFrame = CGRect(x: frame.origin.x + translation.x, y: frame.origin.y + translation.y, width: frame.size.width - translation.x, height: frame.size.height - translation.y)
-				case cornerButtons[1]:	// Top Right
-					newFrame = CGRect(x: frame.origin.x, y: frame.origin.y + translation.y, width: frame.size.width + translation.x, height: frame.size.height - translation.y)
-				case cornerButtons[2]:	// Bottom Left
-					newFrame = CGRect(x: frame.origin.x + translation.x, y: frame.origin.y, width: frame.size.width - translation.x, height: frame.size.height + translation.y)
-				case cornerButtons[3]:	// Bottom Right
-					newFrame = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width + translation.x, height: frame.size.height + translation.y)
-				default:
-					newFrame = CGRect.zero
-				}
-
-                let minimumFrame = CGRect(x: newFrame.origin.x, y: newFrame.origin.y, width: max(newFrame.size.width, minimumSize.width + 2 * outterGap), height: max(newFrame.size.height, minimumSize.height + 2 * outterGap))
-				frame = minimumFrame
-				layoutSubviews()
-
-				gestureRecognizer.setTranslation(CGPoint.zero, in: self)
-			}
-		} else if isMovable {
-			if gestureRecognizer.state == .began || gestureRecognizer.state == .changed {
-				let translation = gestureRecognizer.translation(in: self)
-				
-				gestureRecognizer.view!.center = CGPoint(x: gestureRecognizer.view!.center.x + translation.x, y: gestureRecognizer.view!.center.y + translation.y)
-				gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: self)
-			}
-		}
-	}
 
     override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
         let view = super.hitTest(point, with: event)
 
-        if !isMovable && isResizable && view != nil {
-            let isButton = cornerButtons.reduce(false) { $1.hitTest(convert(point, to: $1), with: event) != nil || $0 }
+        if !isMovable && isResizable {
+            let isButton = buttons.reduce(false) { $1.hitTest(convert(point, to: $1), with: event) != nil || $0 }
             if !isButton {
                 return nil
             }
@@ -212,4 +68,219 @@ internal class CropOverlay: UIView {
 
         return view
     }
+
+    private func setup() {
+        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(move(gestureRecognizer:)))
+        addGestureRecognizer(panGesture)
+
+        loadButtons()
+        loadPrecisionView()
+    }
+
+    private func loadButtons() {
+        buttons.forEach {
+            $0.translatesAutoresizingMaskIntoConstraints = false
+            addSubview($0)
+
+            let panGesture = UIPanGestureRecognizer(target: self, action: #selector(move(gestureRecognizer:)))
+            $0.addGestureRecognizer(panGesture)
+        }
+
+        buttons[0].topAnchor.constraint(equalTo: topAnchor).isActive = true
+        buttons[0].leftAnchor.constraint(equalTo: leftAnchor).isActive = true
+        buttons[0].widthAnchor.constraint(equalToConstant: cornerButtonWidth).isActive = true
+        buttons[0].heightAnchor.constraint(equalTo: buttons[0].widthAnchor).isActive = true
+
+        buttons[1].topAnchor.constraint(equalTo: topAnchor).isActive = true
+        buttons[1].rightAnchor.constraint(equalTo: rightAnchor).isActive = true
+        buttons[1].widthAnchor.constraint(equalToConstant: cornerButtonWidth).isActive = true
+        buttons[1].heightAnchor.constraint(equalTo: buttons[1].widthAnchor).isActive = true
+
+        buttons[2].bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
+        buttons[2].leftAnchor.constraint(equalTo: leftAnchor).isActive = true
+        buttons[2].widthAnchor.constraint(equalToConstant: cornerButtonWidth).isActive = true
+        buttons[2].heightAnchor.constraint(equalTo: buttons[2].widthAnchor).isActive = true
+
+        buttons[3].bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
+        buttons[3].rightAnchor.constraint(equalTo: rightAnchor).isActive = true
+        buttons[3].widthAnchor.constraint(equalToConstant: cornerButtonWidth).isActive = true
+        buttons[3].heightAnchor.constraint(equalTo: buttons[3].widthAnchor).isActive = true
+    }
+
+    private func loadPrecisionView() {
+        precisionView.translatesAutoresizingMaskIntoConstraints = false
+        addSubview(precisionView)
+
+        precisionView.isUserInteractionEnabled = false
+        precisionView.layer.borderWidth = 1
+        precisionView.layer.borderColor = UIColor.white.cgColor
+
+        precisionView.topAnchor.constraint(equalTo: topAnchor, constant: outterGap).isActive = true
+        precisionView.leftAnchor.constraint(equalTo: leftAnchor, constant: outterGap).isActive = true
+        precisionView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -outterGap).isActive = true
+        precisionView.rightAnchor.constraint(equalTo: rightAnchor, constant: -outterGap).isActive = true
+
+        loadCornerLines()
+        loadPrecisionLines()
+    }
+
+    private func loadCornerLines() {
+        let cornerLines = [UIView(), UIView(),  // top left
+            UIView(), UIView(),  // top right
+            UIView(), UIView(),  // bottom left
+            UIView(), UIView()]  // bottom right
+
+        cornerLines.forEach {
+            $0.translatesAutoresizingMaskIntoConstraints = false
+            precisionView.addSubview($0)
+
+            $0.isUserInteractionEnabled = false
+            $0.backgroundColor = .white
+
+            let index = cornerLines.firstIndex(of: $0)!
+
+            if index % 2 == 0 {
+                $0.widthAnchor.constraint(equalToConstant: cornerLineDepth).isActive = true
+                $0.heightAnchor.constraint(equalToConstant: cornerLineLength).isActive = true
+
+                if index <= 3 {
+                    $0.topAnchor.constraint(equalTo: precisionView.topAnchor, constant: -cornerLineDepth).isActive = true
+                } else {
+                    $0.bottomAnchor.constraint(equalTo: precisionView.bottomAnchor, constant: cornerLineDepth).isActive = true
+                }
+
+                if index % 4 == 0 {
+                    $0.rightAnchor.constraint(equalTo: precisionView.leftAnchor).isActive = true
+                } else {
+                    $0.leftAnchor.constraint(equalTo: precisionView.rightAnchor).isActive = true
+                }
+            } else {
+                $0.widthAnchor.constraint(equalToConstant: cornerLineLength).isActive = true
+                $0.heightAnchor.constraint(equalToConstant: cornerLineDepth).isActive = true
+
+                if index <= 3 {
+                    $0.leftAnchor.constraint(equalTo: precisionView.leftAnchor, constant: -cornerLineDepth).isActive = true
+                } else {
+                    $0.rightAnchor.constraint(equalTo: precisionView.rightAnchor, constant: cornerLineDepth).isActive = true
+                }
+
+                if index % 4 == 1 {
+                    $0.bottomAnchor.constraint(equalTo: precisionView.topAnchor).isActive = true
+                } else {
+                    $0.topAnchor.constraint(equalTo: precisionView.bottomAnchor).isActive = true
+                }
+            }
+        }
+    }
+
+    private func loadPrecisionLines() {
+        let centeredViews = [UIView(), UIView()]
+
+        centeredViews.forEach {
+            $0.translatesAutoresizingMaskIntoConstraints = false
+            precisionView.addSubview($0)
+
+            $0.isUserInteractionEnabled = false
+
+            $0.layer.borderWidth = 1
+            $0.layer.borderColor = UIColor.white.cgColor
+        }
+
+        // Horizontal view
+        centeredViews[0].leftAnchor.constraint(equalTo: precisionView.leftAnchor).isActive = true
+        centeredViews[0].rightAnchor.constraint(equalTo: precisionView.rightAnchor).isActive = true
+        centeredViews[0].heightAnchor.constraint(equalTo: precisionView.heightAnchor, multiplier: 1/3).isActive = true
+        centeredViews[0].centerYAnchor.constraint(equalTo: precisionView.centerYAnchor).isActive = true
+
+        // Vertical view
+        centeredViews[1].topAnchor.constraint(equalTo: precisionView.topAnchor).isActive = true
+        centeredViews[1].bottomAnchor.constraint(equalTo: precisionView.bottomAnchor).isActive = true
+        centeredViews[1].widthAnchor.constraint(equalTo: precisionView.widthAnchor, multiplier: 1/3).isActive = true
+        centeredViews[1].centerXAnchor.constraint(equalTo: precisionView.centerXAnchor).isActive = true
+    }
+
+    @objc func move(gestureRecognizer: UIPanGestureRecognizer) {
+        if isResizable, let button = gestureRecognizer.view as? UIButton {
+            if gestureRecognizer.state == .began || gestureRecognizer.state == .changed {
+                let translation = gestureRecognizer.translation(in: self)
+                let realMinimumSize = CGSize(width: minimumSize.width + 2 * outterGap,
+                                             height: minimumSize.height + 2 * outterGap)
+
+                var newFrame: CGRect
+
+                switch button {
+                case buttons[0]:    // Top Left
+                    let hasEnoughWidth = frame.size.width - translation.x >= realMinimumSize.width
+                    let hasEnoughHeight = frame.size.height - translation.y >= realMinimumSize.height
+
+                    let xPossibleTranslation = hasEnoughWidth ? translation.x : 0
+                    let yPossibleTranslation = hasEnoughHeight ? translation.y : 0
+
+                    newFrame = CGRect(x: frame.origin.x + xPossibleTranslation,
+                                      y: frame.origin.y + yPossibleTranslation,
+                                      width: frame.size.width - xPossibleTranslation,
+                                      height: frame.size.height - yPossibleTranslation)
+                case buttons[1]:    // Top Right
+                    let hasEnoughWidth = frame.size.width + translation.x >= realMinimumSize.width
+                    let hasEnoughHeight = frame.size.height - translation.y >= realMinimumSize.height
+
+                    let xPossibleTranslation = hasEnoughWidth ? translation.x : 0
+                    let yPossibleTranslation = hasEnoughHeight ? translation.y : 0
+
+                    newFrame = CGRect(x: frame.origin.x,
+                                      y: frame.origin.y + yPossibleTranslation,
+                                      width: frame.size.width + xPossibleTranslation,
+                                      height: frame.size.height - yPossibleTranslation)
+                case buttons[2]:    // Bottom Left
+                    let hasEnoughWidth = frame.size.width - translation.x >= realMinimumSize.width
+                    let hasEnoughHeight = frame.size.height + translation.y >= realMinimumSize.height
+
+                    let xPossibleTranslation = hasEnoughWidth ? translation.x : 0
+                    let yPossibleTranslation = hasEnoughHeight ? translation.y : 0
+
+                    newFrame = CGRect(x: frame.origin.x + xPossibleTranslation,
+                                      y: frame.origin.y,
+                                      width: frame.size.width - xPossibleTranslation,
+                                      height: frame.size.height + yPossibleTranslation)
+                case buttons[3]:    // Bottom Right
+                    let hasEnoughWidth = frame.size.width + translation.x >= realMinimumSize.width
+                    let hasEnoughHeight = frame.size.height + translation.y >= realMinimumSize.height
+
+                    let xPossibleTranslation = hasEnoughWidth ? translation.x : 0
+                    let yPossibleTranslation = hasEnoughHeight ? translation.y : 0
+
+                    newFrame = CGRect(x: frame.origin.x,
+                                      y: frame.origin.y,
+                                      width: frame.size.width + xPossibleTranslation,
+                                      height: frame.size.height + yPossibleTranslation)
+                default:
+                    newFrame = CGRect.zero
+                }
+
+                let minimumFrame = CGRect(x: newFrame.origin.x,
+                                          y: newFrame.origin.y,
+                                          width: max(newFrame.size.width,
+                                                     minimumSize.width + 2 * outterGap),
+                                          height: max(newFrame.size.height,
+                                                      minimumSize.height + 2 * outterGap))
+
+                gestureRecognizer.setTranslation(CGPoint.zero, in: self)
+                
+                delegate?.didMoveCropOverlay(newFrame: minimumFrame)
+            }
+        } else if isMovable {
+            if gestureRecognizer.state == .began || gestureRecognizer.state == .changed {
+                let translation = gestureRecognizer.translation(in: self)
+
+                let newFrame = CGRect(x: frame.origin.x + translation.x,
+                                      y: frame.origin.y + translation.y,
+                                      width: frame.size.width,
+                                      height: frame.size.height)
+
+                gestureRecognizer.setTranslation(CGPoint.zero, in: self)
+
+                delegate?.didMoveCropOverlay(newFrame: newFrame)
+            }
+        }
+    }
 }

+ 1 - 1
Pods/ALCameraViewController/ALCameraViewController/Views/PermissionsView.swift

@@ -69,7 +69,7 @@ internal class PermissionsView: UIView {
         let icon = UIImage(named: "permissionsIcon", in: CameraGlobals.shared.bundle, compatibleWith: nil)!
         iconView.image = icon
         
-        settingsButton.contentEdgeInsets = UIEdgeInsets(top: 6, left: 12, bottom: 6, right: 12)
+        settingsButton.contentEdgeInsets = UIEdgeInsets.init(top: 6, left: 12, bottom: 6, right: 12)
         settingsButton.setTitle(localizedString("permissions.settings"), for: UIControl.State())
         settingsButton.setTitleColor(UIColor.white, for: UIControl.State())
         settingsButton.layer.cornerRadius = 4

+ 0 - 27
Pods/Local Podspecs/ALCameraViewController.podspec.json

@@ -1,27 +0,0 @@
-{
-  "name": "ALCameraViewController",
-  "version": "3.0.3",
-  "summary": "A camera view controller with custom image picker and image cropping.",
-  "source": {
-    "git": "https://github.com/AlexLittlejohn/ALCameraViewController.git",
-    "tag": "3.0.3"
-  },
-  "requires_arc": true,
-  "platforms": {
-    "ios": "8.0"
-  },
-  "license": "MIT",
-  "source_files": "ALCameraViewController/**/*.{swift}",
-  "resources": [
-    "ALCameraViewController/ViewController/ConfirmViewController.xib",
-    "ALCameraViewController/CameraViewAssets.xcassets",
-    "ALCameraViewController/CameraView.strings"
-  ],
-  "homepage": "https://github.com/AlexLittlejohn/ALCameraViewController",
-  "authors": {
-    "Alex Littlejohn": "alexlittlejohn@me.com"
-  },
-  "pod_target_xcconfig": {
-    "SWIFT_VERSION": "4.2"
-  }
-}

+ 5 - 13
Pods/Manifest.lock

@@ -1,5 +1,5 @@
 PODS:
-  - ALCameraViewController (3.0.3)
+  - ALCameraViewController (3.1)
   - DBDebugToolkit (0.5.0)
   - InputBarAccessoryView (4.3.1):
     - InputBarAccessoryView/Core (= 4.3.1)
@@ -13,7 +13,7 @@ PODS:
   - UICircularProgressRing (6.2.0)
 
 DEPENDENCIES:
-  - ALCameraViewController (from `https://github.com/dignifiedquire/ALCameraViewController`)
+  - ALCameraViewController
   - DBDebugToolkit
   - InputBarAccessoryView
   - JGProgressHUD
@@ -26,6 +26,7 @@ DEPENDENCIES:
 
 SPEC REPOS:
   trunk:
+    - ALCameraViewController
     - DBDebugToolkit
     - InputBarAccessoryView
     - JGProgressHUD
@@ -36,17 +37,8 @@ SPEC REPOS:
     - SwiftyBeaver
     - UICircularProgressRing
 
-EXTERNAL SOURCES:
-  ALCameraViewController:
-    :git: https://github.com/dignifiedquire/ALCameraViewController
-
-CHECKOUT OPTIONS:
-  ALCameraViewController:
-    :commit: e0fcc4338276e4fc6240e8726ecc77aa39ce1385
-    :git: https://github.com/dignifiedquire/ALCameraViewController
-
 SPEC CHECKSUMS:
-  ALCameraViewController: dd13cf0a5b44a4d542c73bbcbebd02bc09e929c7
+  ALCameraViewController: 144dea45fd749a623aafd8cb7b10cea88499fcc3
   DBDebugToolkit: c04bb6f618051d3de447a4b4323f37826116cfed
   InputBarAccessoryView: 58a348be7ea2736c7eec60e5c315511c2dbb39fd
   JGProgressHUD: 12b20a8f4ffe05258f8635c1ab92816e451f904d
@@ -57,6 +49,6 @@ SPEC CHECKSUMS:
   SwiftyBeaver: 4cc0080d2e23f980652e28978db11a5c9da39165
   UICircularProgressRing: 0ff679b05a17f15ad6301a7886686837b8c301a9
 
-PODFILE CHECKSUM: 4ccd5fdf02ac6afab3d4113bde3777593b2ff412
+PODFILE CHECKSUM: 37ab88c96ba5352ae1d3f997dc2615856fbd7e4c
 
 COCOAPODS: 1.8.4

+ 25 - 23
Pods/Pods.xcodeproj/project.pbxproj

@@ -3131,9 +3131,9 @@
 			};
 			name = Release;
 		};
-		65C09A7119BAEA8CDA3CB5454186E528 /* Release */ = {
+		629EC00C7F45A272EC89DD7E64D571B4 /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 2C06AEAAAA759239398F0C94216CF48D /* SwiftyBeaver.xcconfig */;
+			baseConfigurationReference = C9CB84A99A27B243860C9D32701F6EBF /* ALCameraViewController.xcconfig */;
 			buildSettings = {
 				CODE_SIGN_IDENTITY = "";
 				"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
@@ -3144,28 +3144,27 @@
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
-				GCC_PREFIX_HEADER = "Target Support Files/SwiftyBeaver/SwiftyBeaver-prefix.pch";
-				INFOPLIST_FILE = "Target Support Files/SwiftyBeaver/SwiftyBeaver-Info.plist";
+				GCC_PREFIX_HEADER = "Target Support Files/ALCameraViewController/ALCameraViewController-prefix.pch";
+				INFOPLIST_FILE = "Target Support Files/ALCameraViewController/ALCameraViewController-Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MODULEMAP_FILE = "Target Support Files/SwiftyBeaver/SwiftyBeaver.modulemap";
-				PRODUCT_MODULE_NAME = SwiftyBeaver;
-				PRODUCT_NAME = SwiftyBeaver;
+				MODULEMAP_FILE = "Target Support Files/ALCameraViewController/ALCameraViewController.modulemap";
+				PRODUCT_MODULE_NAME = ALCameraViewController;
+				PRODUCT_NAME = ALCameraViewController;
 				SDKROOT = iphoneos;
 				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
-				SWIFT_VERSION = 4.2;
+				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
-				VALIDATE_PRODUCT = YES;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
 			};
-			name = Release;
+			name = Debug;
 		};
-		6D7A537F3751229AEEE45C01E50C5377 /* Debug */ = {
+		65C09A7119BAEA8CDA3CB5454186E528 /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = C9CB84A99A27B243860C9D32701F6EBF /* ALCameraViewController.xcconfig */;
+			baseConfigurationReference = 2C06AEAAAA759239398F0C94216CF48D /* SwiftyBeaver.xcconfig */;
 			buildSettings = {
 				CODE_SIGN_IDENTITY = "";
 				"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
@@ -3176,24 +3175,26 @@
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
-				GCC_PREFIX_HEADER = "Target Support Files/ALCameraViewController/ALCameraViewController-prefix.pch";
-				INFOPLIST_FILE = "Target Support Files/ALCameraViewController/ALCameraViewController-Info.plist";
+				GCC_PREFIX_HEADER = "Target Support Files/SwiftyBeaver/SwiftyBeaver-prefix.pch";
+				INFOPLIST_FILE = "Target Support Files/SwiftyBeaver/SwiftyBeaver-Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MODULEMAP_FILE = "Target Support Files/ALCameraViewController/ALCameraViewController.modulemap";
-				PRODUCT_MODULE_NAME = ALCameraViewController;
-				PRODUCT_NAME = ALCameraViewController;
+				MODULEMAP_FILE = "Target Support Files/SwiftyBeaver/SwiftyBeaver.modulemap";
+				PRODUCT_MODULE_NAME = SwiftyBeaver;
+				PRODUCT_NAME = SwiftyBeaver;
 				SDKROOT = iphoneos;
 				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+				SWIFT_VERSION = 4.2;
 				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
 			};
-			name = Debug;
+			name = Release;
 		};
-		82A8949CFA9005F7C316D5D1EC2DFE47 /* Release */ = {
+		7E495E15F2AE34EB52B8A875B6B3079A /* Release */ = {
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = C9CB84A99A27B243860C9D32701F6EBF /* ALCameraViewController.xcconfig */;
 			buildSettings = {
@@ -3209,7 +3210,7 @@
 				GCC_PREFIX_HEADER = "Target Support Files/ALCameraViewController/ALCameraViewController-prefix.pch";
 				INFOPLIST_FILE = "Target Support Files/ALCameraViewController/ALCameraViewController-Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MODULEMAP_FILE = "Target Support Files/ALCameraViewController/ALCameraViewController.modulemap";
 				PRODUCT_MODULE_NAME = ALCameraViewController;
@@ -3217,6 +3218,7 @@
 				SDKROOT = iphoneos;
 				SKIP_INSTALL = YES;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 				VALIDATE_PRODUCT = YES;
 				VERSIONING_SYSTEM = "apple-generic";
@@ -3692,8 +3694,8 @@
 		3C824956660F94A02DB7586D859AC471 /* Build configuration list for PBXNativeTarget "ALCameraViewController" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
-				6D7A537F3751229AEEE45C01E50C5377 /* Debug */,
-				82A8949CFA9005F7C316D5D1EC2DFE47 /* Release */,
+				629EC00C7F45A272EC89DD7E64D571B4 /* Debug */,
+				7E495E15F2AE34EB52B8A875B6B3079A /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;

+ 1 - 1
Pods/Target Support Files/ALCameraViewController/ALCameraViewController-Info.plist

@@ -15,7 +15,7 @@
   <key>CFBundlePackageType</key>
   <string>FMWK</string>
   <key>CFBundleShortVersionString</key>
-  <string>3.0.3</string>
+  <string>3.1.0</string>
   <key>CFBundleSignature</key>
   <string>????</string>
   <key>CFBundleVersion</key>

+ 0 - 1
Pods/Target Support Files/ALCameraViewController/ALCameraViewController.xcconfig

@@ -7,5 +7,4 @@ PODS_ROOT = ${SRCROOT}
 PODS_TARGET_SRCROOT = ${PODS_ROOT}/ALCameraViewController
 PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
 SKIP_INSTALL = YES
-SWIFT_VERSION = 4.2
 USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 12 - 4
deltachat-ios.xcodeproj/project.pbxproj

@@ -81,7 +81,9 @@
 		305962102346154D00C80F33 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3059620F2346154D00C80F33 /* String+Extension.swift */; };
 		3060119C22DDE24000C1CE6F /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3060119E22DDE24000C1CE6F /* Localizable.strings */; };
 		306011B622E5E7FB00C1CE6F /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 306011B422E5E7FB00C1CE6F /* Localizable.stringsdict */; };
+		3095A351237DD1F700AB07F7 /* MediaPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3095A350237DD1F700AB07F7 /* MediaPicker.swift */; };
 		30A4D9AE2332672700544344 /* QrInviteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A4D9AD2332672600544344 /* QrInviteViewController.swift */; };
+		30AC265F237F1807002A943F /* AvatarHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AC265E237F1807002A943F /* AvatarHelper.swift */; };
 		30C0D49D237C4908008E2A0E /* CertificateCheckController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C0D49C237C4908008E2A0E /* CertificateCheckController.swift */; };
 		30F9B9EC235F2116006E7ACF /* MessageCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30F9B9EB235F2116006E7ACF /* MessageCounter.swift */; };
 		6795F63A82E94FF7CD2CEC0F /* Pods_deltachat_iosTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F7009234DB9408201A6CDCB /* Pods_deltachat_iosTests.framework */; };
@@ -113,7 +115,7 @@
 		8B6D425BC604F7C43B65D436 /* Pods_deltachat_ios.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6241BE1534A653E79AD5D01D /* Pods_deltachat_ios.framework */; };
 		AE0D26FD1FB1FE88002FAFCE /* ChatListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE0D26FC1FB1FE88002FAFCE /* ChatListController.swift */; };
 		AE18F294228C602A0007B1BE /* SecuritySettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE18F293228C602A0007B1BE /* SecuritySettingsController.swift */; };
-		AE25F09022807AD800CDEA66 /* GroupNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE25F08F22807AD800CDEA66 /* GroupNameCell.swift */; };
+		AE25F09022807AD800CDEA66 /* AvatarSelectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE25F08F22807AD800CDEA66 /* AvatarSelectionCell.swift */; };
 		AE38B31822672DFC00EC37A1 /* ActionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE38B31722672DFC00EC37A1 /* ActionCell.swift */; };
 		AE38B31A2267328200EC37A1 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE38B3192267328200EC37A1 /* Colors.swift */; };
 		AE4AEE3522B1030D000AA495 /* PreviewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE4AEE3422B1030D000AA495 /* PreviewController.swift */; };
@@ -288,7 +290,9 @@
 		306011C722E5E82E00C1CE6F /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = lt; path = lt.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
 		306011C822E5E83100C1CE6F /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
 		306011C922E5E83500C1CE6F /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
+		3095A350237DD1F700AB07F7 /* MediaPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPicker.swift; sourceTree = "<group>"; };
 		30A4D9AD2332672600544344 /* QrInviteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QrInviteViewController.swift; sourceTree = "<group>"; };
+		30AC265E237F1807002A943F /* AvatarHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarHelper.swift; sourceTree = "<group>"; };
 		30C0D49C237C4908008E2A0E /* CertificateCheckController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificateCheckController.swift; sourceTree = "<group>"; };
 		30F9B9EB235F2116006E7ACF /* MessageCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCounter.swift; sourceTree = "<group>"; };
 		6241BE1534A653E79AD5D01D /* Pods_deltachat_ios.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_deltachat_ios.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -327,7 +331,7 @@
 		A8615D4600859851E53CAA9C /* Pods-deltachat-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-deltachat-ios.release.xcconfig"; path = "Pods/Target Support Files/Pods-deltachat-ios/Pods-deltachat-ios.release.xcconfig"; sourceTree = "<group>"; };
 		AE0D26FC1FB1FE88002FAFCE /* ChatListController.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = ChatListController.swift; sourceTree = "<group>"; tabWidth = 4; };
 		AE18F293228C602A0007B1BE /* SecuritySettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecuritySettingsController.swift; sourceTree = "<group>"; };
-		AE25F08F22807AD800CDEA66 /* GroupNameCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupNameCell.swift; sourceTree = "<group>"; };
+		AE25F08F22807AD800CDEA66 /* AvatarSelectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarSelectionCell.swift; sourceTree = "<group>"; };
 		AE38B31722672DFC00EC37A1 /* ActionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCell.swift; sourceTree = "<group>"; };
 		AE38B3192267328200EC37A1 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = "<group>"; };
 		AE4AEE3422B1030D000AA495 /* PreviewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewController.swift; sourceTree = "<group>"; };
@@ -690,6 +694,8 @@
 				AEACE2E41FB32E1900DCDD78 /* Utils.swift */,
 				AE38B3192267328200EC37A1 /* Colors.swift */,
 				AE851AC4227C755A00ED86F0 /* Protocols.swift */,
+				3095A350237DD1F700AB07F7 /* MediaPicker.swift */,
+				30AC265E237F1807002A943F /* AvatarHelper.swift */,
 			);
 			path = Helper;
 			sourceTree = "<group>";
@@ -706,7 +712,7 @@
 				789E879C21D6DF86003ED1C5 /* ProgressHud.swift */,
 				AE38B31722672DFC00EC37A1 /* ActionCell.swift */,
 				AE851ACD227CA54300ED86F0 /* InitialsBadge.swift */,
-				AE25F08F22807AD800CDEA66 /* GroupNameCell.swift */,
+				AE25F08F22807AD800CDEA66 /* AvatarSelectionCell.swift */,
 				AE728F14229D5C390047565B /* PhotoPickerAlertAction.swift */,
 				AE52EA18229EB53C00C586C9 /* ContactDetailHeader.swift */,
 				AE9DAF0E22C278C6004C9591 /* ChatTitleView.swift */,
@@ -1038,6 +1044,7 @@
 				305961D72346125100C80F33 /* MessageKit+Availability.swift in Sources */,
 				3040F45E234DFBC000FA34D5 /* Audio.swift in Sources */,
 				305961FE2346125100C80F33 /* InsetLabel.swift in Sources */,
+				3095A351237DD1F700AB07F7 /* MediaPicker.swift in Sources */,
 				B21005DB23383664004C70C5 /* SettingsClassicViewController.swift in Sources */,
 				305961F62346125100C80F33 /* MessageContentCell.swift in Sources */,
 				305961E42346125100C80F33 /* MessageKitDateFormatter.swift in Sources */,
@@ -1048,11 +1055,12 @@
 				305961ED2346125100C80F33 /* DetectorType.swift in Sources */,
 				305962062346125100C80F33 /* TypingIndicatorCellSizeCalculator.swift in Sources */,
 				AE851AC7227C776400ED86F0 /* Location.swift in Sources */,
+				30AC265F237F1807002A943F /* AvatarHelper.swift in Sources */,
 				7AE0A5491FC42F65005ECB4B /* NewChatViewController.swift in Sources */,
 				305961E52346125100C80F33 /* LabelAlignment.swift in Sources */,
 				305961E82346125100C80F33 /* Sender.swift in Sources */,
 				305961EE2346125100C80F33 /* AvatarPosition.swift in Sources */,
-				AE25F09022807AD800CDEA66 /* GroupNameCell.swift in Sources */,
+				AE25F09022807AD800CDEA66 /* AvatarSelectionCell.swift in Sources */,
 				305961E62346125100C80F33 /* LocationMessageSnapshotOptions.swift in Sources */,
 				AEE6EC3F2282C59C00EDC689 /* GroupMembersViewController.swift in Sources */,
 				B26B3BC7236DC3DC008ED35A /* SwitchCell.swift in Sources */,

+ 31 - 9
deltachat-ios/Controller/EditGroupViewController.swift

@@ -6,7 +6,17 @@ class EditGroupViewController: UITableViewController {
 
     private let chat: DcChat
 
-    var groupNameCell: GroupLabelCell
+    private let rowAvatar = 0
+    private let rowGroupName = 1
+
+    var avatarSelectionCell: AvatarSelectionCell
+
+    lazy var groupNameCell: TextFieldCell = {
+        let cell = TextFieldCell(description: String.localized("group_name"), placeholder: self.chat.name)
+        cell.setText(text: self.chat.name)
+        cell.onTextFieldChange = self.groupNameEdited(_:)
+        return cell
+    }()
 
     lazy var doneButton: UIBarButtonItem = {
         let button = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(saveContactButtonPressed))
@@ -21,11 +31,11 @@ class EditGroupViewController: UITableViewController {
 
     init(chat: DcChat) {
         self.chat = chat
-        self.groupNameCell = GroupLabelCell(chat: chat)
+        self.avatarSelectionCell = AvatarSelectionCell(chat: chat)
         super.init(style: .grouped)
-        groupNameCell.inputField.text = chat.name
-        self.groupNameCell.onTextChanged = groupNameEdited(_:)
-        self.groupNameCell.selectionStyle = .none
+        self.avatarSelectionCell.selectionStyle = .none
+        self.avatarSelectionCell.hintLabel.text = String.localized("group_avatar")
+        title = String.localized("menu_edit_group")
     }
 
     required init?(coder aDecoder: NSCoder) {
@@ -39,7 +49,11 @@ class EditGroupViewController: UITableViewController {
     }
 
     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-        return groupNameCell
+        if indexPath.row == rowAvatar {
+            return avatarSelectionCell
+        } else {
+            return groupNameCell
+        }
     }
 
     override func numberOfSections(in tableView: UITableView) -> Int {
@@ -47,11 +61,18 @@ class EditGroupViewController: UITableViewController {
     }
 
     override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
-        return 1
+        return 2
     }
 
+    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        if indexPath.row == rowAvatar {
+            return AvatarSelectionCell.cellSize
+        }
+        return Constants.stdCellHeight
+    }
+    
     @objc func saveContactButtonPressed() {
-        let newName = groupNameCell.getGroupName()
+        let newName = groupNameCell.getText()
         dc_set_chat_name(mailboxPointer, UInt32(chat.id), newName)
         coordinator?.navigateBack()
     }
@@ -60,7 +81,8 @@ class EditGroupViewController: UITableViewController {
         coordinator?.navigateBack()
     }
 
-    private func groupNameEdited(_ newName: String) {
+    private func groupNameEdited(_ textField: UITextField) {
+        avatarSelectionCell.onInitialsChanged(text: textField.text)
         doneButton.isEnabled = true
     }
 }

+ 86 - 26
deltachat-ios/Controller/EditSettingsController.swift

@@ -1,15 +1,19 @@
 import UIKit
 
-class EditSettingsController: UITableViewController {
+class EditSettingsController: UITableViewController, MediaPickerDelegate {
 
     private let dcContext: DcContext
+    weak var coordinator: EditSettingsCoordinator?
     private var displayNameBackup: String?
     private var statusCellBackup: String?
 
+    private let groupBadgeSize: CGFloat = 72
+
     private let section1 = 0
-    private let section1Name = 0
-    private let section1Status = 1
-    private let section1RowCount = 2
+    private let section1Avatar = 0
+    private let section1Name = 1
+    private let section1Status = 2
+    private let section1RowCount = 3
 
     private let section2 = 1
     private let section2AccountSettings = 0
@@ -17,13 +21,9 @@ class EditSettingsController: UITableViewController {
 
     private let sectionCount = 2
 
-    private var childCoordinators: Coordinator?
+    private let tagAccountSettingsCell = 1
 
-    private lazy var displayNameCell: TextFieldCell = {
-        let cell = TextFieldCell(description: String.localized("pref_your_name"), placeholder: String.localized("pref_your_name"))
-        cell.setText(text: DcConfig.displayname ?? nil)
-        return cell
-    }()
+    private var childCoordinators: Coordinator?
 
     private lazy var statusCell: TextFieldCell = {
         let cell = TextFieldCell(description: String.localized("pref_default_status_label"), placeholder: String.localized("pref_default_status_label"))
@@ -31,11 +31,22 @@ class EditSettingsController: UITableViewController {
         return cell
     }()
 
-    lazy var accountSettingsCell: UITableViewCell = {
+    private lazy var accountSettingsCell: UITableViewCell = {
         let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
         cell.textLabel?.text = String.localized("pref_password_and_account_settings")
         cell.accessoryType = .disclosureIndicator
-        cell.accessibilityIdentifier = "accountSettingsCell"
+        cell.tag = tagAccountSettingsCell
+        return cell
+    }()
+
+
+    private lazy var avatarSelectionCell: AvatarSelectionCell = {
+        return createPictureAndNameCell()
+    }()
+
+    private lazy var nameCell: TextFieldCell = {
+        let cell = TextFieldCell(description: String.localized("pref_your_name"), placeholder: String.localized("pref_your_name"))
+        cell.setText(text: DcConfig.displayname)
         return cell
     }()
 
@@ -52,19 +63,13 @@ class EditSettingsController: UITableViewController {
     override func viewDidLoad() {
         super.viewDidLoad()
         title = String.localized("pref_profile_info_headline")
-    }
-
-    override func viewWillAppear(_ animated: Bool) {
-        displayNameBackup = DcConfig.displayname
-        statusCellBackup = DcConfig.selfstatus
+        avatarSelectionCell.onAvatarTapped = onAvatarTapped
     }
 
     override func viewWillDisappear(_ animated: Bool) {
-        if displayNameBackup != displayNameCell.getText() || statusCellBackup != displayNameCell.getText() {
-            DcConfig.selfstatus = statusCell.getText()
-            DcConfig.displayname = displayNameCell.getText()
-            dc_configure(mailboxPointer)
-        }
+        DcConfig.selfstatus = statusCell.getText()
+        DcConfig.displayname = nameCell.getText()
+        dc_configure(mailboxPointer)
     }
 
     // MARK: - Table view data source
@@ -83,16 +88,29 @@ class EditSettingsController: UITableViewController {
 
     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         if indexPath.section == section1 {
-            if indexPath.row == section1Name {
-                return displayNameCell
-            } else {
+            switch indexPath.row {
+            case section1Avatar:
+                return avatarSelectionCell
+            case section1Name:
+                return nameCell
+            case section1Status:
                 return statusCell
+            default:
+               return UITableViewCell()
             }
         } else {
             return accountSettingsCell
         }
     }
 
+    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        if indexPath.section == section1 && indexPath.row == section1Avatar {
+            return AvatarSelectionCell.cellSize
+        } else {
+            return Constants.stdCellHeight
+        }
+    }
+
     override func tableView(_: UITableView, titleForFooterInSection section: Int) -> String? {
         if section == section1 {
             return String.localized("pref_who_can_see_profile_explain")
@@ -103,7 +121,7 @@ class EditSettingsController: UITableViewController {
 
     override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
         guard let cell = tableView.cellForRow(at: indexPath) else { return }
-        if cell.accessibilityIdentifier == "accountSettingsCell" {
+        if cell.tag == tagAccountSettingsCell {
             tableView.deselectRow(at: indexPath, animated: true)
             guard let nc = navigationController else { return }
             let accountSetupVC = AccountSetupController(dcContext: dcContext, editView: true)
@@ -113,4 +131,46 @@ class EditSettingsController: UITableViewController {
             nc.pushViewController(accountSetupVC, animated: true)
         }
     }
+
+
+      private func galleryButtonPressed(_ action: UIAlertAction) {
+        coordinator?.showPhotoPicker(delegate: self)
+      }
+
+      private func cameraButtonPressed(_ action: UIAlertAction) {
+        coordinator?.showCamera(delegate: self)
+      }
+
+    private func onAvatarTapped() {
+        let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
+                 let photoAction = PhotoPickerAlertAction(title: String.localized("gallery"), style: .default, handler: galleryButtonPressed(_:))
+                 let videoAction = PhotoPickerAlertAction(title: String.localized("camera"), style: .default, handler: cameraButtonPressed(_:))
+
+                 alert.addAction(photoAction)
+                 alert.addAction(videoAction)
+                 alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil))
+
+        self.present(alert, animated: true, completion: nil)
+    }
+
+    func onImageSelected(image: UIImage) {
+        AvatarHelper.saveSelfAvatarImage(image: image)
+
+        self.avatarSelectionCell = createPictureAndNameCell()
+        self.avatarSelectionCell.onAvatarTapped = onAvatarTapped
+
+        self.tableView.beginUpdates()
+        let indexPath = IndexPath(row: section1Avatar, section: section1)
+        self.tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.none)
+        self.tableView.endUpdates()
+    }
+
+    func onDismiss() { }
+
+    private func createPictureAndNameCell() -> AvatarSelectionCell {
+        let cell = AvatarSelectionCell(context: dcContext)
+        cell.selectionStyle = .none
+        return cell
+    }
+
 }

+ 41 - 10
deltachat-ios/Controller/GroupNameController.swift

@@ -9,6 +9,24 @@ class GroupNameController: UITableViewController {
     let contactIdsForGroup: Set<Int> // TODO: check if array is sufficient
     let groupContactIds: [Int]
 
+    private let sectionGroupDetails = 0
+    private let sectionGroupDetailsRowAvatar = 0
+    private let sectionGroupDetailsRowName = 1
+    private let countSectionGroupDetails = 2
+
+    lazy var groupNameCell: TextFieldCell = {
+        let cell = TextFieldCell(description: String.localized("group_name"), placeholder: String.localized("menu_edit_group_name"))
+        cell.onTextFieldChange = self.updateGroupName
+        return cell
+    }()
+
+    lazy var avatarSelectionCell: AvatarSelectionCell = {
+        let cell = AvatarSelectionCell(context: nil)
+        cell.selectionStyle = .none
+        cell.hintLabel.text = String.localized("group_avatar")
+        return cell
+    }()    
+
     init(contactIdsForGroup: Set<Int>) {
         self.contactIdsForGroup = contactIdsForGroup
         groupContactIds = Array(contactIdsForGroup)
@@ -26,9 +44,7 @@ class GroupNameController: UITableViewController {
         navigationItem.rightBarButtonItem = doneButton
         tableView.bounces = false
         doneButton.isEnabled = false
-        tableView.register(GroupLabelCell.self, forCellReuseIdentifier: "groupLabelCell")
         tableView.register(ContactCell.self, forCellReuseIdentifier: "contactCell")
-        // setupSubviews()
     }
 
     @objc func doneButtonPressed() {
@@ -58,12 +74,12 @@ class GroupNameController: UITableViewController {
         let section = indexPath.section
         let row = indexPath.row
 
-        if section == 0 {
-            let cell = tableView.dequeueReusableCell(withIdentifier: "groupLabelCell", for: indexPath)
-            if let groupLabelCell = cell as? GroupLabelCell {
-                groupLabelCell.onTextChanged = updateGroupName
+        if section == sectionGroupDetails {
+            if row == sectionGroupDetailsRowAvatar {
+                return avatarSelectionCell
+            } else {
+                return groupNameCell
             }
-            return cell
         } else {
             let cell = tableView.dequeueReusableCell(withIdentifier: "contactCell", for: indexPath)
             if let contactCell = cell as? ContactCell {
@@ -78,9 +94,23 @@ class GroupNameController: UITableViewController {
         }
     }
 
+    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        let section = indexPath.section
+        let row = indexPath.row
+        if section == sectionGroupDetails {
+            if row == sectionGroupDetailsRowAvatar {
+                return AvatarSelectionCell.cellSize
+            } else {
+                return Constants.stdCellHeight
+            }
+        } else {
+            return ContactCell.cellSize
+        }
+    }
+
     override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
-        if section == 0 {
-            return 1
+        if section == sectionGroupDetails {
+            return countSectionGroupDetails
         } else {
             return contactIdsForGroup.count
         }
@@ -94,7 +124,8 @@ class GroupNameController: UITableViewController {
         }
     }
 
-    private func updateGroupName(name: String) {
+    private func updateGroupName(textView: UITextField) {
+        let name = textView.text ?? ""
         groupName = name
         doneButton.isEnabled = name.containsCharacters()
     }

+ 7 - 0
deltachat-ios/Controller/QrViewController.swift

@@ -34,6 +34,13 @@ class QrViewController: UITableViewController, QrCodeReaderDelegate {
         tableView.separatorStyle = .none
     }
 
+    override func viewDidAppear(_ animated: Bool) {
+        let indexPath = IndexPath(row: rowContact, section: 0)
+        tableView.beginUpdates()
+        tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.none)
+        tableView.endUpdates()
+    }
+    
     override func tableView(_: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let row = indexPath.row
         switch row {

+ 24 - 0
deltachat-ios/Coordinator/AppCoordinator.swift

@@ -175,6 +175,9 @@ class SettingsCoordinator: Coordinator {
 
     func showEditSettingsController() {
         let editController = EditSettingsController(dcContext: dcContext)
+        let coordinator = EditSettingsCoordinator(dcContext: dcContext, navigationController: navigationController)
+        childCoordinators.append(coordinator)
+        editController.coordinator = coordinator
         navigationController.pushViewController(editController, animated: true)
     }
 
@@ -189,6 +192,27 @@ class SettingsCoordinator: Coordinator {
     }
 }
 
+class EditSettingsCoordinator: Coordinator {
+    var dcContext: DcContext
+    let navigationController: UINavigationController
+    let mediaPicker: MediaPicker
+
+    init(dcContext: DcContext, navigationController: UINavigationController) {
+        self.dcContext = dcContext
+        self.navigationController = navigationController
+        self.mediaPicker = MediaPicker(navigationController: navigationController)
+    }
+
+    func showPhotoPicker(delegate: MediaPickerDelegate) {
+        mediaPicker.showImageCropper(delegate: delegate)
+    }
+
+    func showCamera(delegate: MediaPickerDelegate) {
+        mediaPicker.showCamera(delegate: delegate)
+    }
+}
+
+
 class AccountSetupCoordinator: Coordinator {
     var dcContext: DcContext
     let navigationController: UINavigationController

+ 15 - 0
deltachat-ios/DC/Wrapper.swift

@@ -138,6 +138,21 @@ class DcContext {
     func isConfigured() -> Bool {
         return dc_is_configured(contextPointer) != 0
     }
+
+    func getSelfAvatarImage() -> UIImage? {
+       guard let fileName = DcConfig.selfavatar else { return nil }
+       let path: URL = URL(fileURLWithPath: fileName, isDirectory: false)
+       if path.isFileURL {
+           do {
+               let data = try Data(contentsOf: path)
+               return UIImage(data: data)
+           } catch {
+               logger.warning("failed to load image: \(fileName), \(error)")
+               return nil
+           }
+       }
+       return nil
+    }
 }
 
 class DcConfig {

+ 25 - 37
deltachat-ios/Extensions/UIImage+Extension.swift

@@ -1,15 +1,14 @@
 import UIKit
 
 extension UIImage {
-    func imageResize(sizeChange: CGSize) -> UIImage {
-        let hasAlpha = true
-        let scale: CGFloat = 0.0 // Use scale factor of main screen
 
-        UIGraphicsBeginImageContextWithOptions(sizeChange, !hasAlpha, scale)
-        draw(in: CGRect(origin: CGPoint.zero, size: sizeChange))
-
-        let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
-        return scaledImage!
+    func invert() -> UIImage {
+        let beginImage = CIImage(image: self)
+        if let filter = CIFilter(name: "CIColorInvert") {
+            filter.setValue(beginImage, forKey: kCIInputImageKey)
+            return UIImage(ciImage: filter.outputImage!)
+        }
+        return self
     }
 
     public convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
@@ -24,31 +23,9 @@ extension UIImage {
         self.init(cgImage: cgImage)
     }
 
-    func resizeImage(targetSize: CGSize) -> UIImage? {
-        let size = self.size
-
-        let widthRatio  = targetSize.width  / size.width
-        let heightRatio = targetSize.height / size.height
-
-        var newSize: CGSize
-        if widthRatio > heightRatio {
-            newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
-        } else {
-            newSize = CGSize(width: size.width * widthRatio, height: size.height *      widthRatio)
-        }
-
-        let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
-
-        UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
-        draw(in: rect)
-        let newImage = UIGraphicsGetImageFromCurrentImageContext()
-        UIGraphicsEndImageContext()
-
-        return newImage
-    }
 
     func dcCompress(toMax target: Float = 1280) -> UIImage? {
-        return resize(toMax: target)
+        return scaleDownAndCompress(toMax: target)
     }
 
     func imageSizeInPixel() -> CGSize {
@@ -59,16 +36,13 @@ extension UIImage {
         return CGSize(width: widthInPixels, height: heightInPixels)
     }
 
-    // source: https://stackoverflow.com/questions/29137488/how-do-i-resize-the-uiimage-to-reduce-upload-image-size // slightly changed
-    func resize(toMax: Float) -> UIImage? {
+    private func getResizedRectangle(toMax: Float) -> CGRect {
         var actualHeight = Float(size.height)
         var actualWidth = Float(size.width)
         let maxHeight: Float = toMax
         let maxWidth: Float = toMax
         var imgRatio: Float = actualWidth / actualHeight
         let maxRatio: Float = maxWidth / maxHeight
-        let compressionQuality: Float = 0.5
-        //50 percent compression
         if actualHeight > maxHeight || actualWidth > maxWidth {
             if imgRatio < maxRatio {
                 //adjust width according to maxHeight
@@ -85,8 +59,23 @@ extension UIImage {
                 actualWidth = maxWidth
             }
         }
+        return CGRect(x: 0.0, y: 0.0, width: CGFloat(actualWidth), height: CGFloat(actualHeight))
+    }
 
-        let rect = CGRect(x: 0.0, y: 0.0, width: CGFloat(actualWidth), height: CGFloat(actualHeight))
+    func scaleDownImage(toMax: CGFloat) -> UIImage? {
+        let rect = getResizedRectangle(toMax: Float(toMax))
+        UIGraphicsBeginImageContextWithOptions(rect.size, false, 1.0)
+        draw(in: rect)
+        let newImage = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+        return newImage
+    }
+
+    // source: https://stackoverflow.com/questions/29137488/how-do-i-resize-the-uiimage-to-reduce-upload-image-size // slightly changed
+    func scaleDownAndCompress(toMax: Float) -> UIImage? {
+        let rect = getResizedRectangle(toMax: toMax)
+        //50 percent compression
+        let compressionQuality: Float = 0.5
         UIGraphicsBeginImageContext(rect.size)
         draw(in: rect)
         let img = UIGraphicsGetImageFromCurrentImageContext()
@@ -94,7 +83,6 @@ extension UIImage {
         UIGraphicsEndImageContext()
         return UIImage(data: imageData!)
     }
-
 }
 
 public enum ImageType: String {

+ 16 - 1
deltachat-ios/Extensions/UIView+Extensions.swift

@@ -31,7 +31,22 @@ internal extension UIView {
     func makeBorder(color: UIColor = UIColor.red) {
         self.layer.borderColor = color.cgColor
         self.layer.borderWidth = 2
-        print("hello")
+    }
+
+    func alignLeadingToAnchor(_ anchor: NSLayoutXAxisAnchor, paddingLeading: CGFloat = 0.0) {
+        self.leadingAnchor.constraint(equalTo: anchor, constant: paddingLeading).isActive = true
+    }
+
+    func alignTrailingToAnchor(_ anchor: NSLayoutXAxisAnchor, paddingTrailing: CGFloat = 0.0) {
+        self.trailingAnchor.constraint(equalTo: anchor, constant: -paddingTrailing).isActive = true
+    }
+
+    func alignTopToAnchor(_ anchor: NSLayoutYAxisAnchor, paddingTop: CGFloat = 0.0) {
+        self.topAnchor.constraint(equalTo: anchor, constant: paddingTop).isActive = true
+    }
+
+    func alignBottomToAnchor(_ anchor: NSLayoutYAxisAnchor, paddingBottom: CGFloat = 0.0) {
+        self.bottomAnchor.constraint(equalTo: anchor, constant: -paddingBottom).isActive = true
     }
 
     func constraintAlignTopTo(_ view: UIView) -> NSLayoutConstraint {

+ 37 - 0
deltachat-ios/Helper/AvatarHelper.swift

@@ -0,0 +1,37 @@
+import Foundation
+import UIKit
+
+class AvatarHelper {
+
+    static let selfAvatarFile = "contact_avatar_self.jpg"
+    private static let avatarPath = "avatars"
+
+    static func saveSelfAvatarImage(image: UIImage) {
+        if let data = image.jpegData(compressionQuality: 1.0) {
+            let filemanager = FileManager.default
+            let docDir = filemanager.urls(for: .documentDirectory, in: .userDomainMask)[0]
+            let avatarDir = docDir.appendingPathComponent(avatarPath)
+            let avatarFile = avatarDir.appendingPathComponent(selfAvatarFile)
+            do {
+                try filemanager.createDirectory(atPath: avatarDir.path,
+                                                withIntermediateDirectories: false)
+            } catch let error as NSError {
+                logger.info("directory not created: \(error.localizedDescription)")
+            }
+
+            if !filemanager.changeCurrentDirectoryPath(avatarDir.path) {
+                logger.warning("Could not change into avatar directory")
+                return
+            }
+
+            do {
+                try data.write(to: avatarFile)
+            } catch let error {
+                logger.warning("Error: \(error.localizedDescription)")
+                return
+            }
+
+            DcConfig.selfavatar = avatarFile.path
+        }
+    }
+}

+ 2 - 0
deltachat-ios/Helper/Constants.swift

@@ -15,4 +15,6 @@ struct Constants {
     static let onlineShadow = UIImage(color: UIColor(hexString: "3ed67e"), size: CGSize(width: 1, height: 1))
 
     static let notificationIdentifier = "deltachat-ios-local-notifications"
+
+    static let stdCellHeight: CGFloat = 48
 }

+ 62 - 0
deltachat-ios/Helper/MediaPicker.swift

@@ -0,0 +1,62 @@
+import UIKit
+import Photos
+import MobileCoreServices
+import ALCameraViewController
+
+protocol MediaPickerDelegate: class {
+    //func onMediaSelected(url: NSURL)
+    func onImageSelected(image: UIImage)
+    func onDismiss()
+}
+
+class MediaPicker: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
+    private let navigationController: UINavigationController
+    private weak var delegate: MediaPickerDelegate?
+
+    init(navigationController: UINavigationController) {
+        self.navigationController = navigationController
+    }
+
+    func showImageCropper(delegate: MediaPickerDelegate) {
+        let croppingParameters = CroppingParameters(isEnabled: true,
+                                                    allowResizing: true,
+                                                    allowMoving: true,
+                                                    minimumSize: CGSize(width: 70, height: 70))
+
+        let controller = CameraViewController.imagePickerViewController(croppingParameters: croppingParameters,
+                                                                        completion: { [weak self] image, _ in
+                                                                            if let image = image {
+                                                                                self?.delegate?.onImageSelected(image: image)
+                                                                            }
+                                                                            self?.navigationController.dismiss(animated: true, completion: delegate.onDismiss)})
+        self.delegate = delegate
+        navigationController.present(controller, animated: true, completion: nil)
+    }
+
+    func showCamera(delegate: MediaPickerDelegate) {
+        if UIImagePickerController.isSourceTypeAvailable(.camera) {
+            let croppingParameters = CroppingParameters(isEnabled: true,
+            allowResizing: true,
+            allowMoving: true,
+            minimumSize: CGSize(width: 70, height: 70))
+            let cameraViewController = CameraViewController(croppingParameters: croppingParameters,
+                                                            allowsLibraryAccess: false,
+                                                            allowsSwapCameraOrientation: true,
+                                                            allowVolumeButtonCapture: false,
+                                                            completion: { [weak self] image, _ in
+                                                                if let image = image {
+                                                                    self?.delegate?.onImageSelected(image: image)
+                                                                }
+                                                                self?.navigationController.dismiss(animated: true, completion: self?.delegate?.onDismiss)})
+            self.delegate = delegate
+            navigationController.present(cameraViewController, animated: true, completion: nil)
+        } else {
+            let alert = UIAlertController(title: String.localized("chat_camera_unavailable"), message: nil, preferredStyle: .alert)
+            alert.addAction(UIAlertAction(title: String.localized("ok"), style: .cancel, handler: { _ in
+                self.navigationController.dismiss(animated: true, completion: nil)
+            }))
+            navigationController.present(alert, animated: true, completion: nil)
+        }
+    }
+
+}

+ 107 - 0
deltachat-ios/View/AvatarSelectionCell.swift

@@ -0,0 +1,107 @@
+import UIKit
+
+class AvatarSelectionCell: UITableViewCell {
+    let badgeSize: CGFloat = 72
+    static let cellSize: CGFloat = 98
+    let downscaleDefaultImage: CGFloat = 0.6
+
+    var onAvatarTapped: (() -> Void)?
+
+    lazy var defaultImage: UIImage = {
+        if let image = UIImage(named: "camera") {
+            return image.invert()
+        }
+        return UIImage()
+    }()
+
+    lazy var badge: InitialsBadge = {
+        let badge = InitialsBadge(size: badgeSize)
+        badge.layer.cornerRadius = badgeSize / 2
+        badge.clipsToBounds = true
+        badge.setColor(UIColor.lightGray)
+        return badge
+    }()
+
+    lazy var hintLabel: UILabel = {
+        let label = UILabel(frame: .zero)
+        label.translatesAutoresizingMaskIntoConstraints = false
+        label.textColor = DcColors.defaultTextColor
+        label.text = String.localized("pref_profile_photo")
+        return label
+    }()
+
+    init(chat: DcChat) {
+        super.init(style: .default, reuseIdentifier: nil)
+        setAvatar(for: chat)
+        setupSubviews()
+    }
+
+    init(context: DcContext?) {
+        super.init(style: .default, reuseIdentifier: nil)
+        setAvatar(image: context?.getSelfAvatarImage(), with: self.defaultImage, downscale: downscaleDefaultImage)
+        setupSubviews()
+    }
+
+    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+        super.init(style: style, reuseIdentifier: reuseIdentifier)
+        setupSubviews()
+    }
+
+    required init?(coder _: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    private func setupSubviews() {
+        contentView.addSubview(badge)
+        badge.alignTrailingToAnchor(contentView.layoutMarginsGuide.trailingAnchor)
+        badge.alignTopToAnchor(contentView.layoutMarginsGuide.topAnchor)
+
+        contentView.addSubview(hintLabel)
+        hintLabel.alignLeadingToAnchor(contentView.layoutMarginsGuide.leadingAnchor)
+        hintLabel.alignTopToAnchor(contentView.layoutMarginsGuide.topAnchor)
+        hintLabel.alignTrailingToAnchor(badge.leadingAnchor)
+        hintLabel.alignBottomToAnchor(contentView.layoutMarginsGuide.bottomAnchor)
+
+        let touchListener = UILongPressGestureRecognizer(target: self, action: #selector(onBadgeTouched))
+        touchListener.minimumPressDuration = 0
+        badge.addGestureRecognizer(touchListener)
+    }
+
+    func onInitialsChanged(text: String?) {
+        if badge.showsInitials() {
+            badge.setName(text ?? "")
+        }
+    }
+
+    @objc func onBadgeTouched(gesture: UILongPressGestureRecognizer) {
+        switch gesture.state {
+        case .began:
+            badge.alpha = 0.7
+        case .ended:
+            badge.alpha = 1
+            onAvatarTapped?()
+        case .cancelled:
+            badge.alpha = 1
+        default:
+            break
+        }
+    }
+
+    func setAvatar(for chat: DcChat) {
+        if let image = chat.profileImage {
+            badge = InitialsBadge(image: image, size: badgeSize)
+        } else {
+            badge = InitialsBadge(name: chat.name, color: chat.color, size: badgeSize)
+        }
+        badge.setVerified(chat.isVerified)
+    }
+
+    func setAvatar(image: UIImage?, with defaultImage: UIImage?, downscale: CGFloat? = nil) {
+        if let image = image {
+            badge = InitialsBadge(image: image, size: badgeSize)
+        } else if let defaultImage = defaultImage {
+            badge = InitialsBadge(image: defaultImage, size: badgeSize, downscale: downscale)
+            badge.backgroundColor = DcColors.grayTextColor
+        }
+    }
+}

+ 2 - 2
deltachat-ios/View/ContactCell.swift

@@ -8,6 +8,7 @@ protocol ContactCellDelegate: class {
 
 class ContactCell: UITableViewCell {
 
+    public static let cellSize: CGFloat = 72
     weak var delegate: ContactCellDelegate?
     var rowIndex = -1
     private let initialsLabelSize: CGFloat = 54
@@ -36,7 +37,6 @@ class ContactCell: UITableViewCell {
         initialsLabel.textAlignment = NSTextAlignment.center
         initialsLabel.textColor = UIColor.white
         initialsLabel.font = UIFont.systemFont(ofSize: 22)
-        initialsLabel.backgroundColor = UIColor.green
         let initialsLabelCornerRadius = (initialsLabelSize - 6) / 2
         initialsLabel.layer.cornerRadius = initialsLabelCornerRadius
         initialsLabel.clipsToBounds = true
@@ -160,7 +160,7 @@ class ContactCell: UITableViewCell {
     }
 
     func setImage(_ img: UIImage) {
-        if let resizedImg = img.resizeImage(targetSize: CGSize(width: initialsLabelSize - 6, height: initialsLabelSize - 6)) {
+        if let resizedImg = img.scaleDownImage(toMax: initialsLabelSize - 6) {
             let attachment = NSTextAttachment()
             attachment.image = resizedImg
             initialsLabel.attributedText = NSAttributedString(attachment: attachment)

+ 0 - 80
deltachat-ios/View/GroupNameCell.swift

@@ -1,80 +0,0 @@
-import UIKit
-
-class GroupLabelCell: UITableViewCell {
-    var groupBadgeSize: CGFloat = 54
-
-    var onTextChanged: ((String) -> Void)? // use this callback to update editButton in navigationController
-
-    lazy var groupBadge: InitialsBadge = {
-        let badge = InitialsBadge(size: groupBadgeSize)
-        badge.layer.cornerRadius = groupBadgeSize / 2
-        badge.clipsToBounds = true
-        badge.setColor(UIColor.lightGray)
-        return badge
-    }()
-
-    lazy var inputField: UITextField = {
-        let textField = UITextField()
-        textField.placeholder = String.localized("group_name")
-        textField.borderStyle = .none
-        textField.becomeFirstResponder()
-        textField.autocorrectionType = .no
-        textField.addTarget(self, action: #selector(nameFieldChanged), for: .editingChanged)
-        return textField
-    }()
-
-    init(chat: DcChat) {
-        super.init(style: .default, reuseIdentifier: nil)
-        setAvatar(for: chat)
-        setupSubviews()
-    }
-
-    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
-        super.init(style: style, reuseIdentifier: reuseIdentifier)
-        setupSubviews()
-    }
-
-    required init?(coder _: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-
-    private func setupSubviews() {
-        contentView.addSubview(groupBadge)
-        groupBadge.translatesAutoresizingMaskIntoConstraints = false
-
-        groupBadge.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor, constant: 0).isActive = true
-        groupBadge.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor, constant: 5).isActive = true
-        groupBadge.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: -5).isActive = true
-        groupBadge.widthAnchor.constraint(equalToConstant: groupBadgeSize).isActive = true
-        groupBadge.heightAnchor.constraint(equalToConstant: groupBadgeSize).isActive = true
-
-        contentView.addSubview(inputField)
-        inputField.translatesAutoresizingMaskIntoConstraints = false
-
-        inputField.leadingAnchor.constraint(equalTo: groupBadge.trailingAnchor, constant: 15).isActive = true
-        inputField.heightAnchor.constraint(equalToConstant: 45).isActive = true
-        inputField.centerYAnchor.constraint(equalTo: groupBadge.centerYAnchor, constant: 0).isActive = true
-        inputField.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor, constant: 0).isActive = true
-    }
-
-    @objc func nameFieldChanged() {
-        let groupName = inputField.text ?? ""
-        if groupBadge.showsInitials() {
-            groupBadge.setName(groupName)
-        }
-        onTextChanged?(groupName)
-    }
-
-    func getGroupName() -> String {
-        return inputField.text ?? ""
-    }
-
-    func setAvatar(for chat: DcChat) {
-        if let image = chat.profileImage {
-            groupBadge = InitialsBadge(image: image, size: groupBadgeSize)
-        } else {
-            groupBadge =  InitialsBadge(name: chat.name, color: chat.color, size: groupBadgeSize)
-        }
-        groupBadge.setVerified(chat.isVerified)
-    }
-}

+ 17 - 6
deltachat-ios/View/InitialsBadge.swift

@@ -2,7 +2,8 @@ import UIKit
 
 class InitialsBadge: UIView {
 
-    private let verificationViewPadding = CGFloat(2)
+    private let verificationViewPadding: CGFloat = 2
+    private let size: CGFloat
 
     private var label: UILabel = {
         let label = UILabel()
@@ -35,12 +36,13 @@ class InitialsBadge: UIView {
         setColor(color)
     }
 
-    convenience init (image: UIImage, size: CGFloat) {
+    convenience init (image: UIImage, size: CGFloat, downscale: CGFloat? = nil) {
         self.init(size: size)
-        setImage(image)
+        setImage(image, downscale: downscale)
     }
 
     init(size: CGFloat) {
+        self.size = size
         super.init(frame: CGRect(x: 0, y: 0, width: size, height: size))
         let radius = size / 2
         layer.cornerRadius = radius
@@ -55,7 +57,8 @@ class InitialsBadge: UIView {
         imageView.layer.cornerRadius = radius
         imageView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
         imageView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
-        imageView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
+        imageView.alignTopToAnchor(topAnchor)
+        imageView.alignBottomToAnchor(bottomAnchor)
 
         addSubview(label)
         label.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
@@ -80,9 +83,17 @@ class InitialsBadge: UIView {
         imageView.isHidden = true
     }
 
-    func setImage(_ image: UIImage) {
-        if let resizedImg = image.resizeImage(targetSize: CGSize(width: self.frame.width, height: self.frame.height)) {
+    func setImage(_ image: UIImage, downscale: CGFloat? = nil) {
+        var scale = downscale ?? 1
+        if scale > 1 {
+            scale = 1
+        } else if scale < 0 {
+            scale = 0
+        }
+
+        if let resizedImg = image.scaleDownImage(toMax: self.size * scale) {
             self.imageView.image = resizedImg
+            self.imageView.contentMode = (downscale == nil) ? UIView.ContentMode.scaleAspectFill : UIView.ContentMode.center
             self.imageView.isHidden = false
             self.label.isHidden = true
         }