소스 검색

initial avatar image picker implementation

cyberta 5 년 전
부모
커밋
f42d8b3f9e

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

@@ -81,6 +81,7 @@
 		305962102346154D00C80F33 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3059620F2346154D00C80F33 /* String+Extension.swift */; };
 		3060119C22DDE24000C1CE6F /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3060119E22DDE24000C1CE6F /* Localizable.strings */; };
 		306011B622E5E7FB00C1CE6F /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 306011B422E5E7FB00C1CE6F /* Localizable.stringsdict */; };
+		3095A351237DD1F700AB07F7 /* MediaPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3095A350237DD1F700AB07F7 /* MediaPicker.swift */; };
 		30A4D9AE2332672700544344 /* QrInviteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A4D9AD2332672600544344 /* QrInviteViewController.swift */; };
 		30C0D49D237C4908008E2A0E /* CertificateCheckController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C0D49C237C4908008E2A0E /* CertificateCheckController.swift */; };
 		30F9B9EC235F2116006E7ACF /* MessageCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30F9B9EB235F2116006E7ACF /* MessageCounter.swift */; };
@@ -113,7 +114,7 @@
 		8B6D425BC604F7C43B65D436 /* Pods_deltachat_ios.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6241BE1534A653E79AD5D01D /* Pods_deltachat_ios.framework */; };
 		AE0D26FD1FB1FE88002FAFCE /* ChatListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE0D26FC1FB1FE88002FAFCE /* ChatListController.swift */; };
 		AE18F294228C602A0007B1BE /* SecuritySettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE18F293228C602A0007B1BE /* SecuritySettingsController.swift */; };
-		AE25F09022807AD800CDEA66 /* GroupNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE25F08F22807AD800CDEA66 /* GroupNameCell.swift */; };
+		AE25F09022807AD800CDEA66 /* AvatarEditTextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE25F08F22807AD800CDEA66 /* AvatarEditTextCell.swift */; };
 		AE38B31822672DFC00EC37A1 /* ActionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE38B31722672DFC00EC37A1 /* ActionCell.swift */; };
 		AE38B31A2267328200EC37A1 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE38B3192267328200EC37A1 /* Colors.swift */; };
 		AE4AEE3522B1030D000AA495 /* PreviewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE4AEE3422B1030D000AA495 /* PreviewController.swift */; };
@@ -288,6 +289,7 @@
 		306011C722E5E82E00C1CE6F /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = lt; path = lt.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
 		306011C822E5E83100C1CE6F /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
 		306011C922E5E83500C1CE6F /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
+		3095A350237DD1F700AB07F7 /* MediaPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPicker.swift; sourceTree = "<group>"; };
 		30A4D9AD2332672600544344 /* QrInviteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QrInviteViewController.swift; sourceTree = "<group>"; };
 		30C0D49C237C4908008E2A0E /* CertificateCheckController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificateCheckController.swift; sourceTree = "<group>"; };
 		30F9B9EB235F2116006E7ACF /* MessageCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCounter.swift; sourceTree = "<group>"; };
@@ -327,7 +329,7 @@
 		A8615D4600859851E53CAA9C /* Pods-deltachat-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-deltachat-ios.release.xcconfig"; path = "Pods/Target Support Files/Pods-deltachat-ios/Pods-deltachat-ios.release.xcconfig"; sourceTree = "<group>"; };
 		AE0D26FC1FB1FE88002FAFCE /* ChatListController.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = ChatListController.swift; sourceTree = "<group>"; tabWidth = 4; };
 		AE18F293228C602A0007B1BE /* SecuritySettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecuritySettingsController.swift; sourceTree = "<group>"; };
-		AE25F08F22807AD800CDEA66 /* GroupNameCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupNameCell.swift; sourceTree = "<group>"; };
+		AE25F08F22807AD800CDEA66 /* AvatarEditTextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarEditTextCell.swift; sourceTree = "<group>"; };
 		AE38B31722672DFC00EC37A1 /* ActionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCell.swift; sourceTree = "<group>"; };
 		AE38B3192267328200EC37A1 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = "<group>"; };
 		AE4AEE3422B1030D000AA495 /* PreviewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewController.swift; sourceTree = "<group>"; };
@@ -690,6 +692,7 @@
 				AEACE2E41FB32E1900DCDD78 /* Utils.swift */,
 				AE38B3192267328200EC37A1 /* Colors.swift */,
 				AE851AC4227C755A00ED86F0 /* Protocols.swift */,
+				3095A350237DD1F700AB07F7 /* MediaPicker.swift */,
 			);
 			path = Helper;
 			sourceTree = "<group>";
@@ -706,7 +709,7 @@
 				789E879C21D6DF86003ED1C5 /* ProgressHud.swift */,
 				AE38B31722672DFC00EC37A1 /* ActionCell.swift */,
 				AE851ACD227CA54300ED86F0 /* InitialsBadge.swift */,
-				AE25F08F22807AD800CDEA66 /* GroupNameCell.swift */,
+				AE25F08F22807AD800CDEA66 /* AvatarEditTextCell.swift */,
 				AE728F14229D5C390047565B /* PhotoPickerAlertAction.swift */,
 				AE52EA18229EB53C00C586C9 /* ContactDetailHeader.swift */,
 				AE9DAF0E22C278C6004C9591 /* ChatTitleView.swift */,
@@ -1038,6 +1041,7 @@
 				305961D72346125100C80F33 /* MessageKit+Availability.swift in Sources */,
 				3040F45E234DFBC000FA34D5 /* Audio.swift in Sources */,
 				305961FE2346125100C80F33 /* InsetLabel.swift in Sources */,
+				3095A351237DD1F700AB07F7 /* MediaPicker.swift in Sources */,
 				B21005DB23383664004C70C5 /* SettingsClassicViewController.swift in Sources */,
 				305961F62346125100C80F33 /* MessageContentCell.swift in Sources */,
 				305961E42346125100C80F33 /* MessageKitDateFormatter.swift in Sources */,
@@ -1052,7 +1056,7 @@
 				305961E52346125100C80F33 /* LabelAlignment.swift in Sources */,
 				305961E82346125100C80F33 /* Sender.swift in Sources */,
 				305961EE2346125100C80F33 /* AvatarPosition.swift in Sources */,
-				AE25F09022807AD800CDEA66 /* GroupNameCell.swift in Sources */,
+				AE25F09022807AD800CDEA66 /* AvatarEditTextCell.swift in Sources */,
 				305961E62346125100C80F33 /* LocationMessageSnapshotOptions.swift in Sources */,
 				AEE6EC3F2282C59C00EDC689 /* GroupMembersViewController.swift in Sources */,
 				B26B3BC7236DC3DC008ED35A /* SwitchCell.swift in Sources */,

+ 10 - 4
deltachat-ios/Controller/EditGroupViewController.swift

@@ -6,7 +6,7 @@ class EditGroupViewController: UITableViewController {
 
     private let chat: DcChat
 
-    var groupNameCell: GroupLabelCell
+    var groupNameCell: AvatarEditTextCell
 
     lazy var doneButton: UIBarButtonItem = {
         let button = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(saveContactButtonPressed))
@@ -21,11 +21,12 @@ class EditGroupViewController: UITableViewController {
 
     init(chat: DcChat) {
         self.chat = chat
-        self.groupNameCell = GroupLabelCell(chat: chat)
+        self.groupNameCell = AvatarEditTextCell(chat: chat)
         super.init(style: .grouped)
-        groupNameCell.inputField.text = chat.name
+        self.groupNameCell.inputField.text = chat.name
         self.groupNameCell.onTextChanged = groupNameEdited(_:)
         self.groupNameCell.selectionStyle = .none
+        self.groupNameCell.hintLabel.text = String.localized("group_name")
     }
 
     required init?(coder aDecoder: NSCoder) {
@@ -50,8 +51,13 @@ class EditGroupViewController: UITableViewController {
         return 1
     }
 
+    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        return AvatarEditTextCell.cellSize
+    }
+
+    
     @objc func saveContactButtonPressed() {
-        let newName = groupNameCell.getGroupName()
+        let newName = groupNameCell.getText()
         dc_set_chat_name(mailboxPointer, UInt32(chat.id), newName)
         coordinator?.navigateBack()
     }

+ 75 - 21
deltachat-ios/Controller/EditSettingsController.swift

@@ -1,13 +1,16 @@
 import UIKit
 
-class EditSettingsController: UITableViewController {
+class EditSettingsController: UITableViewController, MediaPickerDelegate {
 
     private let dcContext: DcContext
+    weak var coordinator: EditSettingsCoordinator?
     private var displayNameBackup: String?
     private var statusCellBackup: String?
 
+    private let groupBadgeSize: CGFloat = 72
+
     private let section1 = 0
-    private let section1Name = 0
+    private let section1PictureAndName = 0
     private let section1Status = 1
     private let section1RowCount = 2
 
@@ -17,12 +20,15 @@ class EditSettingsController: UITableViewController {
 
     private let sectionCount = 2
 
+    private let tagAccountSettingsCell = 1
+
     private var childCoordinators: Coordinator?
 
-    private lazy var displayNameCell: TextFieldCell = {
-        let cell = TextFieldCell(description: String.localized("pref_your_name"), placeholder: String.localized("pref_your_name"))
-        cell.setText(text: DcConfig.displayname ?? nil)
-        return cell
+    private lazy var defaultImage: UIImage = {
+        if let image = UIImage(named: "camera") {
+            return image.invert()
+        }
+        return UIImage()
     }()
 
     private lazy var statusCell: TextFieldCell = {
@@ -31,14 +37,24 @@ class EditSettingsController: UITableViewController {
         return cell
     }()
 
-    lazy var accountSettingsCell: UITableViewCell = {
+    private lazy var accountSettingsCell: UITableViewCell = {
         let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
         cell.textLabel?.text = String.localized("pref_password_and_account_settings")
         cell.accessoryType = .disclosureIndicator
-        cell.accessibilityIdentifier = "accountSettingsCell"
+        cell.tag = tagAccountSettingsCell
         return cell
     }()
 
+
+    private lazy var pictureAndNameCell: AvatarEditTextCell = {
+        let contact = DcContact(id: Int(DC_CONTACT_ID_SELF))
+        let cell = AvatarEditTextCell(context: dcContext, defaultImage: defaultImage)
+        cell.inputField.text = contact.displayName
+        cell.selectionStyle = .none
+        return cell
+    }()
+
+
     init(dcContext: DcContext) {
         self.dcContext = dcContext
         super.init(style: .grouped)
@@ -52,19 +68,16 @@ class EditSettingsController: UITableViewController {
     override func viewDidLoad() {
         super.viewDidLoad()
         title = String.localized("pref_profile_info_headline")
-    }
 
-    override func viewWillAppear(_ animated: Bool) {
-        displayNameBackup = DcConfig.displayname
-        statusCellBackup = DcConfig.selfstatus
+        tableView.register(AvatarEditTextCell.self, forCellReuseIdentifier: "groupLabelCell")
+        tableView.register(ContactCell.self, forCellReuseIdentifier: "contactCell")
+        pictureAndNameCell.onAvatarTapped = onAvatarTapped
     }
 
     override func viewWillDisappear(_ animated: Bool) {
-        if displayNameBackup != displayNameCell.getText() || statusCellBackup != displayNameCell.getText() {
-            DcConfig.selfstatus = statusCell.getText()
-            DcConfig.displayname = displayNameCell.getText()
-            dc_configure(mailboxPointer)
-        }
+        DcConfig.selfstatus = statusCell.getText()
+        DcConfig.displayname = pictureAndNameCell.getText()
+        dc_configure(mailboxPointer)
     }
 
     // MARK: - Table view data source
@@ -83,16 +96,27 @@ class EditSettingsController: UITableViewController {
 
     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         if indexPath.section == section1 {
-            if indexPath.row == section1Name {
-                return displayNameCell
-            } else {
+            switch indexPath.row {
+            case section1PictureAndName:
+                return pictureAndNameCell
+            case section1Status:
                 return statusCell
+            default:
+               return UITableViewCell()
             }
         } else {
             return accountSettingsCell
         }
     }
 
+    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        if indexPath.section == section1 && indexPath.row == section1PictureAndName {
+            return AvatarEditTextCell.cellSize
+        } else {
+            return 48
+        }
+    }
+
     override func tableView(_: UITableView, titleForFooterInSection section: Int) -> String? {
         if section == section1 {
             return String.localized("pref_who_can_see_profile_explain")
@@ -103,7 +127,7 @@ class EditSettingsController: UITableViewController {
 
     override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
         guard let cell = tableView.cellForRow(at: indexPath) else { return }
-        if cell.accessibilityIdentifier == "accountSettingsCell" {
+        if cell.tag == tagAccountSettingsCell {
             tableView.deselectRow(at: indexPath, animated: true)
             guard let nc = navigationController else { return }
             let accountSetupVC = AccountSetupController(dcContext: dcContext, editView: true)
@@ -113,4 +137,34 @@ class EditSettingsController: UITableViewController {
             nc.pushViewController(accountSetupVC, animated: true)
         }
     }
+
+
+      private func photoButtonPressed(_ action: UIAlertAction) {
+        coordinator?.showPhotoPicker(delegate: self)
+      }
+
+      private func videoButtonPressed(_ action: UIAlertAction) {
+        ///TODO implement me!
+      }
+
+    private func onAvatarTapped() {
+        let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
+                 let photoAction = PhotoPickerAlertAction(title: String.localized("gallery"), style: .default, handler: photoButtonPressed(_:))
+                 let videoAction = PhotoPickerAlertAction(title: String.localized("camera"), style: .default, handler: videoButtonPressed(_:))
+
+                 alert.addAction(photoAction)
+                 alert.addAction(videoAction)
+                 alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil))
+
+        self.present(alert, animated: true, completion: nil)
+    }
+
+    func onMediaSelected(url: NSURL) {
+        logger.info("onMediaSelected: \(url)")
+        DcConfig.selfavatar = "\(url)"
+        pictureAndNameCell.setSelfAvatar(context: dcContext, with: defaultImage)
+    }
+
+    func onDismiss() { }
+
 }

+ 12 - 2
deltachat-ios/Controller/GroupNameController.swift

@@ -26,7 +26,7 @@ class GroupNameController: UITableViewController {
         navigationItem.rightBarButtonItem = doneButton
         tableView.bounces = false
         doneButton.isEnabled = false
-        tableView.register(GroupLabelCell.self, forCellReuseIdentifier: "groupLabelCell")
+        tableView.register(AvatarEditTextCell.self, forCellReuseIdentifier: "groupLabelCell")
         tableView.register(ContactCell.self, forCellReuseIdentifier: "contactCell")
         // setupSubviews()
     }
@@ -60,8 +60,9 @@ class GroupNameController: UITableViewController {
 
         if section == 0 {
             let cell = tableView.dequeueReusableCell(withIdentifier: "groupLabelCell", for: indexPath)
-            if let groupLabelCell = cell as? GroupLabelCell {
+            if let groupLabelCell = cell as? AvatarEditTextCell {
                 groupLabelCell.onTextChanged = updateGroupName
+                groupLabelCell.hintLabel.text = String.localized("group_name")
             }
             return cell
         } else {
@@ -78,6 +79,15 @@ class GroupNameController: UITableViewController {
         }
     }
 
+    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        let section = indexPath.section
+        if section == 0 {
+            return AvatarEditTextCell.cellSize
+        } else {
+            return ContactCell.cellSize
+        }
+    }
+
     override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
         if section == 0 {
             return 1

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

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

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

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

+ 10 - 0
deltachat-ios/Extensions/UIImage+Extension.swift

@@ -1,6 +1,16 @@
 import UIKit
 
 extension UIImage {
+
+    func invert() -> UIImage {
+        let beginImage = CIImage(image: self)
+        if let filter = CIFilter(name: "CIColorInvert") {
+            filter.setValue(beginImage, forKey: kCIInputImageKey)
+            return UIImage(ciImage: filter.outputImage!)
+        }
+        return self
+    }
+
     func imageResize(sizeChange: CGSize) -> UIImage {
         let hasAlpha = true
         let scale: CGFloat = 0.0 // Use scale factor of main screen

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

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

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

@@ -0,0 +1,59 @@
+import UIKit
+import Photos
+import MobileCoreServices
+
+protocol MediaPickerDelegate: class {
+    func onMediaSelected(url: NSURL)
+    func onDismiss()
+}
+
+class MediaPicker: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
+    private let navigationController: UINavigationController
+    private weak var delegate: MediaPickerDelegate?
+
+    init(navigationController: UINavigationController) {
+        self.navigationController = navigationController
+    }
+
+    func showPhotoLibrary(delegate: MediaPickerDelegate) {
+        if PHPhotoLibrary.authorizationStatus() != .authorized {
+            PHPhotoLibrary.requestAuthorization { status in
+                DispatchQueue.main.async {
+                    [weak self] in
+                    switch status {
+                    case  .denied, .notDetermined, .restricted:
+                        print("denied")
+                    case .authorized:
+                        self?.presentPhotoLibrary(delegate: delegate)
+                    }
+                }
+            }
+        } else {
+            self.presentPhotoLibrary(delegate: delegate)
+        }
+    }
+
+    private func presentPhotoLibrary(delegate: MediaPickerDelegate) {
+        if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
+            let photoPicker = UIImagePickerController()
+            photoPicker.title = String.localized("photo")
+            photoPicker.delegate = self
+            photoPicker.sourceType = .photoLibrary
+            photoPicker.allowsEditing = false
+            photoPicker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary) ?? []
+            navigationController.present(photoPicker, animated: true, completion: nil)
+            self.delegate = delegate
+        }
+    }
+
+    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
+        if let imageUrl = info[UIImagePickerController.InfoKey.imageURL] as? NSURL {
+            logger.debug("image selected: \(imageUrl)")
+            delegate?.onMediaSelected(url: imageUrl)
+        } else {
+            logger.warning("could not select image")
+        }
+        navigationController.dismiss(animated: true, completion: delegate?.onDismiss)
+    }
+
+}

+ 126 - 0
deltachat-ios/View/AvatarEditTextCell.swift

@@ -0,0 +1,126 @@
+import UIKit
+
+class AvatarEditTextCell: UITableViewCell {
+    let badgeSize: CGFloat = 72
+    static let cellSize: CGFloat = 98
+
+    var onTextChanged: ((String) -> Void)? // use this callback to update editButton in navigationController
+    var onAvatarTapped: (() -> Void)?
+
+    lazy var badge: InitialsBadge = {
+        let badge = InitialsBadge(size: badgeSize)
+        badge.layer.cornerRadius = badgeSize / 2
+        badge.clipsToBounds = true
+        badge.setColor(UIColor.lightGray)
+        return badge
+    }()
+
+    lazy var inputField: UITextField = {
+        let textField = UITextField()
+        textField.borderStyle = .none
+        textField.becomeFirstResponder()
+        textField.autocorrectionType = .no
+        textField.addTarget(self, action: #selector(inputFieldChanged), for: .editingChanged)
+        textField.translatesAutoresizingMaskIntoConstraints = false
+        textField.textAlignment = .right
+        return textField
+    }()
+
+    lazy var hintLabel: UILabel = {
+        let label = UILabel(frame: .zero)
+        label.translatesAutoresizingMaskIntoConstraints = false
+        label.textColor = DcColors.defaultTextColor
+        label.font = UIFont.preferredFont(forTextStyle: .footnote)
+        label.text = String.localized("pref_your_name")
+        label.textAlignment = .right
+        return label
+    }()
+
+    init(chat: DcChat) {
+        super.init(style: .default, reuseIdentifier: nil)
+        setAvatar(for: chat)
+        setupSubviews()
+    }
+
+
+    init(context: DcContext, defaultImage: UIImage) {
+        super.init(style: .default, reuseIdentifier: nil)
+        setSelfAvatar(context: context, with: defaultImage)
+        setupSubviews()
+    }
+
+    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+        super.init(style: style, reuseIdentifier: reuseIdentifier)
+        setupSubviews()
+    }
+
+    required init?(coder _: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    private func setupSubviews() {
+        contentView.addSubview(badge)
+
+        badge.alignLeadingToAnchor(contentView.layoutMarginsGuide.leadingAnchor)
+        badge.alignTopToAnchor(contentView.layoutMarginsGuide.topAnchor)
+        contentView.addSubview(inputField)
+
+        inputField.alignLeadingToAnchor(badge.trailingAnchor, paddingLeading: 15)
+        inputField.addConstraints(heightConstant: CGFloat(20))
+        inputField.alignTrailingToAnchor(contentView.layoutMarginsGuide.trailingAnchor)
+        inputField.alignBottomToAnchor(contentView.layoutMarginsGuide.bottomAnchor, paddingBottom: 15)
+        contentView.addSubview(hintLabel)
+
+        hintLabel.alignTopToAnchor(contentView.layoutMarginsGuide.topAnchor)
+        hintLabel.alignTrailingToAnchor(contentView.layoutMarginsGuide.trailingAnchor)
+        hintLabel.addConstraints(heightConstant: CGFloat(20))
+
+        let touchListener = UILongPressGestureRecognizer(target: self, action: #selector(onBadgeTouched))
+        touchListener.minimumPressDuration = 0
+        badge.addGestureRecognizer(touchListener)
+    }
+
+    @objc func inputFieldChanged() {
+        let groupName = inputField.text ?? ""
+        if badge.showsInitials() {
+            badge.setName(groupName)
+        }
+        onTextChanged?(groupName)
+    }
+
+    @objc func onBadgeTouched(gesture: UILongPressGestureRecognizer) {
+        switch gesture.state {
+        case .began:
+            badge.alpha = 0.7
+        case .ended:
+            badge.alpha = 1
+            onAvatarTapped?()
+        case .cancelled:
+            badge.alpha = 1
+        default:
+            break
+        }
+    }
+
+    func getText() -> String {
+        return inputField.text ?? ""
+    }
+
+    func setAvatar(for chat: DcChat) {
+        if let image = chat.profileImage {
+            badge = InitialsBadge(image: image, size: badgeSize)
+        } else {
+            badge = InitialsBadge(name: chat.name, color: chat.color, size: badgeSize)
+        }
+        badge.setVerified(chat.isVerified)
+    }
+
+    func setSelfAvatar(context: DcContext, with defaultImage: UIImage) {
+        if let image = context.getSelfAvatarImage() {
+            badge = InitialsBadge(image: image, size: badgeSize)
+        } else {
+            badge = InitialsBadge(image: defaultImage, size: badgeSize, downscale: 0.6)
+            badge.backgroundColor = DcColors.grayTextColor
+        }
+    }
+}

+ 1 - 0
deltachat-ios/View/ContactCell.swift

@@ -8,6 +8,7 @@ protocol ContactCellDelegate: class {
 
 class ContactCell: UITableViewCell {
 
+    public static let cellSize: CGFloat = 72
     weak var delegate: ContactCellDelegate?
     var rowIndex = -1
     private let initialsLabelSize: CGFloat = 54

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

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

+ 21 - 0
deltachat-ios/View/InitialsBadge.swift

@@ -40,6 +40,11 @@ class InitialsBadge: UIView {
         setImage(image)
     }
 
+    convenience init (image: UIImage, size: CGFloat, downscale: CGFloat) {
+        self.init(size: size)
+        setImage(image, downscale: downscale)
+    }
+
     init(size: CGFloat) {
         super.init(frame: CGRect(x: 0, y: 0, width: size, height: size))
         let radius = size / 2
@@ -88,6 +93,22 @@ class InitialsBadge: UIView {
         }
     }
 
+    func setImage(_ image: UIImage, downscale: CGFloat) {
+        var scale = downscale
+        if (scale > 1) {
+            scale = 1
+        } else if (scale < 0) {
+            scale = 0
+        }
+
+        if let resizedImg = image.resizeImage(targetSize: CGSize(width: self.frame.width * scale, height: self.frame.height * scale)) {
+            self.imageView.image = resizedImg
+            self.imageView.contentMode = UIView.ContentMode.center
+            self.imageView.isHidden = false
+            self.label.isHidden = true
+        }
+    }
+
     func showsInitials() -> Bool {
         return !label.isHidden
     }