Programmatic UIView with subview not rendering in correct position - ios

I'm trying to render a view that has a subview in it. However, the subview renders in the incorrect y-position.
ViewController is the parent view.
ViewController2 is a subview of ViewController and has it's own subview (let's call it X). X is being rendered in the incorrect y-position, even though ViewController2 and X have the same y value for their frames.
Edit:
I should note, X should be appear in the same position within ViewController2. Or at least, that's the intention.
See code below:
ViewController.swift
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
var y: CGFloat = 10
for vc in getVcs() {
self.view.addSubview(vc.render(x: 10, y: y))
y += 75
}
super.viewDidLoad()
}
func getVcs() -> [ViewController2] {
return [
ViewController2(),
ViewController2(),
ViewController2()
]
}
}
ViewController2.swift
import UIKit
class ViewController2: UIViewController {
func render(x: CGFloat, y: CGFloat) -> UIView {
let viewFrame = CGRect(x: x, y: y, width: 300, height: 50)
let xview = UIView(frame: viewFrame)
let subViewFrame = CGRect(x: x, y: y, width: 200, height: 25) // X
let subView = UIView(frame: subViewFrame)
subView.layer.borderColor = UIColor.green.cgColor
subView.layer.borderWidth = 1
xview.addSubview(subView)
xview.layer.borderColor = UIColor.red.cgColor
xview.layer.borderWidth = 1
return xview
}
}
How they appear at runtime:
(Green border represents X and the red border represents ViewController2.)
Any help would be appreciated!

A view defines its own frame in relation to its parent view.
In ViewController2, the subview you instantiated based on y parameter is getting added in relation to its parent view (the red box).
The solution is to change y value in relation to parent frame on your subview frame
let subViewFrame = CGRect(x: x, y: y, width: 200, height: 25) // X
Also to get a clear idea what's happening, try adding another ViewController2() in getVCs() and it will look like this.
As you can see, your code is not misplacing the second view as it looks like from your screen shot. It's placing the green box further and further in relation to its parent frame. Third frame was just a lucky hit. Hope this helps =)

Frames are specified in their SUPERVIEW'S COORDINATES. Your bounds are in the current view's coordinates. Therefore ViewController2's frame needs to be expressed as relative to ViewController 's frame, not relative to the window.

Related

UIImageView frame not placing correctly on the parent ImageView

I have a color palette imageview, in that I want to place a plus icon(imageView) according to the x and y-axis I am getting from the backend. If I get x = 0 and y = 0 the frame of the plus is placing correctly
For y axis if I set height of the color palette imageView to the plus icon's frame's y axis, the icon is not going to the actual (0,0)
.
The code I used is below
let cWidth = self.colorPalleteImageView.frame.size.width // 348
let cHeight = self.colorPalleteImageView.frame.size.height // 378.5
let imageView = UIImageView(image: appImages.roundPlusIcon.image)
imageView.frame = CGRect(x: 0, y: cHeight, width: 22, height: 22)
colorPalleteImageView.addSubview(imageView)
I am checking this with iPad 12.9 inch simulator. Am I missing anything to achieve that x=0 and y=0, If I give the width of the colorPaletteImage to the x-axis of plusIconImageView it is not going to end fo the x-axis, It stays before the end of the width of the imageview, I don't know why it is happening, Need help
You're using frame sizes before the frames are finished being set by auto-layout.
I'd suggest using constraints, but if you want to stick to frame coordinates...
add the "round plus" icon imageView in viewDidLoad()
set its frame.origin in viewDidLayoutSubviews() or viewDidAppear()
imageView.frame = CGRect(x: cWidth, y: cHeight, width: 22, height: 22)
mast be:
imageView.frame = CGRect(x: 0, y: 0, width: 22, height: 22)
as axes are:
0,0-----------------> x
| ▢
|
|
|
V
with 0,0 you put top left of little image to to top left of big.
You can fix this issue by modifying the height property to the super view's bound's height. You need to know the difference between frame and bounds to understand why this is happening. In simple words frame is the CGRect with respect to it's super view and bounds is the CGRect with respect to it's own coordinates.
Plenty of detailed explanations are available online just need to google frame vs bounds and you'll get used to both of these after you play with it many times.
Here's how you fix this issue.
let cHeight = self.colorPalleteImageView.bounds.size.height
sorry.. I am back
I did (to follow your code I forced values..)
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var colorPalleteImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let v = self.view
print(v!.frame)
let cHeight = CGFloat(378.5)
colorPalleteImageView.frame.size.width = 348
colorPalleteImageView.frame.size.height = cHeight
let plusImg = UIImage(named: "plus")
let imageView = UIImageView(image: plusImg)
imageView.frame = CGRect(x: 0, y: cHeight-22, width: 22, height: 22)
colorPalleteImageView.addSubview(imageView)
}
}
and I got: (on iPad)

Increasing frame.origin.x vs constant

I'm trying to understand how iOS frame and bounds works.
I put an subView:UIView on UIViewController and a button which can increase subView's frame origin coordinate and change textlabel with its value.
like this,
let subView: UIView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 100))
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(subView)
subView.backgroundColor = .blue
}
#IBAction func btnMoveBottomView(_ sender: Any) {
subView.frame.origin.y = subView.frame.origin.y + 100
lbFrameInfo.text = String(format:"sub = (%.1f, %.1f)", subView.frame.origin.x, subView.frame.origin.y)
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
After I make this action, I see subView go down but text doesn't change.
on debug console I see this change
po subView.frame.origin
▿ (50.0, 2350.0)
- x : 50.0
- y : 2350.0
updateViewConstraints has been called too.
override func updateViewConstraints() {
print("updateViewConstraints")
super.updateViewConstraints()
}
Instead of increasing frame.origin.x like this, increasing value of leading constant works perfectly.
It would be appreciated if someone can guide me the differences of these and the concept of frame and if it is related with auto-layout things
According to your question,
You have added subView to ViewController view so when u create view
let subView: UIView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 100))
and do this
view.addSubview(subView)
it means your subview will be at 50 position down and away from left of the screen.
so when u do this.
subView.frame.origin.y = subView.frame.origin.y + 100
it will move your frame further down in the screen as you are changing the y position of frame which is the starting position of creating your view on screen.
At its simplest, a view’s bounds refers to its coordinates relative to its own view (as if the rest of your view hierarchy didn’t exist), whereas its frame refers to its coordinates relative to its parent’s view. Frame will reflect the position in its parents view.
This means a few things:
1.If you create a view at X:0, Y:0, width:100, height:100, its frame and bounds are the same.
2.If you move that view to X:100, its frame will reflect that change but its bounds will not. Remember, the bounds is relative to the view’s own space, and internally to the view nothing has changed.
3.If you transform the view, e.g. rotating it or scaling it up, the frame will change to reflect that, but the bounds still won’t – as far as the view is concerned internally, it hasn’t changed
hope it clears your doubts

Want to present modal view but how to make it take up screen partially?

I have two viewcontrollers. (See the attachment) Viewcontroller on the left is called "ViewController" and Modal viewController on the right is known as "draggableViewController". When I run this app, blue view object on the right will take over the first viewController. Blue view object on the Right Modal viewController should slide over on top of the first viewController, but I am presenting that view with no animation, so it will appear as if blue view has been resting on top of first ViewController.
What I want to achieve is to show only 1/3 of the blue view and I want it to rest on bottom 1/3 of the screen. I have no idea on how to set the position of blue view
to show it only on bottom 1/3 of the screen.
Here is my source code for each of viewControllers.
1: ViewController:
class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
let newView = self.storyboard!.instantiateViewController(withIdentifier: "draggableViewController") as! draggableViewController
newView.modalPresentationStyle = UIModalPresentationStyle.overFullScreen
self.present(newView, animated: false, completion: nil)
}
}
2: draggableViewController:
class draggableViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.frame = CGRect(x: 0, y: 300, width: self.view.frame.width, height: self.view.frame.height)
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panGestureRecognizerAction(_:)))
self.view.addGestureRecognizer(panGestureRecognizer)
}
#objc func panGestureRecognizerAction(_ gestureRecognizer: UIPanGestureRecognizer){
// 1. use these values to restrict the left and right sides so the orangeImageView won't go beyond these points
let leftSideRestrction = self.view.frame.minX
let rightSideRestriction = self.view.frame.maxX
let topRestriction = 0
// 2. use these values to redraw the orangeImageView's correct size in either Step 6 or Step 8 below
let imageViewHeight = self.view.frame.size.height
let imageViewWidth = self.view.frame.size.width
if gestureRecognizer.state == .changed || gestureRecognizer.state == .began {
let translation: CGPoint = gestureRecognizer.translation(in: self.view)
gestureRecognizer.view!.center = CGPoint(x: gestureRecognizer.view!.center.x + translation.x, y: gestureRecognizer.view!.center.y + translation.y)
gestureRecognizer.setTranslation(CGPoint.zero, in: self.view)
/*
3.
-get the the upper left hand corner of the imageView's X and Y origin to get the current location of the imageView as it's dragged across the screen.
-you need the orangeImageView.frame.origin.x value to make sure it doesn't go beyond the left or right edges
-you need the orangeImageView.frame.origin.y value to redraw it in Steps 6 and 8 at whatever Y position it's in when it hits either the left or right sides
*/
var imageViewCurrentOrginXValue = self.view.frame.origin.x
var imageViewCurrentOrginYValue = self.view.frame.origin.y
print("origin x: \(imageViewCurrentOrginXValue)")
print("origin y: \(imageViewCurrentOrginYValue)")
// 4. get the right side of the orangeImageView. It's computed using the orangeImageView.frame.origin.x + orangeImageView.frame.size.width
let imageViewRightEdgePosition = imageViewCurrentOrginXValue + imageViewWidth
// 5. if the the orangeImageView.frame.origin.x touches the left edge of the screen or beyond it proceed to Step 6
if imageViewCurrentOrginXValue <= leftSideRestrction {
// 6. redraw the orangeImageView's frame with x: being the far left side of the screen and Y being where ever the current orangeImageView.frame.origin.y is currently positioned at
if imageViewCurrentOrginYValue <= 0 {
// 6. redraw the orangeImageView's frame with x: being the far left side of the screen and Y being where ever the current orangeImageView.frame.origin.y is currently positioned at
imageViewCurrentOrginYValue = 0
self.view.frame = CGRect(x: leftSideRestrction, y: imageViewCurrentOrginYValue, width: imageViewWidth, height: imageViewHeight)
}
if imageViewCurrentOrginYValue >= self.view.frame.height-200 {
// 6. redraw the orangeImageView's frame with x: being the far left side of the screen and Y being where ever the current orangeImageView.frame.origin.y is currently positioned at
imageViewCurrentOrginYValue = self.view.frame.height - 200
print(imageViewCurrentOrginYValue)
self.view.frame = CGRect(x: leftSideRestrction, y: imageViewCurrentOrginYValue, width: imageViewWidth, height: imageViewHeight)
}
self.view.frame = CGRect(x: leftSideRestrction, y: imageViewCurrentOrginYValue, width: imageViewWidth, height: imageViewHeight)
}
// 7. if the the orangeImageView.frame.origin.x touches the right edge of the screen or beyond it proceed to Step 8
if imageViewRightEdgePosition >= rightSideRestriction{
// 8. redraw the orangeImageView's frame with x: being the rightSide of the screen - the orangeImageView's width and y: being where ever the current orangeImageView.frame.origin.y is currently positioned at
if imageViewCurrentOrginYValue <= 0 {
// 6. redraw the orangeImageView's frame with x: being the far left side of the screen and Y being where ever the current orangeImageView.frame.origin.y is currently positioned at
imageViewCurrentOrginYValue = 0
self.view.frame = CGRect(x: leftSideRestrction, y: imageViewCurrentOrginYValue, width: imageViewWidth, height: imageViewHeight)
}
if imageViewCurrentOrginYValue >= self.view.frame.height - 200 {
// 6. redraw the orangeImageView's frame with x: being the far left side of the screen and Y being where ever the current orangeImageView.frame.origin.y is currently positioned at
imageViewCurrentOrginYValue = self.view.frame.height - 200
print(imageViewCurrentOrginYValue)
self.view.frame = CGRect(x: leftSideRestrction, y: imageViewCurrentOrginYValue, width: imageViewWidth, height: imageViewHeight)
}
self.view.frame = CGRect(x: rightSideRestriction - imageViewWidth, y: imageViewCurrentOrginYValue, width: imageViewWidth, height: imageViewHeight)
}
}
}
}
Where do I set the position of that blue view?
A simple solution is to set constraints in the blue view to be 1/3 the size of the main controller, and make the background transparent, or blurry so it shows as its sliding up only 1/3 the size.
Steps to this, set a View inside the blue and use that view, and everything behind it to make it transparent.
Where do I set the position of that blue view
In a UIPresentationController subclass, when you do this properly as a custom transition animation.

Getting details about the pinch gesture in ARKit

In my project I have a pinch to resize option for the object that has been placed in scene view. But when someone pinch the screen to reduce or enlarge the actual size of the object I need to get that scale. I need to display the scale in which the object is being changed in the screen. How do I get the scale when the action is being performed?
Thank you
Within your main ViewController Class for the ARSCNView
declare the label view, and the label itself at the top.
let scaleLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, 70))
let labelView = UIView(frame: CGRect(x: 300, y: 300, width: 300, height: 70))
Now within LoadView or ViewDidLoad you can set the attributes for the label such backgroundColor, textColor etc... and also add the view and label to sceneView.
// add your attributes for label,view
labelView.backgroundColor = .clear
scaleLabel.textColor = .white
scaleLabel.adjustsFontSizeToFitWidth
// add you views to sceneView
labelView.addSubview(scaleLabel)
sceneView.addSubview(labelView)
Lastly, with the pinch gesture function for scaling.. which should look something like this.
#objc func pinchGesture(_ gesture: UIPinchGestureRecognizer) {
if nodeYouScale != nil {
let action = SCNAction.scale(by: gesture.scale, duration: 0.1)
nodeYouScale.runAction(action)
gesture.scale = 1
// this part updates the label with the current scale factor
scaleLabel.text = "X: \(nodeYouScale.scale.x) Y: \(nodeYouScale.scale.y) Z:\(nodeYouScale.scale.z)"
} else {
return
}

Why iOS coordinate system is difficult to understand?? only me?

I'm studying iOS' UIView!
And I found that I can't understand how bounds works.
For example,
Please run this code ... and see red box's moving.
The red box goes up! and white root view is static!
Why!? why red box goes up! ?? please let me know OTL!
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let childView : UIView = UIView(frame: CGRect(x: 50, y: 50, width: 200, height: 200) )
childView.backgroundColor = UIColor.red
self.view.addSubview(childView)
}
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 8, animations: {
// I cannot unnerstand how bounds works!!
self.view.bounds = CGRect(x: 0, y: -300, width:self.view.bounds.width, height: 700)
//I can understand how frame works
self.view.frame = CGRect(x: 200, y: 0, width: self.view.bounds.width, height: self.view.frame.height)
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I read this article, but I can not understand
CompositedPosition.x = View.frame.origin.x - Superview.bounds.origin.x;
CompositedPosition.y = View.frame.origin.y - Superview.bounds.origin.y;
HERE! this is right ( according to what I tested, it was right. ) but, I don't know why. Why not "View.frame.origin.x + Superview.bounds.origin.x" ?
If we change the origin of view's bounds and the view moves on screen , what is the difference between "bounds" and "frame" in perspective of origin(or position) ? Is there a difference other than simply acting in reverse?
Basically, The bounds of an UIView is the rectangle, expressed as a location (x,y) and size (width,height) relative to its own coordinate system (0,0).
The frame of an UIView is the rectangle, expressed as a location (x,y) and size (width,height) relative to the superview it is contained within.
Please follow this link for clear example. Cocoa: What's the difference between the frame and the bounds?

Resources