-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Modal Layout Picker] Create Layout Picker category bar UI using static data #14631
Merged
Merged
Changes from all commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
53c6bca
Create basic category bar cells
chipsnyder e4b193e
Merge branch 'gutenberg/issue/2436-thumbailPreviews' into gutenberg/i…
chipsnyder 64ebf88
Merge branch 'gutenberg/issue/2436-thumbailPreviews' into gutenberg/i…
chipsnyder 2fd02b2
Fill in labels and emoji's for filterbar
chipsnyder 1e1860e
Configure dynamic font for filter bar
chipsnyder 43c1b76
Handle selection background color of filter cells
chipsnyder af0b82d
Update dark colors for the selected pill color
chipsnyder 93772d4
Handle selection background color of filter cells on dark mode
chipsnyder 10864da
Add checkmarks to selected filter pills
chipsnyder 45f693c
Adjust checkmark estimated size and support no emoji's
chipsnyder 925767e
Animate out the change it category selections
chipsnyder b143db9
Merge branch 'gutenberg/issue/2436-thumbailPreviews' into gutenberg/i…
chipsnyder 7882abe
Merge branch 'gutenberg/issue/2436-thumbailPreviews' into gutenberg/i…
chipsnyder 8d1fcdd
Adjust the selection of categories to maintain the correct layout sel…
chipsnyder 1f7d4fd
Merge branch 'gutenberg/issue/2436-thumbailPreviews' into gutenberg/i…
chipsnyder 2716f1a
Adjust shadow to fill in footer when there is one item
chipsnyder 6a0329a
Merge branch 'gutenberg/issue/2436-thumbailPreviews' into gutenberg/i…
chipsnyder 8b83cc6
Remove duplicate file from rename
chipsnyder 4347988
Rename category bar for more generic naming convention
chipsnyder 8586e7b
Update inline documentation
chipsnyder 63d20cb
Merge branch 'gutenberg/issue/2436-thumbailPreviews' into gutenberg/i…
chipsnyder d614251
Merge branch 'gutenberg/issue/2436-thumbailPreviews' into gutenberg/i…
chipsnyder c090097
Update category animation style
chipsnyder c9754b3
Continue to refine shadow
chipsnyder 5d12e74
Support Multi select
chipsnyder 4cecbd6
Add support for SF image on the filter checkmark
chipsnyder 0931631
Merge branch 'gutenberg/issue/2436-thumbailPreviews' into gutenberg/i…
chipsnyder bbdece4
Merge remote-tracking branch 'origin/develop' into gutenberg/issue/24…
chipsnyder 8e1631f
Remove unused var
chipsnyder 348f2ab
Adjust the presentation style to account for difference in Max size d…
chipsnyder 6896894
Resolve style related feedback on using constants
chipsnyder f567888
Adjust Filter bar colors in iOS 12
chipsnyder e4fccb6
Fix display issue on iOS 13 devices
chipsnyder File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
68 changes: 68 additions & 0 deletions
68
WordPress/Classes/ViewRelated/Gutenberg/Layout Picker/GutenbergLayoutFilterBar.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 UIKit | ||
|
||
protocol FilterBarDelegate { | ||
func numberOfFilters() -> Int | ||
func filter(forIndex: Int) -> GutenbergLayoutSection | ||
func didSelectFilter(withIndex selectedIndex: IndexPath, withSelectedIndexes selectedIndexes: [IndexPath]) | ||
func didDeselectFilter(withIndex index: IndexPath, withSelectedIndexes selectedIndexes: [IndexPath]) | ||
} | ||
|
||
class GutenbergLayoutFilterBar: UICollectionView { | ||
var filterDelegate: FilterBarDelegate? | ||
private let defaultCellHeight: CGFloat = 44 | ||
private let defaultCellWidth: CGFloat = 105 | ||
|
||
required init?(coder: NSCoder) { | ||
super.init(coder: coder) | ||
register(LayoutPickerFilterCollectionViewCell.nib, forCellWithReuseIdentifier: LayoutPickerFilterCollectionViewCell.cellReuseIdentifier) | ||
self.delegate = self | ||
self.dataSource = self | ||
} | ||
|
||
private func deselectItem(_ indexPath: IndexPath) { | ||
deselectItem(at: indexPath, animated: true) | ||
collectionView(self, didDeselectItemAt: indexPath) | ||
} | ||
} | ||
|
||
extension GutenbergLayoutFilterBar: UICollectionViewDelegate { | ||
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { | ||
if collectionView.cellForItem(at: indexPath)?.isSelected ?? false { | ||
deselectItem(indexPath) | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { | ||
guard let indexPathsForSelectedItems = collectionView.indexPathsForSelectedItems else { return } | ||
filterDelegate?.didSelectFilter(withIndex: indexPath, withSelectedIndexes: indexPathsForSelectedItems) | ||
} | ||
|
||
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { | ||
filterDelegate?.didDeselectFilter(withIndex: indexPath, withSelectedIndexes: collectionView.indexPathsForSelectedItems ?? []) | ||
} | ||
} | ||
|
||
extension GutenbergLayoutFilterBar: UICollectionViewDelegateFlowLayout { | ||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { | ||
guard let filter = filterDelegate?.filter(forIndex: indexPath.item) else { | ||
return CGSize(width: defaultCellWidth, height: defaultCellHeight) | ||
} | ||
|
||
let width = LayoutPickerFilterCollectionViewCell.estimatedWidth(forFilter: filter) | ||
return CGSize(width: width, height: defaultCellHeight) | ||
} | ||
} | ||
|
||
extension GutenbergLayoutFilterBar: UICollectionViewDataSource { | ||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { | ||
return filterDelegate?.numberOfFilters() ?? 0 | ||
} | ||
|
||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { | ||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LayoutPickerFilterCollectionViewCell.cellReuseIdentifier, for: indexPath) as! LayoutPickerFilterCollectionViewCell | ||
cell.filter = filterDelegate?.filter(forIndex: indexPath.item) | ||
return cell | ||
} | ||
} |
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 |
---|---|---|
|
@@ -27,7 +27,7 @@ class GutenbergLayoutPickerViewController: UIViewController { | |
@IBOutlet weak var titleView: UILabel! | ||
@IBOutlet weak var largeTitleView: UILabel! | ||
@IBOutlet weak var promptView: UILabel! | ||
@IBOutlet weak var categoryBar: UICollectionView! | ||
@IBOutlet weak var filterBar: GutenbergLayoutFilterBar! | ||
@IBOutlet weak var tableView: UITableView! | ||
@IBOutlet weak var footerView: UIView! | ||
@IBOutlet weak var createBlankPageBtn: UIButton! | ||
|
@@ -108,7 +108,9 @@ class GutenbergLayoutPickerViewController: UIViewController { | |
layoutSelected(selectedLayout != nil) | ||
} | ||
} | ||
private var sections = [GutenbergLayoutSection]() | ||
|
||
private var filteredSections: [GutenbergLayoutSection]? | ||
private var sections: [GutenbergLayoutSection] = [] | ||
var layouts = GutenbergPageLayouts(layouts: [], categories: []) { | ||
didSet { | ||
sections = layouts.categories.map({ | ||
|
@@ -135,11 +137,14 @@ class GutenbergLayoutPickerViewController: UIViewController { | |
override func viewDidLoad() { | ||
super.viewDidLoad() | ||
tableView.register(LayoutPickerSectionTableViewCell.nib, forCellReuseIdentifier: LayoutPickerSectionTableViewCell.cellReuseIdentifier) | ||
filterBar.filterDelegate = self | ||
filterBar.allowsMultipleSelection = true | ||
setStaticText() | ||
closeButton.setImage(UIImage.gridicon(.crossSmall), for: .normal) | ||
styleButtons() | ||
layoutHeader() | ||
layouts = GutenbergPageLayoutFactory.makeDefaultPageLayouts() | ||
filterBar.reloadData() | ||
} | ||
|
||
override func viewWillAppear(_ animated: Bool) { | ||
|
@@ -231,8 +236,8 @@ class GutenbergLayoutPickerViewController: UIViewController { | |
} | ||
|
||
private func calculateHeaderSnapPoints() { | ||
minHeaderHeight = categoryBar.frame.height + minHeaderBottomSpacing.constant | ||
_midHeaderHeight = titleToSubtitleSpacing.constant + promptView.frame.height + subtitleToCategoryBarSpacing.constant + categoryBar.frame.height + maxHeaderBottomSpacing.constant | ||
minHeaderHeight = filterBar.frame.height + minHeaderBottomSpacing.constant | ||
_midHeaderHeight = titleToSubtitleSpacing.constant + promptView.frame.height + subtitleToCategoryBarSpacing.constant + filterBar.frame.height + maxHeaderBottomSpacing.constant | ||
_maxHeaderHeight = largeTitleView.frame.height + _midHeaderHeight | ||
} | ||
|
||
|
@@ -317,15 +322,15 @@ extension GutenbergLayoutPickerViewController: UITableViewDelegate { | |
} | ||
|
||
private func containsSelectedLayout(_ layout: GutenbergSelectedLayout, atIndexPath indexPath: IndexPath) -> Bool { | ||
let rowSection = sections[indexPath.row] | ||
let rowSection = (filteredSections ?? sections)[indexPath.row] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about creating a getter for |
||
return (layout.sectionSlug == rowSection.section.slug) | ||
} | ||
} | ||
|
||
extension GutenbergLayoutPickerViewController: UITableViewDataSource { | ||
|
||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | ||
return sections.count | ||
return (filteredSections ?? sections).count | ||
} | ||
|
||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | ||
|
@@ -335,8 +340,9 @@ extension GutenbergLayoutPickerViewController: UITableViewDataSource { | |
} | ||
cell.delegate = self | ||
cell.selectionStyle = UITableViewCell.SelectionStyle.none | ||
cell.section = sections[indexPath.row] | ||
|
||
cell.section = (filteredSections ?? sections)[indexPath.row] | ||
cell.layer.masksToBounds = false | ||
cell.clipsToBounds = false | ||
if let selectedLayout = selectedLayout, containsSelectedLayout(selectedLayout, atIndexPath: indexPath) { | ||
cell.selectItemAt(selectedLayout.position) | ||
} | ||
|
@@ -374,3 +380,86 @@ extension GutenbergLayoutPickerViewController: LayoutPickerSectionTableViewCellD | |
} | ||
} | ||
} | ||
|
||
extension GutenbergLayoutPickerViewController: FilterBarDelegate { | ||
func numberOfFilters() -> Int { | ||
return sections.count | ||
} | ||
|
||
func filter(forIndex index: Int) -> GutenbergLayoutSection { | ||
return sections[index] | ||
} | ||
|
||
func didSelectFilter(withIndex selectedIndex: IndexPath, withSelectedIndexes selectedIndexes: [IndexPath]) { | ||
guard filteredSections == nil else { | ||
insertFilterRow(withIndex: selectedIndex, withSelectedIndexes: selectedIndexes) | ||
return | ||
} | ||
|
||
let rowsToRemove = (0..<sections.count).compactMap { ($0 == selectedIndex.item) ? nil : IndexPath(row: $0, section: 0) } | ||
|
||
filteredSections = [sections[selectedIndex.item]] | ||
tableView.performBatchUpdates({ | ||
tableView.deleteRows(at: rowsToRemove, with: .fade) | ||
}) { _ in | ||
self.snapToHeight(self.tableView, height: self.maxHeaderHeight) | ||
} | ||
} | ||
|
||
func insertFilterRow(withIndex selectedIndex: IndexPath, withSelectedIndexes selectedIndexes: [IndexPath]) { | ||
|
||
var row: IndexPath? = nil | ||
let sortedIndexes = selectedIndexes.sorted(by: { $0.item < $1.item }) | ||
for i in 0..<sortedIndexes.count { | ||
if sortedIndexes[i].item == selectedIndex.item { | ||
let indexPath = IndexPath(row: i, section: 0) | ||
filteredSections?.insert(sections[selectedIndex.item], at: i) | ||
row = indexPath | ||
break | ||
} | ||
} | ||
|
||
guard let rowToAdd = row else { return } | ||
tableView.performBatchUpdates({ | ||
tableView.insertRows(at: [rowToAdd], with: .fade) | ||
}) | ||
} | ||
|
||
func didDeselectFilter(withIndex index: IndexPath, withSelectedIndexes selectedIndexes: [IndexPath]) { | ||
guard selectedIndexes.count == 0 else { | ||
removeFilterRow(withIndex: index) | ||
return | ||
} | ||
|
||
let currentRowSlug = filteredSections?.first?.section.slug | ||
filteredSections = nil | ||
let rowsToAdd = (0..<sections.count).compactMap { (sections[$0].section.slug == currentRowSlug) ? nil : IndexPath(row: $0, section: 0) } | ||
tableView.performBatchUpdates({ | ||
tableView.insertRows(at: rowsToAdd, with: .fade) | ||
}) | ||
} | ||
|
||
func removeFilterRow(withIndex index: IndexPath) { | ||
guard let filteredSections = filteredSections else { return } | ||
|
||
var row: IndexPath? = nil | ||
let rowSlug = sections[index.item].section.slug | ||
for i in 0..<filteredSections.count { | ||
if filteredSections[i].section.slug == rowSlug { | ||
let indexPath = IndexPath(row: i, section: 0) | ||
self.filteredSections?.remove(at: i) | ||
row = indexPath | ||
break | ||
} | ||
} | ||
|
||
guard let rowToRemove = row else { return } | ||
tableView.performBatchUpdates({ | ||
tableView.deleteRows(at: [rowToRemove], with: .fade) | ||
}) { _ in | ||
if (self.filteredSections?.count ?? 0) < 2 { | ||
self.snapToHeight(self.tableView, height: self.maxHeaderHeight) | ||
} | ||
} | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
.../Classes/ViewRelated/Gutenberg/Layout Picker/LayoutPickerCategoryCollectionViewCell.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,16 @@ | ||
import UIKit | ||
|
||
class LayoutPickerFilterCollectionViewCell: UICollectionViewCell { | ||
|
||
static var cellReuseIdentifier: String { | ||
return "LayoutPickerCategoryCollectionViewCell" | ||
} | ||
|
||
static var nib: UINib { | ||
return UINib(nibName: "LayoutPickerCategoryCollectionViewCell", bundle: Bundle.main) | ||
} | ||
guarani marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@IBOutlet weak var filterLabel: UILabel! | ||
|
||
var displayCategory: GutenbergLayoutDisplayCategory? = nil | ||
} |
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
105 changes: 105 additions & 0 deletions
105
...ss/Classes/ViewRelated/Gutenberg/Layout Picker/LayoutPickerFilterCollectionViewCell.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,105 @@ | ||
import UIKit | ||
|
||
class LayoutPickerFilterCollectionViewCell: UICollectionViewCell { | ||
|
||
static let cellReuseIdentifier = "LayoutPickerFilterCollectionViewCell" | ||
static let nib = UINib(nibName: "LayoutPickerFilterCollectionViewCell", bundle: Bundle.main) | ||
|
||
static var font: UIFont { | ||
return WPStyleGuide.fontForTextStyle(.subheadline, fontWeight: .semibold) | ||
} | ||
guarani marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
private static let combinedLeftRightMargin: CGFloat = 32 | ||
static func estimatedWidth(forFilter filter: GutenbergLayoutSection) -> CGFloat { | ||
/// The emoji below is used as a placeholder to estimate the size of the title. We don't use the actual emoji provided by the API because this could be nil | ||
/// and we want to allow space for a checkmark when the cell is selected. | ||
let size = "👋 \(filter.section.title)".size(withAttributes: [ | ||
NSAttributedString.Key.font: font | ||
]) | ||
|
||
return size.width + combinedLeftRightMargin | ||
} | ||
|
||
@IBOutlet weak var filterLabel: UILabel! | ||
@IBOutlet weak var pillBackgroundView: UIView! | ||
@IBOutlet weak var checkmark: UIImageView! | ||
|
||
var filter: GutenbergLayoutSection? = nil { | ||
didSet { | ||
let section = filter?.section | ||
filterLabel.text = filterTitle | ||
filterLabel.accessibilityLabel = section?.title | ||
} | ||
} | ||
|
||
var filterTitle: String { | ||
let section = filter?.section | ||
let emoji = isSelected ? nil : section?.emoji | ||
return [emoji, section?.title].compactMap { $0 }.joined(separator: " ") | ||
} | ||
|
||
var checkmarkTintColor: UIColor { | ||
if #available(iOS 13.0, *) { | ||
return UIColor { (traitCollection: UITraitCollection) -> UIColor in | ||
if traitCollection.userInterfaceStyle == .dark { | ||
return UIColor.darkText | ||
} else { | ||
return UIColor.white | ||
} | ||
} | ||
} else { | ||
return UIColor.white | ||
} | ||
} | ||
|
||
override var isSelected: Bool { | ||
didSet { | ||
checkmark.isHidden = !isSelected | ||
filterLabel.text = filterTitle | ||
updateSelectedStyle() | ||
} | ||
} | ||
|
||
override func awakeFromNib() { | ||
super.awakeFromNib() | ||
filterLabel.font = LayoutPickerFilterCollectionViewCell.font | ||
if #available(iOS 13.0, *) { | ||
checkmark.image = UIImage(systemName: "checkmark") | ||
} else { | ||
checkmark.image = UIImage.gridicon(.checkmark) | ||
} | ||
checkmark.tintColor = checkmarkTintColor | ||
updateSelectedStyle() | ||
} | ||
|
||
override func prepareForReuse() { | ||
super.prepareForReuse() | ||
filter = nil | ||
} | ||
|
||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { | ||
super.traitCollectionDidChange(previousTraitCollection) | ||
|
||
if #available(iOS 13.0, *) { | ||
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) { | ||
updateSelectedStyle() | ||
} | ||
} | ||
} | ||
|
||
private func updateSelectedStyle() { | ||
if #available(iOS 13.0, *) { | ||
let oppositeInterfaceStyle: UIUserInterfaceStyle = (traitCollection.userInterfaceStyle == .dark) ? .light : .dark | ||
let selectedColor: UIColor = UIColor.systemGray6.color(for: UITraitCollection(userInterfaceStyle: oppositeInterfaceStyle)) | ||
pillBackgroundView.backgroundColor = isSelected ? selectedColor : .quaternarySystemFill | ||
} else { | ||
pillBackgroundView.backgroundColor = isSelected ? .black : .gray(.shade0) | ||
} | ||
|
||
if #available(iOS 13.0, *), traitCollection.userInterfaceStyle == .dark { | ||
filterLabel.textColor = isSelected ? .darkText : .white | ||
} else { | ||
filterLabel.textColor = isSelected ? .white : .darkText | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed this just to support a more generic naming structure as this might be used for Gutenboarding later.