Skip to content

Commit 3b51440

Browse files
Add FXIOS-12495 ⁃ [Menu Redesign] Apply latest design for menu cells and menu (backport #27539) (#27552)
Add FXIOS-12495 ⁃ [Menu Redesign] Apply latest design for menu cells and menu (#27539) * FXIOS-12495 #27238 ⁃ [Menu Redesign] Apply latest design for menu cells and menu * Refactored some logic (cherry picked from commit 192ccd2) Co-authored-by: dicarobinho <[email protected]>
1 parent bc76faf commit 3b51440

File tree

9 files changed

+141
-34
lines changed

9 files changed

+141
-34
lines changed

BrowserKit/Sources/MenuKit/MenuRedesign/MenuAccountCell.swift

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ final class MenuAccountCell: UITableViewCell, ReusableCell, ThemeApplicable {
1313
static let largeIconSize: CGFloat = 48
1414
static let contentSpacing: CGFloat = 3
1515
static let noDescriptionContentSpacing: CGFloat = 0
16+
static let cornerRadius: CGFloat = 16
17+
static let backgroundAlpha: CGFloat = 0.8
1618
}
1719

1820
// MARK: - UI Elements
@@ -39,6 +41,9 @@ final class MenuAccountCell: UITableViewCell, ReusableCell, ThemeApplicable {
3941
return model?.iconImage != nil && (model?.needsReAuth == nil || model?.needsReAuth == false)
4042
}
4143

44+
private var isFirstCell = false
45+
private var isLastCell = false
46+
4247
// MARK: - Initializers
4348
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
4449
super.init(style: style, reuseIdentifier: reuseIdentifier)
@@ -57,15 +62,20 @@ final class MenuAccountCell: UITableViewCell, ReusableCell, ThemeApplicable {
5762
iconImageView.layer.cornerRadius = 0
5863
}
5964
iconImageView.clipsToBounds = shouldConfigureImageView
65+
configureCornerRadiusForCellPosition()
6066
}
6167

6268
override func prepareForReuse() {
6369
super.prepareForReuse()
6470
iconImageView.image = nil
71+
isFirstCell = false
72+
isLastCell = false
6573
}
6674

67-
func configureCellWith(model: MenuElement, theme: Theme) {
75+
func configureCellWith(model: MenuElement, theme: Theme, isFirstCell: Bool, isLastCell: Bool) {
6876
self.model = model
77+
self.isFirstCell = isFirstCell
78+
self.isLastCell = isLastCell
6979
titleLabel.text = model.title
7080
descriptionLabel.text = model.description
7181
contentStackView.spacing = model.description != nil ? UX.contentSpacing : UX.noDescriptionContentSpacing
@@ -116,10 +126,22 @@ final class MenuAccountCell: UITableViewCell, ReusableCell, ThemeApplicable {
116126
iconImageView.heightAnchor.constraint(equalToConstant: iconSize).isActive = true
117127
}
118128

129+
private func configureCornerRadiusForCellPosition() {
130+
guard isFirstCell || isLastCell else { return }
131+
self.clipsToBounds = true
132+
layer.cornerRadius = UX.cornerRadius
133+
layer.maskedCorners = {
134+
var corners: CACornerMask = []
135+
if isFirstCell { corners.formUnion([.layerMinXMinYCorner, .layerMaxXMinYCorner]) }
136+
if isLastCell { corners.formUnion([.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) }
137+
return corners
138+
}()
139+
}
140+
119141
// MARK: - Theme Applicable
120142
func applyTheme(theme: Theme) {
121143
guard let model else { return }
122-
backgroundColor = theme.colors.layer2
144+
backgroundColor = theme.colors.layer2.withAlphaComponent(UX.backgroundAlpha)
123145
if let needsReAuth = model.needsReAuth, needsReAuth {
124146
descriptionLabel.textColor = theme.colors.textCritical
125147
} else if model.iconImage != nil {

BrowserKit/Sources/MenuKit/MenuRedesign/MenuInfoCell.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ final class MenuInfoCell: UITableViewCell, ReusableCell, ThemeApplicable {
1212
static let infoLabelHorizontalMargin: CGFloat = 8
1313
static let infoLabelVerticalPadding: CGFloat = 7
1414
static let infoLabelHorizontalPadding: CGFloat = 14
15+
static let backgroundAlpha: CGFloat = 0.8
1516
}
1617

1718
// MARK: - UI Elements
@@ -94,7 +95,7 @@ final class MenuInfoCell: UITableViewCell, ReusableCell, ThemeApplicable {
9495
// MARK: - Theme Applicable
9596
func applyTheme(theme: Theme) {
9697
guard let model else { return }
97-
backgroundColor = theme.colors.layer2
98+
backgroundColor = theme.colors.layer2.withAlphaComponent(UX.backgroundAlpha)
9899
if model.isActive {
99100
titleLabel.textColor = theme.colors.textAccent
100101
infoLabelView.textColor = theme.colors.textPrimary

BrowserKit/Sources/MenuKit/MenuRedesign/MenuRedesignCell.swift

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ final class MenuRedesignCell: UITableViewCell, ReusableCell, ThemeApplicable {
1313
static let largeIconSize: CGFloat = 48
1414
static let contentSpacing: CGFloat = 3
1515
static let noDescriptionContentSpacing: CGFloat = 0
16+
static let cornerRadius: CGFloat = 16
17+
static let backgroundAlpha: CGFloat = 0.8
1618
}
1719

1820
// MARK: - UI Elements
@@ -32,6 +34,9 @@ final class MenuRedesignCell: UITableViewCell, ReusableCell, ThemeApplicable {
3234

3335
private var iconImageView: UIImageView = .build()
3436

37+
private var isFirstCell = false
38+
private var isLastCell = false
39+
3540
// MARK: - Properties
3641
var model: MenuElement?
3742

@@ -45,13 +50,22 @@ final class MenuRedesignCell: UITableViewCell, ReusableCell, ThemeApplicable {
4550
fatalError("init(coder:) has not been implemented")
4651
}
4752

53+
override func layoutSubviews() {
54+
super.layoutSubviews()
55+
configureCornerRadiusForCellPosition()
56+
}
57+
4858
override func prepareForReuse() {
4959
super.prepareForReuse()
5060
iconImageView.image = nil
61+
isFirstCell = false
62+
isLastCell = false
5163
}
5264

53-
func configureCellWith(model: MenuElement, theme: Theme) {
65+
func configureCellWith(model: MenuElement, theme: Theme, isFirstCell: Bool, isLastCell: Bool) {
5466
self.model = model
67+
self.isFirstCell = isFirstCell
68+
self.isLastCell = isLastCell
5569
self.titleLabel.text = model.title
5670
self.descriptionLabel.text = model.description
5771
self.contentStackView.spacing = model.description != nil ? UX.contentSpacing : UX.noDescriptionContentSpacing
@@ -91,10 +105,22 @@ final class MenuRedesignCell: UITableViewCell, ReusableCell, ThemeApplicable {
91105
iconImageView.heightAnchor.constraint(equalToConstant: iconSize).isActive = true
92106
}
93107

108+
private func configureCornerRadiusForCellPosition() {
109+
guard isFirstCell || isLastCell else { return }
110+
self.clipsToBounds = true
111+
layer.cornerRadius = UX.cornerRadius
112+
layer.maskedCorners = {
113+
var corners: CACornerMask = []
114+
if isFirstCell { corners.formUnion([.layerMinXMinYCorner, .layerMaxXMinYCorner]) }
115+
if isLastCell { corners.formUnion([.layerMinXMaxYCorner, .layerMaxXMaxYCorner]) }
116+
return corners
117+
}()
118+
}
119+
94120
// MARK: - Theme Applicable
95121
func applyTheme(theme: Theme) {
96122
guard let model else { return }
97-
backgroundColor = theme.colors.layer2
123+
backgroundColor = theme.colors.layer2.withAlphaComponent(UX.backgroundAlpha)
98124
if model.isActive {
99125
titleLabel.textColor = theme.colors.textAccent
100126
descriptionLabel.textColor = theme.colors.textSecondary

BrowserKit/Sources/MenuKit/MenuRedesign/MenuRedesignMainView.swift

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ import ComponentLibrary
99
public final class MenuRedesignMainView: UIView,
1010
ThemeApplicable {
1111
private struct UX {
12-
static let headerTopMargin: CGFloat = 15
12+
static let headerTopMargin: CGFloat = 24
1313
static let horizontalMargin: CGFloat = 16
1414
static let closeButtonSize: CGFloat = 30
1515
static let headerTopMarginWithButton: CGFloat = 8
1616
}
1717

1818
public var closeButtonCallback: (() -> Void)?
19+
public var onCalculatedHeight: ((CGFloat) -> Void)?
1920

2021
// MARK: - UI Elements
2122
private var tableView: MenuRedesignTableView = .build()
@@ -81,6 +82,29 @@ public final class MenuRedesignMainView: UIView,
8182
public func reloadDataView(with data: [MenuSection]) {
8283
setupView(with: data)
8384
tableView.reloadTableView(with: data)
85+
updateMenuHeight(for: data)
86+
}
87+
88+
private func updateMenuHeight(for data: [MenuSection]) {
89+
// To avoid a glitch when expand the menu, we should not handle this action under DispatchQueue.main.async
90+
if let expandedSection = data.first(where: { $0.isExpanded ?? false }),
91+
let isExpanded = expandedSection.isExpanded,
92+
isExpanded {
93+
let height = tableView.tableViewContentSize + UX.headerTopMargin
94+
onCalculatedHeight?(height + siteProtectionHeader.frame.height)
95+
layoutIfNeeded()
96+
} else {
97+
DispatchQueue.main.async { [weak self] in
98+
guard let self else { return }
99+
let height = tableView.tableViewContentSize + UX.headerTopMargin
100+
if let section = data.first(where: { $0.isHomepage }), section.isHomepage {
101+
onCalculatedHeight?(height + UX.closeButtonSize + UX.headerTopMarginWithButton)
102+
} else {
103+
onCalculatedHeight?(height + siteProtectionHeader.frame.height)
104+
}
105+
layoutIfNeeded()
106+
}
107+
}
84108
}
85109

86110
// MARK: - Callbacks

BrowserKit/Sources/MenuKit/MenuRedesign/MenuRedesignTableView.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ final class MenuRedesignTableView: UIView,
2020
private var menuData: [MenuSection]
2121
private var theme: Theme?
2222

23-
var updateHeaderLineView: ((_ isHidden: Bool) -> Void)?
23+
public var tableViewContentSize: CGFloat {
24+
tableView.contentSize.height
25+
}
2426

2527
override init(frame: CGRect) {
2628
tableView = UITableView(frame: .zero, style: .insetGrouped)
@@ -117,7 +119,10 @@ final class MenuRedesignTableView: UIView,
117119
return UITableViewCell()
118120
}
119121
if let theme {
120-
cell.configureCellWith(model: rowOption, theme: theme)
122+
let numberOfRows = tableView.numberOfRows(inSection: indexPath.section)
123+
let isFirst = indexPath.row == 0
124+
let isLast = indexPath.row == numberOfRows - 1
125+
cell.configureCellWith(model: rowOption, theme: theme, isFirstCell: isFirst, isLastCell: isLast)
121126
cell.applyTheme(theme: theme)
122127
}
123128
return cell
@@ -144,7 +149,10 @@ final class MenuRedesignTableView: UIView,
144149
return UITableViewCell()
145150
}
146151
if let theme {
147-
cell.configureCellWith(model: rowOption, theme: theme)
152+
let numberOfRows = tableView.numberOfRows(inSection: indexPath.section)
153+
let isFirst = indexPath.row == 0
154+
let isLast = indexPath.row == numberOfRows - 1
155+
cell.configureCellWith(model: rowOption, theme: theme, isFirstCell: isFirst, isLastCell: isLast)
148156
cell.applyTheme(theme: theme)
149157
}
150158
return cell
@@ -185,14 +193,6 @@ final class MenuRedesignTableView: UIView,
185193
tableView.reloadData()
186194
}
187195

188-
func scrollViewDidScroll(_ scrollView: UIScrollView) {
189-
if scrollView.contentOffset.y >= UX.topPadding {
190-
updateHeaderLineView?(false)
191-
} else {
192-
updateHeaderLineView?(true)
193-
}
194-
}
195-
196196
// MARK: - Theme Applicable
197197
func applyTheme(theme: Theme) {
198198
self.theme = theme

BrowserKit/Sources/MenuKit/MenuRedesign/MenuSiteProtectionsHeader.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ public final class MenuSiteProtectionsHeader: UIView, ThemeApplicable {
1919
static let siteProtectionsContentHorizontalPadding: CGFloat = 10
2020
static let siteProtectionsContentVerticalPadding: CGFloat = 6
2121
static let siteProtectionsIcon: CGFloat = 16
22+
static let protectionIconMargin: CGFloat = 2
2223
static let siteProtectionsMoreSettingsIcon: CGFloat = 20
2324
static let siteProtectionsContentSpacing: CGFloat = 4
25+
static let closeButtonBackgroundAlpha: CGFloat = 0.8
2426
}
2527

2628
public var closeButtonCallback: (() -> Void)?
@@ -126,7 +128,10 @@ public final class MenuSiteProtectionsHeader: UIView, ThemeApplicable {
126128

127129
siteProtectionsContent.topAnchor.constraint(equalTo: contentLabels.bottomAnchor,
128130
constant: UX.siteProtectionsContentTopMargin),
129-
siteProtectionsContent.leadingAnchor.constraint(equalTo: favicon.trailingAnchor),
131+
siteProtectionsContent.leadingAnchor.constraint(
132+
equalTo: favicon.trailingAnchor,
133+
constant: UX.horizontalContentMargin - UX.siteProtectionsContentHorizontalPadding - UX.protectionIconMargin
134+
),
130135
siteProtectionsContent.trailingAnchor.constraint(lessThanOrEqualTo: closeButton.leadingAnchor),
131136
siteProtectionsContent.bottomAnchor.constraint(equalTo: self.bottomAnchor),
132137
])
@@ -165,7 +170,7 @@ public final class MenuSiteProtectionsHeader: UIView, ThemeApplicable {
165170
titleLabel.textColor = theme.colors.textPrimary
166171
subtitleLabel.textColor = theme.colors.textSecondary
167172
closeButton.tintColor = theme.colors.iconSecondary
168-
closeButton.backgroundColor = theme.colors.layer2
173+
closeButton.backgroundColor = theme.colors.layer2.withAlphaComponent(UX.closeButtonBackgroundAlpha)
169174
siteProtectionsLabel.textColor = theme.colors.textSecondary
170175
siteProtectionsContent.layer.borderColor = theme.colors.actionSecondaryHover.cgColor
171176
siteProtectionsIcon.tintColor = theme.colors.iconSecondary

BrowserKit/Sources/MenuKit/MenuRedesign/MenuSquareCell.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ final class MenuSquareView: UIView, ThemeApplicable {
1515
static let contentViewTopMargin: CGFloat = 12
1616
static let contentViewBottomMargin: CGFloat = 8
1717
static let contentViewHorizontalMargin: CGFloat = 4
18+
static let cornerRadius: CGFloat = 16
19+
static let backgroundAlpha: CGFloat = 0.8
1820
}
1921

2022
// MARK: - UI Elements
@@ -49,6 +51,14 @@ final class MenuSquareView: UIView, ThemeApplicable {
4951
fatalError("init(coder:) not yet supported")
5052
}
5153

54+
override func layoutSubviews() {
55+
super.layoutSubviews()
56+
self.layer.cornerRadius = UX.cornerRadius
57+
self.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner,
58+
.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
59+
self.clipsToBounds = true
60+
}
61+
5262
func configureCellWith(model: MenuElement) {
5363
self.model = model
5464
self.titleLabel.text = model.title
@@ -97,7 +107,7 @@ final class MenuSquareView: UIView, ThemeApplicable {
97107
func applyTheme(theme: Theme) {
98108
backgroundColor = .clear
99109
contentStackView.backgroundColor = .clear
100-
backgroundContentView.backgroundColor = theme.colors.layer2
110+
backgroundContentView.backgroundColor = theme.colors.layer2.withAlphaComponent(UX.backgroundAlpha)
101111
icon.tintColor = theme.colors.iconPrimary
102112
titleLabel.textColor = theme.colors.textSecondary
103113
}

firefox-ios/Client/Frontend/Browser/MainMenu/Views/MainMenuViewController.swift

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class MainMenuViewController: UIViewController,
2121
static let hintViewCornerRadius: CGFloat = 20
2222
static let hintViewHeight: CGFloat = 140
2323
static let hintViewMargin: CGFloat = 20
24+
static let backgroundAlpha: CGFloat = 0.8
2425
}
2526
typealias SubscriberStateType = MainMenuState
2627

@@ -123,6 +124,16 @@ class MainMenuViewController: UIViewController,
123124
self.dispatchCloseMenuAction()
124125
}
125126

127+
menuRedesignContent.onCalculatedHeight = { [weak self] height in
128+
guard let self else { return }
129+
if #available(iOS 16.0, *), UIDevice.current.userInterfaceIdiom == .phone {
130+
let customDetent = UISheetPresentationController.Detent.custom { context in
131+
return height
132+
}
133+
self.sheetPresentationController?.detents = [customDetent]
134+
}
135+
}
136+
126137
menuRedesignContent.siteProtectionHeader.siteProtectionsButtonCallback = { [weak self] in
127138
guard let self else { return }
128139
self.dispatchSiteProtectionAction()
@@ -222,6 +233,7 @@ class MainMenuViewController: UIViewController,
222233
}
223234

224235
private func setupRedesignView() {
236+
view.addBlurEffectWithClearBackgroundAndClipping(using: .regular)
225237
view.addSubview(menuRedesignContent)
226238

227239
NSLayoutConstraint.activate([
@@ -376,9 +388,13 @@ class MainMenuViewController: UIViewController,
376388
// MARK: - UX related
377389
func applyTheme() {
378390
let theme = themeManager.getCurrentTheme(for: windowUUID)
379-
view.backgroundColor = theme.colors.layer3
380-
menuRedesignContent.applyTheme(theme: theme)
381-
menuContent.applyTheme(theme: theme)
391+
if isMenuRedesign {
392+
view.backgroundColor = theme.colors.layer3.withAlphaComponent(UX.backgroundAlpha)
393+
menuRedesignContent.applyTheme(theme: theme)
394+
} else {
395+
view.backgroundColor = theme.colors.layer3
396+
menuContent.applyTheme(theme: theme)
397+
}
382398
}
383399

384400
private func updateHeaderWith(accountData: AccountData, icon: UIImage?) {
@@ -503,12 +519,15 @@ class MainMenuViewController: UIViewController,
503519
}
504520

505521
private func changeDetentIfNecessary() {
506-
if let element = menuState.menuElements.first(where: { $0.isExpanded ?? false }),
507-
let isExpanded = element.isExpanded,
508-
isExpanded {
509-
if let sheet = self.sheetPresentationController, !hasBeenExpanded {
510-
sheet.selectedDetentIdentifier = .large
511-
hasBeenExpanded = true
522+
// For iOS 16 or above we are using custom detents
523+
if #unavailable(iOS 16) {
524+
if let element = menuState.menuElements.first(where: { $0.isExpanded ?? false }),
525+
let isExpanded = element.isExpanded,
526+
isExpanded {
527+
if let sheet = self.sheetPresentationController, !hasBeenExpanded {
528+
sheet.selectedDetentIdentifier = .large
529+
hasBeenExpanded = true
530+
}
512531
}
513532
}
514533
}

0 commit comments

Comments
 (0)