Skip to content

Commit aa62eeb

Browse files
author
featherless
authored
[NavigationBar] Fix bug where titleView would disappear. (#7310)
Closes https://github.com/material-components/material-components-ios/issues/7207 Context ------- UINavigationBar will attempt to steal a navigationItem's titleView property for its own use, even if the navigation bar is not visible. Our own MDCNavigationBar also wants to show the titleView instance but we can't stop UINavigationBar from stealing the titleView. To protect against this behavior, MDCNavigationBar implements a "sandbag swap" of the titleView when it's assigned. UINavigationBar ends up stealing the sandbag away, while our MDCNavigationBar keeps an internal reference to the desired titleView. Before this fix --------------- The sandbag view would be swapped the first time titleView is assigned. If the same titleView was assigned to the navigationItem again, however, we would not swap it with a sandbag. The result is that navigationItem.titleView would be pointing at the actual view (not the sandbag) when UINavigationBar comes around to steal the titleView. The result is that the titleView would disappear from MDCNavigationBar. After this fix -------------- We now assign the sandbag view on every assignment, regardless of whether the view is the same or not. I wrote a test to simulate the theft behavior of UINavigationBar. I verified that the test failed before this patch and that it passes after this patch. I also verified the original bug with the internal client's code and example.
1 parent 7d1a1b5 commit aa62eeb

File tree

2 files changed

+36
-37
lines changed

2 files changed

+36
-37
lines changed

components/NavigationBar/src/MDCNavigationBar.m

+4-5
Original file line numberDiff line numberDiff line change
@@ -563,9 +563,6 @@ - (NSString *)title {
563563
}
564564

565565
- (void)setTitleView:(UIView *)titleView {
566-
if (self.titleView == titleView) {
567-
return;
568-
}
569566
// Ignore sandbag KVO events
570567
if ([_observedNavigationItem.titleView isKindOfClass:[MDCNavigationBarSandbagView class]]) {
571568
return;
@@ -580,8 +577,10 @@ - (void)setTitleView:(UIView *)titleView {
580577
_observedNavigationItem.titleView = nil;
581578
}
582579

583-
[self.titleView removeFromSuperview];
584-
_titleView = titleView;
580+
if (self.titleView != titleView) {
581+
[self.titleView removeFromSuperview];
582+
_titleView = titleView;
583+
}
585584

586585
if (_titleView != nil) {
587586
[self addSubview:_titleView];

components/NavigationBar/tests/unit/NavigationBarTitleViewTests.swift

+32-32
Original file line numberDiff line numberDiff line change
@@ -58,20 +58,20 @@ class NavigationBarTitleViewTests: XCTestCase {
5858
}
5959

6060
// Designed to keep https://github.com/material-components/material-components-ios/issues/7207
61-
// from regressing. Currently fails.
62-
// func testTitleViewAssignmentThenRemovalFromViewHierarchyThenReassignmentAddsItAsASubview() {
63-
// // Given
64-
// let navigationBar = MDCNavigationBar()
65-
// let titleView = UIView()
66-
//
67-
// // When
68-
// navigationBar.titleView = titleView
69-
// titleView.removeFromSuperview()
70-
// navigationBar.titleView = titleView
71-
//
72-
// // Then
73-
// XCTAssertEqual(titleView.superview, navigationBar)
74-
// }
61+
// from regressing.
62+
func testTitleViewAssignmentThenRemovalFromViewHierarchyThenReassignmentAddsItAsASubview() {
63+
// Given
64+
let navigationBar = MDCNavigationBar()
65+
let titleView = UIView()
66+
67+
// When
68+
navigationBar.titleView = titleView
69+
titleView.removeFromSuperview()
70+
navigationBar.titleView = titleView
71+
72+
// Then
73+
XCTAssertEqual(titleView.superview, navigationBar)
74+
}
7575

7676
// MARK: Assignment view UINavigationItem
7777

@@ -121,22 +121,22 @@ class NavigationBarTitleViewTests: XCTestCase {
121121
}
122122

123123
// Designed to keep https://github.com/material-components/material-components-ios/issues/7207
124-
// from regressing. Currently fails.
125-
// func testNavigationItemTitleViewAssignmentWithReassignmentThenTheftKeepsTitleViewAsSubview() {
126-
// // Given
127-
// let navigationBar = MDCNavigationBar()
128-
// let titleView = UIView()
129-
// let navigationItem = UINavigationItem()
130-
// let simulatedThiefView = UIView()
131-
//
132-
// // When
133-
// navigationItem.titleView = titleView
134-
// navigationBar.observe(navigationItem)
135-
// navigationItem.titleView = titleView
136-
// simulatedThiefView.addSubview(navigationItem.titleView!)
137-
//
138-
// // Then
139-
// XCTAssertEqual(navigationItem.titleView?.superview, simulatedThiefView)
140-
// XCTAssertEqual(titleView.superview, navigationBar)
141-
// }
124+
// from regressing.
125+
func testNavigationItemTitleViewAssignmentWithReassignmentThenTheftKeepsTitleViewAsSubview() {
126+
// Given
127+
let navigationBar = MDCNavigationBar()
128+
let titleView = UIView()
129+
let navigationItem = UINavigationItem()
130+
let simulatedThiefView = UIView()
131+
132+
// When
133+
navigationItem.titleView = titleView
134+
navigationBar.observe(navigationItem)
135+
navigationItem.titleView = titleView
136+
simulatedThiefView.addSubview(navigationItem.titleView!)
137+
138+
// Then
139+
XCTAssertEqual(navigationItem.titleView?.superview, simulatedThiefView)
140+
XCTAssertEqual(titleView.superview, navigationBar)
141+
}
142142
}

0 commit comments

Comments
 (0)