Does self.view has constraints? - ios

self.view is on the ViewController. Does it have any constraints? I found it had a top and a left constraint. It has a frame for sure. The autolayout system starts from self.view. I mean until self.view the iOS system is still using frame for positioning the views. After that, our views are able to be added with autolayout. So in the viewcontroller, I use self.frame.size.width for the width constraint for my own view is the correct approach to do? Am I right?

Use SCREEN_WIDTH and It's very easy to get your device size and manage your own view properly:
let SCREEN_WIDTH : CGFloat = UIScreen.main.bounds.width
let SCREEN_HEIGHT : CGFloat = UIScreen.main.bounds.height
For more information you can refer this : link
Example :
You should add your own new view programatically.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let SCREEN_WIDTH : CGFloat = UIScreen.main.bounds.width
let SCREEN_HEIGHT : CGFloat = UIScreen.main.bounds.height
let newView = UIView()
newView.translatesAutoresizingMaskIntoConstraints = false
newView.backgroundColor = UIColor.black
newView.frame.size = CGSize(width: SCREEN_WIDTH * 0.5 , height: SCREEN_HEIGHT * 0.5)
self.view.addSubview(newView)
self.view.layoutIfNeeded()
}
}
Here I am giving the frame to the constraints of my customized view. I don't want to give this value in a format of frame.size. I want to use somethings like constraint.constant. I mean pure autolayout.

self.view.frame is only accurate once viewDidLayoutSubviews() is called for the first time, typically just before viewDidAppear meaning if you try and access in viewDidLoad or first time viewWillAppear the frame will be wrong.
So you should just check in viewDidLayoutSubviews() (but note that function is called whenever the layout changes (this does not mean you scroll on a table view, it means the actual constraints change bc of a screen rotation for ex, or otherwise)
If you can't wait until that method or have some code that's not idempotent and don't want to have some like hasSetup bool to make sure you only do it once... Then you can either access the screen size directly via UIScreen.main.bounds
OR
I wouldn't necessarily recommend the following but for the sake of completeness I've also gotten away with tricks to get the view to layout earlier like calling view.layoutIfNeeded() earlier in the view lifecycle or on the view controller that initializes:
let myVC = MyViewController()
_ = myVC.view // fixes frame for some reason
Using AutoLayout correctly here
You want to use constraints but the frame shouldn't matter at all if you make relative constraints. Aka instead of saying height = constant you would say "make this the same height as vc.view" or "make the leading edge of this view 20 pixels right of the leading edge of vc.view".
something like:
let myView = UIView()
self.view.addSubview(myView)
myView.translatesAutoresizingMaskIntoConstraints = false
myView.heightAnchor.constraint(equalTo: self.view.heightAnchor)
myView.leadingAnchor.constraint(equalTo: vc.view.leadingAnchor, constant: 20.0) // 20 pixels to right of vc.view leading

Related

How do I prevent Keyboard from adding its own height constraint when using view as inputAccessoryView

I'm trying to use a custom view as an accessory view over the keyboard, for various reasons, in this case, it is much preferred over manual keyboard aligning because of some other features.
Unfortunately, this is a dynamic view that defines its own height. The constraints all work fine outside of the context of an accessoryView without errors, and properly resizing
When added as a keyboardAccessoryView it seems to impose a height of whatever the frame is at the time and break other height constraints
It appears as:
"<NSLayoutConstraint:0x600003e682d0 '_UIKBAutolayoutHeightConstraint' Turntable.ChatInput:0x7fb629c15050.height == 0 (active)>"
(where 0 would correspond to whatever height had been used at initialization
It is also labeled accessoryHeight which should make it easy to remove, but unfortunately, before I can do this, I'm getting unsatisfiable constraints and the system is tossing my height constraints
Tried:
in the inputAccessoryView override, I tried to check for the constraints and remove it, but it doesn't exist at this time
setting translatesAutoresizing...Constraints = false
tl;dr
Using a view as a KeyboardAccessoryView is adding its own height constraint after the fact, can I remove this?
Looks like keyboard doesn't like inputAccessoryView with height constraint. However you still can have inputAccessoryView with dynamic height by using frame (it is still possible to use constraints inside your custom inputAccessoryView).
Please check this example:
import UIKit
final class ViewController: UIViewController {
private let textField: UITextField = {
let view = UITextField()
view.frame = .init(x: 100, y: 100, width: 200, height: 40)
view.borderStyle = .line
return view
}()
private let customView: UIView = {
let view = UIView()
view.backgroundColor = .red
view.frame.size.height = 100
view.autoresizingMask = .flexibleHeight // without this line height won't change
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(textField)
textField.inputAccessoryView = customView
textField.becomeFirstResponder()
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.customView.frame.size.height = 50
self.textField.reloadInputViews()
}
}
}

How to make subviews resize to UIScrollView ContentSize?

I have a UIViewController that acts as a Container View Controller. It has a UIScrollView that has the same width as the screen, but it's height is smaller than the height of the screen.
The UIScrollView contains the views of two other UIViewControllers and those views can be horizontally scrolled through.
I set my contentSize like this:
scrollView.contentSize.width = self.view.bounds.width * 2
This works and allows me to scroll through my UIViewController views horizontally.
The following is how I add the UIViewController views to my scrollView:
private func addPanel(viewController: UIViewController, panel: Panel) {
let xOffset: CGFloat!
switch panel {
case .left:
xOffset = 0.0
case .right:
xOffset = self.view.bounds.width
}
let panelView = UIView(frame: CGRect(x: xOffset, y: 0, width: self.view.bounds.width, height: self.scrollView.bounds.height))
scrollView.addSubview(panelView)
let panelViewController: UIViewController! = viewController
var viewBounds = view.bounds
viewBounds.height = panelView.bounds.height
panelViewController.view.frame = view.bounds
panelView.addSubview(panelViewController.view)
addChildViewController(panelViewController)
panelViewController.didMove(toParentViewController: self)
}
For some reason, the UIViewController views don't resize to fit the height of the UIScrollView.
Should I be doing it constraint based? How would I do this. I've been struggling with this for a few days and am at a loss.
Essentially, the UIViewController views will just like like full screen views offset and so you can't see the bottom of the views because the bottom of the screen cuts them off.
I'm just taking a guess without knowing all the code, but one reason could be if you're adding the child view controllers before the scrollview has been layouted.
After you add and set the child views sizes, the scrollview adjusts its size to the phone size but you never update the child views.
My suggestion here would be to add the child view controllers to the scrollview, but move the frame setting code into a layouting method where you know your views have the correct(visible) frames/bounds.
Given you are writing this in a view controller, one method could be -viewDidLayoutSubviews
Hope this makes sense, feel free to ask more questions if it doesn't.

how to get current size of a subview when device size changes in swift?

I have to view: view_1 and view_2 . view_1 height is proportional to the main view controller height and I am adding view_2 programmatically as a subview of view_1 with a proportional height.
Problem
when the device size changes say from iPhone 6 to iPhone 5, the view_1 adjust correctly but its height I am passing to a function to draw its subview view_2 is still what was set in interface builder and so it is out of bounds in iPhone 4 moving from iPhone 6
Thus I want to know how can I get the correct size of a view that what resize automatically to fit the current device?
The size of the view isn't established until Auto Layout runs. Your first opportunity to get the correct size is in viewDidLayoutSubviews:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// access view size here
}
Here's a complete example that adds a second view as a subview of the first view. The red view was added in the Storyboard; it is centered horizontally and vertically and it's height/width is 50% of its superview.
The yellow view is added programmatically. It's frame is computed from the frame of the red view to be 50% of the width of the red view.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var view1: UIView!
var view2: UIView?
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if view2 == nil {
view2 = UIView()
view2?.backgroundColor = .yellowColor()
view1.addSubview(view2!)
}
let width = view1.frame.size.width
let height = view1.frame.size.height
let frame = CGRect(x: width * 0.25, y: height * 0.25, width: width * 0.5, height: height * 0.5)
view2?.frame = frame
}
}
Not that viewDidLayoutSubviews gets called multiple times, so you must take care not to add view2 every time it is called. That is why the code checks to see if view2 has been added.
Try this,you can find the screen size of device.
let screenSize: CGRect = UIScreen.mainScreen().bounds
let screenWidth = screenSize.width
let screenHeight = screenSize.height
Try to do this in Viewdidload
First you need to set view one frame
view_1.frame = CGRectMake(0 , 0, self.view.frame.width, self.view.frame.height)
then set frame of view_2
view_2.frame = CGRectMake(0 , 0, view_1.frame.width, view_1.frame.height)

Discrepancy in the widths of programmatically added views vs. interface builder views

I have come across a conundrum of sorts in regards unexpected (at least for me) sizes of UIViews.
Consider this UIViewController class. interfaceBuilderView was declared in a Storyboard file and constrained to take up the whole area of the UIView. So, I would expect to have interfaceBuilderView be the same size as programicallyCreatedView when calling *.frame.width. But they aren't.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var interfaceBuilderView: MyCustomView!
override func viewDidLoad() {
super.viewDidLoad()
let programmicallyCreatedView = MyCustomView(frame: self.view.frame)
//commented out this to get first picture
self.view.addSubview(programmicallyCreatedView)
self.interfaceBuilderView.setAppearance()
self.programmicallyCreatedView.setAppearance()
print(self.view.frame.width)//prints 375
print(self.interfaceBuilderView.frame.width)//prints 600
print(self.programmicallyCreatedView.frame.width)//prints 375
}
}
Now, consider this implementation of the MyCustomView class.
import UIKit
class MyCustomView: UIView {
func setAppearance() {
let testViewWidth: CGFloat = 200.0
let centerXCoor = (self.frame.width - testViewWidth) / 2.0
let testView = UIView(frame: CGRectMake(centerXCoor, 0, testViewWidth, 100))
testView.backgroundColor = UIColor.redColor()
self.addSubview(testView)
}
}
As you can see, I simply draw a red rectagle of width 200.0, and it is supposed to be centered. Here are the results.
Using the Interface Builder created view.
And using the programmatically created view.
As you can see, the programmatically created view achieves the desired results. No doubt because the size printed is the same as the superview (375).
Therefore, my question is simply why is this happening? Furthermore, how can I use a view declared in interface builder and programmatically add other views to it with dimensions and placement that I expect?
A few thoughts:
This code is accessing frame values in viewDidLoad, but the frame values are not yet reliable at that point. The view hasn't been laid out yet. If you're going to mess around with custom frame values, do this in viewDidAppear or viewDidLayoutSubviews.
Nowadays, we really don't generally use frame values anymore. Instead, we define constraints to define the layout programmatically. Unlike custom frame values, you can define constraints when you add the subviews in viewDidLoad.
You have the scene's main view, the MyCustomView and then yet another UIView which is red. That strikes me as unnecessarily confusing.
I would advise that you just add your programmatically created subview in viewDidLoad and specify its constraints. Using the new iOS 9 constraints syntax, you can just specify that it should be centered, adjacent to the top of the view, half the width, and one quarter the height:
override func viewDidLoad() {
super.viewDidLoad()
let redView = UIView()
redView.backgroundColor = UIColor.redColor()
redView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(redView)
NSLayoutConstraint.activateConstraints([
redView.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor),
redView.topAnchor.constraintEqualToAnchor(view.topAnchor),
redView.widthAnchor.constraintEqualToAnchor(view.widthAnchor, multiplier: 0.5),
redView.heightAnchor.constraintEqualToAnchor(view.heightAnchor, multiplier: 0.25)
])
}
Clearly, adjust these constraints as suits you, but hopefully this illustrates the idea. Don't use frame anymore, but rather use constraints.

getting wrong center of frame for detail view for UISplitView

learning SWIFT. I can't seem to access bounds from my view... no matter what platform I work in the bounds return the same values (600,600).
I have a graphView : UIView & controller embedded in a navigationController as the Detail of a SplitViewController.
I am trying to get the center of my graphView : UIView on screen (the Detail of the splitViewController), but center keeps returning a point too far to the right (in portrait)/bottom (in landscape).
I tried accessing it multiple ways, but maybe my understanding of it is wrong?
example: var screenCenter: CGPoint = convertPoint( center, fromView: superview)
println("bounds are \(bounds)") // view boundaries
println("frame is at \(frame)") // frame where the view resides
println(" center is at \(center)")
println(" ScreenCenter is at \(screenCenter)")
(output)
bounds are (0.0,0.0,600.0,600.0)
frame is at (0.0,0.0,600.0,600.0)
center is at (300.0,300.0)
ScreenCenter is at (300.0,300.0)
-> how do I actually get to the center? Is SplitView geography joint master + detail?
-> it seem the x positioning is correct in portrait (but not the y), and the y is correct in portrait (but not the x)
SOLVED! the graphView in Storyboard was missing constraints and using the default 600,600 values. "Reset to Suggested Constraints" in storyboard with the view selected fixed the issue
If you do not use storyboard this can help.
Create a view where you set up the autoconstraints to false.
Constraint the view to the safeAreaLayoutGuide.It will find the centre of your secondary view or detail view.
var logoView :UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFit
iv.backgroundColor = .green
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(logoView)
logoView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor).isActive = true
logoView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
logoView.heightAnchor.constraint(equalToConstant: 200).isActive = true
logoView.widthAnchor.constraint(equalToConstant: 200).isActive = true
}

Resources