mirror of
https://github.com/material-components/material-components-ios.git
synced 2026-02-20 08:27:32 +08:00
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.
143 lines
4.2 KiB
Swift
143 lines
4.2 KiB
Swift
// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
import XCTest
|
|
import MaterialComponents.MaterialNavigationBar
|
|
|
|
class NavigationBarTitleViewTests: XCTestCase {
|
|
|
|
// MARK: Direct assignment
|
|
|
|
func testTitleViewAssignmentAddsItAsASubview() {
|
|
// Given
|
|
let navigationBar = MDCNavigationBar()
|
|
let titleView = UIView()
|
|
|
|
// When
|
|
navigationBar.titleView = titleView
|
|
|
|
// Then
|
|
XCTAssertEqual(titleView.superview, navigationBar)
|
|
}
|
|
|
|
func testTitleViewAssignmentTwiceStillAddsItAsASubview() {
|
|
// Given
|
|
let navigationBar = MDCNavigationBar()
|
|
let titleView = UIView()
|
|
|
|
// When
|
|
navigationBar.titleView = titleView
|
|
navigationBar.titleView = titleView
|
|
|
|
// Then
|
|
XCTAssertEqual(titleView.superview, navigationBar)
|
|
}
|
|
|
|
func testTitleViewAssignmentAndThenRemovalFromViewHierarchyLeavesItWithoutASuperview() {
|
|
// Given
|
|
let navigationBar = MDCNavigationBar()
|
|
let titleView = UIView()
|
|
|
|
// When
|
|
navigationBar.titleView = titleView
|
|
titleView.removeFromSuperview()
|
|
|
|
// Then
|
|
XCTAssertNil(titleView.superview)
|
|
}
|
|
|
|
// Designed to keep https://github.com/material-components/material-components-ios/issues/7207
|
|
// from regressing.
|
|
func testTitleViewAssignmentThenRemovalFromViewHierarchyThenReassignmentAddsItAsASubview() {
|
|
// Given
|
|
let navigationBar = MDCNavigationBar()
|
|
let titleView = UIView()
|
|
|
|
// When
|
|
navigationBar.titleView = titleView
|
|
titleView.removeFromSuperview()
|
|
navigationBar.titleView = titleView
|
|
|
|
// Then
|
|
XCTAssertEqual(titleView.superview, navigationBar)
|
|
}
|
|
|
|
// MARK: Assignment view UINavigationItem
|
|
|
|
func testNavigationItemTitleViewAssignmentBeforeObservationAddsTitleViewAsASubview() {
|
|
// Given
|
|
let navigationBar = MDCNavigationBar()
|
|
let titleView = UIView()
|
|
let navigationItem = UINavigationItem()
|
|
|
|
// When
|
|
navigationItem.titleView = titleView
|
|
navigationBar.observe(navigationItem)
|
|
|
|
// Then
|
|
XCTAssertEqual(titleView.superview, navigationBar)
|
|
}
|
|
|
|
func testNavigationItemTitleViewAssignmentAfterObservationAddsTitleViewAsASubview() {
|
|
// Given
|
|
let navigationBar = MDCNavigationBar()
|
|
let titleView = UIView()
|
|
let navigationItem = UINavigationItem()
|
|
|
|
// When
|
|
navigationBar.observe(navigationItem)
|
|
navigationItem.titleView = titleView
|
|
|
|
// Then
|
|
XCTAssertEqual(titleView.superview, navigationBar)
|
|
}
|
|
|
|
func testNavigationItemTitleViewAssignmentWithSimulatedTheftKeepsTitleViewAsSubview() {
|
|
// Given
|
|
let navigationBar = MDCNavigationBar()
|
|
let titleView = UIView()
|
|
let navigationItem = UINavigationItem()
|
|
let simulatedThiefView = UIView()
|
|
|
|
// When
|
|
navigationItem.titleView = titleView
|
|
navigationBar.observe(navigationItem)
|
|
simulatedThiefView.addSubview(navigationItem.titleView!)
|
|
|
|
// Then
|
|
XCTAssertEqual(navigationItem.titleView?.superview, simulatedThiefView)
|
|
XCTAssertEqual(titleView.superview, navigationBar)
|
|
}
|
|
|
|
// Designed to keep https://github.com/material-components/material-components-ios/issues/7207
|
|
// from regressing.
|
|
func testNavigationItemTitleViewAssignmentWithReassignmentThenTheftKeepsTitleViewAsSubview() {
|
|
// Given
|
|
let navigationBar = MDCNavigationBar()
|
|
let titleView = UIView()
|
|
let navigationItem = UINavigationItem()
|
|
let simulatedThiefView = UIView()
|
|
|
|
// When
|
|
navigationItem.titleView = titleView
|
|
navigationBar.observe(navigationItem)
|
|
navigationItem.titleView = titleView
|
|
simulatedThiefView.addSubview(navigationItem.titleView!)
|
|
|
|
// Then
|
|
XCTAssertEqual(navigationItem.titleView?.superview, simulatedThiefView)
|
|
XCTAssertEqual(titleView.superview, navigationBar)
|
|
}
|
|
}
|