Kaynağa Gözat

fix truncated empty state lables (#1009)

cyberta 4 yıl önce
ebeveyn
işleme
96d3b9f2a4

+ 1 - 6
DcShare/Controller/ChatListController.swift

@@ -91,12 +91,7 @@ class ChatListController: UITableViewController {
 
     // MARK: - setup
     private func setupSubviews() {
-        view.addSubview(emptySearchStateLabel)
-        emptySearchStateLabel.translatesAutoresizingMaskIntoConstraints = false
-        emptySearchStateLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40).isActive = true
-        emptySearchStateLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40).isActive = true
-        emptySearchStateLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -40).isActive = true
-        emptySearchStateLabel.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
+        emptySearchStateLabel.addCenteredTo(parentView: view)
     }
 
     // MARK: - configuration

+ 10 - 6
deltachat-ios.xcodeproj/project.pbxproj

@@ -11,6 +11,11 @@
 		3008CB7424F9436C00E6A617 /* AudioPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3008CB7324F9436C00E6A617 /* AudioPlayerView.swift */; };
 		3008CB7624F95B6D00E6A617 /* AudioController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3008CB7524F95B6D00E6A617 /* AudioController.swift */; };
 		30149D9322F21129003C12B5 /* QrViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30149D9222F21129003C12B5 /* QrViewController.swift */; };
+		30152C9425A5D91400377714 /* PaddingTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30F4BFED252E3E020006B9B3 /* PaddingTextView.swift */; };
+		30152C9725A5D91900377714 /* MessageLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30EF7323252FF15F00E2C54A /* MessageLabel.swift */; };
+		30152C9A25A5D92200377714 /* DetectorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3052C60D253F088E007D13EA /* DetectorType.swift */; };
+		30152C9D25A5D95400377714 /* MessageLabelDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3052C609253F082E007D13EA /* MessageLabelDelegate.swift */; };
+		30152CA025A5D97900377714 /* UIEdgeInsets+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 305961832346125000C80F33 /* UIEdgeInsets+Extensions.swift */; };
 		3015634423A003BA00E9DEF4 /* AudioRecorderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3015634323A003BA00E9DEF4 /* AudioRecorderController.swift */; };
 		3022E6BE22E8768800763272 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3022E6C022E8768800763272 /* InfoPlist.strings */; };
 		302589FF2452FA280086C1CD /* ShareAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302589FE2452FA280086C1CD /* ShareAttachment.swift */; };
@@ -36,8 +41,6 @@
 		3057028724C5C88300D84EFC /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 306011B422E5E7FB00C1CE6F /* Localizable.stringsdict */; };
 		3057028C24C5E7B600D84EFC /* ContactCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE77838E23E4276D0093EABD /* ContactCellViewModel.swift */; };
 		3057029B24C6441300D84EFC /* EmptyStateLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3057029A24C6441300D84EFC /* EmptyStateLabel.swift */; };
-		3057029D24C6442800D84EFC /* FlexLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3057029C24C6442800D84EFC /* FlexLabel.swift */; };
-		3057029E24C6444D00D84EFC /* FlexLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3057029C24C6442800D84EFC /* FlexLabel.swift */; };
 		3057029F24C6445000D84EFC /* EmptyStateLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3057029A24C6441300D84EFC /* EmptyStateLabel.swift */; };
 		305702A124C6453700D84EFC /* TypeAlias.swift in Sources */ = {isa = PBXBuildFile; fileRef = 305702A024C6453700D84EFC /* TypeAlias.swift */; };
 		305702A224C6455400D84EFC /* TypeAlias.swift in Sources */ = {isa = PBXBuildFile; fileRef = 305702A024C6453700D84EFC /* TypeAlias.swift */; };
@@ -237,7 +240,6 @@
 		3052C60D253F088E007D13EA /* DetectorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetectorType.swift; sourceTree = "<group>"; };
 		3057027E24C5B2F800D84EFC /* ChatListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListViewModel.swift; sourceTree = "<group>"; };
 		3057029A24C6441300D84EFC /* EmptyStateLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyStateLabel.swift; sourceTree = "<group>"; };
-		3057029C24C6442800D84EFC /* FlexLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlexLabel.swift; sourceTree = "<group>"; };
 		305702A024C6453700D84EFC /* TypeAlias.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeAlias.swift; sourceTree = "<group>"; };
 		305961832346125000C80F33 /* UIEdgeInsets+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIEdgeInsets+Extensions.swift"; sourceTree = "<group>"; };
 		305961852346125000C80F33 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = "<group>"; };
@@ -795,7 +797,6 @@
 			isa = PBXGroup;
 			children = (
 				AE406EEE240FA454005F7022 /* Cell */,
-				3057029C24C6442800D84EFC /* FlexLabel.swift */,
 				3057029A24C6441300D84EFC /* EmptyStateLabel.swift */,
 				AEF53BD4248904BF00D309C1 /* GalleryTimeLabel.swift */,
 				B26B3BC6236DC3DC008ED35A /* SwitchCell.swift */,
@@ -1173,14 +1174,18 @@
 			buildActionMask = 2147483647;
 			files = (
 				3057027F24C5B2F800D84EFC /* ChatListViewModel.swift in Sources */,
-				3057029E24C6444D00D84EFC /* FlexLabel.swift in Sources */,
 				302589FF2452FA280086C1CD /* ShareAttachment.swift in Sources */,
 				3057029F24C6445000D84EFC /* EmptyStateLabel.swift in Sources */,
 				305702A224C6455400D84EFC /* TypeAlias.swift in Sources */,
+				30152C9D25A5D95400377714 /* MessageLabelDelegate.swift in Sources */,
 				30E8F2442449C64100CE2C90 /* ChatListCell.swift in Sources */,
 				30E8F2132447285600CE2C90 /* ShareViewController.swift in Sources */,
+				30152C9A25A5D92200377714 /* DetectorType.swift in Sources */,
+				30152CA025A5D97900377714 /* UIEdgeInsets+Extensions.swift in Sources */,
 				30E8F253244DAD0E00CE2C90 /* SendingController.swift in Sources */,
 				3057028C24C5E7B600D84EFC /* ContactCellViewModel.swift in Sources */,
+				30152C9425A5D91400377714 /* PaddingTextView.swift in Sources */,
+				30152C9725A5D91900377714 /* MessageLabel.swift in Sources */,
 				30E8F2422448B77600CE2C90 /* ChatListController.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -1206,7 +1211,6 @@
 				30734326249A280B00BF9AD1 /* MediaQualityController.swift in Sources */,
 				AE6EC5282497B9B200A400E4 /* ThumbnailCache.swift in Sources */,
 				30FDB70524D1C1000066C48D /* ChatViewController.swift in Sources */,
-				3057029D24C6442800D84EFC /* FlexLabel.swift in Sources */,
 				AE52EA20229EB9F000C586C9 /* EditGroupViewController.swift in Sources */,
 				AE18F294228C602A0007B1BE /* SecuritySettingsController.swift in Sources */,
 				AE39D323249CFC1A007346A1 /* DocumentGalleryController.swift in Sources */,

+ 2 - 6
deltachat-ios/Chat/ChatViewController.swift

@@ -109,6 +109,7 @@ class ChatViewController: UITableViewController {
 
     var emptyStateView: EmptyStateLabel = {
         let view =  EmptyStateLabel()
+        view.isHidden = true
         return view
     }()
 
@@ -195,12 +196,7 @@ class ChatViewController: UITableViewController {
     }
 
     private func configureEmptyStateView() {
-        view.addSubview(emptyStateView)
-        emptyStateView.translatesAutoresizingMaskIntoConstraints = false
-        emptyStateView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40).isActive = true
-        emptyStateView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -40).isActive = true
-        emptyStateView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor).isActive = true
-        emptyStateView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
+        emptyStateView.addCenteredTo(parentView: view)
     }
 
     override func viewWillAppear(_ animated: Bool) {

+ 1 - 6
deltachat-ios/Controller/BlockedContactsViewController.swift

@@ -36,12 +36,7 @@ class BlockedContactsViewController: GroupMembersViewController, GroupMemberSele
 
     // MARK: - setup
     private func setupSubviews() {
-        view.addSubview(emptyStateView)
-        emptyStateView.translatesAutoresizingMaskIntoConstraints = false
-        emptyStateView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor).isActive = true
-        emptyStateView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
-        emptyStateView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40).isActive = true
-        emptyStateView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -40).isActive = true
+        emptyStateView.addCenteredTo(parentView: view)
     }
 
     // MARK: - actions + updates

+ 1 - 6
deltachat-ios/Controller/ChatListController.swift

@@ -146,12 +146,7 @@ class ChatListController: UITableViewController {
     }
     // MARK: - setup
     private func setupSubviews() {
-        view.addSubview(emptySearchStateLabel)
-        emptySearchStateLabel.translatesAutoresizingMaskIntoConstraints = false
-        emptySearchStateLabel.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor).isActive = true
-        emptySearchStateLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40).isActive = true
-        emptySearchStateLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -40).isActive = true
-        emptySearchStateLabel.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
+        emptySearchStateLabel.addCenteredTo(parentView: view)
     }
 
     // MARK: - configuration

+ 1 - 6
deltachat-ios/Controller/DocumentGalleryController.swift

@@ -80,12 +80,7 @@ class DocumentGalleryController: UIViewController {
         tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
         tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
 
-        view.addSubview(emptyStateView)
-        emptyStateView.translatesAutoresizingMaskIntoConstraints = false
-        emptyStateView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
-        emptyStateView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
-        emptyStateView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor).isActive = true
-        emptyStateView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
+        emptyStateView.addCenteredTo(parentView: view)
     }
 
     private func setupContextMenuIfNeeded() {

+ 1 - 6
deltachat-ios/Controller/GalleryViewController.swift

@@ -115,12 +115,7 @@ class GalleryViewController: UIViewController {
         timeLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10).isActive = true
         timeLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
 
-        view.addSubview(emptyStateView)
-        emptyStateView.translatesAutoresizingMaskIntoConstraints = false
-        emptyStateView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
-        emptyStateView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
-        emptyStateView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor).isActive = true
-        emptyStateView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
+        emptyStateView.addCenteredTo(parentView: view)
     }
 
     private func setupContextMenuIfNeeded() {

+ 15 - 1
deltachat-ios/Controller/GroupMembersViewController.swift

@@ -66,6 +66,10 @@ class GroupMembersViewController: UITableViewController {
         return label
     }()
 
+    private lazy var emptySearchStateLabelWidthConstraint: NSLayoutConstraint? = {
+        return emptySearchStateLabel.widthAnchor.constraint(equalTo: tableView.widthAnchor)
+    }()
+
     init() {
         self.dcContext = DcContext.shared
         super.init(style: .grouped)
@@ -88,6 +92,7 @@ class GroupMembersViewController: UITableViewController {
 
     private func configureTableView() {
         tableView.register(ContactCell.self, forCellReuseIdentifier: ContactCell.reuseIdentifier)
+        tableView.sectionHeaderHeight = UITableView.automaticDimension
     }
 
     // MARK: - UITableView datasource + delegate
@@ -167,13 +172,22 @@ extension GroupMembersViewController: UISearchResultsUpdating {
                 searchText
             )
             emptySearchStateLabel.text = text
-            emptySearchStateLabel.frame = CGRect(x: 0, y: 0, width: 0, height: emptySearchStateLabel.intrinsicContentSize.height)
             emptySearchStateLabel.isHidden = false
             tableView.tableHeaderView = emptySearchStateLabel
+            emptySearchStateLabelWidthConstraint?.isActive = true
         } else {
             emptySearchStateLabel.text = nil
             emptySearchStateLabel.isHidden = true
+            emptySearchStateLabelWidthConstraint?.isActive = false
             tableView.tableHeaderView = nil
         }
     }
+
+    override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        //ensure the empty search state message can be fully read
+        if searchController.isActive && filteredContactIds.isEmpty {
+            tableView.scrollRectToVisible(emptySearchStateLabel.frame, animated: false)
+        }
+    }
 }

+ 15 - 1
deltachat-ios/Controller/NewChatViewController.swift

@@ -31,6 +31,10 @@ class NewChatViewController: UITableViewController {
         return label
     }()
 
+    private lazy var emptySearchStateLabelWidthConstraint: NSLayoutConstraint? = {
+        return emptySearchStateLabel.widthAnchor.constraint(equalTo: tableView.widthAnchor)
+    }()
+
     private var contactIds: [Int]
     private var filteredContactIds: [Int] = []
 
@@ -84,6 +88,7 @@ class NewChatViewController: UITableViewController {
         }
         tableView.register(ActionCell.self, forCellReuseIdentifier: "actionCell")
         tableView.register(ContactCell.self, forCellReuseIdentifier: "contactCell")
+        tableView.sectionHeaderHeight = UITableView.automaticDimension
     }
 
     override func viewWillAppear(_ animated: Bool) {
@@ -284,15 +289,24 @@ class NewChatViewController: UITableViewController {
             )
             emptySearchStateLabel.text = text
             emptySearchStateLabel.isHidden = false
-            emptySearchStateLabel.frame = CGRect(x: 0, y: 0, width: 0, height: emptySearchStateLabel.intrinsicContentSize.height)
             tableView.tableHeaderView = emptySearchStateLabel
+            emptySearchStateLabelWidthConstraint?.isActive = true
         } else {
             emptySearchStateLabel.text = nil
             emptySearchStateLabel.isHidden = true
+            emptySearchStateLabelWidthConstraint?.isActive = false
             tableView.tableHeaderView = nil
         }
     }
 
+    override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        //ensure the empty search state message can be fully read
+        if searchController.isActive && filteredContactIds.isEmpty {
+            tableView.scrollRectToVisible(emptySearchStateLabel.frame, animated: false)
+        }
+    }
+
     // MARK: - coordinator
     private func showNewGroupController(isVerified: Bool) {
         let newGroupController = NewGroupController(dcContext: dcContext, isVerified: isVerified)

+ 16 - 32
deltachat-ios/View/EmptyStateLabel.swift

@@ -1,43 +1,27 @@
 import UIKit
 import DcCore
 
-class EmptyStateLabel: FlexLabel {
-
-    override var text: String? {
-        set {
-            guard let newValue = newValue else {
-                super.label.attributedText = nil
-                return
-            }
-
-            guard let style = NSMutableParagraphStyle.default.mutableCopy() as? NSMutableParagraphStyle else {
-                label.attributedText = NSAttributedString(string: newValue)
-                return
-            }
-            style.alignment = NSTextAlignment.natural
-            style.lineBreakMode = .byWordWrapping
-            attributedText = NSAttributedString(
-                string: newValue,
-                attributes: [.paragraphStyle: style]
-            )
-        }
-        get {
-            return super.label.text
-        }
-    }
-
-    override var intrinsicContentSize: CGSize {
-        let width = layoutMargins.left + layoutMargins.right + label.intrinsicContentSize.width
-        let height = layoutMargins.top + layoutMargins.bottom + label.intrinsicContentSize.height
-        return CGSize(width: width, height: height)
-    }
+class EmptyStateLabel: PaddingTextView {
 
     override init() {
         super.init()
-        label.backgroundColor = DcColors.systemMessageBackgroundColor
+        backgroundColor = DcColors.systemMessageBackgroundColor
         label.textColor = DcColors.defaultTextColor
-        label.layer.cornerRadius = 10
+        layer.cornerRadius = 10
         label.clipsToBounds = true
+        paddingTop = 15
+        paddingBottom = 15
+        paddingLeading = 15
+        paddingTrailing = 15
+        translatesAutoresizingMaskIntoConstraints = false
+    }
+
+    func addCenteredTo(parentView: UIView) {
+        parentView.addSubview(self)
+        leadingAnchor.constraint(equalTo: parentView.leadingAnchor, constant: 40).isActive = true
+        trailingAnchor.constraint(equalTo: parentView.trailingAnchor, constant: -40).isActive = true
+        centerYAnchor.constraint(equalTo: parentView.safeAreaLayoutGuide.centerYAnchor).isActive = true
+        centerXAnchor.constraint(equalTo: parentView.safeAreaLayoutGuide.centerXAnchor).isActive = true
     }
 
     required init?(coder: NSCoder) {

+ 0 - 84
deltachat-ios/View/FlexLabel.swift

@@ -1,84 +0,0 @@
-import UIKit
-
-/// view that contains a label (horizontally centered) and
-/// allows it's label to grow/shrink within it's available space
-class FlexLabel: UIView {
-
-    var text: String? {
-        set {
-            label.text = newValue
-        }
-        get {
-            return label.text
-        }
-    }
-
-    var textColor: UIColor {
-        set {
-            label.textColor = newValue
-        }
-        get {
-            return label.textColor
-        }
-    }
-
-    var attributedText: NSAttributedString? {
-        set {
-            label.attributedText = newValue
-        }
-        get {
-            return label.attributedText
-        }
-    }
-
-    lazy var label: UILabel = {
-        let label = PaddingLabel(top: 15, left: 15, bottom: 15, right: 15)
-        label.numberOfLines = 0
-        return label
-    }()
-
-    init() {
-        super.init(frame: .zero)
-        setupSubviews()
-    }
-
-    required init?(coder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-
-    private func setupSubviews() {
-        addSubview(label)
-        label.translatesAutoresizingMaskIntoConstraints = false
-        label.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0).isActive = true
-        label.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0).isActive = true
-        label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
-        label.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor).isActive = true
-        label.widthAnchor.constraint(lessThanOrEqualTo: widthAnchor, multiplier: 0.95).isActive = true
-    }
-
-    ///FIXME: - replace this implementation, it's cutting off long texts, check PaddingTextView
-    class PaddingLabel: UILabel {
-
-        let insets: UIEdgeInsets
-        init(top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat) {
-            self.insets = UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)
-            super.init(frame: .zero)
-        }
-
-        required init?(coder: NSCoder) {
-            fatalError("init(coder:) has not been implemented")
-        }
-
-        override func drawText(in rect: CGRect) {
-            super.drawText(in: rect.inset(by: insets))
-        }
-
-        override var intrinsicContentSize: CGSize {
-            let size = super.intrinsicContentSize
-            return CGSize(
-                width: size.width + insets.left + insets.right,
-                height: size.height + insets.top + insets.bottom
-            )
-        }
-    }
-}