The following has been reported as FB25281446
I've come across a serious user interface issue on the iPhone Duo when you show a popover with the permitted arrow direction set to only left or right. Here's a screenshot from a test app of a correct popover shown from a form sheet on a fully open iPhone Duo:
Here's the same UI after switching the Duo from fully open to partially open:
Note how the popover is now partially offscreen. Oddly the toolbar is still kept onscreen. You are unable to scroll to the last rows of the table view.
You can replicate this problem by taking the following steps:
- Create a new Storyboard/Swift app with Xcode 27.2 RC
- Edit the Main storyboard by removing the default view controller
- Add a navigation controller (and its default root view controller)
- Set the navigation controller as the initial view controller
- Replace the contents of the default ViewController.swift file with the following code:
import UIKit
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
let showPopover = UIBarButtonItem()
// Show the view controller as a popover
let action = UIAction(title: "Popover", handler: { _ in
let vc = ViewController()
let nc = UINavigationController(rootViewController: vc)
nc.modalPresentationStyle = .popover
nc.popoverPresentationController?.sourceItem = showPopover
nc.popoverPresentationController?.permittedArrowDirections = .any
self.present(nc, animated: true)
})
showPopover.primaryAction = action
self.navigationItem.rightBarButtonItem = showPopover
// Show the view controller in a form sheet
let showFormSheet = UIBarButtonItem()
let action2 = UIAction(title: "FormSheet", handler: { _ in
let vc = ViewController()
// Applying a preferred content size affects the results in one
// case - the popover works correctly when the Duo is fully open
// in portrait and you show a popover from a form sheet.
//vc.preferredContentSize = .init(width: 540, height: 620)
let nc = UINavigationController(rootViewController: vc)
nc.modalPresentationStyle = .formSheet
self.present(nc, animated: true)
})
showFormSheet.primaryAction = action2
self.navigationItem.leftBarButtonItem = showFormSheet
// Show some dummy toolbar items to help demonstrate the issue
self.navigationController?.isToolbarHidden = false
self.toolbarItems = [
UIBarButtonItem(systemItem: .add),
UIBarButtonItem.flexibleSpace(),
UIBarButtonItem(systemItem: .action),
]
}
}
extension ViewController {
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ?? UITableViewCell(style: .subtitle, reuseIdentifier: "cell")
cell.textLabel?.text = "Row \(indexPath.row)"
cell.detailTextLabel?.text = "Tap me to show popover"
return cell
}
// Show a popover from a selected cell. The popover will appear partially
// offscreen under the following conditions:
// - When the Duo is fully open and landscape:
// - A row near the bottom of the root view controller is selected.
// - When the Duo is fully open and portrait:
// - A row near the bottom of the root view controller is selected.
// - A row near the bottom of a form sheet is selected (unless the form
// sheet has a preferredContentSize set).
// - When the Duo is partially open and landscape or portrait:
// - A row near the bottom of the root view controller is selected.
// - A row near the bottom of a form sheet is selected.
// - If you then fully open the Duo, the partially offscreen popover
// moves into the correct, fully visible position.
// Note that the toolbar of the partially offscreen popover stays on screen.
//
// There are no issues when the Duo is closed.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
let vc = ViewController()
let nc = UINavigationController(rootViewController: vc)
nc.modalPresentationStyle = .popover
nc.popoverPresentationController?.sourceItem = cell
// The problem only happens if the arrow is limited to left or right
nc.popoverPresentationController?.permittedArrowDirections = [ .left, .right ]
self.present(nc, animated: true)
}
}
}
- In the Main storyboard, change the root view controller's class to ViewController
- Build and run the app against an iPhone Duo running iOS 27.2 RC
You can now replicate the two screenshots by fully opening the Duo and putting it in landscape. Then do the following:
- Tap the FormSheet button
- In the form sheet table, select a row near the bottom of the form sheet
- Note the popover appears correctly with an arrow on the right side pointing to the selected row
- Now put the iPhone Duo in the partially open mode (still landscape)
- Note the problem. The popover moves partially offscreen. You can’t even scroll to the last row. The popover show remain fully onscreen and of reasonable height just like it was when the Duo was fully open.
As noted in the ViewController.swift file, the issue appears under the following conditions:
- When the Duo is fully open and landscape:
- A row near the bottom of the root view controller is selected.
- When the Duo is fully open and portrait:
- A row near the bottom of the root view controller is selected.
- A row near the bottom of a presented form sheet is selected (unless the form sheet has a preferredContentSize set).
- When the Duo is partially open and landscape or portrait:
- A row near the bottom of the root view controller is selected.
- A row near the bottom of a presented form sheet is selected.
- If you then fully open the Duo, the partially offscreen popover moves into the correct, fully visible position.