I am newcomer in swift and tried to learn how to add several subviews and their constraints using a loop. I tried to follow some answer to similar questions but it is not working. Could you help me with my code? Thank you in advance.
Here is my code:
let card = UIView()
let view2 = UIView()
view2.backgroundColor = UIColor.greenColor()
view2.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(view2)
let leftSideConstraint3 = NSLayoutConstraint(item: view2, attribute: .Left, relatedBy: .Equal, toItem: view, attribute: .Left, multiplier: 1, constant: 0.0)
let topConstraint3 = NSLayoutConstraint(item: view2, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1, constant: 0.0)
let widthConstraint3 = NSLayoutConstraint(item: view2, attribute: .Width, relatedBy: .Equal, toItem: view, attribute: .Width, multiplier: 1, constant: 0.0)
let heightConstraint3 = NSLayoutConstraint(item: view2, attribute: .Height, relatedBy: .Equal, toItem: view, attribute: .Height, multiplier: 1, constant: 0.0)
view.addConstraints([leftSideConstraint3, topConstraint3, heightConstraint3, widthConstraint3])
var cards = [UIView](count: 16, repeatedValue: card) // array with 16 cards
card.layer.borderWidth = 3
card.layer.borderColor = UIColor.blackColor().CGColor
card.backgroundColor = UIColor.orangeColor()
var columnCounter:Int = 0
var rowCounter:Int = 0
//Loop through each card in the array
for index in 0...cards.count-1 {
// place the card in the view and turn off translateAutoresizingMask
let thisCard = cards[index]
thisCard.translatesAutoresizingMaskIntoConstraints = false
view2.addSubview(thisCard)
//set the height and width constraints
let widthConstraint = NSLayoutConstraint(item: thisCard, attribute: .Width, relatedBy: .Equal, toItem: view2, attribute: .Width, multiplier: 0.25, constant: 0)
let heightConstraint = NSLayoutConstraint(item: thisCard, attribute: .Height, relatedBy: .Equal, toItem: view2, attribute: .Height, multiplier: 0.25, constant: 0)
view2.addConstraints([heightConstraint, widthConstraint])
//set the horizontal position
if (columnCounter > 0) {
// card is not in the first column
let cardOnTheLeft = cards[index-1]
let leftSideConstraint = NSLayoutConstraint(item: thisCard, attribute: .Left, relatedBy: .Equal, toItem: cardOnTheLeft, attribute: .Right, multiplier: 1, constant: 0)
//add constraint to the contentView
view2.addConstraint(leftSideConstraint)
} else {
//card is in the first column
let leftSideConstraint = NSLayoutConstraint(item: thisCard, attribute: .Left, relatedBy: .Equal, toItem: view2, attribute: .Left, multiplier: 1, constant: 0)
//add constraint to the contentView
view2.addConstraint(leftSideConstraint)
}
//set the vertical position
if (rowCounter > 0) {
// card is not in the first row
let cardOnTop = cards[index-4]
let topConstraint = NSLayoutConstraint(item: thisCard, attribute: .Top, relatedBy: .Equal, toItem: cardOnTop, attribute: .Bottom, multiplier: 1, constant: 0)
// add constraint to the contentView
view2.addConstraint(topConstraint)
} else {
//card is in the first row
let topConstraint = NSLayoutConstraint(item: thisCard, attribute: .Top, relatedBy: .Equal, toItem: view2, attribute: .Top, multiplier: 1, constant: 0)
//add constraint to the contentView
view2.addConstraint(topConstraint)
}
//increment the column counter
columnCounter = columnCounter+1
//if the column counter reaches the fifth column reset it and increase the row counter
if (columnCounter >= 4) {
columnCounter = 0
rowCounter = rowCounter+1
}
} // end of the loop
This creates an array with 16 references to the same card.
var cards = [UIView](count: 16, repeatedValue: card)
Replace it with:
var cards = (1...16).map { _ in UIView() }
which will result in an array with 16 unique UIViews.
Related
As I want to move away from xib and make my layout programmatically, I found that using the same exact constraints doesn't work as I would expect.
I want to make this UITableViewCell
It's a quite simple cell with a small icon to its right as well as an Activity Indicator so I can toggle which one I want to see. They are inside a View and to their left is a label
Those are my constraints in the outline view
And it works perfectly. However when I'm removing the XIB and doing all of the code myself, nothing works anymore
So here's my code:
class StandardRow: UITableViewCell {
private var initialWidth: CGFloat = 20
public var fetching: Bool = false {
didSet {
if (fetching) {
activityIndicator?.startAnimating()
} else {
activityIndicator?.stopAnimating()
}
changeImageWidth()
}
}
public var rightImage: UIImage? = nil {
didSet {
rightImageView?.image = rightImage
changeImageWidth()
}
}
private func changeImageWidth() {
if (activityIndicator?.isAnimating) ?? false || rightImage != nil {
imageWidth?.constant = initialWidth
} else {
imageWidth?.constant = 0
}
}
override func prepareForReuse() {
valueLabel?.text = ""
imageView?.image = nil
rightImage = nil
fetching = false
textLabel?.text = ""
accessoryType = .none
}
//Views
private var imageContainer = UIView()
private var rightImageView = UIImageView()
private var activityIndicator: UIActivityIndicatorView? = UIActivityIndicatorView()
public var valueLabel: UILabel? = UILabel()
private var imageWidth: NSLayoutConstraint? = nil
override init(style: UITableViewCell.CellStyle = .default, reuseIdentifier: String? = nil) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
buildView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
buildView()
}
func buildView() {
contentView.addSubview(valueLabel!)
imageContainer.addSubview(rightImageView)
imageContainer.addSubview(activityIndicator!)
contentView.addSubview(imageContainer)
imageContainer.backgroundColor = .red
}
override func layoutSubviews() {
super.layoutSubviews()
//IMAGE CONTAINER CONSTRAINTS
imageWidth = NSLayoutConstraint(item: imageContainer, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: initialWidth)
imageWidth?.priority = UILayoutPriority(rawValue: 999)
imageWidth?.isActive = true
let bottomImageContainerConstraint = NSLayoutConstraint(item: imageContainer, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottom, multiplier: 1, constant: 0)
bottomImageContainerConstraint.isActive = true
bottomImageContainerConstraint.priority = UILayoutPriority(rawValue: 999)
let topImageContainerConstraint = NSLayoutConstraint(item: imageContainer, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .top, multiplier: 1, constant: 0)
topImageContainerConstraint.isActive = true
topImageContainerConstraint.priority = UILayoutPriority(rawValue: 999)
let trailingImageContainerConstraint = NSLayoutConstraint(item: imageContainer, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1, constant: 5)
trailingImageContainerConstraint.priority = UILayoutPriority(rawValue: 999)
trailingImageContainerConstraint.isActive = true
let centerYImageContainerConstraint = NSLayoutConstraint(item: imageContainer, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1, constant: 0)
centerYImageContainerConstraint.isActive = true
centerYImageContainerConstraint.priority = UILayoutPriority(rawValue: 999)
//VALUE LABEL CONSTRAINTS
let trailingValueLabelConstraint = NSLayoutConstraint(item: valueLabel!, attribute: .trailing, relatedBy: .equal, toItem: imageContainer, attribute: .leading, multiplier: 1, constant: 5)
trailingValueLabelConstraint.isActive = true
trailingValueLabelConstraint.priority = UILayoutPriority(rawValue: 999)
let centerYValueLabelConstraint = NSLayoutConstraint(item: valueLabel!, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1, constant: 0)
centerYValueLabelConstraint.isActive = true
centerYValueLabelConstraint.priority = UILayoutPriority(rawValue: 999)
//ACTIVITY INDICATOR CONSTRAINGS
NSLayoutConstraint(item: activityIndicator!, attribute: .trailing, relatedBy: .equal, toItem: imageContainer, attribute: .trailing, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: activityIndicator!, attribute: .leading, relatedBy: .equal, toItem: imageContainer, attribute: .leading, multiplier: 1, constant: 11).isActive = false
NSLayoutConstraint(item: activityIndicator!, attribute: .bottom, relatedBy: .equal, toItem: imageContainer, attribute: .bottom, multiplier: 1, constant: 11).isActive = false
NSLayoutConstraint(item: activityIndicator!, attribute: .top, relatedBy: .equal, toItem: imageContainer, attribute: .top, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: activityIndicator!, attribute: .centerY, relatedBy: .equal, toItem: imageContainer, attribute: .centerY, multiplier: 1, constant: 0).isActive = true
//RIGHT IMAGE VIEW CONSTRAINTS
NSLayoutConstraint(item: rightImageView, attribute: .trailing, relatedBy: .equal, toItem: activityIndicator!, attribute: .trailing, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: rightImageView, attribute: .leading, relatedBy: .equal, toItem: rightImageView, attribute: .leading, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: rightImageView, attribute: .bottom, relatedBy: .equal, toItem: activityIndicator!, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: rightImageView, attribute: .top, relatedBy: .equal, toItem: activityIndicator!, attribute: .top, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: rightImageView, attribute: .centerY, relatedBy: .equal, toItem: activityIndicator!, attribute: .centerY, multiplier: 1, constant: 0).isActive = true
//changeImageWidth()
}}
So I have a few ideas to where it can come from, firstly being "translatesAutoresizingMaskIntoConstraints" set to true by default, but when I'm setting it to false in the superview then my cell doesn't show anymore and in the contentView, Xcode tells me I shouldn't do that because of an undefined behaviour
I'm also using Reveal to debug my UI and then I found those peculiar values:
Which is not what I want, Reveal is reporting that those constraints are translating the autoresizing mask of the view to autolayout so it would confirm the previous theory. I did set the priority to 999 to some of the constraints because otherwise they would be broken.
I'm actually at a dead end and I think I'm missing something but I can't pinpoint what as I don't have enough experience with non-interface builder constraints
Try Anchors, it's much easier.
Example
var redView = UIView()
redView.backgroundColor = .red
anyView.addsubView(redView)
redView.translatesAutoresizingMaskIntoConstraints = false
redView.centerXAnchor.constraint(equalTo: self.parentView.centerXAnchor).isActive = true
redView.centerYAnchor.constraint(equalTo: self.parentView.centerYAnchor).isActive = true
redView.heightAnchor.constraint(equalToConstant: 100).isActive = true
redView.widthAnchor.constraint(equalToConstant: 100).isActive = true
You can add the same method to your UIView extension
func constrainToEdges(_ subview: UIView, top: CGFloat = 0, bottom: CGFloat = 0, leading: CGFloat = 0, trailing: CGFloat = 0) {
subview.translatesAutoresizingMaskIntoConstraints = false
let topContraint = NSLayoutConstraint(
item: subview,
attribute: .top,
relatedBy: .equal,
toItem: self,
attribute: .top,
multiplier: 1.0,
constant: top)
let bottomConstraint = NSLayoutConstraint(
item: subview,
attribute: .bottom,
relatedBy: .equal,
toItem: self,
attribute: .bottom,
multiplier: 1.0,
constant: bottom)
let leadingContraint = NSLayoutConstraint(
item: subview,
attribute: .leading,
relatedBy: .equal,
toItem: self,
attribute: .leading,
multiplier: 1.0,
constant: leading)
let trailingContraint = NSLayoutConstraint(
item: subview,
attribute: .trailing,
relatedBy: .equal,
toItem: self,
attribute: .trailing,
multiplier: 1.0,
constant: trailing)
addConstraints([
topContraint,
bottomConstraint,
leadingContraint,
trailingContraint])
}
I recommend using this framework for building constraint based layouts programmatically, it makes the process straightforward and faster. Take the setup for the contentView of this cell for example:
contentView.addSubview(descriptionLabel)
contentView.addSubview(amountLabel)
contentView.addSubview(dateLabel)
contentView.addSubview(bottomRightLabel)
constrain(descriptionLabel, amountLabel, dateLabel, bottomRightLabel) { desc, amount, date, bottomRight in
desc.top == desc.superview!.top + 16
desc.left == desc.superview!.left + 16
desc.right <= amount.left + 12
desc.bottom == date.top - 12
amount.centerY == desc.centerY
amount.right == amount.superview!.right - 12
date.left == date.superview!.left + 16
date.right <= bottomRight.left - 12
date.bottom == date.superview!.bottom - 16
bottomRight.centerY == date.centerY
bottomRight.right == bottomRight.superview!.right - 12
}
I know this is a hard problem to debug. I will try to explain, you can ask question from comments section.
I want to use message bubbles in my app. I wrote some code for constraints, but it is not working well.
How can i put the left edge of blue bubble to red line?
Here is my code:
When tableview cell loads this method is running:
private func setup() {
bubbleImageView = UIImageView(image: bubbleImage.incoming, highlightedImage: bubbleImage.incomingHighlighed)
bubbleImageView.userInteractionEnabled = true
messageLabel = UILabel(frame: CGRectZero)
messageLabel.font = UIFont.systemFontOfSize(15)
messageLabel.numberOfLines = 0
messageLabel.userInteractionEnabled = true
selectionStyle = .None
contentView.addSubview(bubbleImageView)
bubbleImageView.addSubview(messageLabel)
messageLabel.translatesAutoresizingMaskIntoConstraints = false
bubbleImageView.translatesAutoresizingMaskIntoConstraints = false
contentView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: .Left, relatedBy: .Equal, toItem: contentView, attribute: .Left, multiplier: 1, constant: 10))
contentView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: .Top, relatedBy: .Equal, toItem: contentView, attribute: .Top, multiplier: 1, constant: 4.5))
bubbleImageView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: .Width, relatedBy: .Equal, toItem: messageLabel, attribute: .Width, multiplier: 1, constant: 30))
contentView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: .Bottom, relatedBy: .Equal, toItem: contentView, attribute: .Bottom, multiplier: 1, constant: -4.5))
bubbleImageView.addConstraint(NSLayoutConstraint(item: messageLabel, attribute: .CenterX, relatedBy: .Equal, toItem: bubbleImageView, attribute: .CenterX, multiplier: 1, constant: -2))
bubbleImageView.addConstraint(NSLayoutConstraint(item: messageLabel, attribute: .CenterY, relatedBy: .Equal, toItem: bubbleImageView, attribute: .CenterY, multiplier: 1, constant: -0.5))
messageLabel.preferredMaxLayoutWidth = 218
bubbleImageView.addConstraint(NSLayoutConstraint(item: messageLabel, attribute: .Height, relatedBy: .Equal, toItem: bubbleImageView, attribute: .Height, multiplier: 1, constant: -15))
}
As you can see i am creating bubble background and label for text.
And second method:
func setCell(message:Message) {
messageLabel.text="testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"
var layoutAttribute: NSLayoutAttribute
var layoutConstant: CGFloat
if message.incoming {
bubbleImageView.image=bgImage.incoming
messageLabel.textColor = UIColor.blackColor()
layoutAttribute = .Left
layoutConstant = 10
}else{
if message.isSent == 1 {
bubbleImageView.image = bgImage.outgoing
}
if message.isSent == 0 {
bubbleImageView.image = bgImage.notYetSent
}
messageLabel.textColor = UIColor.whiteColor()
layoutAttribute = .Right
layoutConstant = -10
}
contentView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: layoutAttribute, relatedBy: .Equal, toItem: contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
}
What is the problem with my code? It is working well for incoming messages (gray bubble) and not working well for outgoing messages (blue bubble)
I get it. You have to add a constraint that doesnt allows the bubble to exceed the max width. So you have to add a constraint like that:
let widthConstraint = NSLayoutConstraint(item: youritem, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.LessThanOrEqual,
toItem: self.youItem.superview, attribute: NSLayoutAttribute.Width,
multiplier: 1.0, constant: 100)
I'm trying to add a subview to the keyWindow of the app, and position it using autolayout. However, autolayout doesn't seem to work at all, whereas setting a frame does. I want to align my view infoSc to the bottom of the keyWindow using the following code:
let infoSc = InfoScreenView()
infoSc.translatesAutoresizingMaskIntoConstraints = false
let keyWindow = UIApplication.sharedApplication().keyWindow!
keyWindow.addSubview(infoSc)
keyWindow.addConstraint(NSLayoutConstraint(item: infoSc, attribute: .Left, relatedBy: .Equal, toItem: keyWindow, attribute: .Left, multiplier: 1, constant: 0))
keyWindow.addConstraint(NSLayoutConstraint(item: infoSc, attribute: .Right, relatedBy: .Equal, toItem: keyWindow, attribute: .Right, multiplier: 1, constant: 0))
keyWindow.addConstraint(NSLayoutConstraint(item: infoSc, attribute: .Bottom, relatedBy: .Equal, toItem: keyWindow, attribute: .Bottom, multiplier: 1, constant: 0))
infoSc.addConstraint(NSLayoutConstraint(item: infoSc, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 100))
However, it appears to have a frame of CGRectZero using this method. Any ideas how to make this work? Ideally I'd also want to align it to something that's inside self.view too, but that throws an error that self.view is not in the view hierarchy of keyWindow.
If you need to draw on the entire window, here is code to do it (in this example I am in the AppDelegate, so window is the AppDelegate.window property).
func tryToDrawOnTheWindow()
{
if let window = window, view = window.rootViewController?.view
{
print("I have a root view")
let infoSc = InfoScreenView(frame: view.frame)
let count = view.subviews.count
view.insertSubview(infoSc, atIndex: count)
infoSc.translatesAutoresizingMaskIntoConstraints = false
let height = NSLayoutConstraint(item: infoSc, attribute: .Height, relatedBy: .Equal, toItem: view, attribute: .Height, multiplier: 1, constant: 0)
let width = NSLayoutConstraint(item: infoSc, attribute: .Width, relatedBy: .Equal, toItem: view, attribute: .Width, multiplier: 1, constant: 0)
let offset = NSLayoutConstraint(item: infoSc, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1, constant: 0)
print([height, width, offset])
view.addConstraints([height, width, offset])
}
else
{
print("No root view on which to draw")
}
}
This will let you draw on top of whatever is in the view hierarchy. In my test app, I added a textfield and a blue rect, and the overlay was orange with 40% opacity. Bear in mind that by default the overlay view will consume all taps in this case.
Place and call a method below, based on David S's answer, where you want, passing your view as variable.
func addToWindow(view : UIView) {
guard let delegate = UIApplication.shared.delegate, let window = delegate.window!, let topView = window.rootViewController?.view else {
print("No root view on which to draw")
return
}
print("I have a root view")
let count = topView.subviews.count
topView.insertSubview(view, at: count)
view.translatesAutoresizingMaskIntoConstraints = false
let height = NSLayoutConstraint(item: view, attribute: .height, relatedBy: .equal, toItem: topView, attribute: .height, multiplier: 1, constant: 0)
let width = NSLayoutConstraint(item: view, attribute: .width, relatedBy: .equal, toItem: topView, attribute: .width, multiplier: 1, constant: 0)
let pinToTop = NSLayoutConstraint(item: view, attribute: .top, relatedBy: .equal, toItem: topView, attribute: .top, multiplier: 1, constant: 0)
print([height, width, pinToTop])
topView.addConstraints([height, width, pinToTop])
}
I added the following code to center a programmatically added view:
let horizontalConstraint = NSLayoutConstraint(item: newView!, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0)
view.addConstraint(horizontalConstraint)
It doesn't work. The view is not centered. It is on the left still.
EDIT:
override func viewDidLoad() {
super.viewDidLoad()
newView = LineChartView(frame: CGRectMake(0, 0, 50, 50))
newView?.delegate = self
newView?.drawBordersEnabled = true
newView?.noDataText = "No Data"
newView?.noDataTextDescription = "No Data"
newView?.borderColor = UIColor.blackColor()
self.view.addSubview(newView!)
You need to pick a side my friend, If you are using auto layout, don't initialise your objects with a frame. Try something like this...
var newView:LineChartView!
override func viewDidLoad() {
super.viewDidLoad()
newView = LineChartView()
newView.translatesAutoresizingMaskIntoConstraints = false
newView.delegate = self
newView.drawBordersEnabled = true
newView.noDataText = "No Data"
newView.noDataTextDescription = "No Data"
newView.borderColor = UIColor.blackColor()
self.view.addSubview(newView)
let width = NSLayoutConstraint(item: newView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 50)
let height = NSLayoutConstraint(item: newView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 50)
newView.addConstraint(width)
newView.addConstraint(height)
let x = NSLayoutConstraint(item: newView, attribute: .CenterX, relatedBy: .Equal, toItem: self.view, attribute: .CenterX, multiplier: 1, constant: 0)
let y = NSLayoutConstraint(item: newView, attribute: .CenterY, relatedBy: .Equal, toItem: self.view, attribute: .CenterY, multiplier: 1, constant: 0)
self.view.addConstraint(x)
self.view.addConstraint(y)
}
If your LineChartView object is a subclass of UIView then this should work, and you should have a 50x50 object in the middle of your superview.
If you are going to be doing constraints like this in code you should consider using Apples Visual Formatting Language.
Colleagues,
I develop a custom keyboard. There was a problem with the speed of switching between types of keyboards (letters, numbers, special characters). This is due to the fact that each time the button is re-drawn. NSLayoutConstraint I set up as follows:
There is a class KeyboardViewController. He adds to his KeyboardView
let left = NSLayoutConstraint(item: self.keyboardView, attribute: .Left, relatedBy: .Equal,
toItem: self.view, attribute: .Left, multiplier: 1.0, constant: 0.0)
let top = NSLayoutConstraint(item: self.keyboardView, attribute: .Top, relatedBy: .Equal,
toItem: placeForSuggestion!, attribute: .Bottom, multiplier: 1.0, constant: 0.0)
let right = NSLayoutConstraint(item: self.keyboardView, attribute: .Right, relatedBy: .Equal,
toItem: self.view, attribute: .Right, multiplier: 1.0, constant: 0.0)
let bottom = NSLayoutConstraint(item: self.keyboardView, attribute: .Bottom, relatedBy: .Equal,
toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: 0.0)
let height = NSLayoutConstraint(item: self.keyboardView, attribute: .Height, relatedBy: .Equal,
toItem: self.view, attribute: .Height, multiplier: 1.0, constant: 216)
left.priority = 999
right.priority = 999
bottom.priority = 999
top.priority = 999
height.priority = 999
self.view.addConstraints([left, right, top, bottom, height])
In the class KeyboardView, buttons are added as follows:
super.updateConstraints()
if !layoutConstrained {
var lastRowView: UIView? = nil
for (rowIndex, keyRow) in keyRows.enumerate() {
var lastKeyView: UIView? = nil
for (keyIndex, key) in keyRow.enumerate() {
var relativeWidth: CGFloat = 0.0;
switch key.type! {
case .ModeChange:
relativeWidth = 0.92/8
case .KeyboardChange:
relativeWidth = 0.92/8
case .Space:
relativeWidth = 3.92/8
case .Return:
relativeWidth = 1.84/8
default:
relativeWidth = 0.0
}
key.translatesAutoresizingMaskIntoConstraints = false
if let lastView = lastKeyView {
let left: NSLayoutConstraint!
if (key.keyCap == "Z" || (key.keyCap == "backspace" && keyRow[keyIndex - 1].keyCap == "M")) {
left = NSLayoutConstraint(item: key, attribute: .Left, relatedBy: .Equal,
toItem: lastView, attribute: .Right, multiplier: 1.0, constant: englishMZSpace)
} else {
left = NSLayoutConstraint(item: key, attribute: .Left, relatedBy: .Equal,
toItem: lastView, attribute: .Right, multiplier: 1.0, constant: distanceBetweenKeys)
}
let top = NSLayoutConstraint(item: key, attribute: .Top, relatedBy: .Equal,
toItem: lastView, attribute: .Top, multiplier: 1.0, constant: 0.0)
let bottom = NSLayoutConstraint(item: key, attribute: .Bottom, relatedBy: .Equal,
toItem: lastView, attribute: .Bottom, multiplier: 1.0, constant: 0.0)
var width: NSLayoutConstraint?
if relativeWidth == 0.0 {
width = NSLayoutConstraint(item: key, attribute: .Width, relatedBy: .Equal,
toItem: lastView, attribute: .Width, multiplier: 1.0, constant: 0.0)
} else {
width = NSLayoutConstraint(item: key, attribute: .Width, relatedBy: .Equal,
toItem: self, attribute: .Width, multiplier: relativeWidth, constant: 0.0)
}
self.addConstraints([left, top, bottom, width!])
} else {
let leftEdge: NSLayoutConstraint
if key.keyCap == "A" {
leftEdge = NSLayoutConstraint(item: key, attribute: .Left, relatedBy: .Equal,
toItem: self, attribute: .Left, multiplier: 1.0, constant: englishALSpace)
} else {
leftEdge = NSLayoutConstraint(item: key, attribute: .Left, relatedBy: .Equal,
toItem: self, attribute: .Left, multiplier: 1.0, constant: leftRightSpace)
}
self.addConstraint(leftEdge)
if let lastRow = lastRowView {
let top = NSLayoutConstraint(item: key, attribute: .Top, relatedBy:.Equal,
toItem: lastRow, attribute: .Bottom, multiplier: 1.0, constant: rowTopInset)
let height = NSLayoutConstraint(item: key, attribute: .Height, relatedBy: .Equal,
toItem: lastRow, attribute: .Height, multiplier: 1.0, constant: 0.0)
self.addConstraints([top, height])
} else {
let topEdge = NSLayoutConstraint(item: key, attribute: .Top, relatedBy:.Equal,
toItem: self, attribute: .Top, multiplier: 1.0, constant: rowTopInset)
self.addConstraint(topEdge)
}
if rowIndex == keyRows.count - 1 {
let bottomEdge = NSLayoutConstraint(item: key, attribute: .Bottom, relatedBy: .Equal,
toItem: self, attribute: .Bottom, multiplier: 1.0, constant: -rowBottomInset)
self.addConstraint(bottomEdge)
}
lastRowView = key
}
if keyIndex == keyRow.count - 1 {
let rightEdge: NSLayoutConstraint
if key.keyCap == "L" {
rightEdge = NSLayoutConstraint(item: key, attribute: .Right, relatedBy: .Equal,
toItem: self, attribute: .Right, multiplier: 1.0, constant: -englishALSpace)
} else {
rightEdge = NSLayoutConstraint(item: key, attribute: .Right, relatedBy: .Equal,
toItem: self, attribute: .Right, multiplier: 1.0, constant: -leftRightSpace)
}
self.addConstraint(rightEdge)
}
lastKeyView = key
}
}
layoutConstrained = true
}
I see 2 variants of optimization:
After the first run to cache all NSLayoutConstraint
Use CGRectMake instead NSLayoutConstraint
You may be able to offer more relevant options?
In this video I try to switch keyboard and print quickly https://yadi.sk/i/36YxEwgtmHJVd
It seems pretty fast to me in the video.
However, instead of re-creating the layout constraints every time I would recommend to create each keyboard type once, and then just switch between the existing keyboards. Regarding the small views that shouldn't be a memory issue, and it's a lot better than recreating and deleting constraints all the time.
Alternatively, if the key amount stays (almost) the same, you could just change the constraint's values and even animate the change of key positions for a maybe nice effect.