I've got an issue with a custom UIView implementation. What I am trying to do is animate a UICollectionViewCell, and creating a new view after the cell scales and 'flips' on the other side. (Think of the UICollectionViewCells as cards).
I successfully manage to achieve the desired animation. However, when I create a view in code from the XIB file, the constraints seem to be applied but everything is blown up in size. I set the minimum font scale for the labels to 0.2, but it doesn't seem to be working.
This is the view designed in IB:
View designed in IB with constraints
Now here is what I get when the animation proceeds to add a new CardBackView to the cell's view:
The horrendous result
This is the code that does the animation and adds the card back view:
// now flip card and show other side of card
UIView.transitionWithView(cell, duration: duration, options: [.BeginFromCurrentState, .TransitionFlipFromRight, .CurveEaseInOut], animations: { () -> Void in
let cardBackView = NSBundle.mainBundle().loadNibNamed("CardBackView", owner: self, options: nil).first as! CardBackView
cardBackView.frame = cell.bounds
cardBackView.titleLabel.text = movie.title
cardBackView.titleLabel.textColor = UIColor.whiteColor()
cardBackView.overviewLabel.text = movie.overview
cardBackView.backgroundColor = UISettings.TabBarColor
cell.addSubview(cardBackView)
}, completion: nil)
When you're adding your CardBackView to your Cell it assumes that your translating your autoresizing mask into constraints, so it might behave weirdly. To fix this you need to create constraints for your CardBackView and add them to your Cell (it's parent).
If your CardBackView should use the same size as your Cell, something like this should work:
cardBackView.translatesAutoresizingMaskIntoConstraints = false
cell.addConstraint(NSLayoutConstraint(item: cardBackView, attribute: .Top, relatedBy: .Equal, toItem: cell, attribute: .Top, multiplier: 1, constant: 0))
cell.addConstraint(NSLayoutConstraint(item: cardBackView, attribute: .Bottom, relatedBy: .Equal, toItem: cell, attribute: .Bottom, multiplier: 1, constant: 0))
cell.addConstraint(NSLayoutConstraint(item: cardBackView, attribute: .Left, relatedBy: .Equal, toItem: cell, attribute: .Left, multiplier: 1, constant: 0))
cell.addConstraint(NSLayoutConstraint(item: cardBackView, attribute: .Right, relatedBy: .Equal, toItem: cell, attribute: .Right, multiplier: 1, constant: 0))
Related
I created a .xib with freeform and implement it like this
if let alertView = Bundle.main.loadNibNamed(Constants.XIB.titleImageLabelThreeButtonsAlertView, owner: self, options: nil)?.first as? TitleImageLabelThreeButtonsAlertView {
view.addSubview(alertView)
alertView.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 20))
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 20))
alertView.autoresizingMask = [UIViewAutoresizing.flexibleLeftMargin, UIViewAutoresizing.flexibleRightMargin, UIViewAutoresizing.flexibleTopMargin, UIViewAutoresizing.flexibleBottomMargin]
self.view.layoutIfNeeded()
}
I call this code in viewDidAppear. The center thing seems to work, but it seems that the trailing and leading don't have any effect. I want them with a distance of 20, my alertView should have a fixed height and appear in center.
The xib has always the same size (see screenshots)
My originally targeted was to get a xib that I can implement in every view for every device. So what is the best way to get this?
my xib file
simulator iphone 7
simulator iphone 4
You are mixing up auto layout and fixed placement (with autoresizing mask). What you want to do is completely use auto layout so that the view will adjust its layout automatically. You say you want a horizontal distance of 20, a fixed height and to be centred so I would do this:
if let alertView = Bundle.main.loadNibNamed(Constants.XIB.titleImageLabelThreeButtonsAlertView, owner: self, options: nil)?.first as? TitleImageLabelThreeButtonsAlertView {
view.addSubview(alertView)
// Start using auto layout
alertView.translatesAutoresizingMaskIntoConstraints = false
// Set the leading and trailing constraints for horizontal placement
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: -20))
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 20))
// Centre it vertically
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0))
// Set the fixed height constraint
let fixedHeight: CGFloat = 100
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 0, constant: fixedHeight))
}
That will get you what you want no matter how the device, superview, orientation, etc changes.
I have a UITableView with about at least 60 cells inside that needs to fit and not scroll. How can I get it to fit? I have already tried:
CGRect frame = self.tableView.frame;
frame.size.height = self.tableView.contentSize.height;
self.tableView.frame = frame;
As said in this answer here but in the comments it was mentioned that it would not work with very large lists and you can tell because the bounce will reveal more cells. There was not a solution provided for this. How can I fix this problem?
Obvious solution is to not use UITableView. You can create your contentView as normal view, either programatically or in xib, and then add it to your main view and connect it to the previous custom one using constraint. I have done it multiple times and it works like a charm.
var lastView: UIView? = nil
for item in model.items {
var customView: CustomContentView
let objects = NSBundle.mainBundle().loadNibNamed("CustomContentView", owner: self, options: nil)
customView = objects.first! as! CustomContentView
customView.translatesAutoresizingMaskIntoConstraints = false
customView.setItemModel(item)
viewWrap.addSubview(customView)
viewWrap.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[customView]|", options: [], metrics: nil, views: ["customView": customView]))
if lastView == nil {
viewWrap.addConstraint(NSLayoutConstraint(item: customView, attribute: .Top, relatedBy: .Equal, toItem: viewWrap, attribute: .Top, multiplier: 1, constant: 0))
} else {
viewWrap.addConstraint(NSLayoutConstraint(item: customView, attribute: .Top, relatedBy: .Equal, toItem: lastView!, attribute: .Bottom, multiplier: 1, constant: 0))
}
lastView = customView
}
if lastView != nil {
viewWrap.addConstraint(NSLayoutConstraint(item: viewWrap, attribute: .Bottom, relatedBy: .Equal, toItem: lastView!, attribute: .Bottom, multiplier: 1, constant: 17))
}
Then you just to make sure that your custom view can resize to fit the screen if there is too many of them. I assume you have some max limit and that they all can fit. If not, then you need to add it to scroll view.
I'm trying to add a custom view from a xib in front of other views. However, for some reason it's never placed as the front view when theres a UITableView on screen. Here's my code:
noInternetView = NSBundle.mainBundle().loadNibNamed("NoInternetView", owner: self, options: nil)[0] as! NoInternetView
noInternetView.translatesAutoresizingMaskIntoConstraints = false
noInternetView.delegate = self
self.view.addSubview(noInternetView)
self.view.addConstraint(NSLayoutConstraint(item: noInternetView, attribute: .CenterX, relatedBy: .Equal, toItem: self.view, attribute: .CenterX, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: noInternetView, attribute: .CenterY, relatedBy: .Equal, toItem: self.view, attribute: .CenterY, multiplier: 1, constant: 0))
I place this at the bottom of my viewDidLoad() code. Why could this be?
noInternetView.hidden = true
your view is hidden, do it visible
noInternetView.hidden = false
self.view.bringSubviewToFront(noInternetView)
Try this it may help you.
First
Check is there a noInternetView have width and height constraints
Second
Add self.view.layoutIfNeeded() after self.view.addConstraint
this help to adjust autolayout you added
If you want your view to be on the top, add it to the window instead.
I can't seem to find a proper answer on SO. I'm trying to rotate a subclassed UISlider and apply constraints to position it properly, but can't seem to get it to work correctly. In the XIB it has constraints so that I don't have any issues with error showing. (Boss hates errors showing). So I remove the constraints first.
My code looks like this:
removeConstraints([sliderHeight, sliderWidth, sliderLeading, sliderBottom])
let rotation = CATransform3DMakeRotation(-CGFloat(M_PI_2), 0.0, 0.0, 1.0)
self.layer.transform = rotation
let views = ["slider":slider, "deviceIcon":deviceIcon]
var constraints = NSLayoutConstraint.constraintsWithVisualFormat("H:[deviceIcon]-8-[slider]", options: .DirectionLeadingToTrailing, metrics: nil, views: views)
constraints.append(NSLayoutConstraint(item: deviceIcon, attribute: .Bottom, relatedBy: .Equal, toItem: slider, attribute: .Bottom, multiplier: 1, constant: 0))
constraints.append(NSLayoutConstraint(item: slider, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 30))
constraints.append(NSLayoutConstraint(item: slider, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 115))
addConstraints(constraints)
Once it runs, the frames look like this (white area between grey and orange vertical dashes is the thumb) :
It doesn't have the proper width/height and it isn't aligned to the bottom of the deviceIcon on its left. (I think it might be the proper distance from the deviceIcon, I can't tell.)
How can I do this properly?
So the answer is: Don't. Simply adjust the existing leading and bottom constraints so that the rotated slider is in the proper position.
As an academic exercise for a future UI, I am trying to add constraints between two table views, belonging to two different child view controllers of the root controller. In my RootViewController class below, tvc is displayed as expected in a 400x500 frame, but tvc2 is consuming the entire frame instead of being a 400x500 frame to the right of tvc. Basically, the constraints are apparently being ignored. I'm using an iPad sim in landscape.
override func viewDidLoad() {
super.viewDidLoad()
let v = self.view
v.backgroundColor = UIColor.greenColor()
var tvc :OrderTableViewController = OrderTableViewController(style: UITableViewStyle.Plain)
var tvc2 :OrderTableViewController = OrderTableViewController(style: UITableViewStyle.Plain)
self.addChildViewController(tvc)
self.addChildViewController(tvc2)
v.addSubview(tvc.view)
v.addSubview(tvc2.view)
tvc.didMoveToParentViewController(self)
tvc2.didMoveToParentViewController(self)
//tvc.view.setTranslatesAutoresizingMaskIntoConstraints(false)
//tvc2.view.setTranslatesAutoresizingMaskIntoConstraints(false)
//self.view.setTranslatesAutoresizingMaskIntoConstraints(false)
tvc.view.frame = CGRectMake(0, 0, 400, 500)
self.view.addConstraint(NSLayoutConstraint(
item: tvc2.view,
attribute: .Top,
relatedBy: .Equal,
toItem: tvc.view,
attribute: .Top,
multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(
item: tvc2.view,
attribute: .Bottom,
relatedBy: .Equal,
toItem: tvc.view,
attribute: .Bottom,
multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(
item: tvc2.view,
attribute: .Width,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1, constant: 400))
self.view.addConstraint(NSLayoutConstraint(
item: tvc2.view,
attribute: .Left,
relatedBy: .Equal,
toItem: tvc.view,
attribute: .Right,
multiplier: 1, constant: 0))
}
This line,
tvc2.view.setTranslatesAutoresizingMaskIntoConstraints(false)
should be uncommented. When you add a view programmatically, and you're using constraints, you should always set that to false. On the other hand, you should not set that to false for controller's main view.
Also, the width constraint, since it only applies to tvc2, should be added to tvc2, not to self.view (although it should work either way).
It's also a bit odd, that you're adding one view using frames, and the other with constraints. It would be better to do both using the same paradigm.