iOS Development

Swift on iOS 17 – Popover with modalPresentationStyle shouldn’t be clear

Spread the love


I’ve an issue with Popover on iOS17.
On iOS15 and iOS16, my code works effective.

Drawback

Even when the backgroundColor of the view of the ViewController’s view to be displayed in Popover is about to clear, it isn’t clear in iOS17.

on iOS17.0

on iOS16.4

My Code

Right here is my code.

class MessageRoomView: UIViewController, UIPopoverPresentationControllerDelegate, MessagePopMenuViewControllerDelegate, UIGestureRecognizerDelegate {
    
    personal func presentPopover(indexPath: IndexPath, sourceRect: CGRect, sort: CustomMessageType, isLoginUser: Bool) {
        guard let currentCell = messageCollectionView.cellForItem(at: indexPath) else {
            initPopoverItem()
            return
        }
        self.popoverItem.indexPath = indexPath
        // present cell place from indexPath
        var cellPosition: CGRect {
            let level = CGPoint(x: currentCell.body.origin.x - messageCollectionView.contentOffset.x, y: currentCell.body.origin.y - messageCollectionView.contentOffset.y)
            let dimension = currentCell.bounds.dimension
            return CGRect(x: level.x, y: level.y, width: dimension.width, top: dimension.top)
        }
        // Dedication of whether or not the person is current within the location/vary
        guard let navBarHeight = self.navigationController?.navigationBar.body.dimension.top else {
            initPopoverItem()
            return
        }
        let isOutOfRange = cellPosition.top > (messageCollectionView.body.top - navBarHeight - 300)
        
        /* 1. Test if Message dimension is inside vary */
        if !isOutOfRange {
            // Determines whether or not it's upward or downward
            let isUpper = cellPosition.minY <= messageCollectionView.body.minY + 210
            
            let storyboard = UIStoryboard(identify: MessagePopMenuViewController.storyboardName, bundle: nil)
            let popMenuVC = storyboard.instantiateViewController(identifier: MessagePopMenuViewController.storybaordId) { coder in
                return MessagePopMenuViewController(coder: coder, isLoginUser: isLoginUser, isUpper: isUpper, sort: sort)
            }
            popMenuVC.delegate = self
            popMenuVC.modalPresentationStyle = .popover
            popMenuVC.popoverPresentationController?.sourceView = currentCell
            popMenuVC.popoverPresentationController?.sourceRect = sourceRect
            popMenuVC.popoverPresentationController?.permittedArrowDirections = sourceRect.width > 50 ? (isUpper ? .up : .down) : .unknown
            popMenuVC.popoverPresentationController?.popoverBackgroundViewClass = MessagePopoverBackgroundView.self
            popMenuVC.popoverPresentationController?.delegate = self
            
            current(popMenuVC, animated: true)
            
        } else {
            
            let minYThreshold = messageCollectionView.body.minY + 60
            let maxYThreshold = messageCollectionView.body.maxY - 180
            let minYInThreshold = cellPosition.minY >= minYThreshold && cellPosition.minY <= maxYThreshold
            let maxYInThreshold = cellPosition.maxY >= minYThreshold && cellPosition.maxY <= maxYThreshold
            let isWithinThreshold = minYInThreshold || maxYInThreshold
            
            /* 2.a. Judges whether or not the PopMenu exists throughout the vary that may be displayed on both the highest or backside of the display screen. */
            if isWithinThreshold {
                // Determines whether or not it's upward or downward
                let isUpper = maxYInThreshold
                
                let storyboard = UIStoryboard(identify: MessagePopMenuViewController.storyboardName, bundle: nil)
                let popMenuVC = storyboard.instantiateViewController(identifier: MessagePopMenuViewController.storybaordId) { coder in
                    return MessagePopMenuViewController(coder: coder, isLoginUser: isLoginUser, isUpper: isUpper, sort: sort)
                }
                popMenuVC.delegate = self
                popMenuVC.modalPresentationStyle = .popover
                popMenuVC.popoverPresentationController?.sourceView = currentCell
                popMenuVC.popoverPresentationController?.sourceRect = sourceRect
                popMenuVC.popoverPresentationController?.permittedArrowDirections = isUpper ? .up : .down
                popMenuVC.popoverPresentationController?.popoverBackgroundViewClass = MessagePopoverBackgroundView.self
                popMenuVC.popoverPresentationController?.delegate = self
                
                current(popMenuVC, animated: true)
                
            } else {
                /* 2.b. Popover on both the highest or backside if it doesn't exist throughout the displayable space, or within the middle if it doesn't exist throughout the displayable space */
                if cellPosition.midY > 0 && cellPosition.midY < messageCollectionView.body.maxY {
                    
                    let storyboard = UIStoryboard(identify: MessagePopMenuViewController.storyboardName, bundle: nil)
                    let popMenuVC = storyboard.instantiateViewController(identifier: MessagePopMenuViewController.storybaordId) { coder in
                        return MessagePopMenuViewController(coder: coder, isLoginUser: isLoginUser, isUpper: false, sort: sort)
                    }
                    let rect = CGRect(x: sourceRect.minX, y: sourceRect.maxY - sourceRect.top / 2, width: sourceRect.width, top: sourceRect.top / 2)
                    popMenuVC.delegate = self
                    popMenuVC.modalPresentationStyle = .popover
                    popMenuVC.popoverPresentationController?.sourceView = currentCell
                    popMenuVC.popoverPresentationController?.sourceRect = rect
                    popMenuVC.popoverPresentationController?.permittedArrowDirections = .down
                    popMenuVC.popoverPresentationController?.popoverBackgroundViewClass = MessagePopoverBackgroundView.self
                    popMenuVC.popoverPresentationController?.delegate = self
                    
                    current(popMenuVC, animated: true)
                } else {
                    /* 2.c. out of vary */
                    initPopoverItem()
                    return
                }
            }
        }
    }
    
    func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
        return .none
    }






import UIKit

closing class MessagePopoverBackgroundView: UIPopoverBackgroundView {
    
    override func layoutSubviews() {
        tremendous.layoutSubviews()
        layer.shadowOpacity = 0
        setupPathLayer()
    }
    
    override func didMoveToWindow() {
        tremendous.didMoveToWindow()
        let scenes = UIApplication.shared.connectedScenes
        if let windowScene = scenes.first as? UIWindowScene, let window = windowScene.home windows.first {
            let transitionViews = window.subviews.filter { String(describing: sort(of: $0)) == "UITransitionView" }
            for transitionView in transitionViews {
                let shadowView = transitionView.subviews.filter { String(describing: sort(of: $0)) == "_UICutoutShadowView" }.first
                shadowView?.isHidden = true
            }
        }
    }
    
    // MARK: - UIPopoverBackgroundViewMethods
    
    override static func arrowBase() -> CGFloat {
        return 10
    }
    
    override static func arrowHeight() -> CGFloat {
        return 10
    }
    
    override static func contentViewInsets() -> UIEdgeInsets {
        return .zero
    }
    
    // MARK: - UIPopoverBackgroundView properties
    
    personal var _arrowOffset: CGFloat = 0
    override var arrowOffset: CGFloat {
        get { return _arrowOffset }
        set { _arrowOffset = newValue }
    }
    
    personal var _arrowDirection: UIPopoverArrowDirection = .unknown
    override var arrowDirection: UIPopoverArrowDirection {
        get { return _arrowDirection }
        set { _arrowDirection = newValue }
    }
    
    
    // MARK: - Drawing
    
    /// UIBezierPathで吹き出しを描画する
    personal func setupPathLayer() {
        layer.sublayers?.forEach { $0.removeFromSuperlayer() }
        
        let rect = bounds
        let pathLayer = CAShapeLayer()
        pathLayer.body = rect
        pathLayer.path = generatePath(rect).cgPath
        pathLayer.fillColor = UIColor.darkGray.cgColor
        pathLayer.strokeColor = UIColor.darkGray.cgColor
        pathLayer.lineWidth = 1.0
        layer.addSublayer(pathLayer)
    }
    
    personal func generatePath(_ rect: CGRect) -> UIBezierPath {
        let insets: UIEdgeInsets = {
            var insets = MessagePopoverBackgroundView.contentViewInsets()
            if _arrowDirection == .up {
                insets.prime += MessagePopoverBackgroundView.arrowHeight()
            } else if _arrowDirection == .down {
                insets.backside += MessagePopoverBackgroundView.arrowHeight()
            }
            return insets
        }()
        
        let arrowBase = MessagePopoverBackgroundView.arrowBase()
        let arrowCenterX = rect.dimension.width / 2 + _arrowOffset
        
        let path = UIBezierPath()
        if _arrowDirection == .up {
            path.transfer(to: CGPoint(x: arrowCenterX - arrowBase / 2, y: insets.prime))
            path.addLine(to: CGPoint(x: arrowCenterX, y: 0))
            path.addLine(to: CGPoint(x: arrowCenterX + arrowBase / 2, y: insets.prime))
            path.addLine(to: CGPoint(x: arrowCenterX - arrowBase / 2, y: insets.prime))
        } else if _arrowDirection == .down {
            path.transfer(to: CGPoint(x: arrowCenterX - arrowBase / 2, y: rect.maxY - insets.backside))
            path.addLine(to: CGPoint(x: arrowCenterX, y: rect.maxY))
            path.addLine(to: CGPoint(x: arrowCenterX + arrowBase / 2, y: rect.maxY - insets.backside))
            path.addLine(to: CGPoint(x: arrowCenterX - arrowBase / 2, y: rect.maxY - insets.backside))
        }
        path.shut()
        
        return path
    }
}

enter image description here

import UIKit

closing class MessagePopMenuViewController: UIViewController {
    
    static let storyboardName = "MessagePopMenuViewController"
    static let storybaordId = "MessagePopMenuViewController"
    
    @IBOutlet weak var replyButton: UIButton!
    @IBOutlet weak var copyButton: UIButton!
    @IBOutlet weak var stickerButton: UIButton!
    @IBOutlet weak var showImageButton: UIButton!
    @IBOutlet weak var unsendButton: UIButton!
    @IBOutlet weak var menuSpaceView: UIView!
    @IBOutlet weak var topReactionStackView: UIStackView!
    @IBOutlet weak var bottomReactionStackView: UIStackView!
    @IBOutlet var reactionButtons: [UIButton]!
    
    personal let isLoginUser: Bool
    personal let isUpper: Bool
    personal let sort: CustomMessageType
    
    weak var delegate: MessagePopMenuViewControllerDelegate?
    personal let reactionArray = ["❤️", "😆", "😥", "😊", "👍"]
    
    init?(coder: NSCoder, isLoginUser: Bool, isUpper: Bool, sort: CustomMessageType) {
        self.isLoginUser = isLoginUser
        self.isUpper = isUpper
        self.sort = sort
        tremendous.init(coder: coder)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been applied")
    }
    
    deinit {
        // print("MessagePopMenuViewController deinit")
    }
    
    override func viewDidLoad() {
        tremendous.viewDidLoad()
        
        reactionButtons.forEach({ $0.backgroundColor = .clear })
        topReactionStackView.backgroundColor = .clear
        bottomReactionStackView.backgroundColor = .clear
        
        swap (isLoginUser, isUpper) {
        case (true, true) :
            topReactionStackView.isHidden = true
            menuSpaceView.isHidden = true
            self.preferredContentSize = CGSize(width: 261, top: 130)
            replyButton.clipsToBounds = true
            replyButton.layer.cornerRadius = 8.0
            replyButton.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
            unsendButton.clipsToBounds = true
            unsendButton.layer.cornerRadius = 8.0
            unsendButton.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
            
        case (true, false):
            bottomReactionStackView.isHidden = true
            menuSpaceView.isHidden = true
            self.preferredContentSize = CGSize(width: 261, top: 130)
            replyButton.clipsToBounds = true
            replyButton.layer.cornerRadius = 8.0
            replyButton.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
            unsendButton.clipsToBounds = true
            unsendButton.layer.cornerRadius = 8.0
            unsendButton.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
            
        case (false, true):
            topReactionStackView.isHidden = true
            unsendButton.isHidden = true
            self.preferredContentSize = CGSize(width: 251, top: 130)
            replyButton.clipsToBounds = true
            replyButton.layer.cornerRadius = 8.0
            replyButton.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
            menuSpaceView.clipsToBounds = true
            menuSpaceView.layer.cornerRadius = 8.0
            menuSpaceView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
            
        case (false, false):
            bottomReactionStackView.isHidden = true
            unsendButton.isHidden = true
            self.preferredContentSize = CGSize(width: 251, top: 130)
            replyButton.clipsToBounds = true
            replyButton.layer.cornerRadius = 8.0
            replyButton.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
            menuSpaceView.clipsToBounds = true
            menuSpaceView.layer.cornerRadius = 8.0
            menuSpaceView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
        }
        
        swap sort {
        case .textual content, .reply:
            stickerButton.isHidden = true
            showImageButton.isHidden = true
        case .picture:
            copyButton.isHidden = true
            stickerButton.isHidden = true
        case .sticker:
            copyButton.isHidden = true
            showImageButton.isHidden = true
        case.discuss:
            self.dismiss(animated: false)
        }
    }
    
    // some IBAction later
}

Xcode – Model 15.0 (15A240d)
iOS – 17.0
macOS – macOS Sonoma version14.0

Leave a Reply

Your email address will not be published. Required fields are marked *