UIImageView frame not placing correctly on the parent ImageView - ios

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)

Related

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

Programmatically adding UILabel is not visible

I'm trying to practice here, I have a SearchBar in my view and i've used
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
Once the user starts writing, I hide on of my views, and make another one programmatically
Here's my concept, Once i start writing, my 2nd view comes in:
But once i get the second view properly added, i'd like to add a UILabel to it. Here's my code:
self.word_of_the_day_view.isHidden = true // main view
self.popularView.frame = CGRect(x: self.searcy_bar_view.frame.minX, y: self.searcy_bar_view.frame.maxY + 15, width: self.searcy_bar_view.frame.width, height: self.searcy_bar_view.frame.height) // don't mind the height and width
self.popularView.backgroundColor = .white
self.popularView.layer.cornerRadius = self.searcy_bar_view.layer.cornerRadius
self.popularView.popIn() // just an animation
is_popular_added = true
self.view.addSubview(popularView)
let label_1 = UILabel()
label_1.frame = CGRect(x: self.popularView.frame.minX + 3, y: self.popularView.frame.minY, width: self.popularView.frame.width, height: 20)
label_1.font = UIFont(name: "avenirnext-regular", size: 13)
label_1.text = "Hello World!"
label_1.layer.borderColor = UIColor.red.cgColor
popularView.addSubview(label_1)
But on runtime, the UILabel isn't even added.
Thank you so much for even reading the question!
You need to understand the difference between Frame and Bounds.
The Frame of the view is the position of the view in it's super view, so his origin can have any value of a CGPoint.
The Bounds are just the view itself, so it always have an origin of (0,0)
I think the problem is
label_1.frame = CGRect(x: self.popularView.frame.minX + 3, y: self.popularView.frame.minY, width: self.popularView.frame.width, height: 20)
try to change in
label_1.frame = CGRect(x: self.popularView.bounds.minX + 3, y: self.popularView.bounds.minY, width: self.popularView.frame.width, height: 20)
or
label_1.frame = CGRect(x: 3, y: 0, width: self.popularView.frame.width, height: 20)
In your code
label_1.frame = CGRect(x: self.popularView.frame.minX + 3, y: self.popularView.frame.minY, width: self.popularView.frame.width, height: 20)
This is because self.popularView.frame.minX and self.popularView.frame.minY have some +value that crosses the boundary of the current view on which you are trying to add label_1.
you should use self.popularView.bounds.origin.x and self.popularView.bounds.origin.y
Difference between bounds and frame
frame = a view's location and size with respect to the parent view's coordinate system
bounds = a view's size using its own coordinate system
This issues is due to confusion between frames and bounds. I also got similar issue in on of my app.
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.
you should use self.popularView.bounds.origin.x and self.popularView.bounds.origin.y in place of self.popularView.frame.minX and self.popularView.frame.minY

Cannot set UIScrollView contentSize based on UIIMage

I am struggling for 2 hours now to get a UIScrollView.contentSize with the size of a UIImage, but the UIImage doesn't return a proper size, I have no idea why.
The result is that my scrollView content is square(the same as the scrollView's frame) and I can't scroll to the side of the image.
This is really frustrating! You can see in the debugger that my image1.size is invalid.
you are accessing an image from array which doesn't exist see your console log it's clearly saying index out of range. that's why your not getting any size and height as well because there's no image in the specific index
Your image view size is showing wrong because your scrollview content size is wrong. Use this method than print your image view frame. It will work
override func viewDidLayoutSubviews()
{
self.scrollView1.delegate = self
self.scrollView1.contentSize = CGSize(width:self.view.frame.size.width, height: self.view.frame.size.height)
}
OK, I have been struggling with this for a while now, and finally I was able to find the answer. The problem was indeed in the content size, and I was finally able to calculate it correctly as below:
let tempScrollView = UIScrollView(frame: self.imageLocationsArray[imageID])
let tempImageView = UIImageView(image: image)
//determine proportions of image against frame
let imageWidthFactor = image.size.width / tempScrollView.frame.width
let imageHeightFactor = image.size.height / tempScrollView.frame.height
//image is too wide
if imageWidthFactor > imageHeightFactor {
tempImageView.frame = CGRect(x: 0, y: 0, width: tempScrollView.frame.width * imageWidthFactor / imageHeightFactor, height: tempScrollView.frame.height)
}
//image is too tall
else {
tempImageView.frame = CGRect(x: 0, y: 0, width: tempScrollView.frame.width, height: tempScrollView.frame.height * imageHeightFactor / imageWidthFactor)
}

Alignment of UIImageView within UIScrollView

I see this topic in quite a few places but I can't figure out why exactly my code doesn't work.
I have an image of an artificial horizon that goes from -90 to 90 degrees. I want to view it through a small window which just shows around -20 to 20 degrees. Then I want to move the image up and down based on the angle my robot is leaning.
I started by adding a UIImage to a UIImageView. All of the alignment is correct. Then I thought the easiest way to move the image up and down was to add the UIImageView to a UIScrollView. Now I can't figure out how to get the alignment right. I see the image in there if I drag in the scrollview but as soon as I let go it goes back to where it was.
Here is the code I have. This is the first Swift code I have written so if there is a better way to do this I welcome any ridicule (just kidding, be gentle)
override func viewDidLoad() {
super.viewDidLoad()
let imageRect = CGRectMake(self.view.frame.width / 2 - 100, self.view.frame.height / 2, 200, 200)
self.myImageView = UIImageView.init()
self.myImageView = UIImageView(frame: imageRect)
self.myImageView.contentMode = UIViewContentMode.Center
self.myImageView.clipsToBounds = true
self.myImageView.image = UIImage.init(named:"horizon")
//self.view.addSubview(self.image)
self.myScrollView = UIScrollView(frame: imageRect)
self.myScrollView.contentSize = CGSize(width: imageRect.width, height: imageRect.height)
self.myImageView.center = self.myScrollView.center
self.myScrollView.frame = imageRect
self.myScrollView.backgroundColor = UIColor.blackColor()
self.myScrollView.contentOffset = CGPoint(x: 0, y: 0)
self.myScrollView.addSubview(self.myImageView)
self.view.addSubview(self.myScrollView)
/////////
self.connectionStatusLabel.text = "Disconnected"
self.connectionStatusLabel.textColor = UIColor.redColor()
self.textBox.font = UIFont(name: self.textBox.font!.fontName, size: 8)
// Watch Bluetooth connection
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.connectionChanged(_:)), name: BLEServiceChangedStatusNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.dataReceived(_:)), name: BLEDataChangedStatusNotification, object: nil)
// Start the Bluetooth discovery process
btDiscoverySharedInstance
}
You have to set the scrollView's contentSize to the actual image size not the imageView's size. And set the imageView's size to the actual image size as well:
let image = UIImage.init(named:"horizon")
// Set the imageView's size to the actual image size
self.myImageView.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
...
...
// Set the scrollView's contentSize to the actual image size
self.myScrollView.contentSize = image.size

How to create percentage of total width using autolayout?

I need to create three dynamic columns, each with a fixed percentage of the total width. Not thirds, but different values. For example, the following illustration shows three columns: the first being 42% wide, the second being 25% wide, and the third being 33% wide.
For a 600 pixel across viewcontroller, that would be 252, 150, and 198 pixels respectively.
However, for any subsequent display sizes (i.e. iPhone 4 landscape (960 wide) or iPad 2 portrait (768 wide), I would like the relative percentages to be the same (not the pixel widths quoted above).
Is there a way to do this using Storyboards (i.e. without code)? I can do this easily in code, but my goal is to put as much of this display logic as possible into the Storyboard.
If, as you say, you know how to do it in code, then you already know how to do it in the storyboard. It's exactly the same constraints, but you are creating them visually rather than in code.
Select both a view and its superview.
Choose Editor -> Pin -> Widths Equally to constrain the width to be equal to the superview's width (actually the "pin" popup dialog at the bottom of the canvas works best here).
Edit the constraint and set the Multiplier to the desired fraction, e.g. 0.42. And so too for the other views.
As Apple introduces UIStackView it made job much easy.
Method 1: Using Nib/StoryBoard:
You have to just add three view in interface builder & embed them into stackview
Xcode ► Editor ► Embed in ► StackView
Select stackView & give constraint with leading, trailing, top & equal height with safeArea
Click to Attribute inspector area &
Set StackView horizontal & distribution to fill proportionally
[3
Give constraint of three view with leading, trailing, top, bottom with respective of sides.
Method 2: Programmatically:
import UIKit
class StackViewProgramatically: UIViewController {
var propotionalStackView: UIStackView!
///Initially defining three views
let redView: UIView = {
let view = UIView()//taking 42 % initially
view.frame = CGRect(x: 0, y: 0, width: 42 * UIScreen.main.bounds.width/100, height: UIScreen.main.bounds.height)
view.backgroundColor = .red
return view
}()
let greenView: UIView = {
let view = UIView()//taking 42* initially
view.frame = CGRect(x: 42 * UIScreen.main.bounds.width/100, y: 0, width: 25 * UIScreen.main.bounds.width/100, height: UIScreen.main.bounds.height)
view.backgroundColor = .green
return view
}()
let blueView: UIView = {
let view = UIView()//taking 33*initially
view.frame = CGRect(x: 67 * UIScreen.main.bounds.width/100, y: 0, width: 33 * UIScreen.main.bounds.width/100, height: UIScreen.main.bounds.height)
view.backgroundColor = .blue
return view
}()
///Changing UIView frame to supports landscape mode.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
DispatchQueue.main.async {
self.redView.frame = CGRect(x: 0, y: 0, width: 42 * self.widthPercent, height: self.screenHeight)
self.greenView.frame = CGRect(x: 42 * self.widthPercent, y: 0, width: 25 * self.widthPercent, height: self.screenHeight)
self.blueView.frame = CGRect(x: 67 * self.widthPercent, y: 0, width: 33 * self.widthPercent, height: self.screenHeight)
}
}
override func viewDidLoad() {
super.viewDidLoad()
//Adding subViews to the stackView
propotionalStackView = UIStackView()
propotionalStackView.addSubview(redView)
propotionalStackView.addSubview(greenView)
propotionalStackView.addSubview(blueView)
propotionalStackView.spacing = 0
///setting up stackView
propotionalStackView.axis = .horizontal
propotionalStackView.distribution = .fillProportionally
propotionalStackView.alignment = .fill
view.addSubview(propotionalStackView)
}
}
//MARK: UIscreen helper extension
extension NSObject {
var widthPercent: CGFloat {
return UIScreen.main.bounds.width/100
}
var screenHeight: CGFloat {
return UIScreen.main.bounds.height
}
}
Output:
Works with landscape & portrait
Demo project - https://github.com/janeshsutharios/UIStackView-with-constraints
https://developer.apple.com/videos/play/wwdc2015/218/
I think this can be explained in more detail so it can be more easily applied to any number of views requiring fixed percentage layouts within a superview.
Left-most view
Anchored to SuperView.Leading
Defines its fixed percentage as a multiplier on the SuperView.Height
Intermediate views
Defines its fixed percentage as a multiplier on the SuperView.Height
Pins its left to its neighbor's right
Right-Most view
Does not define a fixed percentage (it is the remainder of the available view)
Pins its left to its neighbor's right
Pins its right to SuperView.Trailing
All Views
Define their non-fixed heights by anchoring to Top Layout Guide.Top and Top Layout Guide.bottom. In the answer above, it is noted that this can also be done by setting equal height to the neighboring view.

Resources