-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #21804 from wordpress-mobile/task/21712-update-pos…
…t-cell-design Posts & Pages: Update posts cell design
- Loading branch information
Showing
6 changed files
with
292 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import Foundation | ||
import UIKit | ||
|
||
final class PostListCell: UITableViewCell, Reusable { | ||
|
||
// MARK: - Views | ||
|
||
private lazy var mainStackView: UIStackView = { | ||
let stackView = UIStackView() | ||
stackView.axis = .vertical | ||
return stackView | ||
}() | ||
|
||
private lazy var contentStackView: UIStackView = { | ||
let stackView = UIStackView() | ||
stackView.axis = .horizontal | ||
return stackView | ||
}() | ||
|
||
private let headerView = PostListHeaderView() | ||
private let titleAndSnippetLabel = UILabel() | ||
private let featuredImageView = CachedAnimatedImageView() | ||
private let statusLabel = UILabel() | ||
|
||
// MARK: - Properties | ||
|
||
private lazy var imageLoader = ImageLoader(imageView: featuredImageView) | ||
|
||
// MARK: - Initializers | ||
|
||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { | ||
super.init(style: style, reuseIdentifier: reuseIdentifier) | ||
setupViews() | ||
} | ||
|
||
required init?(coder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
|
||
// MARK: - Public | ||
|
||
func configure(with viewModel: PostListItemViewModel) { | ||
headerView.configure(with: viewModel) | ||
|
||
configureTitleAndSnippet(with: viewModel) | ||
|
||
imageLoader.prepareForReuse() | ||
featuredImageView.isHidden = viewModel.imageURL == nil | ||
if let imageURL = viewModel.imageURL { | ||
let host = MediaHost(with: viewModel.post) { error in | ||
WordPressAppDelegate.crashLogging?.logError(error) | ||
} | ||
let preferredSize = CGSize(width: 44, height: 44) | ||
imageLoader.loadImage(with: imageURL, from: host, preferredSize: preferredSize) | ||
} | ||
|
||
statusLabel.text = viewModel.status | ||
statusLabel.textColor = viewModel.statusColor | ||
statusLabel.isHidden = viewModel.status.isEmpty | ||
} | ||
|
||
private func configureTitleAndSnippet(with viewModel: PostListItemViewModel) { | ||
var titleAndSnippetString = NSMutableAttributedString() | ||
|
||
if let title = viewModel.title, !title.isEmpty { | ||
let attributes: [NSAttributedString.Key: Any] = [ | ||
.font: WPStyleGuide.fontForTextStyle(.callout, fontWeight: .semibold), | ||
.foregroundColor: UIColor.text | ||
] | ||
let titleAttributedString = NSAttributedString(string: "\(title)\n", attributes: attributes) | ||
titleAndSnippetString.append(titleAttributedString) | ||
} | ||
|
||
if let snippet = viewModel.snippet, !snippet.isEmpty { | ||
let attributes: [NSAttributedString.Key: Any] = [ | ||
.font: WPStyleGuide.fontForTextStyle(.footnote, fontWeight: .regular), | ||
.foregroundColor: UIColor.textSubtle | ||
] | ||
let snippetAttributedString = NSAttributedString(string: snippet, attributes: attributes) | ||
titleAndSnippetString.append(snippetAttributedString) | ||
} | ||
|
||
titleAndSnippetLabel.attributedText = titleAndSnippetString | ||
} | ||
|
||
// MARK: - Setup | ||
|
||
private func setupViews() { | ||
setupTitleAndSnippetLabel() | ||
setupFeaturedImageView() | ||
setupStatusLabel() | ||
|
||
contentStackView.translatesAutoresizingMaskIntoConstraints = false | ||
contentStackView.addArrangedSubviews([ | ||
titleAndSnippetLabel, | ||
featuredImageView | ||
]) | ||
contentStackView.spacing = 16 | ||
contentStackView.alignment = .top | ||
|
||
mainStackView.translatesAutoresizingMaskIntoConstraints = false | ||
mainStackView.addArrangedSubviews([ | ||
headerView, | ||
contentStackView, | ||
statusLabel | ||
]) | ||
mainStackView.spacing = 4 | ||
mainStackView.isLayoutMarginsRelativeArrangement = true | ||
mainStackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 12, leading: 16, bottom: 12, trailing: 16) | ||
|
||
contentView.addSubview(mainStackView) | ||
contentView.pinSubviewToAllEdges(mainStackView) | ||
contentView.backgroundColor = .systemBackground | ||
} | ||
|
||
private func setupTitleAndSnippetLabel() { | ||
titleAndSnippetLabel.translatesAutoresizingMaskIntoConstraints = false | ||
titleAndSnippetLabel.adjustsFontForContentSizeCategory = true | ||
titleAndSnippetLabel.numberOfLines = 3 | ||
} | ||
|
||
private func setupFeaturedImageView() { | ||
featuredImageView.translatesAutoresizingMaskIntoConstraints = false | ||
featuredImageView.contentMode = .scaleAspectFill | ||
featuredImageView.layer.masksToBounds = true | ||
featuredImageView.layer.cornerRadius = 5 | ||
|
||
NSLayoutConstraint.activate([ | ||
featuredImageView.widthAnchor.constraint(equalToConstant: 64), | ||
featuredImageView.heightAnchor.constraint(equalToConstant: 64), | ||
]) | ||
} | ||
|
||
private func setupStatusLabel() { | ||
statusLabel.translatesAutoresizingMaskIntoConstraints = false | ||
statusLabel.adjustsFontForContentSizeCategory = true | ||
statusLabel.numberOfLines = 1 | ||
statusLabel.font = WPStyleGuide.fontForTextStyle(.footnote, fontWeight: .regular) | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
WordPress/Classes/ViewRelated/Post/PostListHeaderView.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import UIKit | ||
|
||
final class PostListHeaderView: UIView { | ||
|
||
// MARK: - Views | ||
|
||
private lazy var mainStackView: UIStackView = { | ||
let stackView = UIStackView() | ||
stackView.axis = .horizontal | ||
return stackView | ||
}() | ||
|
||
private lazy var labelsStackView: UIStackView = { | ||
let stackView = UIStackView() | ||
stackView.axis = .horizontal | ||
return stackView | ||
}() | ||
|
||
private let dateLabel = UILabel() | ||
private let dotLabel = UILabel() | ||
private let authorLabel = UILabel() | ||
private let ellipsisButton = UIButton(type: .custom) | ||
|
||
// MARK: - Initializer | ||
|
||
override init(frame: CGRect) { | ||
super.init(frame: frame) | ||
setupView() | ||
} | ||
|
||
required init?(coder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
|
||
// MARK: - Public | ||
|
||
func configure(with viewModel: PostListItemViewModel) { | ||
authorLabel.text = viewModel.author | ||
dateLabel.text = viewModel.date | ||
} | ||
|
||
// MARK: - Setup | ||
|
||
private func setupView() { | ||
setupLabelStackView() | ||
setupEllipsisButton() | ||
|
||
mainStackView.translatesAutoresizingMaskIntoConstraints = false | ||
mainStackView.addArrangedSubviews([ | ||
labelsStackView, | ||
ellipsisButton | ||
]) | ||
mainStackView.spacing = 16 | ||
|
||
addSubview(mainStackView) | ||
pinSubviewToAllEdges(mainStackView) | ||
} | ||
|
||
private func setupLabelStackView() { | ||
dotLabel.text = "\u{2022}" | ||
|
||
let labels = [ | ||
dateLabel, | ||
dotLabel, | ||
authorLabel | ||
] | ||
|
||
labels.enumerated().forEach { (index, label) in | ||
label.translatesAutoresizingMaskIntoConstraints = false | ||
label.adjustsFontForContentSizeCategory = true | ||
label.numberOfLines = 1 | ||
label.font = WPStyleGuide.fontForTextStyle(.footnote, fontWeight: .regular) | ||
label.textColor = .textSubtle | ||
|
||
if index < labels.count - 1 { | ||
label.setContentHuggingPriority(.defaultHigh, for: .horizontal) | ||
} | ||
} | ||
|
||
labelsStackView.translatesAutoresizingMaskIntoConstraints = false | ||
labelsStackView.addArrangedSubviews(labels) | ||
labelsStackView.spacing = 2 | ||
} | ||
|
||
private func setupEllipsisButton() { | ||
ellipsisButton.translatesAutoresizingMaskIntoConstraints = false | ||
ellipsisButton.setImage(UIImage(named: "more-horizontal-mobile"), for: .normal) | ||
ellipsisButton.tintColor = .listIcon | ||
ellipsisButton.addTarget(self, action: #selector(ellipsisButtonTapped), for: .touchUpInside) | ||
|
||
NSLayoutConstraint.activate([ | ||
ellipsisButton.widthAnchor.constraint(equalToConstant: 24) | ||
]) | ||
} | ||
|
||
@objc private func ellipsisButtonTapped() { | ||
// TODO | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
WordPress/Classes/ViewRelated/Post/PostListItemViewModel.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import Foundation | ||
|
||
struct PostListItemViewModel { | ||
let post: Post | ||
let title: String? | ||
let snippet: String? | ||
let imageURL: URL? | ||
let date: String? | ||
let accessibilityIdentifier: String? | ||
|
||
private var statusViewModel: PostCardStatusViewModel { .init(post: post) } | ||
|
||
var status: String { statusViewModel.statusAndBadges(separatedBy: " · ")} | ||
var statusColor: UIColor { statusViewModel.statusColor } | ||
var author: String { statusViewModel.author } | ||
|
||
init(post: Post) { | ||
self.post = post | ||
self.title = post.titleForDisplay() | ||
self.snippet = post.contentPreviewForDisplay() | ||
self.imageURL = post.featuredImageURL | ||
self.date = post.displayDate()?.capitalizeFirstWord | ||
self.accessibilityIdentifier = post.slugForDisplay() | ||
} | ||
} | ||
|
||
private extension String { | ||
var capitalizeFirstWord: String { | ||
let firstLetter = self.prefix(1).capitalized | ||
let remainingLetters = self.dropFirst() | ||
return firstLetter + remainingLetters | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters