In UIKIT I have two uiview's main view and uiview installed with storyboard at top with high in 1/3 of main View.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var TopView: UIView!
@IBOutlet weak var MiddleView: UIView!
@IBOutlet weak var BottomView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let t = Vvp(inView: TopView)
TopView.addSubview(t)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: TopView.frame.maxX, y: 0))
bezierPath.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = UIColor.red.cgColor
shapeLayer.lineWidth = 1.0
TopView.layer.addSublayer(shapeLayer)
}
}
second view:
func Vvp(inView: UIView)-> UIView {
let viewWithBeizer = UIView(frame: inView.frame)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: inView.frame.maxX, y: 0))
bezierPath.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
// apply other properties related to the path
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.blue.cgColor
shapeLayer.lineWidth = 1.0
viewWithBeizer.layer.addSublayer(shapeLayer)
return viewWithBeizer
}
both views work with the same frame, at storyboard all borders are at zero why lines are not the same?
CodePudding user response:
I think that this is a bug with iPod touch 7' emulator - with another emulators code works well. Below you can see the code from my question where I add 2px to red line.
CodePudding user response:
The problem has nothing to do with where the lines are being drawn...
The issue is that you are referring to frame
when you should be using bounds
, and you're setting the frames before auto-layout has configured your views.
Based on your screen-shots, you are laying out your views in Storyboard based on an iPhone model with a Notch... so, in viewDidLoad()
your TopView
has the frame that was set in Storyboard.
This is how it looks using an iPhone 13 Pro in Storyboard:
As you can see, even though the yellow TopView is constrained to the top of the safe area, its Y
position is 44
. So, your code in your func Vvp(inView: UIView)
is setting the Frame Y-position to 44
, instead of Zero.
If you add these 4 lines at the end of viewDidLoad()
:
TopView.layer.addSublayer(shapeLayer)
// move t (from Vvp(inView: TopView))
// 40-pts to the right
t.frame.origin.x = 40.0
// give it an orange background color
t.backgroundColor = .orange
// allow it to show outside the bounds of TopView
TopView.clipsToBounds = false
// bring TopView to the front of the view hierarchy
view.bringSubviewToFront(TopView)
The output on an iPad Touch 7th Gen looks like this:
as you can see, TopView's subview (the orange view) is much larger than TopView, and is showing up where you told it to: 44-pts from the top of TopView.
To use the code the way you've written it, you need to call that func - along with the shapeLayer code for TopView - later in the controller's lifecycle... such as in viewDidLayoutSubviews()
. If you do that, though, you need to remember it will be called multiple times (any time the main view changes, such as on device rotation), so you'll want to make sure you don't repeatedly add new subviews and layers.
Here's a quick modification of your code:
class ViewController: UIViewController {
@IBOutlet weak var TopView: UIView!
@IBOutlet weak var MiddleView: UIView!
@IBOutlet weak var BottomView: UIView!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if TopView.subviews.count == 0 {
// we haven't added the subview or shape layer,
// so let's do that here
let t = Vvp(inView: TopView)
TopView.addSubview(t)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: TopView.frame.maxX, y: 0))
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = UIColor.red.cgColor
shapeLayer.lineWidth = 1.0
TopView.layer.addSublayer(shapeLayer)
}
}
func Vvp(inView: UIView)-> UIView {
let viewWithBeizer = UIView(frame: inView.bounds)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: inView.bounds.maxX, y: 0))
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
// apply other properties related to the path
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.blue.cgColor
shapeLayer.lineWidth = 1.0
viewWithBeizer.layer.addSublayer(shapeLayer)
return viewWithBeizer
}
}
Result (blue line is not visible, because we've added the red line on top of it):
A better approach, though, is to A) use auto-layout constraints, and B) handle your shapeLayer logic inside a custom UIView
subclass -- but that's another topic.