ソースを参照

Add Modify Photo [test]

Marino Faggiana 6 年 前
コミット
722210f498
47 ファイル変更3429 行追加6 行削除
  1. 156 0
      Nextcloud.xcodeproj/project.pbxproj
  2. 1 1
      iOSClient/Brand/File_Provider_Extension.plist
  3. 1 1
      iOSClient/Brand/Notification_Service_Extension.plist
  4. 1 1
      iOSClient/Brand/Share.plist
  5. 1 1
      iOSClient/Brand/iOSClient.plist
  6. 2 1
      iOSClient/Database/NCManageDatabase.swift
  7. 23 0
      iOSClient/Images.xcassets/modifyPhoto.imageset/Contents.json
  8. BIN
      iOSClient/Images.xcassets/modifyPhoto.imageset/modifyPhoto.png
  9. BIN
      iOSClient/Images.xcassets/modifyPhoto.imageset/modifyPhoto@2x.png
  10. BIN
      iOSClient/Images.xcassets/modifyPhoto.imageset/modifyPhoto@3x.png
  11. 27 0
      iOSClient/Library/PhotoEditor/Base.lproj/LaunchScreen.storyboard
  12. 45 0
      iOSClient/Library/PhotoEditor/ColorCollectionViewCell.swift
  13. 41 0
      iOSClient/Library/PhotoEditor/ColorCollectionViewCell.xib
  14. 57 0
      iOSClient/Library/PhotoEditor/ColorsCollectionViewDelegate.swift
  15. 324 0
      iOSClient/Library/PhotoEditor/CropRectView.swift
  16. 489 0
      iOSClient/Library/PhotoEditor/CropView.swift
  17. 247 0
      iOSClient/Library/PhotoEditor/CropViewController.swift
  18. 19 0
      iOSClient/Library/PhotoEditor/EmojiCollectionViewCell.swift
  19. 39 0
      iOSClient/Library/PhotoEditor/EmojiCollectionViewCell.xib
  20. 64 0
      iOSClient/Library/PhotoEditor/EmojisCollectionViewDelegate.swift
  21. 35 0
      iOSClient/Library/PhotoEditor/GradientView.swift
  22. BIN
      iOSClient/Library/PhotoEditor/PhotoCropEditorBorder.png
  23. BIN
      iOSClient/Library/PhotoEditor/PhotoCropEditorBorder@2x.png
  24. BIN
      iOSClient/Library/PhotoEditor/PhotoCropEditorBorder@3x.png
  25. 139 0
      iOSClient/Library/PhotoEditor/PhotoEditor+Controls.swift
  26. 24 0
      iOSClient/Library/PhotoEditor/PhotoEditor+Crop.swift
  27. 80 0
      iOSClient/Library/PhotoEditor/PhotoEditor+Drawing.swift
  28. 28 0
      iOSClient/Library/PhotoEditor/PhotoEditor+Font.swift
  29. 238 0
      iOSClient/Library/PhotoEditor/PhotoEditor+Gestures.swift
  30. 48 0
      iOSClient/Library/PhotoEditor/PhotoEditor+Keyboard.swift
  31. 104 0
      iOSClient/Library/PhotoEditor/PhotoEditor+StickersViewController.swift
  32. 53 0
      iOSClient/Library/PhotoEditor/PhotoEditor+UITextView.swift
  33. 156 0
      iOSClient/Library/PhotoEditor/PhotoEditorViewController.swift
  34. 328 0
      iOSClient/Library/PhotoEditor/PhotoEditorViewController.xib
  35. 54 0
      iOSClient/Library/PhotoEditor/Protocols.swift
  36. 61 0
      iOSClient/Library/PhotoEditor/ResizeControl.swift
  37. 18 0
      iOSClient/Library/PhotoEditor/StickerCollectionViewCell.swift
  38. 39 0
      iOSClient/Library/PhotoEditor/StickerCollectionViewCell.xib
  39. 247 0
      iOSClient/Library/PhotoEditor/StickersViewController.swift
  40. 67 0
      iOSClient/Library/PhotoEditor/StickersViewController.xib
  41. 34 0
      iOSClient/Library/PhotoEditor/UIImage+Crop.swift
  42. 33 0
      iOSClient/Library/PhotoEditor/UIImage+Size.swift
  43. 36 0
      iOSClient/Library/PhotoEditor/UIImageView+Alpha.swift
  44. 22 0
      iOSClient/Library/PhotoEditor/UIView+Image.swift
  45. BIN
      iOSClient/Library/PhotoEditor/icomoon.ttf
  46. 47 1
      iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift
  47. 1 0
      iOSClient/Supporting Files/en.lproj/Localizable.strings

+ 156 - 0
Nextcloud.xcodeproj/project.pbxproj

@@ -255,6 +255,41 @@
 		F75ADF451DC75FFE008A7347 /* CCLogin.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F75ADF441DC75FFE008A7347 /* CCLogin.storyboard */; };
 		F75EDFBD1E8C112F00E6F369 /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = F75EDFBC1E8C112F00E6F369 /* libsqlite3.0.tbd */; };
 		F75EDFBF1E8C116D00E6F369 /* libstdc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = F75EDFBE1E8C116D00E6F369 /* libstdc++.tbd */; };
+		F760F77B21F21F61006B1A73 /* icomoon.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F760F75721F21F61006B1A73 /* icomoon.ttf */; };
+		F760F77C21F21F61006B1A73 /* StickersViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F760F75821F21F61006B1A73 /* StickersViewController.xib */; };
+		F760F77D21F21F61006B1A73 /* ColorsCollectionViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F75921F21F61006B1A73 /* ColorsCollectionViewDelegate.swift */; };
+		F760F77E21F21F61006B1A73 /* PhotoEditor+Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F75A21F21F61006B1A73 /* PhotoEditor+Keyboard.swift */; };
+		F760F77F21F21F61006B1A73 /* PhotoEditor+Controls.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F75B21F21F61006B1A73 /* PhotoEditor+Controls.swift */; };
+		F760F78021F21F61006B1A73 /* PhotoEditorViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F760F75C21F21F61006B1A73 /* PhotoEditorViewController.xib */; };
+		F760F78121F21F61006B1A73 /* PhotoEditor+StickersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F75D21F21F61006B1A73 /* PhotoEditor+StickersViewController.swift */; };
+		F760F78221F21F61006B1A73 /* PhotoEditor+UITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F75E21F21F61006B1A73 /* PhotoEditor+UITextView.swift */; };
+		F760F78321F21F61006B1A73 /* UIImageView+Alpha.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F75F21F21F61006B1A73 /* UIImageView+Alpha.swift */; };
+		F760F78421F21F61006B1A73 /* ColorCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F760F76021F21F61006B1A73 /* ColorCollectionViewCell.xib */; };
+		F760F78521F21F61006B1A73 /* EmojiCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F760F76121F21F61006B1A73 /* EmojiCollectionViewCell.xib */; };
+		F760F78621F21F61006B1A73 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F760F76221F21F61006B1A73 /* LaunchScreen.storyboard */; };
+		F760F78721F21F61006B1A73 /* PhotoEditor+Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F76421F21F61006B1A73 /* PhotoEditor+Font.swift */; };
+		F760F78821F21F61006B1A73 /* PhotoCropEditorBorder@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F760F76521F21F61006B1A73 /* PhotoCropEditorBorder@2x.png */; };
+		F760F78921F21F61006B1A73 /* UIView+Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F76621F21F61006B1A73 /* UIView+Image.swift */; };
+		F760F78A21F21F61006B1A73 /* UIImage+Size.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F76721F21F61006B1A73 /* UIImage+Size.swift */; };
+		F760F78B21F21F61006B1A73 /* UIImage+Crop.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F76821F21F61006B1A73 /* UIImage+Crop.swift */; };
+		F760F78C21F21F61006B1A73 /* StickersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F76921F21F61006B1A73 /* StickersViewController.swift */; };
+		F760F78D21F21F61006B1A73 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F76A21F21F61006B1A73 /* Protocols.swift */; };
+		F760F78E21F21F61006B1A73 /* PhotoEditor+Drawing.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F76B21F21F61006B1A73 /* PhotoEditor+Drawing.swift */; };
+		F760F78F21F21F61006B1A73 /* PhotoCropEditorBorder@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = F760F76C21F21F61006B1A73 /* PhotoCropEditorBorder@3x.png */; };
+		F760F79021F21F61006B1A73 /* PhotoCropEditorBorder.png in Resources */ = {isa = PBXBuildFile; fileRef = F760F76D21F21F61006B1A73 /* PhotoCropEditorBorder.png */; };
+		F760F79121F21F61006B1A73 /* EmojiCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F76E21F21F61006B1A73 /* EmojiCollectionViewCell.swift */; };
+		F760F79221F21F61006B1A73 /* EmojisCollectionViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F76F21F21F61006B1A73 /* EmojisCollectionViewDelegate.swift */; };
+		F760F79321F21F61006B1A73 /* CropView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F77021F21F61006B1A73 /* CropView.swift */; };
+		F760F79421F21F61006B1A73 /* ResizeControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F77121F21F61006B1A73 /* ResizeControl.swift */; };
+		F760F79521F21F61006B1A73 /* PhotoEditor+Gestures.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F77221F21F61006B1A73 /* PhotoEditor+Gestures.swift */; };
+		F760F79621F21F61006B1A73 /* PhotoEditorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F77321F21F61006B1A73 /* PhotoEditorViewController.swift */; };
+		F760F79721F21F61006B1A73 /* StickerCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F760F77421F21F61006B1A73 /* StickerCollectionViewCell.xib */; };
+		F760F79821F21F61006B1A73 /* StickerCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F77521F21F61006B1A73 /* StickerCollectionViewCell.swift */; };
+		F760F79921F21F61006B1A73 /* CropViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F77621F21F61006B1A73 /* CropViewController.swift */; };
+		F760F79A21F21F61006B1A73 /* PhotoEditor+Crop.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F77721F21F61006B1A73 /* PhotoEditor+Crop.swift */; };
+		F760F79B21F21F61006B1A73 /* ColorCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F77821F21F61006B1A73 /* ColorCollectionViewCell.swift */; };
+		F760F79C21F21F61006B1A73 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F77921F21F61006B1A73 /* GradientView.swift */; };
+		F760F79D21F21F61006B1A73 /* CropRectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760F77A21F21F61006B1A73 /* CropRectView.swift */; };
 		F761855A2198A2B500A65DC4 /* NCPhotosPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76185592198A2B500A65DC4 /* NCPhotosPickerViewController.swift */; };
 		F762CAF71EACB66200B38484 /* XLFormBaseCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F762CAA41EACB66200B38484 /* XLFormBaseCell.m */; };
 		F762CAF81EACB66200B38484 /* XLFormButtonCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F762CAA61EACB66200B38484 /* XLFormButtonCell.m */; };
@@ -946,6 +981,41 @@
 		F75CDBF51DF063AD00116AD0 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; name = .gitignore; path = ../.gitignore; sourceTree = "<group>"; };
 		F75EDFBC1E8C112F00E6F369 /* libsqlite3.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; };
 		F75EDFBE1E8C116D00E6F369 /* libstdc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libstdc++.tbd"; path = "usr/lib/libstdc++.tbd"; sourceTree = SDKROOT; };
+		F760F75721F21F61006B1A73 /* icomoon.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = icomoon.ttf; sourceTree = "<group>"; };
+		F760F75821F21F61006B1A73 /* StickersViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = StickersViewController.xib; sourceTree = "<group>"; };
+		F760F75921F21F61006B1A73 /* ColorsCollectionViewDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorsCollectionViewDelegate.swift; sourceTree = "<group>"; };
+		F760F75A21F21F61006B1A73 /* PhotoEditor+Keyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PhotoEditor+Keyboard.swift"; sourceTree = "<group>"; };
+		F760F75B21F21F61006B1A73 /* PhotoEditor+Controls.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PhotoEditor+Controls.swift"; sourceTree = "<group>"; };
+		F760F75C21F21F61006B1A73 /* PhotoEditorViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PhotoEditorViewController.xib; sourceTree = "<group>"; };
+		F760F75D21F21F61006B1A73 /* PhotoEditor+StickersViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PhotoEditor+StickersViewController.swift"; sourceTree = "<group>"; };
+		F760F75E21F21F61006B1A73 /* PhotoEditor+UITextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PhotoEditor+UITextView.swift"; sourceTree = "<group>"; };
+		F760F75F21F21F61006B1A73 /* UIImageView+Alpha.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+Alpha.swift"; sourceTree = "<group>"; };
+		F760F76021F21F61006B1A73 /* ColorCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ColorCollectionViewCell.xib; sourceTree = "<group>"; };
+		F760F76121F21F61006B1A73 /* EmojiCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EmojiCollectionViewCell.xib; sourceTree = "<group>"; };
+		F760F76321F21F61006B1A73 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+		F760F76421F21F61006B1A73 /* PhotoEditor+Font.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PhotoEditor+Font.swift"; sourceTree = "<group>"; };
+		F760F76521F21F61006B1A73 /* PhotoCropEditorBorder@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "PhotoCropEditorBorder@2x.png"; sourceTree = "<group>"; };
+		F760F76621F21F61006B1A73 /* UIView+Image.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Image.swift"; sourceTree = "<group>"; };
+		F760F76721F21F61006B1A73 /* UIImage+Size.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Size.swift"; sourceTree = "<group>"; };
+		F760F76821F21F61006B1A73 /* UIImage+Crop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Crop.swift"; sourceTree = "<group>"; };
+		F760F76921F21F61006B1A73 /* StickersViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickersViewController.swift; sourceTree = "<group>"; };
+		F760F76A21F21F61006B1A73 /* Protocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = "<group>"; };
+		F760F76B21F21F61006B1A73 /* PhotoEditor+Drawing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PhotoEditor+Drawing.swift"; sourceTree = "<group>"; };
+		F760F76C21F21F61006B1A73 /* PhotoCropEditorBorder@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "PhotoCropEditorBorder@3x.png"; sourceTree = "<group>"; };
+		F760F76D21F21F61006B1A73 /* PhotoCropEditorBorder.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = PhotoCropEditorBorder.png; sourceTree = "<group>"; };
+		F760F76E21F21F61006B1A73 /* EmojiCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmojiCollectionViewCell.swift; sourceTree = "<group>"; };
+		F760F76F21F21F61006B1A73 /* EmojisCollectionViewDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmojisCollectionViewDelegate.swift; sourceTree = "<group>"; };
+		F760F77021F21F61006B1A73 /* CropView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CropView.swift; sourceTree = "<group>"; };
+		F760F77121F21F61006B1A73 /* ResizeControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResizeControl.swift; sourceTree = "<group>"; };
+		F760F77221F21F61006B1A73 /* PhotoEditor+Gestures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PhotoEditor+Gestures.swift"; sourceTree = "<group>"; };
+		F760F77321F21F61006B1A73 /* PhotoEditorViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoEditorViewController.swift; sourceTree = "<group>"; };
+		F760F77421F21F61006B1A73 /* StickerCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = StickerCollectionViewCell.xib; sourceTree = "<group>"; };
+		F760F77521F21F61006B1A73 /* StickerCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerCollectionViewCell.swift; sourceTree = "<group>"; };
+		F760F77621F21F61006B1A73 /* CropViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CropViewController.swift; sourceTree = "<group>"; };
+		F760F77721F21F61006B1A73 /* PhotoEditor+Crop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PhotoEditor+Crop.swift"; sourceTree = "<group>"; };
+		F760F77821F21F61006B1A73 /* ColorCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorCollectionViewCell.swift; sourceTree = "<group>"; };
+		F760F77921F21F61006B1A73 /* GradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = "<group>"; };
+		F760F77A21F21F61006B1A73 /* CropRectView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CropRectView.swift; sourceTree = "<group>"; };
 		F76185592198A2B500A65DC4 /* NCPhotosPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCPhotosPickerViewController.swift; sourceTree = "<group>"; };
 		F762CAA31EACB66200B38484 /* XLFormBaseCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XLFormBaseCell.h; sourceTree = "<group>"; };
 		F762CAA41EACB66200B38484 /* XLFormBaseCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XLFormBaseCell.m; sourceTree = "<group>"; };
@@ -2004,6 +2074,48 @@
 			path = Scan;
 			sourceTree = "<group>";
 		};
+		F760F75621F21F61006B1A73 /* PhotoEditor */ = {
+			isa = PBXGroup;
+			children = (
+				F760F75721F21F61006B1A73 /* icomoon.ttf */,
+				F760F75821F21F61006B1A73 /* StickersViewController.xib */,
+				F760F75921F21F61006B1A73 /* ColorsCollectionViewDelegate.swift */,
+				F760F75A21F21F61006B1A73 /* PhotoEditor+Keyboard.swift */,
+				F760F75B21F21F61006B1A73 /* PhotoEditor+Controls.swift */,
+				F760F75C21F21F61006B1A73 /* PhotoEditorViewController.xib */,
+				F760F75D21F21F61006B1A73 /* PhotoEditor+StickersViewController.swift */,
+				F760F75E21F21F61006B1A73 /* PhotoEditor+UITextView.swift */,
+				F760F75F21F21F61006B1A73 /* UIImageView+Alpha.swift */,
+				F760F76021F21F61006B1A73 /* ColorCollectionViewCell.xib */,
+				F760F76121F21F61006B1A73 /* EmojiCollectionViewCell.xib */,
+				F760F76221F21F61006B1A73 /* LaunchScreen.storyboard */,
+				F760F76421F21F61006B1A73 /* PhotoEditor+Font.swift */,
+				F760F76521F21F61006B1A73 /* PhotoCropEditorBorder@2x.png */,
+				F760F76621F21F61006B1A73 /* UIView+Image.swift */,
+				F760F76721F21F61006B1A73 /* UIImage+Size.swift */,
+				F760F76821F21F61006B1A73 /* UIImage+Crop.swift */,
+				F760F76921F21F61006B1A73 /* StickersViewController.swift */,
+				F760F76A21F21F61006B1A73 /* Protocols.swift */,
+				F760F76B21F21F61006B1A73 /* PhotoEditor+Drawing.swift */,
+				F760F76C21F21F61006B1A73 /* PhotoCropEditorBorder@3x.png */,
+				F760F76D21F21F61006B1A73 /* PhotoCropEditorBorder.png */,
+				F760F76E21F21F61006B1A73 /* EmojiCollectionViewCell.swift */,
+				F760F76F21F21F61006B1A73 /* EmojisCollectionViewDelegate.swift */,
+				F760F77021F21F61006B1A73 /* CropView.swift */,
+				F760F77121F21F61006B1A73 /* ResizeControl.swift */,
+				F760F77221F21F61006B1A73 /* PhotoEditor+Gestures.swift */,
+				F760F77321F21F61006B1A73 /* PhotoEditorViewController.swift */,
+				F760F77421F21F61006B1A73 /* StickerCollectionViewCell.xib */,
+				F760F77521F21F61006B1A73 /* StickerCollectionViewCell.swift */,
+				F760F77621F21F61006B1A73 /* CropViewController.swift */,
+				F760F77721F21F61006B1A73 /* PhotoEditor+Crop.swift */,
+				F760F77821F21F61006B1A73 /* ColorCollectionViewCell.swift */,
+				F760F77921F21F61006B1A73 /* GradientView.swift */,
+				F760F77A21F21F61006B1A73 /* CropRectView.swift */,
+			);
+			path = PhotoEditor;
+			sourceTree = "<group>";
+		};
 		F76185582198A28E00A65DC4 /* NCPhotosPickerViewController */ = {
 			isa = PBXGroup;
 			children = (
@@ -2708,6 +2820,7 @@
 				F754EEC321772B6100BB1CDF /* DropdownMenu */,
 				F7F54CAD1E5B14C700E19C62 /* MWPhotoBrowser */,
 				F7B4F1C51F44356F00B53B42 /* NCUchardet */,
+				F760F75621F21F61006B1A73 /* PhotoEditor */,
 				F762CB7B1EACB81000B38484 /* REMenu */,
 				F7B1FBAF1E72E3D1001781FE /* SwiftWebVC */,
 				F762CB8B1EACB84400B38484 /* TWMessageBarManager */,
@@ -3165,6 +3278,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				F760F78421F21F61006B1A73 /* ColorCollectionViewCell.xib in Resources */,
 				F75ADF451DC75FFE008A7347 /* CCLogin.storyboard in Resources */,
 				F7F54CED1E5B14C700E19C62 /* ImageSelectedOn@3x.png in Resources */,
 				F7D4234D1F0596AC009C9782 /* Reader-Thumbs.png in Resources */,
@@ -3184,6 +3298,7 @@
 				F7F54CF81E5B14C700E19C62 /* PlayButtonOverlayLargeTap@2x.png in Resources */,
 				F73B4EEE1F470D9100BBEE4B /* Big5Freq.tab in Resources */,
 				F7D4233B1F0596AC009C9782 /* Reader-Button-N.png in Resources */,
+				F760F78821F21F61006B1A73 /* PhotoCropEditorBorder@2x.png in Resources */,
 				F7F54CF31E5B14C700E19C62 /* ImageSelectedSmallOn@3x.png in Resources */,
 				F7D423441F0596AC009C9782 /* Reader-Mark-N.png in Resources */,
 				F7F54CFA1E5B14C700E19C62 /* UIBarButtonItemArrowLeft.png in Resources */,
@@ -3197,6 +3312,7 @@
 				F7B1FBC41E72E3D1001781FE /* Media.xcassets in Resources */,
 				F78F74342163757000C2ADAD /* NCTrash.storyboard in Resources */,
 				F7D4234E1F0596AC009C9782 /* Reader-Thumbs@2x.png in Resources */,
+				F760F78521F21F61006B1A73 /* EmojiCollectionViewCell.xib in Resources */,
 				F79A65C32191D90F00FF6DCC /* NCSelect.storyboard in Resources */,
 				F77B0F2F1D118A16002130FE /* CCMove.storyboard in Resources */,
 				F7F54D001E5B14C700E19C62 /* UIBarButtonItemGrid.png in Resources */,
@@ -3223,19 +3339,23 @@
 				F77B0F4D1D118A16002130FE /* CCShare.storyboard in Resources */,
 				F7F54D021E5B14C700E19C62 /* UIBarButtonItemGrid@3x.png in Resources */,
 				F7F54CEA1E5B14C700E19C62 /* ImageSelectedOff@3x.png in Resources */,
+				F760F78621F21F61006B1A73 /* LaunchScreen.storyboard in Resources */,
 				F7381EE5218218C9000B1560 /* NCOffline.storyboard in Resources */,
 				F7F54CF11E5B14C700E19C62 /* ImageSelectedSmallOn.png in Resources */,
+				F760F77C21F21F61006B1A73 /* StickersViewController.xib in Resources */,
 				F762CB1B1EACB66200B38484 /* XLForm.bundle in Resources */,
 				F78ACD4B21903F850088454D /* NCTrashListCell.xib in Resources */,
 				F762CB9A1EACB84400B38484 /* icon-success@2x.png in Resources */,
 				F77B0F571D118A16002130FE /* synchronizedcrypto.gif in Resources */,
 				F7F54CEE1E5B14C700E19C62 /* ImageSelectedSmallOff.png in Resources */,
+				F760F79721F21F61006B1A73 /* StickerCollectionViewCell.xib in Resources */,
 				F73B4EF31F470D9100BBEE4B /* GB2312Freq.tab in Resources */,
 				F7B2DEF11F976859007CF4D2 /* english.txt in Resources */,
 				F7F54D051E5B14C800E19C62 /* VideoOverlay@3x.png in Resources */,
 				F7F54D011E5B14C700E19C62 /* UIBarButtonItemGrid@2x.png in Resources */,
 				F7C9555321F0C4CA0024296E /* NCActivity.storyboard in Resources */,
 				F7F54CE71E5B14C700E19C62 /* ImageError@3x.png in Resources */,
+				F760F78F21F21F61006B1A73 /* PhotoCropEditorBorder@3x.png in Resources */,
 				F7D423461F0596AC009C9782 /* Reader-Mark-N@3x.png in Resources */,
 				F7F54CE61E5B14C700E19C62 /* ImageError@2x.png in Resources */,
 				F7F54CEF1E5B14C700E19C62 /* ImageSelectedSmallOff@2x.png in Resources */,
@@ -3264,6 +3384,7 @@
 				F7D423381F0596AC009C9782 /* Reader-Button-H.png in Resources */,
 				F7F54CFC1E5B14C700E19C62 /* UIBarButtonItemArrowLeft@3x.png in Resources */,
 				F77B0F7D1D118A16002130FE /* Images.xcassets in Resources */,
+				F760F79021F21F61006B1A73 /* PhotoCropEditorBorder.png in Resources */,
 				F78ACD56219047E90088454D /* NCSectionHeader.xib in Resources */,
 				F7D423491F0596AC009C9782 /* Reader-Mark-Y@3x.png in Resources */,
 				F7D423421F0596AC009C9782 /* Reader-Export@2x.png in Resources */,
@@ -3272,7 +3393,9 @@
 				F78ACD58219048040088454D /* NCSectionHeaderMenu.xib in Resources */,
 				F77B0F8C1D118A16002130FE /* CCCellMainTransfer.xib in Resources */,
 				F73B4EF41F470D9100BBEE4B /* JISFreq.tab in Resources */,
+				F760F77B21F21F61006B1A73 /* icomoon.ttf in Resources */,
 				F7D423451F0596AC009C9782 /* Reader-Mark-N@2x.png in Resources */,
+				F760F78021F21F61006B1A73 /* PhotoEditorViewController.xib in Resources */,
 				F774DF101FCC26BE002AF9FC /* iTunesArtwork@2x.png in Resources */,
 				F7D423481F0596AC009C9782 /* Reader-Mark-Y@2x.png in Resources */,
 				F7D4233F1F0596AC009C9782 /* Reader-Email@2x.png in Resources */,
@@ -3497,11 +3620,13 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				F760F79521F21F61006B1A73 /* PhotoEditor+Gestures.swift in Sources */,
 				F70022D71EC4C9100080073F /* NSDate+RFC1123.m in Sources */,
 				F70022A41EC4C9100080073F /* AFNetworkReachabilityManager.m in Sources */,
 				F762CAFD1EACB66200B38484 /* XLFormInlineSelectorCell.m in Sources */,
 				F77B0DF21D118A16002130FE /* CCUploadFromOtherUpp.m in Sources */,
 				F754EECB21772B6100BB1CDF /* DropdownMenu.swift in Sources */,
+				F760F78221F21F61006B1A73 /* PhotoEditor+UITextView.swift in Sources */,
 				F77B0DF41D118A16002130FE /* CCMain.m in Sources */,
 				F7E9C41B20F4CA870040CF18 /* CCTransfers.m in Sources */,
 				F73B4F0D1F470D9100BBEE4B /* nsLatin1Prober.cpp in Sources */,
@@ -3511,6 +3636,7 @@
 				F7F54D0E1E5B14C800E19C62 /* UIImage+MWPhotoBrowser.m in Sources */,
 				F7F54D091E5B14C800E19C62 /* MWPhoto.m in Sources */,
 				F73B4F041F470D9100BBEE4B /* nsBig5Prober.cpp in Sources */,
+				F760F78321F21F61006B1A73 /* UIImageView+Alpha.swift in Sources */,
 				F73B4EEF1F470D9100BBEE4B /* CharDistribution.cpp in Sources */,
 				F7B0C0CD1EE7E7750033AC24 /* CCSynchronize.m in Sources */,
 				F754EEC921772B6100BB1CDF /* DropdownItem.swift in Sources */,
@@ -3528,6 +3654,7 @@
 				F7D423881F0596C6009C9782 /* ReaderThumbView.m in Sources */,
 				F73B4EFE1F470D9100BBEE4B /* LangHungarianModel.cpp in Sources */,
 				F7D4238A1F0596C6009C9782 /* ThumbsMainToolbar.m in Sources */,
+				F760F79921F21F61006B1A73 /* CropViewController.swift in Sources */,
 				F70022EC1EC4C9100080073F /* OCXMLSharedParser.m in Sources */,
 				F7F54D061E5B14C800E19C62 /* MWCaptionView.m in Sources */,
 				F7A55420204EF8AF008468EC /* UIScrollView+TOScrollBar.m in Sources */,
@@ -3539,6 +3666,7 @@
 				F762CB061EACB66200B38484 /* XLFormTextViewCell.m in Sources */,
 				F78ACD4221903CE00088454D /* NCListCell.swift in Sources */,
 				F78ACD4F2190440D0088454D /* NCLayout.swift in Sources */,
+				F760F79C21F21F61006B1A73 /* GradientView.swift in Sources */,
 				F762CB881EACB81000B38484 /* REMenuContainerView.m in Sources */,
 				F7D4237F1F0596C6009C9782 /* ReaderDocumentOutline.m in Sources */,
 				F73F537F1E929C8500F8678D /* CCMore.swift in Sources */,
@@ -3550,17 +3678,21 @@
 				F78E7065219F096B006F23E4 /* NCAvatar.swift in Sources */,
 				F7DFB7F0219C5B8000680748 /* NCCreateFormUploadAssets.swift in Sources */,
 				F762CB0C1EACB66200B38484 /* XLFormSectionDescriptor.m in Sources */,
+				F760F79421F21F61006B1A73 /* ResizeControl.swift in Sources */,
 				F77B0E131D118A16002130FE /* AppDelegate.m in Sources */,
 				F7DFB7F2219C5C0000680748 /* NCCreateFormUploadFileText.swift in Sources */,
 				F762CB861EACB81000B38484 /* RECommonFunctions.m in Sources */,
 				F76C6F8E21943C8C0063591B /* NCActionSheetHeader.swift in Sources */,
+				F760F79121F21F61006B1A73 /* EmojiCollectionViewCell.swift in Sources */,
 				F750374F1DBFA91A008FB480 /* NSArray+PureLayout.m in Sources */,
 				F77B0E141D118A16002130FE /* CCError.m in Sources */,
 				F73B4F131F470D9100BBEE4B /* nsUniversalDetector.cpp in Sources */,
 				F7B0C1751EE839A30033AC24 /* NCAutoUpload.m in Sources */,
 				F77B0E161D118A16002130FE /* AFViewShaker.m in Sources */,
+				F760F77E21F21F61006B1A73 /* PhotoEditor+Keyboard.swift in Sources */,
 				F73B4F111F470D9100BBEE4B /* nsSBCSGroupProber.cpp in Sources */,
 				F738E8421F90FFD100F95C8E /* NCManageEndToEndEncryption.m in Sources */,
+				F760F79D21F21F61006B1A73 /* CropRectView.swift in Sources */,
 				F73B4F091F470D9100BBEE4B /* nsEUCKRProber.cpp in Sources */,
 				F758B460212C56A400515F55 /* ScanCollectionView.swift in Sources */,
 				F762CB021EACB66200B38484 /* XLFormSliderCell.m in Sources */,
@@ -3577,6 +3709,7 @@
 				F7F54D0A1E5B14C800E19C62 /* MWPhotoBrowser.m in Sources */,
 				F762CB081EACB66200B38484 /* XLFormOptionsViewController.m in Sources */,
 				F73CC0721E813DFF006E3047 /* BKPasscodeLockScreenManager.m in Sources */,
+				F760F78B21F21F61006B1A73 /* UIImage+Crop.swift in Sources */,
 				F761855A2198A2B500A65DC4 /* NCPhotosPickerViewController.swift in Sources */,
 				F73B4F101F470D9100BBEE4B /* nsSBCharSetProber.cpp in Sources */,
 				F762CB0E1EACB66200B38484 /* NSExpression+XLFormAdditions.m in Sources */,
@@ -3606,6 +3739,8 @@
 				F73B4F171F470D9100BBEE4B /* uchardet.cpp in Sources */,
 				F7D4237A1F0596C6009C9782 /* ReaderConstants.m in Sources */,
 				F73B4F121F470D9100BBEE4B /* nsSJISProber.cpp in Sources */,
+				F760F78121F21F61006B1A73 /* PhotoEditor+StickersViewController.swift in Sources */,
+				F760F78D21F21F61006B1A73 /* Protocols.swift in Sources */,
 				F762CAFF1EACB66200B38484 /* XLFormPickerCell.m in Sources */,
 				F7A321AD1E9E6AD50069AD1B /* CCAdvanced.m in Sources */,
 				F710E8101EF95C9C00DC2427 /* CCIntro.m in Sources */,
@@ -3614,16 +3749,19 @@
 				F7A3218C1E9E42B30069AD1B /* CCMenuAccount.m in Sources */,
 				F77B0E4C1D118A16002130FE /* CCDetail.m in Sources */,
 				F762CB191EACB66200B38484 /* XLFormValidator.m in Sources */,
+				F760F78C21F21F61006B1A73 /* StickersViewController.swift in Sources */,
 				F762CB0D1EACB66200B38484 /* NSArray+XLFormAdditions.m in Sources */,
 				F7F801031D98205A007537BC /* CCCertificate.m in Sources */,
 				F77B0E4F1D118A16002130FE /* CCManageAutoUpload.m in Sources */,
 				F7FCFFE01D707B83000E6E29 /* CCPeekPop.m in Sources */,
+				F760F77D21F21F61006B1A73 /* ColorsCollectionViewDelegate.swift in Sources */,
 				F7BAADC81ED5A87C00B7EAD4 /* NCDatabase.swift in Sources */,
 				F77B0E541D118A16002130FE /* CCMove.m in Sources */,
 				F7A5541E204EF8AF008468EC /* TOScrollBarGestureRecognizer.m in Sources */,
 				F70022E61EC4C9100080073F /* OCXMLServerErrorsParser.m in Sources */,
 				F762CB171EACB66200B38484 /* XLFormRegexValidator.m in Sources */,
 				F729B92D217A2F1B00FE2150 /* NCActionSheetHeaderView.swift in Sources */,
+				F760F78721F21F61006B1A73 /* PhotoEditor+Font.swift in Sources */,
 				F73CC0691E813DFF006E3047 /* BKPasscodeDummyViewController.m in Sources */,
 				F762CB1A1EACB66200B38484 /* XLForm.m in Sources */,
 				F73B4EFC1F470D9100BBEE4B /* LangGreekModel.cpp in Sources */,
@@ -3664,6 +3802,7 @@
 				F7B1FBC91E72E3D1001781FE /* SwiftWebVCActivityChrome.swift in Sources */,
 				F762CB8A1EACB81000B38484 /* REMenuItemView.m in Sources */,
 				F7BF1B431D51E893000854F6 /* CCLogin.m in Sources */,
+				F760F79821F21F61006B1A73 /* StickerCollectionViewCell.swift in Sources */,
 				F70022FB1EC4C9100080073F /* NSString+Encode.m in Sources */,
 				F75037511DBFA91A008FB480 /* NSLayoutConstraint+PureLayout.m in Sources */,
 				F762CAFA1EACB66200B38484 /* XLFormDateCell.m in Sources */,
@@ -3673,6 +3812,7 @@
 				F77B0E8F1D118A16002130FE /* CCSection.m in Sources */,
 				F7CA1ED720E7E3FE002CC65E /* PKDownloadButton.m in Sources */,
 				F72AAECB1E5C60C700BB17E1 /* AHKActionSheetViewController.m in Sources */,
+				F760F79B21F21F61006B1A73 /* ColorCollectionViewCell.swift in Sources */,
 				F77B0E921D118A16002130FE /* CCCellMainTransfer.m in Sources */,
 				F7B1FBC81E72E3D1001781FE /* SwiftWebVCActivity.swift in Sources */,
 				F77B0E981D118A16002130FE /* CCManageAccount.m in Sources */,
@@ -3680,6 +3820,7 @@
 				F755BD9B20594AC7008C5FBB /* NCService.swift in Sources */,
 				F70022AD1EC4C9100080073F /* AFURLResponseSerialization.m in Sources */,
 				F77B0E9B1D118A16002130FE /* CCBKPasscode.m in Sources */,
+				F760F77F21F21F61006B1A73 /* PhotoEditor+Controls.swift in Sources */,
 				F7169A1D1EE590930086BD69 /* NCSharesCell.m in Sources */,
 				F77B0EA61D118A16002130FE /* NSString+TruncateToWidth.m in Sources */,
 				F70022C21EC4C9100080073F /* OCNotifications.m in Sources */,
@@ -3692,8 +3833,10 @@
 				F70022BC1EC4C9100080073F /* OCExternalSites.m in Sources */,
 				F73CC07B1E813DFF006E3047 /* BKTouchIDManager.m in Sources */,
 				F762CB031EACB66200B38484 /* XLFormStepCounterCell.m in Sources */,
+				F760F79321F21F61006B1A73 /* CropView.swift in Sources */,
 				F762CAF71EACB66200B38484 /* XLFormBaseCell.m in Sources */,
 				F70022E01EC4C9100080073F /* OCXMLListParser.m in Sources */,
+				F760F78A21F21F61006B1A73 /* UIImage+Size.swift in Sources */,
 				F70022B31EC4C9100080073F /* OCActivity.m in Sources */,
 				F734A8BF21B59137009DE2E8 /* WKCookieWebView.swift in Sources */,
 				F70022D41EC4C9100080073F /* NSDate+ISO8601.m in Sources */,
@@ -3717,10 +3860,14 @@
 				F73B4F021F470D9100BBEE4B /* LangTurkishModel.cpp in Sources */,
 				F7F54D0B1E5B14C800E19C62 /* MWTapDetectingImageView.m in Sources */,
 				F7D423821F0596C6009C9782 /* ReaderThumbCache.m in Sources */,
+				F760F79A21F21F61006B1A73 /* PhotoEditor+Crop.swift in Sources */,
 				F70022A71EC4C9100080073F /* AFSecurityPolicy.m in Sources */,
 				F7CA1ED220E7E3FE002CC65E /* PKCircleView.m in Sources */,
 				F7F54D0D1E5B14C800E19C62 /* MWZoomingScrollView.m in Sources */,
+				F760F78E21F21F61006B1A73 /* PhotoEditor+Drawing.swift in Sources */,
 				F762CB0B1EACB66200B38484 /* XLFormRowDescriptor.m in Sources */,
+				F760F78921F21F61006B1A73 /* UIView+Image.swift in Sources */,
+				F760F79221F21F61006B1A73 /* EmojisCollectionViewDelegate.swift in Sources */,
 				F7169A1C1EE590930086BD69 /* NCShares.m in Sources */,
 				F77B0EC61D118A16002130FE /* CCCellMain.m in Sources */,
 				F7C9555521F0C5470024296E /* NCActivity.swift in Sources */,
@@ -3733,6 +3880,7 @@
 				F73B4F0A1F470D9100BBEE4B /* nsEUCTWProber.cpp in Sources */,
 				F762CB871EACB81000B38484 /* REMenu.m in Sources */,
 				F762CB091EACB66200B38484 /* XLFormViewController.m in Sources */,
+				F760F79621F21F61006B1A73 /* PhotoEditorViewController.swift in Sources */,
 				F762CB161EACB66200B38484 /* XLFormTextView.m in Sources */,
 				F75AC2431F1F62450073EC19 /* NCManageAutoUploadFileName.swift in Sources */,
 				F79630EE215527D40015EEA5 /* NCViewerMedia.swift in Sources */,
@@ -3778,6 +3926,14 @@
 /* End PBXTargetDependency section */
 
 /* Begin PBXVariantGroup section */
+		F760F76221F21F61006B1A73 /* LaunchScreen.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				F760F76321F21F61006B1A73 /* Base */,
+			);
+			name = LaunchScreen.storyboard;
+			sourceTree = "<group>";
+		};
 		F7E70DE91A24DE4100E1B66A /* Localizable.strings */ = {
 			isa = PBXVariantGroup;
 			children = (

+ 1 - 1
iOSClient/Brand/File_Provider_Extension.plist

@@ -19,7 +19,7 @@
 	<key>CFBundleShortVersionString</key>
 	<string>2.22.9</string>
 	<key>CFBundleVersion</key>
-	<string>3</string>
+	<string>4</string>
 	<key>NSExtension</key>
 	<dict>
 		<key>NSExtensionFileProviderDocumentGroup</key>

+ 1 - 1
iOSClient/Brand/Notification_Service_Extension.plist

@@ -19,7 +19,7 @@
 	<key>CFBundleShortVersionString</key>
 	<string>2.22.9</string>
 	<key>CFBundleVersion</key>
-	<string>3</string>
+	<string>4</string>
 	<key>NSExtension</key>
 	<dict>
 		<key>NSExtensionPointIdentifier</key>

+ 1 - 1
iOSClient/Brand/Share.plist

@@ -19,7 +19,7 @@
 	<key>CFBundleShortVersionString</key>
 	<string>2.22.9</string>
 	<key>CFBundleVersion</key>
-	<string>3</string>
+	<string>4</string>
 	<key>NSAppTransportSecurity</key>
 	<dict>
 		<key>NSAllowsArbitraryLoads</key>

+ 1 - 1
iOSClient/Brand/iOSClient.plist

@@ -50,7 +50,7 @@
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>
-	<string>3</string>
+	<string>4</string>
 	<key>FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED</key>
 	<true/>
 	<key>Fabric</key>

+ 2 - 1
iOSClient/Database/NCManageDatabase.swift

@@ -57,7 +57,7 @@ class NCManageDatabase: NSObject {
         let config = Realm.Configuration(
         
             fileURL: dirGroup?.appendingPathComponent("\(k_appDatabaseNextcloud)/\(k_databaseDefault)"),
-            schemaVersion: 40,
+            schemaVersion: 41,
             
             // 10 : Version 2.18.0
             // 11 : Version 2.18.2
@@ -90,6 +90,7 @@ class NCManageDatabase: NSObject {
             // 38 : Version 2.22.8.20
             // 39 : Version 2.22.9.1
             // 40 : Version 2.22.9.3
+            // 41 : Version 2.22.9.4
 
             migrationBlock: { migration, oldSchemaVersion in
                 // We haven’t migrated anything yet, so oldSchemaVersion == 0

+ 23 - 0
iOSClient/Images.xcassets/modifyPhoto.imageset/Contents.json

@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "modifyPhoto.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "modifyPhoto@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "modifyPhoto@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

BIN
iOSClient/Images.xcassets/modifyPhoto.imageset/modifyPhoto.png


BIN
iOSClient/Images.xcassets/modifyPhoto.imageset/modifyPhoto@2x.png


BIN
iOSClient/Images.xcassets/modifyPhoto.imageset/modifyPhoto@3x.png


+ 27 - 0
iOSClient/Library/PhotoEditor/Base.lproj/LaunchScreen.storyboard

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
+                        <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="53" y="375"/>
+        </scene>
+    </scenes>
+</document>

+ 45 - 0
iOSClient/Library/PhotoEditor/ColorCollectionViewCell.swift

@@ -0,0 +1,45 @@
+//
+//  ColorCollectionViewCell.swift
+//  Photo Editor
+//
+//  Created by Mohamed Hamed on 5/1/17.
+//  Copyright © 2017 Mohamed Hamed. All rights reserved.
+//
+
+import UIKit
+
+class ColorCollectionViewCell: UICollectionViewCell {
+    
+    @IBOutlet weak var colorView: UIView!
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+    }
+    
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        colorView.layer.cornerRadius = colorView.frame.width / 2
+        colorView.clipsToBounds = true
+        colorView.layer.borderWidth = 1.0
+        colorView.layer.borderColor = UIColor.white.cgColor
+    }
+    
+    override var isSelected: Bool {
+        didSet {
+            if isSelected {
+                let previouTransform =  colorView.transform
+                UIView.animate(withDuration: 0.2,
+                               animations: {
+                                self.colorView.transform = self.colorView.transform.scaledBy(x: 1.3, y: 1.3)
+                },
+                               completion: { _ in
+                                UIView.animate(withDuration: 0.2) {
+                                    self.colorView.transform  = previouTransform
+                                }
+                })
+            } else {
+                // animate deselection
+            }
+        }
+    }
+}

+ 41 - 0
iOSClient/Library/PhotoEditor/ColorCollectionViewCell.xib

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12118" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12086"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="ColorCollectionViewCell" id="gTV-IL-0wX" customClass="ColorCollectionViewCell" customModule="Photo_Editor" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
+                <rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
+                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                <subviews>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ncL-uU-y1P">
+                        <rect key="frame" x="40" y="40" width="20" height="20"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="20" id="1NF-oX-6Ag"/>
+                            <constraint firstAttribute="width" constant="20" id="LcG-gU-6ME"/>
+                        </constraints>
+                    </view>
+                </subviews>
+            </view>
+            <constraints>
+                <constraint firstItem="ncL-uU-y1P" firstAttribute="centerY" secondItem="gTV-IL-0wX" secondAttribute="centerY" id="1QZ-sk-wr6"/>
+                <constraint firstItem="ncL-uU-y1P" firstAttribute="centerX" secondItem="gTV-IL-0wX" secondAttribute="centerX" id="8F5-g3-Cfh"/>
+            </constraints>
+            <size key="customSize" width="100" height="99"/>
+            <connections>
+                <outlet property="colorView" destination="ncL-uU-y1P" id="7PV-0E-mnW"/>
+            </connections>
+            <point key="canvasLocation" x="59" y="78"/>
+        </collectionViewCell>
+    </objects>
+</document>

+ 57 - 0
iOSClient/Library/PhotoEditor/ColorsCollectionViewDelegate.swift

@@ -0,0 +1,57 @@
+//
+//  ColorsCollectionViewDelegate.swift
+//  Photo Editor
+//
+//  Created by Mohamed Hamed on 5/1/17.
+//  Copyright © 2017 Mohamed Hamed. All rights reserved.
+//
+
+import UIKit
+
+class ColorsCollectionViewDelegate: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
+    
+    var colorDelegate : ColorDelegate?
+    
+    /**
+     Array of Colors that will show while drawing or typing
+     */
+    var colors = [UIColor.black,
+                  UIColor.darkGray,
+                  UIColor.gray,
+                  UIColor.lightGray,
+                  UIColor.white,
+                  UIColor.blue,
+                  UIColor.green,
+                  UIColor.red,
+                  UIColor.yellow,
+                  UIColor.orange,
+                  UIColor.purple,
+                  UIColor.cyan,
+                  UIColor.brown,
+                  UIColor.magenta]
+    
+    override init() {
+        super.init()
+    }
+    
+    var stickersViewControllerDelegate : StickersViewControllerDelegate?
+    
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return colors.count
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        colorDelegate?.didSelectColor(color: colors[indexPath.item])
+    }
+    
+    func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return 1
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        let cell  = collectionView.dequeueReusableCell(withReuseIdentifier: "ColorCollectionViewCell", for: indexPath) as! ColorCollectionViewCell
+        cell.colorView.backgroundColor = colors[indexPath.item]
+        return cell
+    }
+    
+}

+ 324 - 0
iOSClient/Library/PhotoEditor/CropRectView.swift

@@ -0,0 +1,324 @@
+
+//
+//  CropRectView.swift
+//  CropViewController
+//
+//  Created by Guilherme Moura on 2/26/16.
+//  Copyright © 2016 Reefactor, Inc. All rights reserved.
+// Credit https://github.com/sprint84/PhotoCropEditor
+
+import UIKit
+
+protocol CropRectViewDelegate: class {
+    func cropRectViewDidBeginEditing(_ view: CropRectView)
+    func cropRectViewDidChange(_ view: CropRectView)
+    func cropRectViewDidEndEditing(_ view: CropRectView)
+}
+
+class CropRectView: UIView, ResizeControlDelegate {
+    weak var delegate: CropRectViewDelegate?
+    var showsGridMajor = true {
+        didSet {
+            setNeedsDisplay()
+        }
+    }
+    var showsGridMinor = false {
+        didSet {
+            setNeedsDisplay()
+        }
+    }
+    var keepAspectRatio = false {
+        didSet {
+            if keepAspectRatio {
+                let width = bounds.width
+                let height = bounds.height
+                fixedAspectRatio = min(width / height, height / width)
+            }
+        }
+    }
+    
+    fileprivate var resizeImageView: UIImageView!
+    fileprivate let topLeftCornerView = ResizeControl()
+    fileprivate let topRightCornerView = ResizeControl()
+    fileprivate let bottomLeftCornerView = ResizeControl()
+    fileprivate let bottomRightCornerView = ResizeControl()
+    fileprivate let topEdgeView = ResizeControl()
+    fileprivate let leftEdgeView = ResizeControl()
+    fileprivate let rightEdgeView = ResizeControl()
+    fileprivate let bottomEdgeView = ResizeControl()
+    fileprivate var initialRect = CGRect.zero
+    fileprivate var fixedAspectRatio: CGFloat = 0.0
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        initialize()
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        initialize()
+    }
+    
+    fileprivate func initialize() {
+        backgroundColor = UIColor.clear
+        contentMode = .redraw
+        
+        resizeImageView = UIImageView(frame: bounds.insetBy(dx: -2.0, dy: -2.0))
+        resizeImageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+        let bundle = Bundle(for: type(of: self))
+        let image = UIImage(named: "PhotoCropEditorBorder", in: bundle, compatibleWith: nil)
+        resizeImageView.image = image?.resizableImage(withCapInsets: UIEdgeInsets(top: 23.0, left: 23.0, bottom: 23.0, right: 23.0))
+        addSubview(resizeImageView)
+        
+        topEdgeView.delegate = self
+        addSubview(topEdgeView)
+        leftEdgeView.delegate = self
+        addSubview(leftEdgeView)
+        rightEdgeView.delegate = self
+        addSubview(rightEdgeView)
+        bottomEdgeView.delegate = self
+        addSubview(bottomEdgeView)
+        
+        topLeftCornerView.delegate = self
+        addSubview(topLeftCornerView)
+        topRightCornerView.delegate = self
+        addSubview(topRightCornerView)
+        bottomLeftCornerView.delegate = self
+        addSubview(bottomLeftCornerView)
+        bottomRightCornerView.delegate = self
+        addSubview(bottomRightCornerView)
+    }
+    
+    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
+        for subview in subviews where subview is ResizeControl {
+            if subview.frame.contains(point) {
+                return subview
+            }
+        }
+        return nil
+    }
+    
+    override func draw(_ rect: CGRect) {
+        super.draw(rect)
+        
+        let width = bounds.width
+        let height = bounds.height
+        
+        for i in 0 ..< 3 {
+            let borderPadding: CGFloat = 0.5
+            
+            if showsGridMinor {
+                for j in 1 ..< 3 {
+                    UIColor(red: 1.0, green: 1.0, blue: 0.0, alpha: 0.3).set()
+                    UIRectFill(CGRect(x: round((width / 9.0) * CGFloat(j) + (width / 3.0) * CGFloat(i)), y: borderPadding, width: 1.0, height: round(height) - borderPadding * 2.0))
+                    UIRectFill(CGRect(x: borderPadding, y: round((height / 9.0) * CGFloat(j) + (height / 3.0) * CGFloat(i)), width: round(width) - borderPadding * 2.0, height: 1.0))
+                }
+            }
+            
+            if showsGridMajor {
+                if i > 0 {
+                    UIColor.white.set()
+                    UIRectFill(CGRect(x: round(CGFloat(i) * width / 3.0), y: borderPadding, width: 1.0, height: round(height) - borderPadding * 2.0))
+                    UIRectFill(CGRect(x: borderPadding, y: round(CGFloat(i) * height / 3.0), width: round(width) - borderPadding * 2.0, height: 1.0))
+                }
+            }
+        }
+    }
+    
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        
+        topLeftCornerView.frame.origin = CGPoint(x: topLeftCornerView.bounds.width / -2.0, y: topLeftCornerView.bounds.height / -2.0)
+        topRightCornerView.frame.origin = CGPoint(x: bounds.width - topRightCornerView.bounds.width - 2.0, y: topRightCornerView.bounds.height / -2.0)
+        bottomLeftCornerView.frame.origin = CGPoint(x: bottomLeftCornerView.bounds.width / -2.0, y: bounds.height - bottomLeftCornerView.bounds.height / 2.0)
+        bottomRightCornerView.frame.origin = CGPoint(x: bounds.width - bottomRightCornerView.bounds.width / 2.0, y: bounds.height - bottomRightCornerView.bounds.height / 2.0)
+        
+        topEdgeView.frame = CGRect(x: topLeftCornerView.frame.maxX, y: topEdgeView.frame.height / -2.0, width: topRightCornerView.frame.minX - topLeftCornerView.frame.maxX, height: topEdgeView.bounds.height)
+        leftEdgeView.frame = CGRect(x: leftEdgeView.frame.width / -2.0, y: topLeftCornerView.frame.maxY, width: leftEdgeView.frame.width, height: bottomLeftCornerView.frame.minY - topLeftCornerView.frame.maxY)
+        bottomEdgeView.frame = CGRect(x: bottomLeftCornerView.frame.maxX, y: bottomLeftCornerView.frame.minY, width: bottomRightCornerView.frame.minX - bottomLeftCornerView.frame.maxX, height: bottomEdgeView.frame.height)
+        rightEdgeView.frame = CGRect(x: bounds.width - rightEdgeView.frame.width / 2.0, y: topRightCornerView.frame.maxY, width: rightEdgeView.frame.width, height: bottomRightCornerView.frame.minY - topRightCornerView.frame.maxY)
+    }
+    
+    func enableResizing(_ enabled: Bool) {
+        resizeImageView.isHidden = !enabled
+        
+        topLeftCornerView.enabled = enabled
+        topRightCornerView.enabled = enabled
+        bottomLeftCornerView.enabled = enabled
+        bottomRightCornerView.enabled = enabled
+        
+        topEdgeView.enabled = enabled
+        leftEdgeView.enabled = enabled
+        bottomEdgeView.enabled = enabled
+        rightEdgeView.enabled = enabled
+    }
+
+    // MARK: - ResizeControl delegate methods
+    func resizeControlDidBeginResizing(_ control: ResizeControl) {
+        initialRect = frame
+        delegate?.cropRectViewDidBeginEditing(self)
+    }
+    
+    func resizeControlDidResize(_ control: ResizeControl) {
+        frame = cropRectWithResizeControlView(control)
+        delegate?.cropRectViewDidChange(self)
+    }
+    
+    func resizeControlDidEndResizing(_ control: ResizeControl) {
+        delegate?.cropRectViewDidEndEditing(self)
+    }
+    
+    fileprivate func cropRectWithResizeControlView(_ resizeControl: ResizeControl) -> CGRect {
+        var rect = frame
+        
+        if resizeControl == topEdgeView {
+            rect = CGRect(x: initialRect.minX,
+                          y: initialRect.minY + resizeControl.translation.y,
+                          width: initialRect.width,
+                          height: initialRect.height - resizeControl.translation.y)
+            
+            if keepAspectRatio {
+                rect = constrainedRectWithRectBasisOfHeight(rect)
+            }
+        } else if resizeControl == leftEdgeView {
+            rect = CGRect(x: initialRect.minX + resizeControl.translation.x,
+                          y: initialRect.minY,
+                          width: initialRect.width - resizeControl.translation.x,
+                          height: initialRect.height)
+            
+            if keepAspectRatio {
+                rect = constrainedRectWithRectBasisOfWidth(rect)
+            }
+        } else if resizeControl == bottomEdgeView {
+            rect = CGRect(x: initialRect.minX,
+                          y: initialRect.minY,
+                          width: initialRect.width,
+                          height: initialRect.height + resizeControl.translation.y)
+            
+            if keepAspectRatio {
+                rect = constrainedRectWithRectBasisOfHeight(rect)
+            }
+        } else if resizeControl == rightEdgeView {
+            rect = CGRect(x: initialRect.minX,
+                          y: initialRect.minY,
+                          width: initialRect.width + resizeControl.translation.x,
+                          height: initialRect.height)
+            
+            if keepAspectRatio {
+                rect = constrainedRectWithRectBasisOfWidth(rect)
+            }
+        } else if resizeControl == topLeftCornerView {
+            rect = CGRect(x: initialRect.minX + resizeControl.translation.x,
+                          y: initialRect.minY + resizeControl.translation.y,
+                          width: initialRect.width - resizeControl.translation.x,
+                          height: initialRect.height - resizeControl.translation.y)
+            
+            if keepAspectRatio {
+                var constrainedFrame: CGRect
+                if abs(resizeControl.translation.x) < abs(resizeControl.translation.y) {
+                    constrainedFrame = constrainedRectWithRectBasisOfHeight(rect)
+                } else {
+                    constrainedFrame = constrainedRectWithRectBasisOfWidth(rect)
+                }
+                constrainedFrame.origin.x -= constrainedFrame.width - rect.width
+                constrainedFrame.origin.y -= constrainedFrame.height - rect.height
+                rect = constrainedFrame
+            }
+        } else if resizeControl == topRightCornerView {
+            rect = CGRect(x: initialRect.minX,
+                          y: initialRect.minY + resizeControl.translation.y,
+                          width: initialRect.width + resizeControl.translation.x,
+                          height: initialRect.height - resizeControl.translation.y)
+            
+            if keepAspectRatio {
+                if abs(resizeControl.translation.x) < abs(resizeControl.translation.y) {
+                    rect = constrainedRectWithRectBasisOfHeight(rect)
+                } else {
+                    rect = constrainedRectWithRectBasisOfWidth(rect)
+                }
+            }
+        } else if resizeControl == bottomLeftCornerView {
+            rect = CGRect(x: initialRect.minX + resizeControl.translation.x,
+                          y: initialRect.minY,
+                          width: initialRect.width - resizeControl.translation.x,
+                          height: initialRect.height + resizeControl.translation.y)
+            
+            if keepAspectRatio {
+                var constrainedFrame: CGRect
+                if abs(resizeControl.translation.x) < abs(resizeControl.translation.y) {
+                    constrainedFrame = constrainedRectWithRectBasisOfHeight(rect)
+                } else {
+                    constrainedFrame = constrainedRectWithRectBasisOfWidth(rect)
+                }
+                constrainedFrame.origin.x -= constrainedFrame.width - rect.width
+                rect = constrainedFrame
+            }
+        } else if resizeControl == bottomRightCornerView {
+            rect = CGRect(x: initialRect.minX,
+                          y: initialRect.minY,
+                          width: initialRect.width + resizeControl.translation.x,
+                          height: initialRect.height + resizeControl.translation.y)
+            
+            if keepAspectRatio {
+                if abs(resizeControl.translation.x) < abs(resizeControl.translation.y) {
+                    rect = constrainedRectWithRectBasisOfHeight(rect)
+                } else {
+                    rect = constrainedRectWithRectBasisOfWidth(rect)
+                }
+            }
+        }
+        
+        let minWidth = leftEdgeView.bounds.width + rightEdgeView.bounds.width
+        if rect.width < minWidth {
+            rect.origin.x = frame.maxX - minWidth
+            rect.size.width = minWidth
+        }
+        
+        let minHeight = topEdgeView.bounds.height + bottomEdgeView.bounds.height
+        if rect.height < minHeight {
+            rect.origin.y = frame.maxY - minHeight
+            rect.size.height = minHeight
+        }
+        
+        if fixedAspectRatio > 0 {
+            var constraintedFrame = rect
+            if rect.width < minWidth {
+                constraintedFrame.size.width = rect.size.height * (minWidth / rect.size.width)
+            }
+            if rect.height < minHeight {
+                constraintedFrame.size.height = rect.size.width * (minHeight / rect.size.height)
+            }
+            rect = constraintedFrame
+        }
+        
+        return rect
+    }
+    
+    fileprivate func constrainedRectWithRectBasisOfWidth(_ frame: CGRect) -> CGRect {
+        var result = frame
+        let width = frame.width
+        var height = frame.height
+        
+        if width < height {
+           height = width / fixedAspectRatio
+        } else {
+            height = width * fixedAspectRatio
+        }
+        result.size = CGSize(width: width, height: height)
+        return result
+    }
+    
+    fileprivate func constrainedRectWithRectBasisOfHeight(_ frame: CGRect) -> CGRect {
+        var result = frame
+        var width = frame.width
+        let height = frame.height
+        
+        if width < height {
+            width = height * fixedAspectRatio
+        } else {
+            width = height / fixedAspectRatio
+        }
+        result.size = CGSize(width: width, height: height)
+        return result
+    }
+}

+ 489 - 0
iOSClient/Library/PhotoEditor/CropView.swift

@@ -0,0 +1,489 @@
+//
+//  CropView.swift
+//  CropViewController
+//
+//  Created by Guilherme Moura on 2/25/16.
+//  Copyright © 2016 Reefactor, Inc. All rights reserved.
+// Credit https://github.com/sprint84/PhotoCropEditor
+
+import UIKit
+import AVFoundation
+
+open class CropView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelegate, CropRectViewDelegate {
+    open var image: UIImage? {
+        didSet {
+            if image != nil {
+                imageSize = image!.size
+            }
+            imageView?.removeFromSuperview()
+            imageView = nil
+            zoomingView?.removeFromSuperview()
+            zoomingView = nil
+            setNeedsLayout()
+        }
+    }
+    open var imageView: UIView? {
+        didSet {
+            if let view = imageView , image == nil {
+                imageSize = view.frame.size
+            }
+            usingCustomImageView = true
+            setNeedsLayout()
+        }
+    }
+    open var croppedImage: UIImage? {
+        return image?.rotatedImageWithTransform(rotation, croppedToRect: zoomedCropRect())
+    }
+    open var keepAspectRatio = false {
+        didSet {
+            cropRectView.keepAspectRatio = keepAspectRatio
+        }
+    }
+    open var cropAspectRatio: CGFloat {
+        set {
+            setCropAspectRatio(newValue, shouldCenter: true)
+        }
+        get {
+            let rect = scrollView.frame
+            let width = rect.width
+            let height = rect.height
+            return width / height
+        }
+    }
+    open var rotation: CGAffineTransform {
+        guard let imgView = imageView else {
+            return CGAffineTransform.identity
+        }
+        return imgView.transform
+    }
+    open var rotationAngle: CGFloat {
+        set {
+            imageView?.transform = CGAffineTransform(rotationAngle: newValue)
+        }
+        get {
+            return atan2(rotation.b, rotation.a)
+        }
+    }
+    open var cropRect: CGRect {
+        set {
+            zoomToCropRect(newValue)
+        }
+        get {
+            return scrollView.frame
+        }
+    }
+    open var imageCropRect = CGRect.zero {
+        didSet {
+            resetCropRect()
+            
+            let scale = min(scrollView.frame.width / imageSize.width, scrollView.frame.height / imageSize.height)
+            let x = imageCropRect.minX * scale + scrollView.frame.minX
+            let y = imageCropRect.minY * scale + scrollView.frame.minY
+            let width = imageCropRect.width * scale
+            let height = imageCropRect.height * scale
+            
+            let rect = CGRect(x: x, y: y, width: width, height: height)
+            let intersection = rect.intersection(scrollView.frame)
+            
+            if !intersection.isNull {
+                cropRect = intersection
+            }
+        }
+    }
+    open var resizeEnabled = true {
+        didSet {
+            cropRectView.enableResizing(resizeEnabled)
+        }
+    }
+    open var showCroppedArea = true {
+        didSet {
+            layoutIfNeeded()
+            scrollView.clipsToBounds = !showCroppedArea
+            showOverlayView(showCroppedArea)
+        }
+    }
+    open var rotationGestureRecognizer: UIRotationGestureRecognizer!
+    fileprivate var imageSize = CGSize(width: 1.0, height: 1.0)
+    fileprivate var scrollView: UIScrollView!
+    fileprivate var zoomingView: UIView?
+    fileprivate let cropRectView = CropRectView()
+    fileprivate let topOverlayView = UIView()
+    fileprivate let leftOverlayView = UIView()
+    fileprivate let rightOverlayView = UIView()
+    fileprivate let bottomOverlayView = UIView()
+    fileprivate var insetRect = CGRect.zero
+    fileprivate var editingRect = CGRect.zero
+    fileprivate var interfaceOrientation = UIApplication.shared.statusBarOrientation
+    fileprivate var resizing = false
+    fileprivate var usingCustomImageView = false
+    fileprivate let MarginTop: CGFloat = 37.0
+    fileprivate let MarginLeft: CGFloat = 20.0
+
+    public override init(frame: CGRect) {
+        super.init(frame: frame)
+        initialize()
+    }
+    
+    public required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        initialize()
+    }
+
+    fileprivate func initialize() {
+        autoresizingMask = [.flexibleWidth, .flexibleHeight]
+        backgroundColor = UIColor.clear
+        
+        scrollView = UIScrollView(frame: bounds)
+        scrollView.delegate = self
+        scrollView.autoresizingMask = [.flexibleTopMargin, .flexibleLeftMargin, .flexibleBottomMargin, .flexibleRightMargin]
+        scrollView.backgroundColor = UIColor.clear
+        scrollView.maximumZoomScale = 20.0
+        scrollView.minimumZoomScale = 1.0
+        scrollView.showsHorizontalScrollIndicator = false
+        scrollView.showsVerticalScrollIndicator = false
+        scrollView.bounces = false
+        scrollView.bouncesZoom = false
+        scrollView.clipsToBounds =  false
+        addSubview(scrollView)
+        
+        rotationGestureRecognizer = UIRotationGestureRecognizer(target: self, action: #selector(CropView.handleRotation(_:)))
+        rotationGestureRecognizer?.delegate = self
+        scrollView.addGestureRecognizer(rotationGestureRecognizer)
+        
+        cropRectView.delegate = self
+        addSubview(cropRectView)
+        
+        showOverlayView(showCroppedArea)
+        addSubview(topOverlayView)
+        addSubview(leftOverlayView)
+        addSubview(rightOverlayView)
+        addSubview(bottomOverlayView)
+    }
+    
+    open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
+        if !isUserInteractionEnabled {
+            return nil
+        }
+        
+        if let hitView = cropRectView.hitTest(convert(point, to: cropRectView), with: event) {
+            return hitView
+        }
+        let locationInImageView = convert(point, to: zoomingView)
+        let zoomedPoint = CGPoint(x: locationInImageView.x * scrollView.zoomScale, y: locationInImageView.y * scrollView.zoomScale)
+        if zoomingView!.frame.contains(zoomedPoint) {
+            return scrollView
+        }
+        return super.hitTest(point, with: event)
+    }
+    
+    open override func layoutSubviews() {
+        super.layoutSubviews()
+        let interfaceOrientation = UIApplication.shared.statusBarOrientation
+        
+        if image == nil && imageView == nil {
+            return
+        }
+        
+        setupEditingRect()
+
+        if imageView == nil {
+            if interfaceOrientation.isPortrait {
+                insetRect = bounds.insetBy(dx: MarginLeft, dy: MarginTop)
+            } else {
+                insetRect = bounds.insetBy(dx: MarginLeft, dy: MarginLeft)
+            }
+            if !showCroppedArea {
+                insetRect = editingRect
+            }
+            setupZoomingView()
+            setupImageView()
+        } else if usingCustomImageView {
+            if interfaceOrientation.isPortrait {
+                insetRect = bounds.insetBy(dx: MarginLeft, dy: MarginTop)
+            } else {
+                insetRect = bounds.insetBy(dx: MarginLeft, dy: MarginLeft)
+            }
+            if !showCroppedArea {
+                insetRect = editingRect
+            }
+            setupZoomingView()
+            imageView?.frame = zoomingView!.bounds
+            zoomingView?.addSubview(imageView!)
+            usingCustomImageView = false
+        }
+        
+        if !resizing {
+            layoutCropRectViewWithCropRect(scrollView.frame)
+            if self.interfaceOrientation != interfaceOrientation {
+                zoomToCropRect(scrollView.frame)
+            }
+        }
+        
+        
+        self.interfaceOrientation = interfaceOrientation
+    }
+    
+    open func setRotationAngle(_ rotationAngle: CGFloat, snap: Bool) {
+        var rotation = rotationAngle
+        if snap {
+            rotation = nearbyint(rotationAngle / CGFloat(Double.pi/2)) * CGFloat(Double.pi/2)
+        }
+        self.rotationAngle = rotation
+    }
+    
+    open func resetCropRect() {
+        resetCropRectAnimated(false)
+    }
+    
+    open func resetCropRectAnimated(_ animated: Bool) {
+        if animated {
+            UIView.beginAnimations(nil, context: nil)
+            UIView.setAnimationDuration(0.25)
+            UIView.setAnimationBeginsFromCurrentState(true)
+        }
+        imageView?.transform = CGAffineTransform.identity
+        let contentSize = scrollView.contentSize
+        let initialRect = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
+        scrollView.zoom(to: initialRect, animated: false)
+        
+        layoutCropRectViewWithCropRect(scrollView.bounds)
+        
+        if animated {
+            UIView.commitAnimations()
+        }
+    }
+    
+    open func zoomedCropRect() -> CGRect {
+        let cropRect = convert(scrollView.frame, to: zoomingView)
+        var ratio: CGFloat = 1.0
+        let orientation = UIApplication.shared.statusBarOrientation
+        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad || orientation.isPortrait) {
+            ratio = AVMakeRect(aspectRatio: imageSize, insideRect: insetRect).width / imageSize.width
+        } else {
+            ratio = AVMakeRect(aspectRatio: imageSize, insideRect: insetRect).height / imageSize.height
+        }
+        
+        let zoomedCropRect = CGRect(x: cropRect.origin.x / ratio,
+            y: cropRect.origin.y / ratio,
+            width: cropRect.size.width / ratio,
+            height: cropRect.size.height / ratio)
+        
+        return zoomedCropRect
+    }
+    
+    open func croppedImage(_ image: UIImage) -> UIImage {
+        imageSize = image.size
+        return image.rotatedImageWithTransform(rotation, croppedToRect: zoomedCropRect())
+    }
+    
+    @objc func handleRotation(_ gestureRecognizer: UIRotationGestureRecognizer) {
+        if let imageView = imageView {
+            let rotation = gestureRecognizer.rotation
+            let transform = imageView.transform.rotated(by: rotation)
+            imageView.transform = transform
+            gestureRecognizer.rotation = 0.0
+        }
+        
+        switch gestureRecognizer.state {
+        case .began, .changed:
+            cropRectView.showsGridMinor = true
+        default:
+            cropRectView.showsGridMinor = false
+        }
+    }
+    
+    // MARK: - Private methods
+    fileprivate func showOverlayView(_ show: Bool) {
+        let color = show ? UIColor(white: 0.0, alpha: 0.4) : UIColor.clear
+        
+        topOverlayView.backgroundColor = color
+        leftOverlayView.backgroundColor = color
+        rightOverlayView.backgroundColor = color
+        bottomOverlayView.backgroundColor = color
+    }
+    
+    fileprivate func setupEditingRect() {
+        let interfaceOrientation = UIApplication.shared.statusBarOrientation
+        if interfaceOrientation.isPortrait {
+            editingRect = bounds.insetBy(dx: MarginLeft, dy: MarginTop)
+        } else {
+            editingRect = bounds.insetBy(dx: MarginLeft, dy: MarginLeft)
+        }
+        if !showCroppedArea {
+            editingRect = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height)
+        }
+    }
+    
+    fileprivate func setupZoomingView() {
+        let cropRect = AVMakeRect(aspectRatio: imageSize, insideRect: insetRect)
+        
+        scrollView.frame = cropRect
+        scrollView.contentSize = cropRect.size
+        
+        zoomingView = UIView(frame: scrollView.bounds)
+        zoomingView?.backgroundColor = .clear
+        scrollView.addSubview(zoomingView!)
+    }
+
+    fileprivate func setupImageView() {
+        let imageView = UIImageView(frame: zoomingView!.bounds)
+        imageView.backgroundColor = .clear
+        imageView.contentMode = .scaleAspectFit
+        imageView.image = image
+        zoomingView?.addSubview(imageView)
+        self.imageView = imageView
+        usingCustomImageView = false
+    }
+    
+    fileprivate func layoutCropRectViewWithCropRect(_ cropRect: CGRect) {
+        cropRectView.frame = cropRect
+        layoutOverlayViewsWithCropRect(cropRect)
+    }
+    
+    fileprivate func layoutOverlayViewsWithCropRect(_ cropRect: CGRect) {
+        topOverlayView.frame = CGRect(x: 0, y: 0, width: bounds.width, height: cropRect.minY)
+        leftOverlayView.frame = CGRect(x: 0, y: cropRect.minY, width: cropRect.minX, height: cropRect.height)
+        rightOverlayView.frame = CGRect(x: cropRect.maxX, y: cropRect.minY, width: bounds.width - cropRect.maxX, height: cropRect.height)
+        bottomOverlayView.frame = CGRect(x: 0, y: cropRect.maxY, width: bounds.width, height: bounds.height - cropRect.maxY)
+    }
+    
+    fileprivate func zoomToCropRect(_ toRect: CGRect) {
+        zoomToCropRect(toRect, shouldCenter: false, animated: true)
+    }
+    
+    fileprivate func zoomToCropRect(_ toRect: CGRect, shouldCenter: Bool, animated: Bool, completion: (() -> Void)? = nil) {
+        if scrollView.frame.equalTo(toRect) {
+            return
+        }
+        
+        let width = toRect.width
+        let height = toRect.height
+        let scale = min(editingRect.width / width, editingRect.height / height)
+        
+        let scaledWidth = width * scale
+        let scaledHeight = height * scale
+        let cropRect = CGRect(x: (bounds.width - scaledWidth) / 2.0, y: (bounds.height - scaledHeight) / 2.0, width: scaledWidth, height: scaledHeight)
+        
+        var zoomRect = convert(toRect, to: zoomingView)
+        zoomRect.size.width = cropRect.width / (scrollView.zoomScale * scale)
+        zoomRect.size.height = cropRect.height / (scrollView.zoomScale * scale)
+        
+        if let imgView = imageView , shouldCenter {
+            let imageViewBounds = imgView.bounds
+            zoomRect.origin.x = (imageViewBounds.width / 2.0) - (zoomRect.width / 2.0)
+            zoomRect.origin.y = (imageViewBounds.height / 2.0) - (zoomRect.height / 2.0)
+        }
+        
+        var duration = 0.0
+        if animated {
+            duration = 0.25
+        }
+        
+        UIView.animate(withDuration: duration, delay: 0.0, options: .beginFromCurrentState, animations: { [unowned self] in
+            self.scrollView.bounds = cropRect
+            self.scrollView.zoom(to: zoomRect, animated: false)
+            self.layoutCropRectViewWithCropRect(cropRect)
+        }) { finished in
+            completion?()
+        }
+    }
+    
+    fileprivate func cappedCropRectInImageRectWithCropRectView(_ cropRectView: CropRectView) -> CGRect {
+        var cropRect = cropRectView.frame
+        
+        let rect = convert(cropRect, to: scrollView)
+        if rect.minX < zoomingView!.frame.minX {
+            cropRect.origin.x = scrollView.convert(zoomingView!.frame, to: self).minX
+            let cappedWidth = rect.maxX
+            let height = !keepAspectRatio ? cropRect.size.height : cropRect.size.height * (cappedWidth / cropRect.size.width)
+            cropRect.size = CGSize(width: cappedWidth, height: height)
+        }
+        
+        if rect.minY < zoomingView!.frame.minY {
+            cropRect.origin.y = scrollView.convert(zoomingView!.frame, to: self).minY
+            let cappedHeight = rect.maxY
+            let width = !keepAspectRatio ? cropRect.size.width : cropRect.size.width * (cappedHeight / cropRect.size.height)
+            cropRect.size = CGSize(width: width, height: cappedHeight)
+        }
+        
+        if rect.maxX > zoomingView!.frame.maxX {
+            let cappedWidth = scrollView.convert(zoomingView!.frame, to: self).maxX - cropRect.minX
+            let height = !keepAspectRatio ? cropRect.size.height : cropRect.size.height * (cappedWidth / cropRect.size.width)
+            cropRect.size = CGSize(width: cappedWidth, height: height)
+        }
+        
+        if rect.maxY > zoomingView!.frame.maxY {
+            let cappedHeight = scrollView.convert(zoomingView!.frame, to: self).maxY - cropRect.minY
+            let width = !keepAspectRatio ? cropRect.size.width : cropRect.size.width * (cappedHeight / cropRect.size.height)
+            cropRect.size = CGSize(width: width, height: cappedHeight)
+        }
+        
+        return cropRect
+    }
+    
+    fileprivate func automaticZoomIfEdgeTouched(_ cropRect: CGRect) {
+        if cropRect.minX < editingRect.minX - 5.0 ||
+            cropRect.maxX > editingRect.maxX + 5.0 ||
+            cropRect.minY < editingRect.minY - 5.0 ||
+            cropRect.maxY > editingRect.maxY + 5.0 {
+                UIView.animate(withDuration: 1.0, delay: 0.0, options: .beginFromCurrentState, animations: { [unowned self] in
+                    self.zoomToCropRect(self.cropRectView.frame)
+                    }, completion: nil)
+        }
+    }
+    
+    fileprivate func setCropAspectRatio(_ ratio: CGFloat, shouldCenter: Bool) {
+        var cropRect = scrollView.frame
+        var width = cropRect.width
+        var height = cropRect.height
+        if ratio <= 1.0 {
+            width = height * ratio
+            if width > imageView!.bounds.width {
+                width = cropRect.width
+                height = width / ratio
+            }
+        } else {
+            height = width / ratio
+            if height > imageView!.bounds.height {
+                height = cropRect.height
+                width = height * ratio
+            }
+        }
+        cropRect.size = CGSize(width: width, height: height)
+        zoomToCropRect(cropRect, shouldCenter: shouldCenter, animated: false) {
+            let scale = self.scrollView.zoomScale
+            self.scrollView.minimumZoomScale = scale
+        }
+    }
+    
+    // MARK: - CropView delegate methods
+    func cropRectViewDidBeginEditing(_ view: CropRectView) {
+        resizing = true
+    }
+    
+    func cropRectViewDidChange(_ view: CropRectView) {
+        let cropRect = cappedCropRectInImageRectWithCropRectView(view)
+        layoutCropRectViewWithCropRect(cropRect)
+        automaticZoomIfEdgeTouched(cropRect)
+    }
+    
+    func cropRectViewDidEndEditing(_ view: CropRectView) {
+        resizing = false
+        zoomToCropRect(cropRectView.frame)
+    }
+    
+    // MARK: - ScrollView delegate methods
+    open func viewForZooming(in scrollView: UIScrollView) -> UIView? {
+        return zoomingView
+    }
+    
+    open func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
+        let contentOffset = scrollView.contentOffset
+        targetContentOffset.pointee = contentOffset
+    }
+    
+    // MARK: - Gesture Recognizer delegate methods
+    open func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+        return true
+    }
+}

+ 247 - 0
iOSClient/Library/PhotoEditor/CropViewController.swift

@@ -0,0 +1,247 @@
+//
+//  CropViewController.swift
+//  CropViewController
+//
+//  Created by Guilherme Moura on 2/25/16.
+//  Copyright © 2016 Reefactor, Inc. All rights reserved.
+// Credit https://github.com/sprint84/PhotoCropEditor
+
+import UIKit
+
+public protocol CropViewControllerDelegate: class {
+    func cropViewController(_ controller: CropViewController, didFinishCroppingImage image: UIImage, transform: CGAffineTransform, cropRect: CGRect)
+    func cropViewControllerDidCancel(_ controller: CropViewController)
+}
+
+open class CropViewController: UIViewController {
+    open weak var delegate: CropViewControllerDelegate?
+    open var image: UIImage? {
+        didSet {
+            cropView?.image = image
+        }
+    }
+    open var keepAspectRatio = false {
+        didSet {
+            cropView?.keepAspectRatio = keepAspectRatio
+        }
+    }
+    open var cropAspectRatio: CGFloat = 0.0 {
+        didSet {
+            cropView?.cropAspectRatio = cropAspectRatio
+        }
+    }
+    open var cropRect = CGRect.zero {
+        didSet {
+            adjustCropRect()
+        }
+    }
+    open var imageCropRect = CGRect.zero {
+        didSet {
+            cropView?.imageCropRect = imageCropRect
+        }
+    }
+    open var toolbarHidden = false
+    open var rotationEnabled = false {
+        didSet {
+            cropView?.rotationGestureRecognizer.isEnabled = rotationEnabled
+        }
+    }
+    open var rotationTransform: CGAffineTransform {
+        return cropView!.rotation
+    }
+    open var zoomedCropRect: CGRect {
+        return cropView!.zoomedCropRect()
+    }
+
+    fileprivate var cropView: CropView?
+    
+    public required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        initialize()
+    }
+    
+    public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
+        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
+        initialize()
+    }
+    
+    fileprivate func initialize() {
+        rotationEnabled = true
+    }
+    
+    open override func loadView() {
+        let contentView = UIView()
+        contentView.autoresizingMask = .flexibleWidth
+        contentView.backgroundColor = UIColor.black
+        view = contentView
+        
+        // Add CropView
+        cropView = CropView(frame: contentView.bounds)
+        contentView.addSubview(cropView!)
+        
+    }
+
+    open override func viewDidLoad() {
+        super.viewDidLoad()
+
+        navigationController?.navigationBar.isTranslucent = false
+        navigationController?.toolbar.isTranslucent = false
+        navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(CropViewController.cancel(_:)))
+        navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(CropViewController.done(_:)))
+        
+        if self.toolbarItems == nil {
+            let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
+            let constrainButton = UIBarButtonItem(title: "Constrain", style: .plain, target: self, action: #selector(CropViewController.constrain(_:)))
+            toolbarItems = [flexibleSpace, constrainButton, flexibleSpace]
+        }
+        
+        navigationController?.isToolbarHidden = toolbarHidden
+        
+        cropView?.image = image
+        cropView?.rotationGestureRecognizer.isEnabled = rotationEnabled
+    }
+    
+    open override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+        
+        if cropAspectRatio != 0 {
+            cropView?.cropAspectRatio = cropAspectRatio
+        }
+        
+        if !cropRect.equalTo(CGRect.zero) {
+            adjustCropRect()
+        }
+        
+        if !imageCropRect.equalTo(CGRect.zero) {
+            cropView?.imageCropRect = imageCropRect
+        }
+        
+        cropView?.keepAspectRatio = keepAspectRatio
+    }
+    
+    open func resetCropRect() {
+        cropView?.resetCropRect()
+    }
+    
+    open func resetCropRectAnimated(_ animated: Bool) {
+        cropView?.resetCropRectAnimated(animated)
+    }
+    
+    @objc func cancel(_ sender: UIBarButtonItem) {
+        delegate?.cropViewControllerDidCancel(self)
+    }
+    
+    @objc func done(_ sender: UIBarButtonItem) {
+        if let image = cropView?.croppedImage {
+            guard let rotation = cropView?.rotation else {
+                return
+            }
+            guard let rect = cropView?.zoomedCropRect() else {
+                return
+            }
+            delegate?.cropViewController(self, didFinishCroppingImage: image, transform: rotation, cropRect: rect)
+        }
+    }
+    
+    @objc func constrain(_ sender: UIBarButtonItem) {
+        let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
+        let original = UIAlertAction(title: "Original", style: .default) { [unowned self] action in
+            guard let image = self.cropView?.image else {
+                return
+            }
+            guard var cropRect = self.cropView?.cropRect else {
+                return
+            }
+            let width = image.size.width
+            let height = image.size.height
+            let ratio: CGFloat
+            if width < height {
+                ratio = width / height
+                cropRect.size = CGSize(width: cropRect.height * ratio, height: cropRect.height)
+            } else {
+                ratio = height / width
+                cropRect.size = CGSize(width: cropRect.width, height: cropRect.width * ratio)
+            }
+            self.cropView?.cropRect = cropRect
+        }
+        actionSheet.addAction(original)
+        let square = UIAlertAction(title: "Square", style: .default) { [unowned self] action in
+            let ratio: CGFloat = 1.0
+//            self.cropView?.cropAspectRatio = ratio
+            if var cropRect = self.cropView?.cropRect {
+                let width = cropRect.width
+                cropRect.size = CGSize(width: width, height: width * ratio)
+                self.cropView?.cropRect = cropRect
+            }
+        }
+        actionSheet.addAction(square)
+        let threeByTwo = UIAlertAction(title: "3 x 2", style: .default) { [unowned self] action in
+            self.cropView?.cropAspectRatio = 2.0 / 3.0
+        }
+        actionSheet.addAction(threeByTwo)
+        let threeByFive = UIAlertAction(title: "3 x 5", style: .default) { [unowned self] action in
+            self.cropView?.cropAspectRatio = 3.0 / 5.0
+        }
+        actionSheet.addAction(threeByFive)
+        let fourByThree = UIAlertAction(title: "4 x 3", style: .default) { [unowned self] action in
+            let ratio: CGFloat = 3.0 / 4.0
+            if var cropRect = self.cropView?.cropRect {
+                let width = cropRect.width
+                cropRect.size = CGSize(width: width, height: width * ratio)
+                self.cropView?.cropRect = cropRect
+            }
+        }
+        actionSheet.addAction(fourByThree)
+        let fourBySix = UIAlertAction(title: "4 x 6", style: .default) { [unowned self] action in
+            self.cropView?.cropAspectRatio = 4.0 / 6.0
+        }
+        actionSheet.addAction(fourBySix)
+        let fiveBySeven = UIAlertAction(title: "5 x 7", style: .default) { [unowned self] action in
+            self.cropView?.cropAspectRatio = 5.0 / 7.0
+        }
+        actionSheet.addAction(fiveBySeven)
+        let eightByTen = UIAlertAction(title: "8 x 10", style: .default) { [unowned self] action in
+            self.cropView?.cropAspectRatio = 8.0 / 10.0
+        }
+        actionSheet.addAction(eightByTen)
+        let widescreen = UIAlertAction(title: "16 x 9", style: .default) { [unowned self] action in
+            let ratio: CGFloat = 9.0 / 16.0
+            if var cropRect = self.cropView?.cropRect {
+                let width = cropRect.width
+                cropRect.size = CGSize(width: width, height: width * ratio)
+                self.cropView?.cropRect = cropRect
+            }
+        }
+        actionSheet.addAction(widescreen)
+        let cancel = UIAlertAction(title: "Cancel", style: .default) { [unowned self] action in
+            self.dismiss(animated: true, completion: nil)
+        }
+        actionSheet.addAction(cancel)
+        
+        if let popoverController = actionSheet.popoverPresentationController {
+            popoverController.barButtonItem = sender
+        }
+        
+        present(actionSheet, animated: true, completion: nil)
+    }
+
+    // MARK: - Private methods
+    fileprivate func adjustCropRect() {
+        imageCropRect = CGRect.zero
+        
+        guard var cropViewCropRect = cropView?.cropRect else {
+            return
+        }
+        cropViewCropRect.origin.x += cropRect.origin.x
+        cropViewCropRect.origin.y += cropRect.origin.y
+        
+        let minWidth = min(cropViewCropRect.maxX - cropViewCropRect.minX, cropRect.width)
+        let minHeight = min(cropViewCropRect.maxY - cropViewCropRect.minY, cropRect.height)
+        let size = CGSize(width: minWidth, height: minHeight)
+        cropViewCropRect.size = size
+        cropView?.cropRect = cropViewCropRect
+    }
+    
+    
+
+}

+ 19 - 0
iOSClient/Library/PhotoEditor/EmojiCollectionViewCell.swift

@@ -0,0 +1,19 @@
+//
+//  EmojiCollectionViewCell.swift
+//  Photo Editor
+//
+//  Created by Mohamed Hamed on 4/30/17.
+//  Copyright © 2017 Mohamed Hamed. All rights reserved.
+//
+
+import UIKit
+
+class EmojiCollectionViewCell: UICollectionViewCell {
+
+    @IBOutlet weak var emojiLabel: UILabel!
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+    }
+
+}

+ 39 - 0
iOSClient/Library/PhotoEditor/EmojiCollectionViewCell.xib

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12120" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="EmojiCollectionViewCell" id="gTV-IL-0wX" customClass="EmojiCollectionViewCell" customModule="iOSPhotoEditor" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="71" height="69"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
+                <rect key="frame" x="0.0" y="0.0" width="71" height="69"/>
+                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                <subviews>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="😂" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HtF-AX-oQb">
+                        <rect key="frame" x="3" y="-1.5" width="65" height="72.5"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="60"/>
+                        <nil key="textColor"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                </subviews>
+            </view>
+            <constraints>
+                <constraint firstItem="HtF-AX-oQb" firstAttribute="centerY" secondItem="gTV-IL-0wX" secondAttribute="centerY" id="aKq-g2-ZSA"/>
+                <constraint firstItem="HtF-AX-oQb" firstAttribute="centerX" secondItem="gTV-IL-0wX" secondAttribute="centerX" id="eLO-Xr-JQg"/>
+            </constraints>
+            <size key="customSize" width="71" height="69"/>
+            <connections>
+                <outlet property="emojiLabel" destination="HtF-AX-oQb" id="Q1y-jj-Bup"/>
+            </connections>
+            <point key="canvasLocation" x="44.5" y="90.5"/>
+        </collectionViewCell>
+    </objects>
+</document>

+ 64 - 0
iOSClient/Library/PhotoEditor/EmojisCollectionViewDelegate.swift

@@ -0,0 +1,64 @@
+//
+//  EmojisCollectionViewDelegate.swift
+//  Photo Editor
+//
+//  Created by Mohamed Hamed on 4/30/17.
+//  Copyright © 2017 Mohamed Hamed. All rights reserved.
+//
+
+import UIKit
+
+class EmojisCollectionViewDelegate: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
+
+    var stickersViewControllerDelegate : StickersViewControllerDelegate?
+
+    let emojiRanges = [
+        0x1F601...0x1F64F, // emoticons
+        0x1F30D...0x1F567, // Other additional symbols
+        0x1F680...0x1F6C0, // Transport and map symbols
+        0x1F681...0x1F6C5 //Additional transport and map symbols
+    ]
+    
+    var emojis: [String] = []
+    
+    override init() {
+        super.init()
+        
+        for range in emojiRanges {
+            for i in range {
+                let c = String(describing: UnicodeScalar(i)!)
+                emojis.append(c)
+            }
+        }
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return emojis.count
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        let emojiLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 90, height: 90))
+        emojiLabel.textAlignment = .center
+        emojiLabel.text = emojis[indexPath.item]
+        emojiLabel.font = UIFont.systemFont(ofSize: 70)
+        stickersViewControllerDelegate?.didSelectView(view: emojiLabel)
+    }
+    
+    func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return 1
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        let cell  = collectionView.dequeueReusableCell(withReuseIdentifier: "EmojiCollectionViewCell", for: indexPath) as! EmojiCollectionViewCell
+        cell.emojiLabel.text = emojis[indexPath.item]
+        return cell
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
+        return 4
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
+        return 0
+    }
+}

+ 35 - 0
iOSClient/Library/PhotoEditor/GradientView.swift

@@ -0,0 +1,35 @@
+//
+//  GradientView.swift
+//  Photo Editor
+//
+//  Created by Mohamed Hamed on 4/11/17.
+//  Copyright © 2017 Mohamed Hamed. All rights reserved.
+//
+
+import UIKit
+
+class GradientView: UIView {
+    
+    @IBInspectable public var gradientFromtop: Bool = true
+    
+    var gradientLayer = CAGradientLayer()
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        if gradientFromtop == false {
+            gradientLayer.colors = [UIColor.clear.cgColor, UIColor(white: 0.0, alpha: 0.5).cgColor]
+        } else {
+            gradientLayer.colors = [UIColor(white: 0.0, alpha: 0.5).cgColor, UIColor.clear.cgColor]
+        }
+
+        gradientLayer.locations = [NSNumber(value: 0.0 as Float), NSNumber(value: 1.0 as Float)]
+        backgroundColor = UIColor.clear
+        layer.addSublayer(gradientLayer)
+    }
+    
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        gradientLayer.frame = bounds
+    }
+    
+}

BIN
iOSClient/Library/PhotoEditor/PhotoCropEditorBorder.png


BIN
iOSClient/Library/PhotoEditor/PhotoCropEditorBorder@2x.png


BIN
iOSClient/Library/PhotoEditor/PhotoCropEditorBorder@3x.png


+ 139 - 0
iOSClient/Library/PhotoEditor/PhotoEditor+Controls.swift

@@ -0,0 +1,139 @@
+//
+//  PhotoEditor+Controls.swift
+//  Pods
+//
+//  Created by Mohamed Hamed on 6/16/17.
+//
+//
+
+import Foundation
+import UIKit
+
+// MARK: - Control
+public enum control {
+    case crop
+    case sticker
+    case draw
+    case text
+    case save
+    case share
+    case clear
+}
+
+extension PhotoEditorViewController {
+
+     //MARK: Top Toolbar
+    
+    @IBAction func cancelButtonTapped(_ sender: Any) {
+        photoEditorDelegate?.canceledEditing()
+        self.dismiss(animated: true, completion: nil)
+    }
+
+    @IBAction func cropButtonTapped(_ sender: UIButton) {
+        let controller = CropViewController()
+        controller.delegate = self
+        controller.image = image
+        let navController = UINavigationController(rootViewController: controller)
+        present(navController, animated: true, completion: nil)
+    }
+
+    @IBAction func stickersButtonTapped(_ sender: Any) {
+        addStickersViewController()
+    }
+
+    @IBAction func drawButtonTapped(_ sender: Any) {
+        isDrawing = true
+        canvasImageView.isUserInteractionEnabled = false
+        doneButton.isHidden = false
+        colorPickerView.isHidden = false
+        hideToolbar(hide: true)
+    }
+
+    @IBAction func textButtonTapped(_ sender: Any) {
+        isTyping = true
+        let textView = UITextView(frame: CGRect(x: 0, y: canvasImageView.center.y,
+                                                width: UIScreen.main.bounds.width, height: 30))
+        
+        textView.textAlignment = .center
+        textView.font = UIFont(name: "Helvetica", size: 30)
+        textView.textColor = textColor
+        textView.layer.shadowColor = UIColor.black.cgColor
+        textView.layer.shadowOffset = CGSize(width: 1.0, height: 0.0)
+        textView.layer.shadowOpacity = 0.2
+        textView.layer.shadowRadius = 1.0
+        textView.layer.backgroundColor = UIColor.clear.cgColor
+        textView.autocorrectionType = .no
+        textView.isScrollEnabled = false
+        textView.delegate = self
+        self.canvasImageView.addSubview(textView)
+        addGestures(view: textView)
+        textView.becomeFirstResponder()
+    }    
+    
+    @IBAction func doneButtonTapped(_ sender: Any) {
+        view.endEditing(true)
+        doneButton.isHidden = true
+        colorPickerView.isHidden = true
+        canvasImageView.isUserInteractionEnabled = true
+        hideToolbar(hide: false)
+        isDrawing = false
+    }
+    
+    //MARK: Bottom Toolbar
+    
+    @IBAction func saveButtonTapped(_ sender: AnyObject) {
+        UIImageWriteToSavedPhotosAlbum(canvasView.toImage(),self, #selector(PhotoEditorViewController.image(_:withPotentialError:contextInfo:)), nil)
+    }
+    
+    @IBAction func shareButtonTapped(_ sender: UIButton) {
+        let activity = UIActivityViewController(activityItems: [canvasView.toImage()], applicationActivities: nil)
+        present(activity, animated: true, completion: nil)
+        
+    }
+    
+    @IBAction func clearButtonTapped(_ sender: AnyObject) {
+        //clear drawing
+        canvasImageView.image = nil
+        //clear stickers and textviews
+        for subview in canvasImageView.subviews {
+            subview.removeFromSuperview()
+        }
+    }
+    
+    @IBAction func continueButtonPressed(_ sender: Any) {
+        let img = self.canvasView.toImage()
+        photoEditorDelegate?.doneEditing(image: img)
+        self.dismiss(animated: true, completion: nil)
+    }
+
+    //MAKR: helper methods
+    
+    @objc func image(_ image: UIImage, withPotentialError error: NSErrorPointer, contextInfo: UnsafeRawPointer) {
+        let alert = UIAlertController(title: "Image Saved", message: "Image successfully saved to Photos library", preferredStyle: UIAlertController.Style.alert)
+        alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
+        self.present(alert, animated: true, completion: nil)
+    }
+    
+    func hideControls() {
+        for control in hiddenControls {
+            switch control {
+                
+            case .clear:
+                clearButton.isHidden = true
+            case .crop:
+                cropButton.isHidden = true
+            case .draw:
+                drawButton.isHidden = true
+            case .save:
+                saveButton.isHidden = true
+            case .share:
+                shareButton.isHidden = true
+            case .sticker:
+                stickerButton.isHidden = true
+            case .text:
+                stickerButton.isHidden = true
+            }
+        }
+    }
+    
+}

+ 24 - 0
iOSClient/Library/PhotoEditor/PhotoEditor+Crop.swift

@@ -0,0 +1,24 @@
+//
+//  PhotoEditor+Crop.swift
+//  Pods
+//
+//  Created by Mohamed Hamed on 6/16/17.
+//
+//
+
+import Foundation
+import UIKit
+
+// MARK: - CropView
+extension PhotoEditorViewController: CropViewControllerDelegate {
+    
+    public func cropViewController(_ controller: CropViewController, didFinishCroppingImage image: UIImage, transform: CGAffineTransform, cropRect: CGRect) {
+        controller.dismiss(animated: true, completion: nil)
+        self.setImageView(image: image)
+    }
+    
+    public func cropViewControllerDidCancel(_ controller: CropViewController) {
+        controller.dismiss(animated: true, completion: nil)
+    }
+    
+}

+ 80 - 0
iOSClient/Library/PhotoEditor/PhotoEditor+Drawing.swift

@@ -0,0 +1,80 @@
+//
+//  PhotoEditor+Drawing.swift
+//  Photo Editor
+//
+//  Created by Mohamed Hamed on 6/16/17.
+//
+//
+import UIKit
+
+extension PhotoEditorViewController {
+    
+    override public func touchesBegan(_ touches: Set<UITouch>,
+                                      with event: UIEvent?){
+        if isDrawing {
+            swiped = false
+            if let touch = touches.first {
+                lastPoint = touch.location(in: self.canvasImageView)
+            }
+        }
+            //Hide stickersVC if clicked outside it
+        else if stickersVCIsVisible == true {
+            if let touch = touches.first {
+                let location = touch.location(in: self.view)
+                if !stickersViewController.view.frame.contains(location) {
+                    removeStickersView()
+                }
+            }
+        }
+        
+    }
+    
+    override public func touchesMoved(_ touches: Set<UITouch>,
+                                      with event: UIEvent?){
+        if isDrawing {
+            // 6
+            swiped = true
+            if let touch = touches.first {
+                let currentPoint = touch.location(in: canvasImageView)
+                drawLineFrom(lastPoint, toPoint: currentPoint)
+                
+                // 7
+                lastPoint = currentPoint
+            }
+        }
+    }
+    
+    override public func touchesEnded(_ touches: Set<UITouch>,
+                                      with event: UIEvent?){
+        if isDrawing {
+            if !swiped {
+                // draw a single point
+                drawLineFrom(lastPoint, toPoint: lastPoint)
+            }
+        }
+        
+    }
+    
+    func drawLineFrom(_ fromPoint: CGPoint, toPoint: CGPoint) {
+        // 1
+        let canvasSize = canvasImageView.frame.integral.size
+        UIGraphicsBeginImageContextWithOptions(canvasSize, false, 0)
+        if let context = UIGraphicsGetCurrentContext() {
+            canvasImageView.image?.draw(in: CGRect(x: 0, y: 0, width: canvasSize.width, height: canvasSize.height))
+            // 2
+            context.move(to: CGPoint(x: fromPoint.x, y: fromPoint.y))
+            context.addLine(to: CGPoint(x: toPoint.x, y: toPoint.y))
+            // 3
+            context.setLineCap( CGLineCap.round)
+            context.setLineWidth(5.0)
+            context.setStrokeColor(drawColor.cgColor)
+            context.setBlendMode( CGBlendMode.normal)
+            // 4
+            context.strokePath()
+            // 5
+            canvasImageView.image = UIGraphicsGetImageFromCurrentImageContext()
+        }
+        UIGraphicsEndImageContext()
+    }
+    
+}

+ 28 - 0
iOSClient/Library/PhotoEditor/PhotoEditor+Font.swift

@@ -0,0 +1,28 @@
+//
+//  PhotoEditor+Font.swift
+//
+//
+//  Created by Mohamed Hamed on 6/16/17.
+//
+//
+
+import Foundation
+import UIKit
+
+extension PhotoEditorViewController {
+    
+    //Resources don't load in main bundle we have to register the font
+    func registerFont(){
+        let bundle = Bundle(for: PhotoEditorViewController.self)
+        let url =  bundle.url(forResource: "icomoon", withExtension: "ttf")
+        
+        guard let fontDataProvider = CGDataProvider(url: url! as CFURL) else {
+            return
+        }
+        guard let font = CGFont(fontDataProvider) else {return}
+        var error: Unmanaged<CFError>?
+        guard CTFontManagerRegisterGraphicsFont(font, &error) else {
+            return
+        }
+    }
+}

+ 238 - 0
iOSClient/Library/PhotoEditor/PhotoEditor+Gestures.swift

@@ -0,0 +1,238 @@
+//
+//  PhotoEditor+Gestures.swift
+//  Photo Editor
+//
+//  Created by Mohamed Hamed on 6/16/17.
+//
+//
+
+import Foundation
+
+
+import UIKit
+
+extension PhotoEditorViewController : UIGestureRecognizerDelegate  {
+    
+    /**
+     UIPanGestureRecognizer - Moving Objects
+     Selecting transparent parts of the imageview won't move the object
+     */
+    @objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
+        if let view = recognizer.view {
+            if view is UIImageView {
+                //Tap only on visible parts on the image
+                if recognizer.state == .began {
+                    for imageView in subImageViews(view: canvasImageView) {
+                        let location = recognizer.location(in: imageView)
+                        let alpha = imageView.alphaAtPoint(location)
+                        if alpha > 0 {
+                            imageViewToPan = imageView
+                            break
+                        }
+                    }
+                }
+                if imageViewToPan != nil {
+                    moveView(view: imageViewToPan!, recognizer: recognizer)
+                }
+            } else {
+                moveView(view: view, recognizer: recognizer)
+            }
+        }
+    }
+    
+    /**
+     UIPinchGestureRecognizer - Pinching Objects
+     If it's a UITextView will make the font bigger so it doen't look pixlated
+     */
+    @objc func pinchGesture(_ recognizer: UIPinchGestureRecognizer) {
+        if let view = recognizer.view {
+            if view is UITextView {
+                let textView = view as! UITextView
+                
+                if textView.font!.pointSize * recognizer.scale < 90 {
+                    let font = UIFont(name: textView.font!.fontName, size: textView.font!.pointSize * recognizer.scale)
+                    textView.font = font
+                    let sizeToFit = textView.sizeThatFits(CGSize(width: UIScreen.main.bounds.size.width,
+                                                                 height:CGFloat.greatestFiniteMagnitude))
+                    textView.bounds.size = CGSize(width: textView.intrinsicContentSize.width,
+                                                  height: sizeToFit.height)
+                } else {
+                    let sizeToFit = textView.sizeThatFits(CGSize(width: UIScreen.main.bounds.size.width,
+                                                                 height:CGFloat.greatestFiniteMagnitude))
+                    textView.bounds.size = CGSize(width: textView.intrinsicContentSize.width,
+                                                  height: sizeToFit.height)
+                }
+                
+                
+                textView.setNeedsDisplay()
+            } else {
+                view.transform = view.transform.scaledBy(x: recognizer.scale, y: recognizer.scale)
+            }
+            recognizer.scale = 1
+        }
+    }
+    
+    /**
+     UIRotationGestureRecognizer - Rotating Objects
+     */
+    @objc func rotationGesture(_ recognizer: UIRotationGestureRecognizer) {
+        if let view = recognizer.view {
+            view.transform = view.transform.rotated(by: recognizer.rotation)
+            recognizer.rotation = 0
+        }
+    }
+    
+    /**
+     UITapGestureRecognizer - Taping on Objects
+     Will make scale scale Effect
+     Selecting transparent parts of the imageview won't move the object
+     */
+    @objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
+        if let view = recognizer.view {
+            if view is UIImageView {
+                //Tap only on visible parts on the image
+                for imageView in subImageViews(view: canvasImageView) {
+                    let location = recognizer.location(in: imageView)
+                    let alpha = imageView.alphaAtPoint(location)
+                    if alpha > 0 {
+                        scaleEffect(view: imageView)
+                        break
+                    }
+                }
+            } else {
+                scaleEffect(view: view)
+            }
+        }
+    }
+    
+    /*
+     Support Multiple Gesture at the same time
+     */
+    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+        return true
+    }
+    
+    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+        return false
+    }
+    
+    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+        return false
+    }
+    
+    @objc func screenEdgeSwiped(_ recognizer: UIScreenEdgePanGestureRecognizer) {
+        if recognizer.state == .recognized {
+            if !stickersVCIsVisible {
+                addStickersViewController()
+            }
+        }
+    }
+    
+    // to Override Control Center screen edge pan from bottom
+    override public var prefersStatusBarHidden: Bool {
+        return true
+    }
+    
+    /**
+     Scale Effect
+     */
+    func scaleEffect(view: UIView) {
+        view.superview?.bringSubviewToFront(view)
+        
+        if #available(iOS 10.0, *) {
+            let generator = UIImpactFeedbackGenerator(style: .heavy)
+            generator.impactOccurred()
+        }
+        let previouTransform =  view.transform
+        UIView.animate(withDuration: 0.2,
+                       animations: {
+                        view.transform = view.transform.scaledBy(x: 1.2, y: 1.2)
+        },
+                       completion: { _ in
+                        UIView.animate(withDuration: 0.2) {
+                            view.transform  = previouTransform
+                        }
+        })
+    }
+    
+    /**
+     Moving Objects 
+     delete the view if it's inside the delete view
+     Snap the view back if it's out of the canvas
+     */
+
+    func moveView(view: UIView, recognizer: UIPanGestureRecognizer)  {
+        
+        hideToolbar(hide: true)
+        deleteView.isHidden = false
+        
+        view.superview?.bringSubviewToFront(view)
+        let pointToSuperView = recognizer.location(in: self.view)
+
+        view.center = CGPoint(x: view.center.x + recognizer.translation(in: canvasImageView).x,
+                              y: view.center.y + recognizer.translation(in: canvasImageView).y)
+        
+        recognizer.setTranslation(CGPoint.zero, in: canvasImageView)
+        
+        if let previousPoint = lastPanPoint {
+            //View is going into deleteView
+            if deleteView.frame.contains(pointToSuperView) && !deleteView.frame.contains(previousPoint) {
+                if #available(iOS 10.0, *) {
+                    let generator = UIImpactFeedbackGenerator(style: .heavy)
+                    generator.impactOccurred()
+                }
+                UIView.animate(withDuration: 0.3, animations: {
+                    view.transform = view.transform.scaledBy(x: 0.25, y: 0.25)
+                    view.center = recognizer.location(in: self.canvasImageView)
+                })
+            }
+                //View is going out of deleteView
+            else if deleteView.frame.contains(previousPoint) && !deleteView.frame.contains(pointToSuperView) {
+                //Scale to original Size
+                UIView.animate(withDuration: 0.3, animations: {
+                    view.transform = view.transform.scaledBy(x: 4, y: 4)
+                    view.center = recognizer.location(in: self.canvasImageView)
+                })
+            }
+        }
+        lastPanPoint = pointToSuperView
+        
+        if recognizer.state == .ended {
+            imageViewToPan = nil
+            lastPanPoint = nil
+
+            if isTyping == true {
+                hideToolbar(hide: true)
+            } else {
+                hideToolbar(hide: false)
+            }
+            
+            deleteView.isHidden = true
+
+            let point = recognizer.location(in: self.view)
+            
+            if deleteView.frame.contains(point) { // Delete the view
+                view.removeFromSuperview()
+                if #available(iOS 10.0, *) {
+                    let generator = UINotificationFeedbackGenerator()
+                    generator.notificationOccurred(.success)
+                }
+            } else if !canvasImageView.bounds.contains(view.center) { //Snap the view back to canvasImageView
+                UIView.animate(withDuration: 0.3, animations: {
+                    view.center = self.canvasImageView.center
+                })
+                
+            }
+        }
+    }
+    
+    func subImageViews(view: UIView) -> [UIImageView] {
+        var imageviews: [UIImageView] = []
+        for imageView in view.subviews {
+            if imageView is UIImageView {
+                imageviews.append(imageView as! UIImageView)
+            }
+        }
+        return imageviews
+    }
+}

+ 48 - 0
iOSClient/Library/PhotoEditor/PhotoEditor+Keyboard.swift

@@ -0,0 +1,48 @@
+//
+//  PhotoEditor+Keyboard.swift
+//  Pods
+//
+//  Created by Mohamed Hamed on 6/16/17.
+//
+//
+
+import Foundation
+import UIKit
+
+extension PhotoEditorViewController {
+    
+    @objc func keyboardDidShow(notification: NSNotification) {
+        if isTyping {
+            doneButton.isHidden = false
+            colorPickerView.isHidden = false
+            hideToolbar(hide: true)
+        }
+    }
+    
+    @objc func keyboardWillHide(notification: NSNotification) {
+        isTyping = false
+        doneButton.isHidden = true
+        hideToolbar(hide: false)
+    }
+    
+    @objc func keyboardWillChangeFrame(_ notification: NSNotification) {
+        if let userInfo = notification.userInfo {
+            let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
+            let duration:TimeInterval = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
+            let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
+            let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
+            let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
+            if (endFrame?.origin.y)! >= UIScreen.main.bounds.size.height {
+                self.colorPickerViewBottomConstraint?.constant = 0.0
+            } else {
+                self.colorPickerViewBottomConstraint?.constant = endFrame?.size.height ?? 0.0
+            }
+            UIView.animate(withDuration: duration,
+                           delay: TimeInterval(0),
+                           options: animationCurve,
+                           animations: { self.view.layoutIfNeeded() },
+                           completion: nil)
+        }
+    }
+
+}

+ 104 - 0
iOSClient/Library/PhotoEditor/PhotoEditor+StickersViewController.swift

@@ -0,0 +1,104 @@
+//
+//  PhotoEditor+StickersViewController.swift
+//  Pods
+//
+//  Created by Mohamed Hamed on 6/16/17.
+//
+//
+
+import Foundation
+import UIKit
+
+extension PhotoEditorViewController {
+    
+    func addStickersViewController() {
+        stickersVCIsVisible = true
+        hideToolbar(hide: true)
+        self.canvasImageView.isUserInteractionEnabled = false
+        stickersViewController.stickersViewControllerDelegate = self
+        
+        for image in self.stickers {
+            stickersViewController.stickers.append(image)
+        }
+        self.addChild(stickersViewController)
+        self.view.addSubview(stickersViewController.view)
+        stickersViewController.didMove(toParent: self)
+        let height = view.frame.height
+        let width  = view.frame.width
+        stickersViewController.view.frame = CGRect(x: 0, y: self.view.frame.maxY , width: width, height: height)
+    }
+    
+    func removeStickersView() {
+        stickersVCIsVisible = false
+        self.canvasImageView.isUserInteractionEnabled = true
+        UIView.animate(withDuration: 0.3,
+                       delay: 0,
+                       options: UIView.AnimationOptions.curveEaseIn,
+                       animations: { () -> Void in
+                        var frame = self.stickersViewController.view.frame
+                        frame.origin.y = UIScreen.main.bounds.maxY
+                        self.stickersViewController.view.frame = frame
+                        
+        }, completion: { (finished) -> Void in
+            self.stickersViewController.view.removeFromSuperview()
+            self.stickersViewController.removeFromParent()
+            self.hideToolbar(hide: false)
+        })
+    }    
+}
+
+extension PhotoEditorViewController: StickersViewControllerDelegate {
+    
+    func didSelectView(view: UIView) {
+        self.removeStickersView()
+        
+        view.center = canvasImageView.center
+        self.canvasImageView.addSubview(view)
+        //Gestures
+        addGestures(view: view)
+    }
+    
+    func didSelectImage(image: UIImage) {
+        self.removeStickersView()
+        
+        let imageView = UIImageView(image: image)
+        imageView.contentMode = .scaleAspectFit
+        imageView.frame.size = CGSize(width: 150, height: 150)
+        imageView.center = canvasImageView.center
+        
+        self.canvasImageView.addSubview(imageView)
+        //Gestures
+        addGestures(view: imageView)
+    }
+    
+    func stickersViewDidDisappear() {
+        stickersVCIsVisible = false
+        hideToolbar(hide: false)
+    }
+    
+    func addGestures(view: UIView) {
+        //Gestures
+        view.isUserInteractionEnabled = true
+        
+        let panGesture = UIPanGestureRecognizer(target: self,
+                                                action: #selector(PhotoEditorViewController.panGesture))
+        panGesture.minimumNumberOfTouches = 1
+        panGesture.maximumNumberOfTouches = 1
+        panGesture.delegate = self
+        view.addGestureRecognizer(panGesture)
+        
+        let pinchGesture = UIPinchGestureRecognizer(target: self,
+                                                    action: #selector(PhotoEditorViewController.pinchGesture))
+        pinchGesture.delegate = self
+        view.addGestureRecognizer(pinchGesture)
+        
+        let rotationGestureRecognizer = UIRotationGestureRecognizer(target: self,
+                                                                    action:#selector(PhotoEditorViewController.rotationGesture) )
+        rotationGestureRecognizer.delegate = self
+        view.addGestureRecognizer(rotationGestureRecognizer)
+        
+        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(PhotoEditorViewController.tapGesture))
+        view.addGestureRecognizer(tapGesture)
+        
+    }
+}

+ 53 - 0
iOSClient/Library/PhotoEditor/PhotoEditor+UITextView.swift

@@ -0,0 +1,53 @@
+//
+//  PhotoEditor+UITextView.swift
+//  Pods
+//
+//  Created by Mohamed Hamed on 6/16/17.
+//
+//
+
+import Foundation
+import UIKit
+
+extension PhotoEditorViewController: UITextViewDelegate {
+    
+    public func textViewDidChange(_ textView: UITextView) {
+        let rotation = atan2(textView.transform.b, textView.transform.a)
+        if rotation == 0 {
+            let oldFrame = textView.frame
+            let sizeToFit = textView.sizeThatFits(CGSize(width: oldFrame.width, height:CGFloat.greatestFiniteMagnitude))
+            textView.frame.size = CGSize(width: oldFrame.width, height: sizeToFit.height)
+        }
+    }
+    public func textViewDidBeginEditing(_ textView: UITextView) {
+        isTyping = true
+        lastTextViewTransform =  textView.transform
+        lastTextViewTransCenter = textView.center
+        lastTextViewFont = textView.font!
+        activeTextView = textView
+        textView.superview?.bringSubviewToFront(textView)
+        textView.font = UIFont(name: "Helvetica", size: 30)
+        UIView.animate(withDuration: 0.3,
+                       animations: {
+                        textView.transform = CGAffineTransform.identity
+                        textView.center = CGPoint(x: UIScreen.main.bounds.width / 2,
+                                                  y:  UIScreen.main.bounds.height / 5)
+        }, completion: nil)
+        
+    }
+    
+    public func textViewDidEndEditing(_ textView: UITextView) {
+        guard lastTextViewTransform != nil && lastTextViewTransCenter != nil && lastTextViewFont != nil
+            else {
+                return
+        }
+        activeTextView = nil
+        textView.font = self.lastTextViewFont!
+        UIView.animate(withDuration: 0.3,
+                       animations: {
+                        textView.transform = self.lastTextViewTransform!
+                        textView.center = self.lastTextViewTransCenter!
+        }, completion: nil)
+    }
+    
+}

+ 156 - 0
iOSClient/Library/PhotoEditor/PhotoEditorViewController.swift

@@ -0,0 +1,156 @@
+//
+//  ViewController.swift
+//  Photo Editor
+//
+//  Created by Mohamed Hamed on 4/23/17.
+//  Copyright © 2017 Mohamed Hamed. All rights reserved.
+//
+
+import UIKit
+
+public final class PhotoEditorViewController: UIViewController {
+    
+    /** holding the 2 imageViews original image and drawing & stickers */
+    @IBOutlet weak var canvasView: UIView!
+    //To hold the image
+    @IBOutlet var imageView: UIImageView!
+    @IBOutlet weak var imageViewHeightConstraint: NSLayoutConstraint!
+    //To hold the drawings and stickers
+    @IBOutlet weak var canvasImageView: UIImageView!
+
+    @IBOutlet weak var topToolbar: UIView!
+    @IBOutlet weak var bottomToolbar: UIView!
+
+    @IBOutlet weak var topGradient: UIView!
+    @IBOutlet weak var bottomGradient: UIView!
+    
+    @IBOutlet weak var doneButton: UIButton!
+    @IBOutlet weak var deleteView: UIView!
+    @IBOutlet weak var colorsCollectionView: UICollectionView!
+    @IBOutlet weak var colorPickerView: UIView!
+    @IBOutlet weak var colorPickerViewBottomConstraint: NSLayoutConstraint!
+    
+    //Controls
+    @IBOutlet weak var cropButton: UIButton!
+    @IBOutlet weak var stickerButton: UIButton!
+    @IBOutlet weak var drawButton: UIButton!
+    @IBOutlet weak var textButton: UIButton!
+    @IBOutlet weak var saveButton: UIButton!
+    @IBOutlet weak var shareButton: UIButton!
+    @IBOutlet weak var clearButton: UIButton!
+    
+    public var image: UIImage?
+    /**
+     Array of Stickers -UIImage- that the user will choose from
+     */
+    public var stickers : [UIImage] = []
+    /**
+     Array of Colors that will show while drawing or typing
+     */
+    public var colors  : [UIColor] = []
+    
+    public var photoEditorDelegate: PhotoEditorDelegate?
+    var colorsCollectionViewDelegate: ColorsCollectionViewDelegate!
+    
+    // list of controls to be hidden
+    public var hiddenControls : [control] = []
+    
+    var stickersVCIsVisible = false
+    var drawColor: UIColor = UIColor.black
+    var textColor: UIColor = UIColor.white
+    var isDrawing: Bool = false
+    var lastPoint: CGPoint!
+    var swiped = false
+    var lastPanPoint: CGPoint?
+    var lastTextViewTransform: CGAffineTransform?
+    var lastTextViewTransCenter: CGPoint?
+    var lastTextViewFont:UIFont?
+    var activeTextView: UITextView?
+    var imageViewToPan: UIImageView?
+    var isTyping: Bool = false
+    
+    
+    var stickersViewController: StickersViewController!
+
+    //Register Custom font before we load XIB
+    public override func loadView() {
+        registerFont()
+        super.loadView()
+    }
+    
+    override public func viewDidLoad() {
+        super.viewDidLoad()
+        self.setImageView(image: image!)
+        
+        deleteView.layer.cornerRadius = deleteView.bounds.height / 2
+        deleteView.layer.borderWidth = 2.0
+        deleteView.layer.borderColor = UIColor.white.cgColor
+        deleteView.clipsToBounds = true
+        
+        let edgePan = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(screenEdgeSwiped))
+        edgePan.edges = .bottom
+        edgePan.delegate = self
+        self.view.addGestureRecognizer(edgePan)
+        
+        NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow),
+                                               name: UIResponder.keyboardDidShowNotification, object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide),
+                                               name: UIResponder.keyboardWillHideNotification, object: nil)
+        NotificationCenter.default.addObserver(self,selector: #selector(keyboardWillChangeFrame(_:)),
+                                               name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
+        
+        
+        configureCollectionView()
+        stickersViewController = StickersViewController(nibName: "StickersViewController", bundle: Bundle(for: StickersViewController.self))
+        hideControls()
+    }
+    
+    func configureCollectionView() {
+        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
+        layout.itemSize = CGSize(width: 30, height: 30)
+        layout.scrollDirection = .horizontal
+        layout.minimumInteritemSpacing = 0
+        layout.minimumLineSpacing = 0
+        colorsCollectionView.collectionViewLayout = layout
+        colorsCollectionViewDelegate = ColorsCollectionViewDelegate()
+        colorsCollectionViewDelegate.colorDelegate = self
+        if !colors.isEmpty {
+            colorsCollectionViewDelegate.colors = colors
+        }
+        colorsCollectionView.delegate = colorsCollectionViewDelegate
+        colorsCollectionView.dataSource = colorsCollectionViewDelegate
+        
+        colorsCollectionView.register(
+            UINib(nibName: "ColorCollectionViewCell", bundle: Bundle(for: ColorCollectionViewCell.self)),
+            forCellWithReuseIdentifier: "ColorCollectionViewCell")
+    }
+    
+    func setImageView(image: UIImage) {
+        imageView.image = image
+        let size = image.suitableSize(widthLimit: UIScreen.main.bounds.width)
+        imageViewHeightConstraint.constant = (size?.height)!
+    }
+    
+    func hideToolbar(hide: Bool) {
+        topToolbar.isHidden = hide
+        topGradient.isHidden = hide
+        bottomToolbar.isHidden = hide
+        bottomGradient.isHidden = hide
+    }
+}
+
+extension PhotoEditorViewController: ColorDelegate {
+    func didSelectColor(color: UIColor) {
+        if isDrawing {
+            self.drawColor = color
+        } else if activeTextView != nil {
+            activeTextView?.textColor = color
+            textColor = color
+        }
+    }
+}
+
+
+
+
+

+ 328 - 0
iOSClient/Library/PhotoEditor/PhotoEditorViewController.xib

@@ -0,0 +1,328 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_5" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <customFonts key="customFonts">
+        <array key="icomoon.ttf">
+            <string>icomoon</string>
+        </array>
+    </customFonts>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="PhotoEditorViewController" customModule="iOSPhotoEditor" customModuleProvider="target">
+            <connections>
+                <outlet property="bottomGradient" destination="ZGa-1B-gkO" id="pYn-g8-ak7"/>
+                <outlet property="bottomToolbar" destination="87h-1G-t5q" id="Pzi-Dw-qOq"/>
+                <outlet property="canvasImageView" destination="n3p-yR-4xc" id="OED-1L-YMz"/>
+                <outlet property="canvasView" destination="S1Y-ZS-dct" id="p6I-kg-UUU"/>
+                <outlet property="clearButton" destination="Kpe-Ez-Cio" id="C0i-3L-kLI"/>
+                <outlet property="colorPickerView" destination="Cy8-ap-fH9" id="KJx-KY-rfM"/>
+                <outlet property="colorPickerViewBottomConstraint" destination="mok-x4-xHA" id="SKq-N5-bcn"/>
+                <outlet property="colorsCollectionView" destination="GaQ-XR-asZ" id="1rf-hU-0nn"/>
+                <outlet property="cropButton" destination="Ghm-bq-BmY" id="f2D-02-o1D"/>
+                <outlet property="deleteView" destination="yAt-sK-1nK" id="0Jn-1A-WAT"/>
+                <outlet property="doneButton" destination="KxU-y4-Jwh" id="19z-FV-eWC"/>
+                <outlet property="drawButton" destination="Qm4-f6-DBr" id="yHl-a1-GgN"/>
+                <outlet property="imageView" destination="5rw-9v-ExQ" id="5lP-en-DGc"/>
+                <outlet property="imageViewHeightConstraint" destination="DdY-qb-dfp" id="ZY9-Em-cs4"/>
+                <outlet property="saveButton" destination="mW4-Jz-sic" id="1Uj-nU-EU1"/>
+                <outlet property="shareButton" destination="zlw-H0-WcJ" id="OZv-z0-8s8"/>
+                <outlet property="stickerButton" destination="PLX-iy-2Rc" id="tzt-tW-R1r"/>
+                <outlet property="textButton" destination="0wH-LJ-SyD" id="ofN-bp-ZaE"/>
+                <outlet property="topGradient" destination="rLz-36-0xz" id="0Yd-em-MMy"/>
+                <outlet property="topToolbar" destination="3MS-N5-3xY" id="kzY-Ga-SoQ"/>
+                <outlet property="view" destination="HPk-Bg-V0q" id="eF4-Ac-Nav"/>
+            </connections>
+        </placeholder>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="HPk-Bg-V0q">
+            <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="S1Y-ZS-dct" userLabel="canvas">
+                    <rect key="frame" x="0.0" y="114.66666666666669" width="414" height="667"/>
+                    <subviews>
+                        <imageView contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="5rw-9v-ExQ">
+                            <rect key="frame" x="0.0" y="0.0" width="414" height="667"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="667" id="DdY-qb-dfp"/>
+                            </constraints>
+                        </imageView>
+                        <imageView contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="n3p-yR-4xc">
+                            <rect key="frame" x="0.0" y="0.0" width="414" height="667"/>
+                        </imageView>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                    <constraints>
+                        <constraint firstItem="n3p-yR-4xc" firstAttribute="centerY" secondItem="5rw-9v-ExQ" secondAttribute="centerY" id="0QX-a7-mia"/>
+                        <constraint firstItem="5rw-9v-ExQ" firstAttribute="centerX" secondItem="S1Y-ZS-dct" secondAttribute="centerX" id="2oa-57-UUp"/>
+                        <constraint firstAttribute="trailing" secondItem="5rw-9v-ExQ" secondAttribute="trailing" id="EPV-Q8-h8f"/>
+                        <constraint firstItem="n3p-yR-4xc" firstAttribute="centerX" secondItem="5rw-9v-ExQ" secondAttribute="centerX" id="Her-VZ-Dem"/>
+                        <constraint firstItem="n3p-yR-4xc" firstAttribute="height" secondItem="5rw-9v-ExQ" secondAttribute="height" id="JzM-Mx-Dfz"/>
+                        <constraint firstItem="5rw-9v-ExQ" firstAttribute="centerY" secondItem="S1Y-ZS-dct" secondAttribute="centerY" id="KyP-cQ-CLv"/>
+                        <constraint firstItem="5rw-9v-ExQ" firstAttribute="top" secondItem="S1Y-ZS-dct" secondAttribute="top" priority="750" id="asb-iT-RHl"/>
+                        <constraint firstAttribute="bottom" secondItem="5rw-9v-ExQ" secondAttribute="bottom" priority="750" id="gN9-UZ-cen"/>
+                        <constraint firstItem="n3p-yR-4xc" firstAttribute="width" secondItem="5rw-9v-ExQ" secondAttribute="width" id="n8M-D5-EAA"/>
+                        <constraint firstItem="5rw-9v-ExQ" firstAttribute="leading" secondItem="S1Y-ZS-dct" secondAttribute="leading" id="v73-ro-BkR"/>
+                    </constraints>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rLz-36-0xz" customClass="GradientView" customModule="iOSPhotoEditor" customModuleProvider="target">
+                    <rect key="frame" x="0.0" y="44" width="414" height="60"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="60" id="SZk-dl-fAI"/>
+                    </constraints>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3MS-N5-3xY">
+                    <rect key="frame" x="0.0" y="44" width="414" height="60"/>
+                    <subviews>
+                        <stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="15" translatesAutoresizingMaskIntoConstraints="NO" id="Doi-2Z-r5q">
+                            <rect key="frame" x="237" y="0.0" width="165" height="60"/>
+                            <subviews>
+                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ghm-bq-BmY">
+                                    <rect key="frame" x="0.0" y="0.0" width="30" height="60"/>
+                                    <fontDescription key="fontDescription" name="icomoon" family="icomoon" pointSize="25"/>
+                                    <size key="titleShadowOffset" width="1" height="0.0"/>
+                                    <state key="normal" title="">
+                                        <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                        <color key="titleShadowColor" red="0.0" green="0.0" blue="0.0" alpha="0.1532266695" colorSpace="calibratedRGB"/>
+                                    </state>
+                                    <connections>
+                                        <action selector="cropButtonTapped:" destination="-1" eventType="touchUpInside" id="fa9-jd-guk"/>
+                                    </connections>
+                                </button>
+                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="PLX-iy-2Rc">
+                                    <rect key="frame" x="45" y="0.0" width="30" height="60"/>
+                                    <fontDescription key="fontDescription" name="icomoon" family="icomoon" pointSize="25"/>
+                                    <size key="titleShadowOffset" width="1" height="0.0"/>
+                                    <state key="normal" title="">
+                                        <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                        <color key="titleShadowColor" red="0.0" green="0.0" blue="0.0" alpha="0.1532266695" colorSpace="calibratedRGB"/>
+                                    </state>
+                                    <connections>
+                                        <action selector="stickersButtonTapped:" destination="-1" eventType="touchUpInside" id="snI-Px-Ngx"/>
+                                    </connections>
+                                </button>
+                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Qm4-f6-DBr">
+                                    <rect key="frame" x="90" y="0.0" width="30" height="60"/>
+                                    <fontDescription key="fontDescription" name="icomoon" family="icomoon" pointSize="25"/>
+                                    <size key="titleShadowOffset" width="1" height="0.0"/>
+                                    <state key="normal" title="">
+                                        <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                        <color key="titleShadowColor" red="0.0" green="0.0" blue="0.0" alpha="0.1532266695" colorSpace="calibratedRGB"/>
+                                    </state>
+                                    <connections>
+                                        <action selector="drawButtonTapped:" destination="-1" eventType="touchUpInside" id="uts-mM-XHI"/>
+                                    </connections>
+                                </button>
+                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0wH-LJ-SyD">
+                                    <rect key="frame" x="135" y="0.0" width="30" height="60"/>
+                                    <fontDescription key="fontDescription" name="icomoon" family="icomoon" pointSize="25"/>
+                                    <size key="titleShadowOffset" width="1" height="0.0"/>
+                                    <state key="normal" title="">
+                                        <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                        <color key="titleShadowColor" red="0.0" green="0.0" blue="0.0" alpha="0.1532266695" colorSpace="calibratedRGB"/>
+                                    </state>
+                                    <connections>
+                                        <action selector="textButtonTapped:" destination="-1" eventType="touchUpInside" id="qcG-gq-62j"/>
+                                    </connections>
+                                </button>
+                            </subviews>
+                        </stackView>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9an-1W-thw">
+                            <rect key="frame" x="12" y="11.666666666666664" width="30" height="37"/>
+                            <fontDescription key="fontDescription" name="icomoon" family="icomoon" pointSize="25"/>
+                            <size key="titleShadowOffset" width="1" height="0.0"/>
+                            <state key="normal" title="">
+                                <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                <color key="titleShadowColor" red="0.0" green="0.0" blue="0.0" alpha="0.1532266695" colorSpace="calibratedRGB"/>
+                            </state>
+                            <connections>
+                                <action selector="cancelButtonTapped:" destination="-1" eventType="touchUpInside" id="jpW-r4-cpQ"/>
+                            </connections>
+                        </button>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                    <constraints>
+                        <constraint firstItem="9an-1W-thw" firstAttribute="centerY" secondItem="3MS-N5-3xY" secondAttribute="centerY" id="1bY-xX-lhc"/>
+                        <constraint firstAttribute="bottom" secondItem="Doi-2Z-r5q" secondAttribute="bottom" id="5UH-pm-VlP"/>
+                        <constraint firstAttribute="trailing" secondItem="Doi-2Z-r5q" secondAttribute="trailing" constant="12" id="Ank-Rs-hHo"/>
+                        <constraint firstAttribute="height" constant="60" id="RSV-Dw-76F"/>
+                        <constraint firstItem="Doi-2Z-r5q" firstAttribute="top" secondItem="3MS-N5-3xY" secondAttribute="top" id="YMJ-5J-baj"/>
+                        <constraint firstItem="9an-1W-thw" firstAttribute="leading" secondItem="3MS-N5-3xY" secondAttribute="leading" constant="12" id="lPL-cU-BVr"/>
+                    </constraints>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ZGa-1B-gkO" customClass="GradientView" customModule="iOSPhotoEditor" customModuleProvider="target">
+                    <rect key="frame" x="0.0" y="782" width="414" height="80"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="80" id="OFW-hK-G1P"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="boolean" keyPath="gradientFromtop" value="NO"/>
+                    </userDefinedRuntimeAttributes>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="87h-1G-t5q">
+                    <rect key="frame" x="0.0" y="802" width="414" height="60"/>
+                    <subviews>
+                        <stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="15" translatesAutoresizingMaskIntoConstraints="NO" id="ZRl-89-gRO">
+                            <rect key="frame" x="12" y="0.0" width="120" height="52"/>
+                            <subviews>
+                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mW4-Jz-sic">
+                                    <rect key="frame" x="0.0" y="0.0" width="30" height="52"/>
+                                    <fontDescription key="fontDescription" name="icomoon" family="icomoon" pointSize="25"/>
+                                    <size key="titleShadowOffset" width="1" height="0.0"/>
+                                    <state key="normal" title="">
+                                        <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                        <color key="titleShadowColor" red="0.0" green="0.0" blue="0.0" alpha="0.1532266695" colorSpace="calibratedRGB"/>
+                                    </state>
+                                    <connections>
+                                        <action selector="saveButtonTapped:" destination="-1" eventType="touchUpInside" id="Sc7-Zy-r4v"/>
+                                    </connections>
+                                </button>
+                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="zlw-H0-WcJ">
+                                    <rect key="frame" x="45" y="0.0" width="30" height="52"/>
+                                    <fontDescription key="fontDescription" name="icomoon" family="icomoon" pointSize="25"/>
+                                    <size key="titleShadowOffset" width="1" height="0.0"/>
+                                    <state key="normal" title="">
+                                        <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                        <color key="titleShadowColor" red="0.0" green="0.0" blue="0.0" alpha="0.1532266695" colorSpace="calibratedRGB"/>
+                                    </state>
+                                    <connections>
+                                        <action selector="shareButtonTapped:" destination="-1" eventType="touchUpInside" id="ZTG-wV-jfh"/>
+                                    </connections>
+                                </button>
+                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Kpe-Ez-Cio">
+                                    <rect key="frame" x="90" y="0.0" width="30" height="52"/>
+                                    <fontDescription key="fontDescription" name="icomoon" family="icomoon" pointSize="25"/>
+                                    <size key="titleShadowOffset" width="1" height="0.0"/>
+                                    <state key="normal" title="">
+                                        <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                        <color key="titleShadowColor" red="0.0" green="0.0" blue="0.0" alpha="0.1532266695" colorSpace="calibratedRGB"/>
+                                    </state>
+                                    <connections>
+                                        <action selector="clearButtonTapped:" destination="-1" eventType="touchUpInside" id="gXm-sD-poT"/>
+                                    </connections>
+                                </button>
+                            </subviews>
+                        </stackView>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wNH-TU-KxG">
+                            <rect key="frame" x="352" y="-14" width="50" height="62"/>
+                            <fontDescription key="fontDescription" name="icomoon" family="icomoon" pointSize="50"/>
+                            <size key="titleShadowOffset" width="1" height="0.0"/>
+                            <state key="normal" title="">
+                                <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                <color key="titleShadowColor" red="0.0" green="0.0" blue="0.0" alpha="0.1532266695" colorSpace="calibratedRGB"/>
+                            </state>
+                            <connections>
+                                <action selector="continueButtonPressed:" destination="-1" eventType="touchUpInside" id="0k9-Sl-9QG"/>
+                            </connections>
+                        </button>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="60" id="7QB-HT-iOq"/>
+                        <constraint firstAttribute="bottom" secondItem="ZRl-89-gRO" secondAttribute="bottom" constant="8" id="Bdt-cg-dLR"/>
+                        <constraint firstAttribute="bottom" secondItem="wNH-TU-KxG" secondAttribute="bottom" constant="12" id="MHF-pQ-8R0"/>
+                        <constraint firstAttribute="trailing" secondItem="wNH-TU-KxG" secondAttribute="trailing" constant="12" id="dUX-Z1-0Ev"/>
+                        <constraint firstItem="ZRl-89-gRO" firstAttribute="top" secondItem="87h-1G-t5q" secondAttribute="top" id="jMd-Og-yCn"/>
+                        <constraint firstItem="ZRl-89-gRO" firstAttribute="leading" secondItem="87h-1G-t5q" secondAttribute="leading" constant="12" id="mn5-Hu-M5A"/>
+                    </constraints>
+                </view>
+                <button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="KxU-y4-Jwh">
+                    <rect key="frame" x="362" y="60" width="40" height="32"/>
+                    <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
+                    <size key="titleShadowOffset" width="1" height="0.0"/>
+                    <state key="normal" title="Done">
+                        <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                        <color key="titleShadowColor" red="0.0" green="0.0" blue="0.0" alpha="0.1532266695" colorSpace="calibratedRGB"/>
+                    </state>
+                    <connections>
+                        <action selector="doneButtonTapped:" destination="-1" eventType="touchUpInside" id="Nk8-0N-Id9"/>
+                    </connections>
+                </button>
+                <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yAt-sK-1nK">
+                    <rect key="frame" x="182" y="800" width="50" height="50"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sqh-uS-U1m">
+                            <rect key="frame" x="9.6666666666666572" y="10" width="31" height="30"/>
+                            <fontDescription key="fontDescription" name="icomoon" family="icomoon" pointSize="30"/>
+                            <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                            <nil key="highlightedColor"/>
+                            <color key="shadowColor" red="0.0" green="0.0" blue="0.0" alpha="0.1532266695" colorSpace="calibratedRGB"/>
+                            <size key="shadowOffset" width="1" height="0.0"/>
+                        </label>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="50" id="Cbh-qN-Nr4"/>
+                        <constraint firstItem="sqh-uS-U1m" firstAttribute="centerY" secondItem="yAt-sK-1nK" secondAttribute="centerY" id="ZDd-OE-AiH"/>
+                        <constraint firstAttribute="height" constant="50" id="iud-MD-vZ2"/>
+                        <constraint firstItem="sqh-uS-U1m" firstAttribute="centerX" secondItem="yAt-sK-1nK" secondAttribute="centerX" id="vx8-XT-NVx"/>
+                    </constraints>
+                </view>
+                <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Cy8-ap-fH9" userLabel="Color Picker">
+                    <rect key="frame" x="0.0" y="846" width="414" height="50"/>
+                    <subviews>
+                        <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="GaQ-XR-asZ">
+                            <rect key="frame" x="0.0" y="0.0" width="414" height="40"/>
+                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="40" id="25X-Kn-avj"/>
+                            </constraints>
+                            <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="7jl-DS-5B7">
+                                <size key="itemSize" width="50" height="50"/>
+                                <size key="headerReferenceSize" width="0.0" height="0.0"/>
+                                <size key="footerReferenceSize" width="0.0" height="0.0"/>
+                                <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
+                            </collectionViewFlowLayout>
+                            <cells/>
+                        </collectionView>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                    <constraints>
+                        <constraint firstItem="GaQ-XR-asZ" firstAttribute="leading" secondItem="Cy8-ap-fH9" secondAttribute="leading" id="5uh-Qw-RZB"/>
+                        <constraint firstAttribute="height" constant="50" id="KBh-Ry-wrN"/>
+                        <constraint firstItem="GaQ-XR-asZ" firstAttribute="top" secondItem="Cy8-ap-fH9" secondAttribute="top" id="LEF-vO-2nq"/>
+                        <constraint firstAttribute="trailing" secondItem="GaQ-XR-asZ" secondAttribute="trailing" id="ZAL-PE-sPi"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
+            <constraints>
+                <constraint firstItem="87h-1G-t5q" firstAttribute="leading" secondItem="HPk-Bg-V0q" secondAttribute="leading" id="0ft-JY-szV"/>
+                <constraint firstItem="KxU-y4-Jwh" firstAttribute="top" secondItem="ymm-th-TTo" secondAttribute="top" constant="16" id="1QI-yc-Gke"/>
+                <constraint firstItem="Cy8-ap-fH9" firstAttribute="centerX" secondItem="HPk-Bg-V0q" secondAttribute="centerX" id="24Z-2f-Tyk"/>
+                <constraint firstItem="S1Y-ZS-dct" firstAttribute="leading" secondItem="HPk-Bg-V0q" secondAttribute="leading" id="2tC-2e-oA6"/>
+                <constraint firstItem="Cy8-ap-fH9" firstAttribute="leading" secondItem="HPk-Bg-V0q" secondAttribute="leading" id="6eK-mF-0OL"/>
+                <constraint firstItem="3MS-N5-3xY" firstAttribute="top" secondItem="ymm-th-TTo" secondAttribute="top" id="Fj6-PW-wbF"/>
+                <constraint firstAttribute="trailing" secondItem="3MS-N5-3xY" secondAttribute="trailing" id="M86-Ia-XcZ"/>
+                <constraint firstItem="rLz-36-0xz" firstAttribute="top" secondItem="ymm-th-TTo" secondAttribute="top" id="RYD-1T-XNJ"/>
+                <constraint firstAttribute="trailing" secondItem="ZGa-1B-gkO" secondAttribute="trailing" id="SbH-L6-vj9"/>
+                <constraint firstItem="yAt-sK-1nK" firstAttribute="centerX" secondItem="HPk-Bg-V0q" secondAttribute="centerX" id="Viz-8c-pzk"/>
+                <constraint firstItem="ymm-th-TTo" firstAttribute="bottom" secondItem="87h-1G-t5q" secondAttribute="bottom" id="XH4-oO-47L"/>
+                <constraint firstItem="ZGa-1B-gkO" firstAttribute="leading" secondItem="HPk-Bg-V0q" secondAttribute="leading" id="YYk-Vg-obo"/>
+                <constraint firstAttribute="trailing" secondItem="KxU-y4-Jwh" secondAttribute="trailing" constant="12" id="Z0h-d5-J2Z"/>
+                <constraint firstAttribute="trailing" secondItem="Cy8-ap-fH9" secondAttribute="trailing" id="dlF-iN-Gjg"/>
+                <constraint firstAttribute="trailing" secondItem="S1Y-ZS-dct" secondAttribute="trailing" id="jwU-EC-jaR"/>
+                <constraint firstAttribute="trailing" secondItem="87h-1G-t5q" secondAttribute="trailing" id="kaD-bR-ZZt"/>
+                <constraint firstItem="S1Y-ZS-dct" firstAttribute="centerY" secondItem="HPk-Bg-V0q" secondAttribute="centerY" id="lCZ-Hj-EUs"/>
+                <constraint firstAttribute="trailing" secondItem="rLz-36-0xz" secondAttribute="trailing" id="moc-tG-rwG"/>
+                <constraint firstAttribute="bottom" secondItem="Cy8-ap-fH9" secondAttribute="bottom" id="mok-x4-xHA"/>
+                <constraint firstItem="ymm-th-TTo" firstAttribute="bottom" secondItem="yAt-sK-1nK" secondAttribute="bottom" constant="12" id="of3-AC-FQE"/>
+                <constraint firstItem="rLz-36-0xz" firstAttribute="leading" secondItem="HPk-Bg-V0q" secondAttribute="leading" id="orL-eE-RPq"/>
+                <constraint firstItem="ymm-th-TTo" firstAttribute="bottom" secondItem="ZGa-1B-gkO" secondAttribute="bottom" id="uXH-MJ-Z8t"/>
+                <constraint firstItem="S1Y-ZS-dct" firstAttribute="centerX" secondItem="HPk-Bg-V0q" secondAttribute="centerX" id="uyk-kx-vrC"/>
+                <constraint firstItem="3MS-N5-3xY" firstAttribute="leading" secondItem="HPk-Bg-V0q" secondAttribute="leading" id="zYw-cX-ezI"/>
+            </constraints>
+            <viewLayoutGuide key="safeArea" id="ymm-th-TTo"/>
+            <point key="canvasLocation" x="137.68115942028987" y="153.34821428571428"/>
+        </view>
+    </objects>
+</document>

+ 54 - 0
iOSClient/Library/PhotoEditor/Protocols.swift

@@ -0,0 +1,54 @@
+//
+//  Protocols.swift
+//  Photo Editor
+//
+//  Created by Mohamed Hamed on 6/15/17.
+//
+//
+
+import Foundation
+import UIKit
+/**
+ - didSelectView
+ - didSelectImage
+ - stickersViewDidDisappear
+ */
+
+public protocol PhotoEditorDelegate {
+    /**
+     - Parameter image: edited Image
+     */
+    func doneEditing(image: UIImage)
+    /**
+     StickersViewController did Disappear
+     */
+    func canceledEditing()
+}
+
+
+/**
+ - didSelectView
+ - didSelectImage
+ - stickersViewDidDisappear
+ */
+protocol StickersViewControllerDelegate {
+    /**
+     - Parameter view: selected view from StickersViewController
+     */
+    func didSelectView(view: UIView)
+    /**
+     - Parameter image: selected Image from StickersViewController
+     */
+    func didSelectImage(image: UIImage)
+    /**
+     StickersViewController did Disappear
+     */
+    func stickersViewDidDisappear()
+}
+
+/**
+ - didSelectColor
+ */
+protocol ColorDelegate {
+    func didSelectColor(color: UIColor)
+}

+ 61 - 0
iOSClient/Library/PhotoEditor/ResizeControl.swift

@@ -0,0 +1,61 @@
+//
+//  ResizeControl.swift
+//  CropViewController
+//
+//  Created by Guilherme Moura on 2/26/16.
+//  Copyright © 2016 Reefactor, Inc. All rights reserved.
+// Credit https://github.com/sprint84/PhotoCropEditor
+
+import UIKit
+
+protocol ResizeControlDelegate: class {
+    func resizeControlDidBeginResizing(_ control: ResizeControl)
+    func resizeControlDidResize(_ control: ResizeControl)
+    func resizeControlDidEndResizing(_ control: ResizeControl)
+}
+
+class ResizeControl: UIView {
+    weak var delegate: ResizeControlDelegate?
+    var translation = CGPoint.zero
+    var enabled = true
+    fileprivate var startPoint = CGPoint.zero
+
+    override init(frame: CGRect) {
+        super.init(frame: CGRect(x: frame.origin.x, y: frame.origin.y, width: 44.0, height: 44.0))
+        initialize()
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        super.init(frame: CGRect(x: 0, y: 0, width: 44.0, height: 44.0))
+        initialize()
+    }
+    
+    fileprivate func initialize() {
+        backgroundColor = UIColor.clear
+        isExclusiveTouch = true
+        
+        let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(ResizeControl.handlePan(_:)))
+        addGestureRecognizer(gestureRecognizer)
+    }
+    
+    @objc func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
+        if !enabled {
+            return
+        }
+        
+        switch gestureRecognizer.state {
+        case .began:
+            let translation = gestureRecognizer.translation(in: superview)
+            startPoint = CGPoint(x: round(translation.x), y: round(translation.y))
+            delegate?.resizeControlDidBeginResizing(self)
+        case .changed:
+            let translation = gestureRecognizer.translation(in: superview)
+            self.translation = CGPoint(x: round(startPoint.x + translation.x), y: round(startPoint.y + translation.y))
+            delegate?.resizeControlDidResize(self)
+        case .ended, .cancelled:
+            delegate?.resizeControlDidEndResizing(self)
+        default: ()
+        }
+        
+    }
+}

+ 18 - 0
iOSClient/Library/PhotoEditor/StickerCollectionViewCell.swift

@@ -0,0 +1,18 @@
+//
+//  StickerCollectionViewCell.swift
+//  Photo Editor
+//
+//  Created by Mohamed Hamed on 4/23/17.
+//  Copyright © 2017 Mohamed Hamed. All rights reserved.
+//
+
+import UIKit
+
+class StickerCollectionViewCell: UICollectionViewCell {
+    @IBOutlet weak var stickerImage: UIImageView!
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+    }
+
+}

+ 39 - 0
iOSClient/Library/PhotoEditor/StickerCollectionViewCell.xib

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12120" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="StickerCollectionViewCell" id="gTV-IL-0wX" customClass="StickerCollectionViewCell" customModule="iOSPhotoEditor" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="139" height="81"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
+                <rect key="frame" x="0.0" y="0.0" width="139" height="81"/>
+                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                <subviews>
+                    <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="KOC-sF-DQ7">
+                        <rect key="frame" x="0.0" y="0.0" width="139" height="81"/>
+                    </imageView>
+                </subviews>
+            </view>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+            <constraints>
+                <constraint firstItem="KOC-sF-DQ7" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="2yY-EC-OBG"/>
+                <constraint firstAttribute="trailing" secondItem="KOC-sF-DQ7" secondAttribute="trailing" id="IjG-hv-eoa"/>
+                <constraint firstAttribute="bottom" secondItem="KOC-sF-DQ7" secondAttribute="bottom" id="LSw-wL-WBM"/>
+                <constraint firstItem="KOC-sF-DQ7" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="Zq3-3X-uWQ"/>
+            </constraints>
+            <size key="customSize" width="139" height="81"/>
+            <connections>
+                <outlet property="stickerImage" destination="KOC-sF-DQ7" id="qYt-m0-cB8"/>
+            </connections>
+            <point key="canvasLocation" x="78.5" y="76.5"/>
+        </collectionViewCell>
+    </objects>
+</document>

+ 247 - 0
iOSClient/Library/PhotoEditor/StickersViewController.swift

@@ -0,0 +1,247 @@
+//
+//  StickersViewController.swift
+//  Photo Editor
+//
+//  Created by Mohamed Hamed on 4/23/17.
+//  Copyright © 2017 Mohamed Hamed. All rights reserved.
+//  Credit https://github.com/AhmedElassuty/IOS-BottomSheet
+
+import UIKit
+
+class StickersViewController: UIViewController, UIGestureRecognizerDelegate {
+    @IBOutlet weak var headerView: UIView!
+    @IBOutlet weak var holdView: UIView!
+    @IBOutlet weak var scrollView: UIScrollView!
+    @IBOutlet weak var pageControl: UIPageControl!
+    
+    var collectioView: UICollectionView!
+    var emojisCollectioView: UICollectionView!
+    
+    var emojisDelegate: EmojisCollectionViewDelegate!
+    
+    var stickers : [UIImage] = []
+    var stickersViewControllerDelegate : StickersViewControllerDelegate?
+    
+    let screenSize = UIScreen.main.bounds.size
+    
+    let fullView: CGFloat = 100 // remainder of screen height
+    var partialView: CGFloat {
+        return UIScreen.main.bounds.height - 380
+    }
+    
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        configureCollectionViews()
+        scrollView.contentSize = CGSize(width: 2.0 * screenSize.width,
+                                        height: scrollView.frame.size.height)
+        
+        scrollView.isPagingEnabled = true
+        scrollView.delegate = self
+        pageControl.numberOfPages = 2
+        
+        holdView.layer.cornerRadius = 3
+        let gesture = UIPanGestureRecognizer.init(target: self, action: #selector(StickersViewController.panGesture))
+        gesture.delegate = self
+        view.addGestureRecognizer(gesture)
+    }
+    
+    func configureCollectionViews() {
+        
+        let frame = CGRect(x: 0,
+                           y: 0,
+                           width: UIScreen.main.bounds.width,
+                           height: view.frame.height - 40)
+        
+        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
+        layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
+        let width = (CGFloat) ((screenSize.width - 30) / 3.0)
+        layout.itemSize = CGSize(width: width, height: 100)
+        
+        collectioView = UICollectionView(frame: frame, collectionViewLayout: layout)
+        collectioView.backgroundColor = .clear
+        scrollView.addSubview(collectioView)
+        
+        collectioView.delegate = self
+        collectioView.dataSource = self
+        
+        collectioView.register(
+            UINib(nibName: "StickerCollectionViewCell", bundle: Bundle(for: StickerCollectionViewCell.self)),
+            forCellWithReuseIdentifier: "StickerCollectionViewCell")
+        
+        //-----------------------------------
+        
+        let emojisFrame = CGRect(x: scrollView.frame.size.width,
+                                 y: 0,
+                                 width: UIScreen.main.bounds.width,
+                                 height: view.frame.height - 40)
+        
+        let emojislayout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
+        emojislayout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
+        emojislayout.itemSize = CGSize(width: 70, height: 70)
+        
+        emojisCollectioView = UICollectionView(frame: emojisFrame, collectionViewLayout: emojislayout)
+        emojisCollectioView.backgroundColor = .clear
+        scrollView.addSubview(emojisCollectioView)
+        emojisDelegate = EmojisCollectionViewDelegate()
+        emojisDelegate.stickersViewControllerDelegate = stickersViewControllerDelegate
+        emojisCollectioView.delegate = emojisDelegate
+        emojisCollectioView.dataSource = emojisDelegate
+        
+        emojisCollectioView.register(
+            UINib(nibName: "EmojiCollectionViewCell", bundle: Bundle(for: EmojiCollectionViewCell.self)),
+            forCellWithReuseIdentifier: "EmojiCollectionViewCell")
+        
+    }
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        prepareBackgroundView()
+    }
+    
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+        
+        UIView.animate(withDuration: 0.6) { [weak self] in
+            guard let `self` = self else { return }
+            let frame = self.view.frame
+            let yComponent = self.partialView
+            self.view.frame = CGRect(x: 0,
+                                     y: yComponent,
+                                     width: frame.width,
+                                     height: UIScreen.main.bounds.height - self.partialView)
+        }
+    }
+    
+    override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        collectioView.frame = CGRect(x: 0,
+                                     y: 0,
+                                     width: UIScreen.main.bounds.width,
+                                     height: view.frame.height - 40)
+        
+        emojisCollectioView.frame = CGRect(x: scrollView.frame.size.width,
+                                           y: 0,
+                                           width: UIScreen.main.bounds.width,
+                                           height: view.frame.height - 40)
+        
+        scrollView.contentSize = CGSize(width: 2.0 * screenSize.width,
+                                        height: scrollView.frame.size.height)
+    }
+    
+    override func didReceiveMemoryWarning() {
+        super.didReceiveMemoryWarning()
+    }
+    
+    //MARK: Pan Gesture
+    
+    @objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
+        
+        let translation = recognizer.translation(in: self.view)
+        let velocity = recognizer.velocity(in: self.view)
+        
+        let y = self.view.frame.minY
+        if y + translation.y >= fullView {
+            let newMinY = y + translation.y
+            self.view.frame = CGRect(x: 0, y: newMinY, width: view.frame.width, height: UIScreen.main.bounds.height - newMinY )
+            self.view.layoutIfNeeded()
+            recognizer.setTranslation(CGPoint.zero, in: self.view)
+        }
+        
+        if recognizer.state == .ended {
+            var duration =  velocity.y < 0 ? Double((y - fullView) / -velocity.y) : Double((partialView - y) / velocity.y )
+            duration = duration > 1.3 ? 1 : duration
+            //velocity is direction of gesture
+            UIView.animate(withDuration: duration, delay: 0.0, options: [.allowUserInteraction], animations: {
+                if  velocity.y >= 0 {
+                    if y + translation.y >= self.partialView  {
+                        self.removeBottomSheetView()
+                    } else {
+                        self.view.frame = CGRect(x: 0, y: self.partialView, width: self.view.frame.width, height: UIScreen.main.bounds.height - self.partialView)
+                        self.view.layoutIfNeeded()
+                    }
+                } else {
+                    if y + translation.y >= self.partialView  {
+                        self.view.frame = CGRect(x: 0, y: self.partialView, width: self.view.frame.width, height: UIScreen.main.bounds.height - self.partialView)
+                        self.view.layoutIfNeeded()
+                    } else {
+                        self.view.frame = CGRect(x: 0, y: self.fullView, width: self.view.frame.width, height: UIScreen.main.bounds.height - self.fullView)
+                        self.view.layoutIfNeeded()
+                    }
+                }
+                
+            }, completion: nil)
+        }
+    }
+    
+    func removeBottomSheetView() {
+        UIView.animate(withDuration: 0.3,
+                       delay: 0,
+                       options: UIView.AnimationOptions.curveEaseIn,
+                       animations: { () -> Void in
+                        var frame = self.view.frame
+                        frame.origin.y = UIScreen.main.bounds.maxY
+                        self.view.frame = frame
+                        
+        }, completion: { (finished) -> Void in
+            self.view.removeFromSuperview()
+            self.removeFromParent()
+            self.stickersViewControllerDelegate?.stickersViewDidDisappear()
+        })
+    }
+    
+    func prepareBackgroundView(){
+        let blurEffect = UIBlurEffect.init(style: .light)
+        let visualEffect = UIVisualEffectView.init(effect: blurEffect)
+        let bluredView = UIVisualEffectView.init(effect: blurEffect)
+        bluredView.contentView.addSubview(visualEffect)
+        visualEffect.frame = UIScreen.main.bounds
+        bluredView.frame = UIScreen.main.bounds
+        view.insertSubview(bluredView, at: 0)
+    }
+    
+    
+    
+}
+
+extension StickersViewController: UIScrollViewDelegate {
+    
+    func scrollViewDidScroll(_ scrollView: UIScrollView) {
+        let pageWidth = scrollView.bounds.width
+        let pageFraction = scrollView.contentOffset.x / pageWidth
+        self.pageControl.currentPage = Int(round(pageFraction))
+    }
+}
+
+// MARK: - UICollectionViewDataSource
+extension StickersViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
+    
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return stickers.count
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        stickersViewControllerDelegate?.didSelectImage(image: stickers[indexPath.item])
+    }
+    
+    func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return 1
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        let identifier = "StickerCollectionViewCell"
+        let cell  = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! StickerCollectionViewCell
+        cell.stickerImage.image = stickers[indexPath.item]
+        return cell
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
+        return 4
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
+        return 0
+    }
+    
+}
+

+ 67 - 0
iOSClient/Library/PhotoEditor/StickersViewController.xib

@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12120" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="StickersViewController" customModule="iOSPhotoEditor" customModuleProvider="target">
+            <connections>
+                <outlet property="headerView" destination="Jqb-lC-sL5" id="2Bt-iO-hyv"/>
+                <outlet property="holdView" destination="caC-0e-fET" id="T8J-gB-ag8"/>
+                <outlet property="pageControl" destination="fPm-Rj-wEI" id="wow-uc-U80"/>
+                <outlet property="scrollView" destination="389-Pb-g4p" id="EiN-Xe-ZxU"/>
+                <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
+            </connections>
+        </placeholder>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
+            <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Jqb-lC-sL5">
+                    <rect key="frame" x="0.0" y="0.0" width="375" height="40"/>
+                    <subviews>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="caC-0e-fET">
+                            <rect key="frame" x="162.5" y="8" width="50" height="5"/>
+                            <color key="backgroundColor" red="0.96470588239999999" green="0.96470588239999999" blue="0.96470588239999999" alpha="1" colorSpace="calibratedRGB"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="50" id="GqZ-hh-S9K"/>
+                                <constraint firstAttribute="height" constant="5" id="uq0-60-mnE"/>
+                            </constraints>
+                        </view>
+                        <pageControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" numberOfPages="3" translatesAutoresizingMaskIntoConstraints="NO" id="fPm-Rj-wEI">
+                            <rect key="frame" x="168" y="13" width="39" height="37"/>
+                        </pageControl>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                    <constraints>
+                        <constraint firstItem="caC-0e-fET" firstAttribute="top" secondItem="Jqb-lC-sL5" secondAttribute="top" constant="8" id="S62-GD-S5G"/>
+                        <constraint firstAttribute="height" constant="40" id="Tl2-jg-Adq"/>
+                        <constraint firstItem="fPm-Rj-wEI" firstAttribute="centerX" secondItem="Jqb-lC-sL5" secondAttribute="centerX" id="jKc-Hd-3e8"/>
+                        <constraint firstItem="fPm-Rj-wEI" firstAttribute="top" secondItem="caC-0e-fET" secondAttribute="bottom" id="oH1-Ez-1Ze"/>
+                        <constraint firstItem="caC-0e-fET" firstAttribute="centerX" secondItem="Jqb-lC-sL5" secondAttribute="centerX" id="qvF-zt-xDj"/>
+                    </constraints>
+                </view>
+                <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" directionalLockEnabled="YES" translatesAutoresizingMaskIntoConstraints="NO" id="389-Pb-g4p">
+                    <rect key="frame" x="0.0" y="40" width="375" height="627"/>
+                </scrollView>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+            <constraints>
+                <constraint firstItem="Jqb-lC-sL5" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" id="534-wp-qUu"/>
+                <constraint firstAttribute="bottom" secondItem="389-Pb-g4p" secondAttribute="bottom" id="alT-c1-6ub"/>
+                <constraint firstItem="389-Pb-g4p" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="eu1-LX-lYG"/>
+                <constraint firstItem="Jqb-lC-sL5" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="fvh-Gm-lz3"/>
+                <constraint firstAttribute="trailing" secondItem="389-Pb-g4p" secondAttribute="trailing" id="gLR-A6-iQ1"/>
+                <constraint firstItem="389-Pb-g4p" firstAttribute="top" secondItem="Jqb-lC-sL5" secondAttribute="bottom" id="iQj-xf-VZX"/>
+                <constraint firstAttribute="trailing" secondItem="Jqb-lC-sL5" secondAttribute="trailing" id="tYL-lr-Vzj"/>
+            </constraints>
+            <nil key="simulatedStatusBarMetrics"/>
+            <point key="canvasLocation" x="33.5" y="54.5"/>
+        </view>
+    </objects>
+</document>

+ 34 - 0
iOSClient/Library/PhotoEditor/UIImage+Crop.swift

@@ -0,0 +1,34 @@
+//
+//  UIImage+Crop.swift
+//  CropViewController
+//
+//  Created by Guilherme Moura on 2/26/16.
+//  Copyright © 2016 Reefactor, Inc. All rights reserved.
+// Credit https://github.com/sprint84/PhotoCropEditor
+
+import UIKit
+
+extension UIImage {
+    func rotatedImageWithTransform(_ rotation: CGAffineTransform, croppedToRect rect: CGRect) -> UIImage {
+        let rotatedImage = rotatedImageWithTransform(rotation)
+        
+        let scale = rotatedImage.scale
+        let cropRect = rect.applying(CGAffineTransform(scaleX: scale, y: scale))
+        
+        let croppedImage = rotatedImage.cgImage?.cropping(to: cropRect)
+        let image = UIImage(cgImage: croppedImage!, scale: self.scale, orientation: rotatedImage.imageOrientation)
+        return image
+    }
+    
+    fileprivate func rotatedImageWithTransform(_ transform: CGAffineTransform) -> UIImage {
+        UIGraphicsBeginImageContextWithOptions(size, true, scale)
+        let context = UIGraphicsGetCurrentContext()
+        context?.translateBy(x: size.width / 2.0, y: size.height / 2.0)
+        context?.concatenate(transform)
+        context?.translateBy(x: size.width / -2.0, y: size.height / -2.0)
+        draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
+        let rotatedImage = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+        return rotatedImage!
+    }
+}

+ 33 - 0
iOSClient/Library/PhotoEditor/UIImage+Size.swift

@@ -0,0 +1,33 @@
+//
+//  UIImage+Size.swift
+//  Photo Editor
+//
+//  Created by Mohamed Hamed on 5/2/17.
+//  Copyright © 2017 Mohamed Hamed. All rights reserved.
+//
+
+import UIKit
+
+public extension UIImage {
+    
+    /**
+     Suitable size for specific height or width to keep same image ratio
+     */
+    func suitableSize(heightLimit: CGFloat? = nil,
+                             widthLimit: CGFloat? = nil )-> CGSize? {
+        
+        if let height = heightLimit {
+            
+            let width = (height / self.size.height) * self.size.width
+            
+            return CGSize(width: width, height: height)
+        }
+        
+        if let width = widthLimit {
+            let height = (width / self.size.width) * self.size.height
+            return CGSize(width: width, height: height)
+        }
+        
+        return nil
+    }
+}

+ 36 - 0
iOSClient/Library/PhotoEditor/UIImageView+Alpha.swift

@@ -0,0 +1,36 @@
+//
+//  UIImageViewExtenstions.swift
+//  Photo Editor
+//
+//  Created by Mohamed Hamed on 5/10/17.
+//
+//
+
+import Foundation
+import UIKit
+
+extension UIImageView {
+    
+    func alphaAtPoint(_ point: CGPoint) -> CGFloat {
+        
+        var pixel: [UInt8] = [0, 0, 0, 0]
+        let colorSpace = CGColorSpaceCreateDeviceRGB();
+        let alphaInfo = CGImageAlphaInfo.premultipliedLast.rawValue
+        
+        guard let context = CGContext(data: &pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: alphaInfo) else {
+            return 0
+        }
+        
+        context.translateBy(x: -point.x, y: -point.y);
+        
+        layer.render(in: context)
+        
+        let floatAlpha = CGFloat(pixel[3])
+        
+        return floatAlpha
+    }
+
+}
+
+
+    

+ 22 - 0
iOSClient/Library/PhotoEditor/UIView+Image.swift

@@ -0,0 +1,22 @@
+//
+//  UIView+Image.swift
+//  Photo Editor
+//
+//  Created by Mohamed Hamed on 4/23/17.
+//  Copyright © 2017 Mohamed Hamed. All rights reserved.
+//
+
+import UIKit
+
+extension UIView {
+    /**
+     Convert UIView to UIImage
+     */
+    func toImage() -> UIImage {
+        UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0)
+        self.drawHierarchy(in: self.bounds, afterScreenUpdates: false)
+        let snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+        return snapshotImageFromMyView!
+    }
+}

BIN
iOSClient/Library/PhotoEditor/icomoon.ttf


+ 47 - 1
iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift

@@ -28,7 +28,7 @@ import Foundation
     func dismissFormUploadAssets()
 }
 
-class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
+class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate, PhotoEditorDelegate {
     
     var serverUrl : String = ""
     var titleServerUrl : String?
@@ -105,6 +105,25 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
         row.cellConfig["textLabel.font"] = UIFont.systemFont(ofSize: 15.0)
         
         section.addFormRow(row)
+
+        // Section Photo Editor only for one photo
+
+        if assets.count == 1 && (assets[0] as! PHAsset).mediaType == PHAssetMediaType.image {
+            
+            section = XLFormSectionDescriptor.formSection()
+            form.addFormSection(section)
+
+            row = XLFormRowDescriptor(tag: "ButtonPhotoEditor", rowType: XLFormRowDescriptorTypeButton, title: NSLocalizedString("_modify_photo_", comment: ""))
+            row.action.formSelector = #selector(photoEditor(_:))
+            
+            let imageFolder = CCGraphics.changeThemingColorImage(UIImage(named: "modifyPhoto")!, multiplier:1, color: NCBrandColor.sharedInstance.icon) as UIImage
+            row.cellConfig["imageView.image"] = imageFolder
+            row.cellConfig["textLabel.textColor"] = UIColor.black
+            row.cellConfig["textLabel.textAlignment"] = NSTextAlignment.left.rawValue
+            row.cellConfig["textLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+            
+            section.addFormRow(row)
+        }
         
         // Section Mode filename
         
@@ -373,4 +392,31 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
         self.present(navigationController, animated: true, completion: nil)
     }
     
+    @objc func photoEditor(_ sender: XLFormRowDescriptor) {
+        
+        self.deselectFormRow(sender)
+        
+        let requestOptions = PHImageRequestOptions()
+        requestOptions.resizeMode = PHImageRequestOptionsResizeMode.exact
+        requestOptions.deliveryMode = PHImageRequestOptionsDeliveryMode.highQualityFormat
+        requestOptions.isSynchronous = true
+        
+        PHImageManager.default().requestImage(for: assets[0] as! PHAsset, targetSize: PHImageManagerMaximumSize, contentMode: PHImageContentMode.default, options: requestOptions, resultHandler: { (image, info) in
+            
+            let photoEditor = PhotoEditorViewController(nibName:"PhotoEditorViewController",bundle: Bundle(for: PhotoEditorViewController.self))
+
+            photoEditor.image = image
+            self.present(photoEditor, animated: true, completion: nil)
+        })
+    }
+    
+    // MARK: - Photo Editor Delegate
+
+    func doneEditing(image: UIImage) {
+        //imageView.image = image
+    }
+    
+    func canceledEditing() {
+        print("Canceled")
+    }
 }

+ 1 - 0
iOSClient/Supporting Files/en.lproj/Localizable.strings

@@ -519,6 +519,7 @@
 "_add_filenametype_"                = "Specify type in filename";
 "_filenametype_photo_video_"        = "Photo/Video";
 "_maintain_original_filename_"      = "Maintain original filename";
+"_modify_photo_"                    = "Modify photo";
 
 // Notification