-
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 #21842 from wordpress-mobile/task/pages-new-cells
Posts & Pages: New design for pages cells
- Loading branch information
Showing
18 changed files
with
272 additions
and
533 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 was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
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,134 @@ | ||
import Foundation | ||
import UIKit | ||
import Combine | ||
|
||
final class PageListCell: UITableViewCell, Reusable { | ||
|
||
// MARK: - Views | ||
|
||
private let titleLabel = UILabel() | ||
private let badgeIconView = UIImageView() | ||
private let badgesLabel = UILabel() | ||
private let featuredImageView = CachedAnimatedImageView() | ||
private let ellipsisButton = UIButton(type: .custom) | ||
private var cancellables: [AnyCancellable] = [] | ||
|
||
// MARK: - Properties | ||
|
||
private lazy var imageLoader = ImageLoader(imageView: featuredImageView, loadingIndicator: SolidColorActivityIndicator()) | ||
|
||
// 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 | ||
|
||
override func prepareForReuse() { | ||
super.prepareForReuse() | ||
|
||
cancellables = [] | ||
imageLoader.prepareForReuse() | ||
} | ||
|
||
func configure(with viewModel: PageListItemViewModel) { | ||
viewModel.$title.sink { [titleLabel] in | ||
titleLabel.attributedText = $0 | ||
}.store(in: &cancellables) | ||
|
||
badgeIconView.image = viewModel.badgeIcon | ||
badgeIconView.isHidden = viewModel.badgeIcon == nil | ||
badgesLabel.text = viewModel.badges | ||
|
||
imageLoader.prepareForReuse() | ||
featuredImageView.isHidden = viewModel.imageURL == nil | ||
if let imageURL = viewModel.imageURL { | ||
let host = MediaHost(with: viewModel.page) { error in | ||
WordPressAppDelegate.crashLogging?.logError(error) | ||
} | ||
imageLoader.loadImage(with: imageURL, from: host, preferredSize: Constants.imageSize) | ||
} | ||
} | ||
|
||
// MARK: - Setup | ||
|
||
private func setupViews() { | ||
separatorInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 0) | ||
|
||
setupLabels() | ||
setupFeaturedImageView() | ||
setupEllipsisButton() | ||
|
||
let badgesStackView = UIStackView(arrangedSubviews: [ | ||
badgeIconView, badgesLabel, UIView() | ||
]) | ||
badgesStackView.alignment = .bottom | ||
badgesStackView.spacing = 2 | ||
|
||
let labelsStackView = UIStackView(arrangedSubviews: [ | ||
titleLabel, badgesStackView | ||
]) | ||
labelsStackView.spacing = 4 | ||
labelsStackView.axis = .vertical | ||
|
||
let contentStackView = UIStackView(arrangedSubviews: [ | ||
labelsStackView, featuredImageView, ellipsisButton | ||
]) | ||
contentStackView.spacing = 8 | ||
contentStackView.alignment = .center | ||
contentStackView.isLayoutMarginsRelativeArrangement = true | ||
contentStackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 12, leading: 16, bottom: 12, trailing: 16) | ||
|
||
NSLayoutConstraint.activate([ | ||
badgeIconView.heightAnchor.constraint(equalToConstant: 18), | ||
badgeIconView.heightAnchor.constraint(equalTo: badgeIconView.widthAnchor, multiplier: 1) | ||
]) | ||
|
||
contentView.addSubview(contentStackView) | ||
contentStackView.translatesAutoresizingMaskIntoConstraints = false | ||
contentView.pinSubviewToAllEdges(contentStackView) | ||
} | ||
|
||
private func setupLabels() { | ||
titleLabel.translatesAutoresizingMaskIntoConstraints = false | ||
titleLabel.adjustsFontForContentSizeCategory = true | ||
titleLabel.numberOfLines = 1 | ||
|
||
badgeIconView.tintColor = UIColor.secondaryLabel | ||
|
||
badgesLabel.font = WPStyleGuide.fontForTextStyle(.footnote) | ||
badgesLabel.textColor = UIColor.secondaryLabel | ||
} | ||
|
||
private func setupFeaturedImageView() { | ||
featuredImageView.translatesAutoresizingMaskIntoConstraints = false | ||
featuredImageView.contentMode = .scaleAspectFill | ||
featuredImageView.layer.masksToBounds = true | ||
featuredImageView.layer.cornerRadius = 5 | ||
|
||
NSLayoutConstraint.activate([ | ||
featuredImageView.widthAnchor.constraint(equalToConstant: Constants.imageSize.width), | ||
featuredImageView.heightAnchor.constraint(equalToConstant: Constants.imageSize.height), | ||
]) | ||
} | ||
|
||
private func setupEllipsisButton() { | ||
ellipsisButton.translatesAutoresizingMaskIntoConstraints = false | ||
ellipsisButton.setImage(UIImage(named: "more-horizontal-mobile"), for: .normal) | ||
ellipsisButton.tintColor = .listIcon | ||
|
||
NSLayoutConstraint.activate([ | ||
ellipsisButton.widthAnchor.constraint(equalToConstant: 24) | ||
]) | ||
} | ||
} | ||
|
||
private enum Constants { | ||
static let imageSize = CGSize(width: 44, height: 44) | ||
} |
68 changes: 68 additions & 0 deletions
68
WordPress/Classes/ViewRelated/Pages/PageListItemViewModel.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,68 @@ | ||
import Foundation | ||
|
||
final class PageListItemViewModel { | ||
let page: Page | ||
@Published var title: NSAttributedString | ||
let badgeIcon: UIImage? | ||
let badges: String | ||
let imageURL: URL? | ||
let accessibilityIdentifier: String? | ||
|
||
init(page: Page) { | ||
self.page = page | ||
self.title = makeContentAttributedString(for: page) | ||
self.badgeIcon = makeBadgeIcon(for: page) | ||
self.badges = makeBadgesString(for: page) | ||
self.imageURL = page.featuredImageURL | ||
self.accessibilityIdentifier = page.slugForDisplay() | ||
} | ||
} | ||
|
||
private func makeContentAttributedString(for page: Page) -> NSAttributedString { | ||
let page = page.hasRevision() ? page.revision : page | ||
let title = page?.titleForDisplay() ?? "" | ||
return NSAttributedString(string: title, attributes: [ | ||
.font: WPStyleGuide.fontForTextStyle(.callout, fontWeight: .semibold), | ||
.foregroundColor: UIColor.text | ||
]) | ||
} | ||
|
||
private func makeBadgeIcon(for page: Page) -> UIImage? { | ||
if page.isSiteHomepage { | ||
return UIImage(named: "home") | ||
} | ||
if page.isSitePostsPage { | ||
return UIImage(named: "posts") | ||
} | ||
return nil | ||
} | ||
|
||
private func makeBadgesString(for page: Page) -> String { | ||
var badges: [String] = [] | ||
if page.isSiteHomepage { | ||
badges.append(Strings.badgeHomepage) | ||
} else if page.isSitePostsPage { | ||
badges.append(Strings.badgePosts) | ||
} | ||
if let displayDate = page.displayDate() { | ||
badges.append(displayDate.capitalized) | ||
} | ||
if page.hasPrivateState { | ||
badges.append(Strings.badgePrivatePage) | ||
} | ||
if page.hasPendingReviewState { | ||
badges.append(Strings.badgePendingReview) | ||
} | ||
if page.hasLocalChanges() { | ||
badges.append(Strings.badgeLocalChanges) | ||
} | ||
return badges.joined(separator: " · ") | ||
} | ||
|
||
private enum Strings { | ||
static let badgeHomepage = NSLocalizedString("pageList.badgeHomepage", value: "Homepage", comment: "Badge for page cells") | ||
static let badgePosts = NSLocalizedString("pageList.badgePosts", value: "Posts page", comment: "Badge for page cells") | ||
static let badgePrivatePage = NSLocalizedString("pageList.badgePrivate", value: "Private", comment: "Badge for page cells") | ||
static let badgePendingReview = NSLocalizedString("pageList.badgePendingReview", value: "Pending review", comment: "Badge for page cells") | ||
static let badgeLocalChanges = NSLocalizedString("pageList.badgeLocalChanges", value: "Local changes", comment: "Badge for page cells") | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.