Anchor constraint shrinks superview instead of enlarging subview - ios

A custom view is located in IB by dragging UIView object from library and it's class name is set to the custom view class name. This custom view has a subview, which is added in init?(coder aDecoder: NSCoder).
How should anchor type constraints be composed, and where should they be located so the _centralLabel subview is set centrally in the custom view, and it's dimensions are 25% of the custom's view dimensions?
If the code is written like this:
override func updateConstraints() {
if !_subviewsConstraintsAdded {
_centralLabel.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.25).isActive = true
_centralLabel.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.25).isActive = true
_centralLabel.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
_centralLabel.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
_subviewsConstraintsAdded = true
}
super.updateConstraints()
}
The result of the code is, that instead of setting the size of _centralLabel to be 25% of the custom view, the custom view is shrunk to (0,0) which is the size of _centralLabel while updateConstraints() is being called.

The missing bit was that:
_centralLabel.translatesAutoresizingMaskIntoConstraints = false
which should be located somewhere after programmatically instantiating the subview with this line:
_centralLabel = UILabel()
After this correction the _centralLabel subview has enlarged and has moved to the center of the view, while the view itself remained in its original size, shape and position.

Related

How to access the leading and trailing of the AVPlayerLayer when the videoGravity is set to resizeAspect?

I was wondering if it's possible to access the leading anchor and the trailing anchor of the AVPlayerLayer especially when the videoGravity property is set to resizeAspect. (I added an image and used red text to indicate where it is).
Currently, I'm working on a video camera app, and the view file, LiveCameraView, has one UIView, videoView, and the videoView is pinned to the LiveCameraView's safe area (only the leading and trailing).
I want to set the videoGravity as .resizeAspect, but I also want to pin the translucent black area's trailing (on the right side of the view which covers red and white record button) to the right-side edge of where the image is captured.
Sorry, it's hard to explain, but when I set the value to .resizeAspectFill, the whole width of the translucent black areas appeared. I want that happens when I use resizeAspect as well. So the right side of the LiveCameraView should display
the full width of the translucent black area
the black area since using resizeAspect
Blue background area
So, I want to make sure somehow get the leading and the trailing of the AVPlayerLayer without the complete black area.
The HKView is a library I'm using and it's super class is UIView.
import AVFoundation
lazy var videoView: HKView = {
let view = HKView()
view.videoGravity = .resizeAspect
return view
}()
lazy var rightContainerView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.translucentBlack()
return view
}()
required override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
setupConstraints()
self.backgroundColor = .blue // put this for testing
}
func setupViews() {
self.addSubview(videoView)
hkView.addSubview(rightContainerView)
rightContainerView.addSubview(recordButton)
}
func setupConstraints() {
caneraView.anchor(top: self.topAnchor, leading: self.safeAreaLayoutGuide.leadingAnchor, bottom: self.bottomAnchor, trailing: self.safeAreaLayoutGuide.trailingAnchor)
rightContainerView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
rightContainerView.topAnchor.constraint(equalTo: cameraView.topAnchor),
rightContainerView.bottomAnchor.constraint(equalTo: cameraView.bottomAnchor),
rightContainerView.trailingAnchor.constraint(equalTo: cameraView.trailingAnchor),
rightContainerView.widthAnchor.constraint(equalTo: cameraView.widthAnchor, multiplier: 0.15)
])
}

Position of a view with .zero frame

I have a view (VIEW A). I need to get it's position inside of it's superview (ideally a CGPoint for it's Center X and Center Y).
This would be easy, except that when I add VIEW A into the view, I set it's frame to be .zero, and use NSLayoutConstraint anchors to position it. So later, when I want to get it's frame (I've also tried it's bounds), it comes back as (0,0,0,0).
VIEW A is visible and positioned inside it's superview...so it has some X/Y coordinates and width/height right? How do I access these values?
A safe place to get access to updated frames is on viewDidLayoutSubviews after the call to super's implementation
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Here we have access to updated frames
}
If you are adding the subview, say, in viewDidLoad, then the UIViewController hasn't make any calculation for the frames. It just has the rules (constraints) describing the relations between its views. In the lifecycle of a UIViewController, viewDidLayoutSubviews is the point where the frame calculations has already been done and it's safe to access them. You can even calculate some frames yourself after the super call.
Call setNeedsLayout() AND layoutIfNeeded() on the parent view:
let myView = UIView(frame: .zero)
myView.backgroundColor = .red
myView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myView)
myView.topAnchor.constraintEqualToSystemSpacingBelow(view.topAnchor, multiplier: 1).isActive = true
myView.leftAnchor.constraintEqualToSystemSpacingAfter(view.leftAnchor, multiplier: 1).isActive = true
myView.widthAnchor.constraint(equalToConstant: 50).isActive = true
myView.heightAnchor.constraint(equalToConstant: 50).isActive = true
view.setNeedsLayout()
view.layoutIfNeeded()
print(myView.frame)

Error UIScrollView in swift 3

I have a view that is composed of an image, a form with 11 UITextfield and a button, but the form is too big for my screen that is why I tried to use a UIScrollview.
The error I have is that my UIScrollview does not work as I can solve this problem.
This is my code:
import UIKit
class LoginCtrl: UIViewController {
let scrollView: UIScrollView = {
let sv = UIScrollView(frame: UIScreen.main.bounds)
sv.isScrollEnabled = true
sv.contentSize = CGSize(width: 2000, height: 5678)
sv.translatesAutoresizingMaskIntoConstraints = false
return sv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(r: 0, g: 150, b: 136)
self.view.addSubview(scrollView)
scrollView.addSubview(contenedorCampos)
setear_posicion_scrollView()
setear_posicion_contenedor()
}
func setear_posicion_scrollView(){
//definir x,y,width,height constraints
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
var heightContenedor: NSLayoutConstraint?
func setear_posicion_contenedor(){
//definir x,y,width,height constraints
contenedorCampos.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
contenedorCampos.topAnchor.constraint(equalTo: tabsInicio.bottomAnchor, constant: 12).isActive = true
contenedorCampos.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -24).isActive = true
heightContenedor = contenedorCampos.heightAnchor.constraint(equalToConstant: 400)
heightContenedor?.isActive = true
contenedorCampos.addSubview(txtNombres)
contenedorCampos.addSubview(divider_txtNombres)....
}
}
Thanks
You've defined the relationship between the scrollview and its superview(which defines its frame), but not the relationship between the scrollview and its subviews (which defines its contentSize). As a result, the actual contentSize of the scrollview will be just be (0, 0).
In other words, you never actually laid anything out, at least not in the code you posted.
What you need to do is define layout constraints for the actual child views (everything that is a subview of the scrollview). Make sure to set up constraints definitively pinning these components to the edges of their parent (the scrollview). Once you have defined the constraints sufficiently, the scrollview should have a content size.
Technical note about this
In general, Auto Layout considers the top, left, bottom, and right
edges of a view to be the visible edges. That is, if you pin a view to
the left edge of its superview, you’re really pinning it to the
minimum x-value of the superview’s bounds. Changing the bounds origin
of the superview does not change the position of the view.
The UIScrollView class scrolls its content by changing the origin of
its bounds. To make this work with Auto Layout, the top, left, bottom,
and right edges within a scroll view now mean the edges of its content
view.
The constraints on the subviews of the scroll view must result in a
size to fill, which is then interpreted as the content size of the
scroll view. (This should not be confused with the
intrinsicContentSize method used for Auto Layout.) To size the scroll
view’s frame with Auto Layout, constraints must either be explicit
regarding the width and height of the scroll view, or the edges of the
scroll view must be tied to views outside of its subtree.

Sizing of table view cell and table view header view

I have a table view which displays table view cells and a header view (search bar). I want the width of the table view cells to be 0.9 of the device screen size, and the header view to have the same size. However, this is not possible because cells and the header view are all contained in a table view.
When I use layout anchor constraints everything gets resized (including the header view).
self.tableView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
// here's the 0.9 multiplier
self.tableView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.9).isActive = true
self.tableView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
self.tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true
How can I make sure that the table view header has the same width of the view controller, while keeping the table view width to be 0.9 of the main view?
It is best not to try to adjust the constraints of a TableViewCell or its contentView. However, you can just set both those views to have a background color of .clear and then add subviews to the contentView with the constraints you want.
class CustomCell: UITableViewCell {
let smallerView = UIView()
override init(frame: CGRect) {
super.init(frame)
smallerView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(smallerView)
smallerView.widthAnchor.contraint(equalTo: contentView.widthAnchor, multiplier: 0.9).isActive = true
smallerView.heightAnchor.contraint(equalTo: contentView.heightAnchor).isActive = true
}
}
Your custom cell "starts with" a ContentView, which uses uses the table view's insets to set its width...
So, if you have a bunch of elements / objects that you are adding to the content view...
Your best bet is to probably first add a standard UIView as a "container view"... (just my naming of it)...
Then, set the constraints on that "containing view" to be 0.9 of the width of the contentView...
All of the elements you add to that "containing view" will be constrained relative to it and thus will all stay inside the 0.9-width overall...

Custom flexible reusable view using xib

I'm trying to create custom reusable view, lets say QuestionView. Now I use this class to be extended by my QuestionView, so it loads view from my xib, then this view added as subview to self. It works ok in case if my view has constant height and width, but I need kind of this layout
This view's file's owner set to QuestionView.
I have label on top which connected with top, left and right via constraints but it's flexible in terms of height - label is multiline. Yes/No buttons view connected to bottom of label, left and right of superview and has constant height. Details view connected to bottom of buttons view, to left and to right, has constant height. So my QuestionView has flexible height. If I change text of label to 2 lines for example, my view should be stretched.
I have ViewController xib, where I put generic view and set its class to QuestionView.
I just add this view as subview of QuestionView so I think there is a problem with constraints between view and subview, I should add them? I tried to add left, right, top, bottom constraints between them with translatesAutoresizingMaskIntoConstraints set to false but anyway got strange (similar to height from xibs) superview(QuestionView) height, subview height is ok in runtime.
So what am I doing wrong here? Do I need to bind subview height to superview height somehow differently?
UPD. Here is screenshot in runtime, gray view is it's size in runtime, should be stretched to TextField bottom. Now it looks like it was false effect of ok subview height in runtime.
Here is my code now
import UIKit
protocol NibDefinable {
var nibName: String { get }
}
#IBDesignable
class NibLoadingView: UIView, NibDefinable {
var containerView: UIView!
var nibName: String {
return String(self.dynamicType)
}
override init(frame: CGRect) {
super.init(frame: frame)
nibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
nibSetup()
}
private func nibSetup() {
//clipsToBounds = true
containerView = loadViewFromNib()
containerView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(containerView)
addConstraint(.Top)
addConstraint(.Left)
addConstraint(.Bottom)
addConstraint(.Right)
}
private func addConstraint(attribute: NSLayoutAttribute) {
self.addConstraint(NSLayoutConstraint(item: self,
attribute: attribute,
relatedBy: .Equal,
toItem: containerView,
attribute: attribute,
multiplier: 1,
constant: 0.0
))
}
private func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: nibName, bundle: bundle)
let nibView = nib.instantiateWithOwner(self, options: nil).first as! UIView
return nibView
}
}
First, you have to add a constraint between QuestionView and bottom of it's superview
Second, looks like problem might be with QuestionView's superview and its parent constraints.
UPD
the best tool to nail those kind of bugs - Reveal

Resources