Search code examples
swiftuiviewuikit

UIView/StackView frame returning wrong .centers on larger screens?


I am trying to find the true position of a UIView named playingCardContainer. You are seeing a snap shot of what is to be a much larger switch case. I have added some text to try to debug and see whats going on. On a smaller screen this works correctly, On a bigger screen its breaking. Here are some screenshots from an Iphone 15 Pro simulator and Iphone SE Simulator. These screenshots are from the same code. playingCardContainer is in view with all the cards in it.

I have included case 4 as an example because some views are embedded in another view, we have to convert it. Which works fine on both Small and Big devices. Am I missing something?

The variable ClearPoint attempts finds the center of the UIView and from there we calculate where to draw it from, alternatively we can also get minx,miny. Nothing seems to be working. Iphone 15 Pro Iphone Se

case 3:
            //var clearPoint = playingCardContainer.center // similiar result
            //var clearPoint = playingCardContainer.convert(playingCardContainer.center, to: view.window) // completely wrong
            let containerFrame = playingCardContainer.frame
            let containerCenter = CGPoint(x: containerFrame.origin.x + containerFrame.width / 2, y: containerFrame.origin.y + containerFrame.height / 2)
            var clearPoint = view.convert(containerCenter, from: playingCardContainer.superview)
            
            let width: CGFloat = playingCardContainer.frame.width // Use the container's width
            let height: CGFloat = playingCardContainer.frame.height // Use the container's height
            let minX = containerFrame.minX
            let minY = containerFrame.minY
            //let minX = playingCardContainer.frame.minX
            //let minY = playingCardContainer.frame.minY
            let Text = UILabel(frame: CGRect(x: clearPoint.x, y: clearPoint.y, width: 50, height: 15))
            Text.text = "center"
            Text.textAlignment = .center
            
            let Text2 = UILabel(frame: CGRect(x: containerFrame.minX, y: containerFrame.minY, width: 50, height: 15))
            Text2.text = "Top left"
            Text2.textAlignment = .center
            Text2.textColor = .green
            
            view.addSubview(Text)
            view.addSubview(Text2)
            ShowUserGuide(atStep: 3, clearPoint: &clearPoint, containerWidth: width, containerHeight: height, minX: minX, minY: minY )
            
        case 4:
            var clearPoint = noDiscardsRemainingLabel.convert(noDiscardsRemainingLabel.center, to: view.window)
            let width: CGFloat = noDiscardsRemainingLabel.frame.width + 30  // Use the container's width
            let height: CGFloat = noDiscardsRemainingLabel.frame.height + 30 // Use the container's height
            ShowUserGuide(atStep: 4, clearPoint: &clearPoint, containerWidth: width, containerHeight: height)

You can ignore this part of the code, its just hows its drawn on to the screen, all the necessary points are put on to it.

func ShowUserGuide(atStep:Int, clearPoint:inout CGPoint, containerWidth:CGFloat, containerHeight: CGFloat, adjustClearPoint:Bool = true , flipBox:Bool = false, minX:CGFloat = 0, minY:CGFloat = 0)
    {
        if flipBox
        {
            step0roundLabelView = RoundedTextLabel(frame: CGRect(x: view.frame.minX + 10, y: view.frame.maxY - 110, width: 300, height: 100), text: HowToPlayGuide.shared.getGuideText(atStep: atStep), textSize: 15)
        } else
        {
            step0roundLabelView = RoundedTextLabel(frame: CGRect(x: view.frame.maxX - 310, y: view.frame.maxY - 110, width: 300, height: 100), text: HowToPlayGuide.shared.getGuideText(atStep: atStep), textSize: 15)
        }
        view.addSubview(step0roundLabelView!)
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(DismissGuideView(_:)))
        tapGestureRecognizer.name = String(atStep)
        view.addGestureRecognizer(tapGestureRecognizer)
        let maskView = UIView(frame: view.bounds)
        let maskLayer = CAShapeLayer()
        if adjustClearPoint
        {
            clearPoint.y = clearPoint.y - 20
        }
        var maskRect:CGRect
        if minY != 0
        {
            maskRect = CGRect(x:minX, y: minY, width: containerWidth, height: containerHeight)
        } else
        {
            maskRect = CGRect(x: clearPoint.x - containerWidth / 2, y: clearPoint.y - containerHeight / 2, width: containerWidth, height: containerHeight)
        }
        print("maskRect = \(maskRect)")
        let maskPath = UIBezierPath(rect: maskView.bounds)
        //maskPath.addArc(withCenter: clearPoint, radius: 50, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
        maskPath.append(UIBezierPath(rect: maskRect))
        maskPath.usesEvenOddFillRule = true
        maskLayer.path = maskPath.cgPath
        maskLayer.fillRule = .evenOdd
        
        let animation = CABasicAnimation(keyPath: "path")
        animation.fromValue = lastMaskPath?.cgPath //UIBezierPath(rect: darkOverlayView!.layer.frame).cgPath
        animation.toValue = maskPath.cgPath //UIBezierPath(rect: maskRect).cgPath
        animation.duration = 1.0 // Set the animation duration
        maskLayer.add(animation, forKey: "pathAnimation2")
        
        darkOverlayView!.layer.mask = nil
        darkOverlayView!.layer.mask = maskLayer
        lastMaskPath = maskPath
    }

Solution

  • It looks like you're points are off because your leading and trailing bounds for your UI elements are different in devices with a safe area(iPhone 15) and those without(iPhone SE). There isn't enough code provided to pin down where the issue is but if you build you're UI using leading and trailing of the edges of the controller's view.layoutMarginsGuide you should get the same result on devices with and without safe areas. The yellow areas in this screenshot illustrate the difference for your leading/trailing edges enter image description here