From bcbc461ea5db3ca988ee9abf0f0eb6d814b930a0 Mon Sep 17 00:00:00 2001 From: Martin Hartl Date: Sat, 11 Dec 2021 08:23:58 +0100 Subject: [PATCH] Migrate ItemTableViewCell to programmatic layout --- Icro.xcodeproj/project.pbxproj | 6 - .../xcschemes/xcschememanagement.plist | 2 +- Icro/ViewController/ListViewController.swift | 2 +- .../Configurator/ItemCellConfigurator.swift | 3 +- IcroUIKit/View/ItemTableViewCell.swift | 208 ++++++++++++------ IcroUIKit/View/ItemTableViewCell.xib | 108 --------- 6 files changed, 149 insertions(+), 180 deletions(-) delete mode 100644 IcroUIKit/View/ItemTableViewCell.xib diff --git a/Icro.xcodeproj/project.pbxproj b/Icro.xcodeproj/project.pbxproj index 2887b3c..a8e7d79 100644 --- a/Icro.xcodeproj/project.pbxproj +++ b/Icro.xcodeproj/project.pbxproj @@ -69,7 +69,6 @@ C5046525274F77C60004B812 /* Font+Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C550D77F2740F79000E10CF6 /* Font+Settings.swift */; }; C5046526274F77C60004B812 /* SingleImageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC1820520790C930061F261 /* SingleImageCollectionViewCell.swift */; }; C5046527274F77C60004B812 /* FakeTableCellButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2A5A7920A6196700B54399 /* FakeTableCellButton.swift */; }; - C5046528274F77C60004B812 /* ItemTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BE9455E2079EFCE004F9FF4 /* ItemTableViewCell.xib */; }; C5046529274F77C60004B812 /* LinkLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3C79AC21C4E58B00F699B8 /* LinkLabel.swift */; }; C504652A274F77C60004B812 /* ItemCellConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B583720212861220076121E /* ItemCellConfigurator.swift */; }; C504652B274F77C60004B812 /* ComposeViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B60CF4D2072A66C00C51258 /* ComposeViewController.xib */; }; @@ -83,7 +82,6 @@ C5046536274F77C70004B812 /* Font+Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C550D77F2740F79000E10CF6 /* Font+Settings.swift */; }; C5046537274F77C70004B812 /* SingleImageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC1820520790C930061F261 /* SingleImageCollectionViewCell.swift */; }; C5046538274F77C70004B812 /* FakeTableCellButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2A5A7920A6196700B54399 /* FakeTableCellButton.swift */; }; - C5046539274F77C70004B812 /* ItemTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BE9455E2079EFCE004F9FF4 /* ItemTableViewCell.xib */; }; C504653A274F77C70004B812 /* LinkLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3C79AC21C4E58B00F699B8 /* LinkLabel.swift */; }; C504653B274F77C70004B812 /* ItemCellConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B583720212861220076121E /* ItemCellConfigurator.swift */; }; C504653C274F77C70004B812 /* ComposeViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B60CF4D2072A66C00C51258 /* ComposeViewController.xib */; }; @@ -365,7 +363,6 @@ 5BDE18F2216A76FA001254F4 /* SettingsStoryboard.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SettingsStoryboard.storyboard; sourceTree = ""; }; 5BDE18F4216A7A47001254F4 /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; 5BE9455D2079EFCE004F9FF4 /* ItemTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemTableViewCell.swift; sourceTree = ""; }; - 5BE9455E2079EFCE004F9FF4 /* ItemTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ItemTableViewCell.xib; sourceTree = ""; }; 5BF2DAA3228800B30055240D /* DiscoveryCategoryComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveryCategoryComponent.swift; sourceTree = ""; }; 5BF2DAA5228802000055240D /* UserDefaultsMigrationComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsMigrationComponent.swift; sourceTree = ""; }; 5BF6637E218961AB00ABA671 /* DiscoveryCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveryCategory.swift; sourceTree = ""; }; @@ -646,7 +643,6 @@ 5BC1820520790C930061F261 /* SingleImageCollectionViewCell.swift */, 5B2A5A7920A6196700B54399 /* FakeTableCellButton.swift */, 5BE9455D2079EFCE004F9FF4 /* ItemTableViewCell.swift */, - 5BE9455E2079EFCE004F9FF4 /* ItemTableViewCell.xib */, 5B3C79AC21C4E58B00F699B8 /* LinkLabel.swift */, C55BAF28275D489D009E8A9C /* ComposeKeyboardInputView.swift */, ); @@ -1224,7 +1220,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - C5046539274F77C70004B812 /* ItemTableViewCell.xib in Resources */, C504653C274F77C70004B812 /* ComposeViewController.xib in Resources */, 5BC1384721C639160040353C /* Assets.xcassets in Resources */, ); @@ -1259,7 +1254,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - C5046528274F77C60004B812 /* ItemTableViewCell.xib in Resources */, C504652B274F77C60004B812 /* ComposeViewController.xib in Resources */, BF707E742131C45600506E64 /* Localizable.strings in Resources */, C5FA2ACE1EB4482C006DEB40 /* LaunchScreen.storyboard in Resources */, diff --git a/Icro.xcodeproj/xcuserdata/martinhartl.xcuserdatad/xcschemes/xcschememanagement.plist b/Icro.xcodeproj/xcuserdata/martinhartl.xcuserdatad/xcschemes/xcschememanagement.plist index 9daf2f6..59a5781 100644 --- a/Icro.xcodeproj/xcuserdata/martinhartl.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Icro.xcodeproj/xcuserdata/martinhartl.xcuserdatad/xcschemes/xcschememanagement.plist @@ -12,7 +12,7 @@ Icro-Next.xcscheme_^#shared#^_ orderHint - 5 + 4 Icro-Share.xcscheme_^#shared#^_ diff --git a/Icro/ViewController/ListViewController.swift b/Icro/ViewController/ListViewController.swift index 27ea4a6..a67fa8a 100644 --- a/Icro/ViewController/ListViewController.swift +++ b/Icro/ViewController/ListViewController.swift @@ -16,7 +16,7 @@ final class ListViewController: UIViewController, LoadingViewController { tableView.separatorColor = Color.separatorColor tableView.refreshControl = UIRefreshControl() tableView.refreshControl?.addTarget(viewModel, action: #selector(ListViewModel.load), for: .valueChanged) - tableView.register(cellType: ItemTableViewCell.self) + tableView.registerClass(cellType: ItemTableViewCell.self) tableView.registerClass(cellType: HostingCell.self) tableView.registerClass(cellType: LoadMoreTableViewCell.self) tableView.estimatedRowHeight = UITableView.automaticDimension diff --git a/IcroUIKit/Configurator/ItemCellConfigurator.swift b/IcroUIKit/Configurator/ItemCellConfigurator.swift index 7da8f07..8fdf7ca 100644 --- a/IcroUIKit/Configurator/ItemCellConfigurator.swift +++ b/IcroUIKit/Configurator/ItemCellConfigurator.swift @@ -36,8 +36,7 @@ public final class ItemCellConfigurator: NSObject { self.itemNavigator.openReply(item: item) } - cell.timeLabel.text = item.relativeDateString - cell.atUsernameLabel.text = "@" + (item.author.username ?? "") + cell.atUsernameLabel.text = "@" + (item.author.username ?? "") + " • \(item.relativeDateString)" cell.didTapMedia = { [weak self] media, index in self?.itemNavigator.openMedia(media: media, index: index) diff --git a/IcroUIKit/View/ItemTableViewCell.swift b/IcroUIKit/View/ItemTableViewCell.swift index 1049b35..e3c6453 100644 --- a/IcroUIKit/View/ItemTableViewCell.swift +++ b/IcroUIKit/View/ItemTableViewCell.swift @@ -12,60 +12,67 @@ import Settings public final class ItemTableViewCell: UITableViewCell { enum Constants { + static let layoutSpace: CGFloat = 16.0 + + static let avatarImageHeightWidth = 42.0 static let actionButtonWidth: CGFloat = 120.0 } var isFavorite: Bool = false - @IBOutlet private weak var imageHeightConstraint: NSLayoutConstraint! + let avatarImageView: UIImageView = { + let imageView = UIImageView() + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.isUserInteractionEnabled = true - @IBOutlet weak var avatarImageView: UIImageView! { - didSet { - avatarImageView.layer.cornerRadius = 20 - avatarImageView.clipsToBounds = true - } - } - @IBOutlet weak var attributedLabel: LinkLabel! { - didSet { - attributedLabel.isOpaque = true - } - } - @IBOutlet weak var usernameLabel: UILabel! { - didSet { - usernameLabel.isOpaque = true - usernameLabel.adjustsFontForContentSizeCategory = true - usernameLabel.font = Font().name - } - } - @IBOutlet weak var atUsernameLabel: UILabel! { - didSet { - atUsernameLabel.textColor = Color.secondaryTextColor - atUsernameLabel.isOpaque = true - atUsernameLabel.adjustsFontForContentSizeCategory = true - atUsernameLabel.font = Font().username - } - } - @IBOutlet weak var timeLabel: UILabel! { - didSet { - timeLabel.textColor = Color.secondaryTextColor - timeLabel.isOpaque = true - timeLabel.adjustsFontForContentSizeCategory = true - timeLabel.font = Font().username - } - } + imageView.layer.cornerRadius = 20 + imageView.clipsToBounds = true + + return imageView + }() - @IBOutlet weak var collectionViewHeightConstraint: NSLayoutConstraint! + let attributedLabel: LinkLabel = { + let label = LinkLabel() + label.isOpaque = true + label.translatesAutoresizingMaskIntoConstraints = false + label.numberOfLines = 0 + label.isUserInteractionEnabled = true + + return label + }() + + let usernameLabel: UILabel = { + let label = UILabel() + + label.isOpaque = true + label.adjustsFontForContentSizeCategory = true + label.font = Font().name + + return label + }() + + let atUsernameLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + + label.textColor = Color.secondaryTextColor + label.isOpaque = true + label.adjustsFontForContentSizeCategory = true + label.font = Font().username + + return label + }() var itemID: String? var media = [Media]() { didSet { if media.count == 1 { - collectionViewHeightConstraint.constant = 240 + collectionViewHeightConstraint?.constant = 240 } else if media.count > 1 { - collectionViewHeightConstraint.constant = 140 + collectionViewHeightConstraint?.constant = 140 } else { - collectionViewHeightConstraint.constant = 0 + collectionViewHeightConstraint?.constant = 0 } imageCollectionView.reloadData() } @@ -80,32 +87,53 @@ public final class ItemTableViewCell: UITableViewCell { return button }() - @IBOutlet weak var imageCollectionView: UICollectionView! { - didSet { - imageCollectionView.registerClass(cellType: SingleImageCollectionViewCell.self) - imageCollectionView.delegate = self - imageCollectionView.dataSource = self - imageCollectionView.allowsSelection = true - } - } + private var collectionViewHeightConstraint: NSLayoutConstraint? + private let imageCollectionView: UICollectionView = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.minimumLineSpacing = 10.0 + layout.minimumInteritemSpacing = 10.0 + + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.translatesAutoresizingMaskIntoConstraints = false + collectionView.registerClass(cellType: SingleImageCollectionViewCell.self) + collectionView.allowsSelection = true + collectionView.layer.cornerRadius = 4.0 + + return collectionView + }() + + private let titleHorizontalStackView: UIStackView = { + let stackView = UIStackView() + stackView.spacing = Constants.layoutSpace / 2.0 + stackView.alignment = .top + stackView.axis = .horizontal + stackView.translatesAutoresizingMaskIntoConstraints = false + + return stackView + }() + + private let usernamesVerticalStackView: UIStackView = { + let stackView = UIStackView() + stackView.axis = .vertical + stackView.alignment = .fill + stackView.translatesAutoresizingMaskIntoConstraints = false + + return stackView + }() var didTapAvatar: (() -> Void)? var didSelectAccessibilityLink :(() -> Void)? var didTapMedia: (([Media], Int) -> Void)? var didTapReply: (() -> Void)? - override public func prepareForReuse() { - media = [] - updateAppearance() - avatarImageView.image = nil - isFavorite = false - super.prepareForReuse() - } - - override public func awakeFromNib() { - super.awakeFromNib() + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) setupActionButton() + setupTitleHorizontalStackView() + setupLinkLabel() + setupCollectionView() updateAppearance() let avatarGestureRecognizer = UITapGestureRecognizer(target: self, @@ -114,12 +142,70 @@ public final class ItemTableViewCell: UITableViewCell { backgroundColor = Color.backgroundColor } + required init?(coder: NSCoder) { + fatalError("Not supported") + } + + override public func prepareForReuse() { + media = [] + updateAppearance() + avatarImageView.image = nil + isFavorite = false + super.prepareForReuse() + } + private func setupActionButton() { - addSubview(actionButton) + contentView.addSubview(actionButton) NSLayoutConstraint.activate([ actionButton.widthAnchor.constraint(equalToConstant: Constants.actionButtonWidth), - actionButton.bottomAnchor.constraint(equalTo: bottomAnchor), - actionButton.centerXAnchor.constraint(equalTo: centerXAnchor) + actionButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + actionButton.centerXAnchor.constraint(equalTo: contentView.centerXAnchor) + ]) + } + + private func setupCollectionView() { + imageCollectionView.delegate = self + imageCollectionView.dataSource = self + + contentView.addSubview(imageCollectionView) + + let heightConstraint = imageCollectionView.heightAnchor.constraint(equalToConstant: 140.0) + self.collectionViewHeightConstraint = heightConstraint + + NSLayoutConstraint.activate([ + heightConstraint, + imageCollectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: Constants.layoutSpace), + imageCollectionView.bottomAnchor.constraint(equalTo: actionButton.topAnchor, constant: -Constants.layoutSpace / 4.0), + imageCollectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constants.layoutSpace), + imageCollectionView.topAnchor.constraint(equalTo: attributedLabel.bottomAnchor, constant: Constants.layoutSpace) + ]) + } + + private func setupLinkLabel() { + contentView.addSubview(attributedLabel) + + NSLayoutConstraint.activate([ + attributedLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: Constants.layoutSpace), + attributedLabel.topAnchor.constraint(equalTo: titleHorizontalStackView.bottomAnchor, constant: Constants.layoutSpace), + attributedLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constants.layoutSpace) + ]) + } + + private func setupTitleHorizontalStackView() { + contentView.addSubview(titleHorizontalStackView) + + titleHorizontalStackView.addArrangedSubview(avatarImageView) + + usernamesVerticalStackView.addArrangedSubview(usernameLabel) + usernamesVerticalStackView.addArrangedSubview(atUsernameLabel) + titleHorizontalStackView.addArrangedSubview(usernamesVerticalStackView) + + NSLayoutConstraint.activate([ + titleHorizontalStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: Constants.layoutSpace), + titleHorizontalStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: Constants.layoutSpace), + titleHorizontalStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constants.layoutSpace), + avatarImageView.heightAnchor.constraint(equalToConstant: Constants.avatarImageHeightWidth), + avatarImageView.widthAnchor.constraint(equalToConstant: Constants.avatarImageHeightWidth) ]) } @@ -167,8 +253,6 @@ public final class ItemTableViewCell: UITableViewCell { atUsernameLabel.textColor = Color.secondaryTextColor usernameLabel.textColor = Color.textColor usernameLabel.backgroundColor = Color.backgroundColor - timeLabel.backgroundColor = Color.backgroundColor - timeLabel.textColor = Color.secondaryTextColor imageCollectionView.backgroundColor = Color.accentSuperLight attributedLabel.backgroundColor = Color.backgroundColor backgroundColor = Color.backgroundColor diff --git a/IcroUIKit/View/ItemTableViewCell.xib b/IcroUIKit/View/ItemTableViewCell.xib deleted file mode 100644 index c91337d..0000000 --- a/IcroUIKit/View/ItemTableViewCell.xib +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -