mon*_*c77 5 uiviewcontroller uiview ios swift ios16
编辑:根据评论者的要求,我们创建了一个小项目,您可以使用它来重现该问题。https://github.com/Coursicle/ReducingiOS16PopUpControllerBug。请注意,当您在 iOS 15 或更低版本上运行此项目时,弹出窗口会响应点击。在 iOS 16 上,它不响应任何点击。
我们有一个视图控制器,允许用户为我们的应用程序中的频道设置通知首选项。视图从屏幕底部向上滑动,并使用半透明背景视图使屏幕的其余部分变暗。在 iOS 15 及更低版本中,它按预期工作:您可以通过点击背景来关闭视图,或者选择一个新选项并点击“应用”。然而,在 iOS 16 中,视图和半透明背景视图对任何点击都没有响应。
最令人惊讶的是,我们有几乎相同的视图控制器(具有半透明背景视图的视图控制器,以弹出窗口的形式呈现UITabBarController等),它们在 iOS 15 和 iOS 16 中都可以正常工作。 iOS 16 中的工作视图和非工作视图之间似乎存在任何显着差异。
我们尝试过的事情:
UITabBarController呈现弹出窗口之外还有其他视图控制器。<UILabel: 0x133db3e10; frame = (78 0; 208 48); text = 'Every Post'; backgroundColor = UIExtendedGrayColorSpace 0 0; layer = <_UILabelLayer: 0x60000233c0a0>。isUserInteractionEnabled为。true它看起来是这样的:

这是创建并显示弹出窗口的代码:
let rootController = UIApplication.shared.windows.first!.rootViewController as! CustomTabBar
let popover = NotificationSettingsPopUpController(customTabBarController: rootController, delegateController: self)
popover.presentationController?.delegate = self
popover.displayFilterPopUp()
Run Code Online (Sandbox Code Playgroud)
这是弹出窗口的代码:
class NotificationSettingsPopUpController: UIViewController {
var customTabBarController : CustomTabBar
let backgroundView : UIView = UIView()
let filterPopUpViewSlideDuration = 0.2
var filterPopUpViewHeight = CGFloat(0.52*Double(deviceScreenHeight))
let filterPopUpViewWidth = CGFloat(deviceScreenWidth)
let filterPopUpViewRadius = CGFloat(10.0)
let filterOptionsContainer = UIStackView()
// These variables will be used to handle the pan gesture
// to drag the class filter view on and off screen.
lazy var startingPosition = filterPopUpViewHeight*2.3
lazy var finalPosition = filterPopUpViewHeight*1.5
lazy var turningPointToShow = finalPosition*1.4
lazy var turningPointToHide = finalPosition*1.2
// Header Sizing Variables
let headerLabel : UILabel = UILabel()
var headerLabelTopMargin : CGFloat = 28
let headerLabelSideMargin : CGFloat = 20
// Header Sizing Variables
let descriptionLabel : UILabel = UILabel()
var descriptionLabelTopMargin : CGFloat = 45
let descriptionLabelSideMargin : CGFloat = 40
// Apply Button Variables
let applyButton = UIButton()
var applyButtonWidth: CGFloat = CGFloat(deviceScreenWidth)*0.85
var applyButtonHeight: CGFloat = 45
var applyButtonFontSize: CGFloat = 18
let applyButtonColor = UIColor.init(hex: "#207af3")
let applyButtonColorLight = UIColor.init(hex: "#3686f3")
// Sort option buttons
let everyPostOption : MenuOptionsView = MenuOptionsView()
let topDailyOption : MenuOptionsView = MenuOptionsView()
let topWeeklyOption : MenuOptionsView = MenuOptionsView()
let topMonthlyOption : MenuOptionsView = MenuOptionsView()
let neverOption : MenuOptionsView = MenuOptionsView()
var currentlySelectedOption : String
// Delegate Controller
var delegateController : PostsInChannelViewController
init(customTabBarController: CustomTabBar, delegateController: PostsInChannelViewController){
// Set the initial sort option
self.currentlySelectedOption = "Top Weekly Post"
if let settings = getSettingsForChannel(delegateController.channel.id){
if settings.keys.contains("notificationPreference"){
self.currentlySelectedOption = settings["notificationPreference"] ?? "Top Weekly Post"
}
}
// set delegate controller
self.delegateController = delegateController
// set viewToReturnTo so the correct view is displayed
// once the class filter view is dismissed
self.customTabBarController = customTabBarController
super.init(nibName: nil, bundle: nil)
// add class filter view and background to the window
customTabBarController.view.addSubview(backgroundView)
customTabBarController.view.addSubview(view)
// display translucent black backdrop to hide classes view
// when class filter view is being displayed
backgroundView.backgroundColor = UIColor(hex: "#000000", alpha: 0)
backgroundView.frame = CGRect(x: 0, y: -deviceScreenHeight, width: deviceScreenWidth, height: deviceScreenHeight)
// adjust layout variables based on screen size
if UIDevice().screenType == .iPhones_6_6s_7_8 || UIDevice().screenType == .iPhone_12Mini {
filterPopUpViewHeight = CGFloat(0.55*Double(deviceScreenHeight))
startingPosition = filterPopUpViewHeight*2.3
finalPosition = filterPopUpViewHeight*1.5
turningPointToShow = finalPosition*1.4
turningPointToHide = finalPosition*1.2
}
if UIDevice().screenType == .iPhones_6_6s_7_8{
filterPopUpViewHeight = CGFloat(0.65*Double(deviceScreenHeight))
}
if UIDevice().screenType == .iPhone_XSMax_ProMax || UIDevice().screenType == .iPhone_12ProMax{
filterPopUpViewHeight = CGFloat(0.47*Double(deviceScreenHeight))
}
// position class filter view off-screen initially (tried to do this
// with layout anchors but seemed more complicated than it was
// worth - could try doing it again if we find it's necessary)
view.frame = CGRect(x: 0, y: CGFloat(deviceScreenHeight), width: filterPopUpViewWidth, height: filterPopUpViewHeight)
view.layer.cornerRadius = filterPopUpViewRadius
view.backgroundColor = .white
}
// add gesture recognizers so that when user taps outside of
// class filter view or swipes down, the class filter view is dismissed
override func viewDidLoad() {
backgroundView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismissMyself)))
// Set up the header label
view.addSubview(headerLabel)
headerLabel.text = "Notify me"
headerLabel.font = .systemFont(ofSize: 24, weight: .bold)
headerLabel.sizeToFit()
if (UIDevice().screenType == .iPhone_XSMax_ProMax || UIDevice().screenType == .iPhones_X_XS_12MiniSimulator || UIDevice().screenType == .iPhone_XR_11) && iOSIsOld {
headerLabelTopMargin += 15
}
headerLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
headerLabel.leftAnchor.constraint(equalTo: view.leftAnchor, constant: headerLabelSideMargin),
headerLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: headerLabelTopMargin),
])
setUpFilterOptions()
setUpApplyButton()
}
func setUpApplyButton() {
// Create apply now button
applyButton.translatesAutoresizingMaskIntoConstraints = false
applyButton.layer.cornerRadius = applyButtonHeight/2
if(UIDevice().screenType == .iPhones_5_5s_5c_SE){applyButtonFontSize -= 4}
applyButton.titleLabel?.font = UIFont.systemFont(ofSize: applyButtonFontSize, weight: UIFont.Weight.regular)
applyButton.setTitle("Apply", for: .normal)
applyButton.setTitleColor(UIColor.white, for: .normal)
applyButton.backgroundColor = applyButtonColor
// Add gesture recognizer for the apply button
applyButton.addTarget(self, action: #selector(applyButtonTapped), for: .touchUpInside)
applyButton.addTarget(self, action: #selector(applyButtonTouchedDown), for: .touchDown)
// Add apply button to the view and set position
view.addSubview(applyButton)
NSLayoutConstraint.activate([
applyButton.topAnchor.constraint(equalTo: filterOptionsContainer.bottomAnchor, constant: 22),
applyButton.widthAnchor.constraint(equalToConstant: applyButtonWidth),
applyButton.heightAnchor.constraint(equalToConstant: applyButtonHeight),
applyButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
])
}
func setUpFilterOptions() {
filterOptionsContainer.translatesAutoresizingMaskIntoConstraints = false
filterOptionsContainer.axis = NSLayoutConstraint.Axis.vertical
filterOptionsContainer.distribution = UIStackView.Distribution.fillEqually
filterOptionsContainer.alignment = UIStackView.Alignment.center
filterOptionsContainer.spacing = 5
view.addSubview(filterOptionsContainer)
filterOptionsContainer.addArrangedSubview(everyPostOption)
filterOptionsContainer.addArrangedSubview(topDailyOption)
filterOptionsContainer.addArrangedSubview(topWeeklyOption)
filterOptionsContainer.addArrangedSubview(topMonthlyOption)
filterOptionsContainer.addArrangedSubview(neverOption)
everyPostOption.iconView.text = String.fontAwesomeIcon(name: .bell)
topDailyOption.iconView.text = String.fontAwesomeIcon(name: .bell)
topWeeklyOption.iconView.text = String.fontAwesomeIcon(name: .bell)
topMonthlyOption.iconView.text = String.fontAwesomeIcon(name: .bell)
neverOption.iconView.text = String.fontAwesomeIcon(name: .bell)
everyPostOption.infoLabel.text = "Every Post"
topDailyOption.infoLabel.text = "Top Daily Post"
topWeeklyOption.infoLabel.text = "Top Weekly Post"
topMonthlyOption.infoLabel.text = "Top Monthly Post"
neverOption.infoLabel.text = "Never"
everyPostOption.addTarget(self, action: #selector(selectNewSortOption(_:)), for: .touchUpInside)
topDailyOption.addTarget(self, action: #selector(selectNewSortOption(_:)), for: .touchUpInside)
topWeeklyOption.addTarget(self, action: #selector(selectNewSortOption(_:)), for: .touchUpInside)
topMonthlyOption.addTarget(self, action: #selector(selectNewSortOption(_:)), for: .touchUpInside)
neverOption.addTarget(self, action: #selector(selectNewSortOption(_:)), for: .touchUpInside)
switch(currentlySelectedOption) {
case "Every Post":
everyPostOption.backgroundColor = UIColor.init(hex: "#F2F2F2")
everyPostOption.checkmarkIconView.isHidden = false
case "Top Daily Post":
topDailyOption.backgroundColor = UIColor.init(hex: "#F2F2F2")
topDailyOption.checkmarkIconView.isHidden = false
case "Top Weekly Post":
topWeeklyOption.backgroundColor = UIColor.init(hex: "#F2F2F2")
topWeeklyOption.checkmarkIconView.isHidden = false
case "Top Monthly Post":
topMonthlyOption.backgroundColor = UIColor.init(hex: "#F2F2F2")
topMonthlyOption.checkmarkIconView.isHidden = false
case "Never":
neverOption.backgroundColor = UIColor.init(hex: "#F2F2F2")
neverOption.checkmarkIconView.isHidden = false
default:
assert(false)
return
}
NSLayoutConstraint.activate([
filterOptionsContainer.topAnchor.constraint(equalTo: headerLabel.bottomAnchor, constant: 20),
filterOptionsContainer.centerXAnchor.constraint(equalTo: view.centerXAnchor),
])
}
@objc func applyButtonTapped(_ sender: UIButton) {
// Store the preference in user defaults so that
// we can display what setting they have next time they open it
storeSettingsForChannel(settingName: "notificationPreference", settingValue: currentlySelectedOption, channel: delegateController.channel.id)
// send the preference to the server
if let uuid = getUUID(){
setUserChannelNotificationPreference(uuid: uuid, channelID: delegateController.channel.id, preference: currentlySelectedOption)
}
// Dismiss Filter modal screen
dismissMyself()
}
@objc func applyButtonTouchedDown(_ sender: UIButton){
sender.backgroundColor = applyButtonColorLight
}
@objc func selectNewSortOption(_ sender: MenuOptionsView) {
everyPostOption.backgroundColor = .white
topDailyOption.backgroundColor = .white
topWeeklyOption.backgroundColor = .white
topMonthlyOption.backgroundColor = .white
neverOption.backgroundColor = .white
everyPostOption.checkmarkIconView.isHidden = true
topDailyOption.checkmarkIconView.isHidden = true
topWeeklyOption.checkmarkIconView.isHidden = true
topMonthlyOption.checkmarkIconView.isHidden = true
neverOption.checkmarkIconView.isHidden = true
sender.backgroundColor = UIColor.init(hex: "#F2F2F2")
sender.checkmarkIconView.isHidden = false
currentlySelectedOption = sender.infoLabel.text ?? "Top Weekly Post"
}
// this function sets up and displays the class filter view -
// this gets called from the home view controller
func displayFilterPopUp() {
backgroundView.frame = CGRect(x: 0, y: 0, width: deviceScreenWidth, height: deviceScreenHeight)
// slide in class filter view with animation
UIView.animate(withDuration: filterPopUpViewSlideDuration, delay: 0, options: .curveEaseOut, animations: {
self.backgroundView.backgroundColor = UIColor(hex: "#000000", alpha: 0.5)
self.view.frame = CGRect(x: 0, y: CGFloat(deviceScreenHeight)-self.filterPopUpViewHeight, width: self.filterPopUpViewWidth, height: self.filterPopUpViewHeight)
})
}
// This function slides the class filter view off-screen and fades out the backgroundView
override func dismissMyself() {
UIView.animate(withDuration: filterPopUpViewSlideDuration, animations: {
self.backgroundView.alpha = 0
self.view.frame = CGRect(x: 0, y: CGFloat(deviceScreenHeight), width: self.filterPopUpViewWidth, height: self.filterPopUpViewHeight)
self.delegateController.headerLabel.textColor = .black
self.delegateController.headerCaret.textColor = .black
}, completion : { finished in
self.backgroundView.frame = CGRect(x: 0, y: deviceScreenHeight, width: deviceScreenWidth, height: deviceScreenHeight)
self.view.removeFromSuperview()
})
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Run Code Online (Sandbox Code Playgroud)
修复方法是将弹出控制器添加到控制器层次结构中
let popover = FlagPopUpController(delegateController: self)
popover.presentationController?.delegate = self
popover.displayFlagPopUp()
self.addChild(popover) // << here !!
popover.didMove(toParent: self) // << here !!
Run Code Online (Sandbox Code Playgroud)
使用 Xcode 14b5 / iOS 16 进行测试
| 归档时间: |
|
| 查看次数: |
1791 次 |
| 最近记录: |