I am new to swift and am trying to achieve a rather simple task. I am creating an iPad application wherein I want to open the keyboard programmatically.
Keyboard should have a textField bar on top to record what's typed using the keyboard.
There should be a button right next to the textField.( as shown in the image )
I tried to achieve the same but doing this :
lazy var textFieldPanel: UIView = {
let view = UIView(frame: CGRect(x: 0.0, y: self.view.bounds.height, width: self.view.bounds.width, height: 50.0))
self.view.addSubview(view)
view.backgroundColor = UIColor.white
view.borderColor = UIColor.blue
view.borderWidth = 2.0
let fieldBottomConstraint = NSLayoutConstraint(item: view, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: 0.0)
customTextFieldBottomConstraint = fieldBottomConstraint
view.translatesAutoresizingMaskIntoConstraints = false
self.view.addConstraint(.init(item: view, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1.0, constant: 0.0))
self.view.addConstraint(.init(item: view, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1.0, constant: 0.0))
self.view.addConstraint(fieldBottomConstraint)
self.view.addConstraint(.init(item: view, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50.0))
return view
}()
lazy var customTextField: UITextField = {
let field = UITextField(frame: CGRect(x: 0.0, y: 0.0, width: self.view.bounds.width - 300 , height: 50.0))
textFieldPanel.addSubview(field)
field.translatesAutoresizingMaskIntoConstraints = false
self.view.addConstraint(.init(item: field, attribute: .left, relatedBy: .equal, toItem: textFieldPanel, attribute: .left, multiplier: 1.0, constant: 8.0))
self.view.addConstraint(.init(item: field, attribute: .right, relatedBy: .equal, toItem: textFieldPanel, attribute: .right, multiplier: 1.0, constant: 8.0))
self.view.addConstraint(.init(item: field, attribute: .top, relatedBy: .equal, toItem: textFieldPanel, attribute: .top, multiplier: 1.0, constant: 2.0))
self.view.addConstraint(.init(item: field, attribute: .bottom, relatedBy: .equal, toItem: textFieldPanel, attribute: .bottom, multiplier: 1.0, constant: 2.0))
let button = UIButton(frame: CGRect(x: self.view.bounds.width - 350, y: 0.0, width: 200 , height: 50.0))
button.backgroundColor = UIColor.clear
button.setTitle("Send", for: .normal)
button.borderWidth = 2.0
button.borderColor = UIColor.blue
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
textFieldPanel.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
return field
}()
and then calling it in the IBAction like :
customTextField.becomeFirstResponder()
customTextFieldBottomConstraint?.constant = -360.0
The issue that I am facing is the following :
The view is comopletely distorted. ( as shown )
Send button View is distorted as it can be seen in the image.
While hiding the keyboard, the textField view still stays which I want it to be removed.
The view is not shifting up when the keyboard launches.
Can anyone help me as to what am I doing wrong here. Any help would be appreciated.
I'll start with better code readability suggestions for you:
Instead of creating constraints with init, there's a much cleaner way to do it:
.init(item: view, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1.0, constant: 0.0)
// vs
view.leftAnchor.constraint(equalTo: self.view.leftAnchor),
Instead of self.view.addConstraint(...) for each constraint, you can easily activate a list of them like this:
NSLayoutConstraint.activate([
view.leftAnchor.constraint(equalTo: self.view.leftAnchor),
view.rightAnchor.constraint(equalTo: self.view.rightAnchor),
fieldBottomConstraint,
view.heightAnchor.constraint(equalToConstant: 50),
])
Now to your mistakes.
You're creating your views with a frame. It's totally pointless when you're using constraints, as auto layout will override original frames with constraint calculated ones.
You're providing no constraints to your button, that's why it's totally misplaced
You need to attach text field right to button left instead of superview right
If you wanna your view to be hidden when there's no keyboard, you need to set fieldBottomConstraint constant to your container height
So your fixed code constraints code will look like:
var customTextFieldBottomConstraint: NSLayoutConstraint!
lazy var textFieldPanel: UIView = {
let view = UIView(frame: CGRect(x: 0.0, y: self.view.bounds.height, width: self.view.bounds.width, height: 50.0))
self.view.addSubview(view)
view.backgroundColor = UIColor.white
let fieldBottomConstraint = view.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: -100)
customTextFieldBottomConstraint = fieldBottomConstraint
view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
view.leftAnchor.constraint(equalTo: self.view.leftAnchor),
view.rightAnchor.constraint(equalTo: self.view.rightAnchor),
fieldBottomConstraint,
view.heightAnchor.constraint(equalToConstant: 50),
])
return view
}()
lazy var customTextField: UITextField = {
let field = UITextField()
field.translatesAutoresizingMaskIntoConstraints = false
textFieldPanel.addSubview(field)
let button = UIButton()
button.backgroundColor = UIColor.clear
button.setTitle("Send", for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
textFieldPanel.addSubview(button)
NSLayoutConstraint.activate([
field.leftAnchor.constraint(equalTo: textFieldPanel.leftAnchor),
field.topAnchor.constraint(equalTo: textFieldPanel.topAnchor),
field.bottomAnchor.constraint(equalTo: textFieldPanel.bottomAnchor),
button.widthAnchor.constraint(equalToConstant: 200),
button.leftAnchor.constraint(equalTo: field.rightAnchor),
button.rightAnchor.constraint(equalTo: textFieldPanel.rightAnchor),
button.topAnchor.constraint(equalTo: textFieldPanel.topAnchor),
button.bottomAnchor.constraint(equalTo: textFieldPanel.bottomAnchor),
])
return field
}()
And the last thing. You're adding a constant to your constraint when expect keyboard to appear. But keyboard size is different on different devices and even with different system settings
I suggest you using my KeyboardNotifier. This helper class will update your constraint constant according to keyboard appearance/disappearance. No need to update it during becomeFirstResponder anymore. Initialize it like this:
KeyboardNotifier(
parentView: view,
constraint: customTextFieldBottomConstraint
)
Related
I got this transition issue with iOS 9, I've attached a GIF below.
It looks like the custom textView is presuming x-axis of the tab bar top before segue and then settling to its original position.
However there's no issue with iOS 11, but same with iOS 10.
I also suspect this might be caused by the push segue, since it transitions fine with the other kinds of segue (without any height settling glitch).
I'm using Auto-layout. The comment textView is pinned to buttom of superView. Any tip would be highly appreciated.
Here's the code that's dismissing UITabBar on push.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "previewVC" {
let destinationController = segue.destination as! PostViewController
destinationController.hidesBottomBarWhenPushed = true
}
}
Try another solution.
Use Your text as input accessory view of UIViewController so remove that bottom view from storyboard
Add Following in your view controller
var viewAcc: UIView?
var sendButton: UIButton!
var inputTextField: UITextField!
override var inputAccessoryView: UIView? {
return viewAcc
}
override var canBecomeFirstResponder: Bool {
return true
}
In View Did load method add following code
Note:Please change constraints according to your requirement
viewAcc = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 44))
viewAcc?.backgroundColor = UIColor.white
inputTextField = UITextField (frame: CGRect(x:8, y:0, width:UIScreen.main.bounds.width, height: 44 ))
inputTextField.inputAccessoryView = nil
inputTextField.delegate = self as? UITextFieldDelegate
inputTextField.placeholder = "Enter message..."
viewAcc?.backgroundColor = .white
viewAcc?.addSubview(inputTextField);
let topBorderView = UIView()
topBorderView.backgroundColor = UIColor(white: 0.5, alpha: 0.5)
viewAcc?.addSubview(topBorderView)
viewAcc?.addConstraintsWithFormat(format: "H:|[v0]|", views: topBorderView)
viewAcc?.addConstraintsWithFormat(format: "V:|[v0(0.5)]", views: topBorderView)
sendButton = UIButton(type: .system)
sendButton.isEnabled = true
sendButton.titleLabel?.font = UIFont.systemFont(ofSize: 16)
sendButton.setTitle("Send", for: .normal)
sendButton.contentEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
sendButton.addTarget(self, action: #selector(handleSend), for: .touchUpInside)
viewAcc?.addSubview(sendButton)
inputTextField.translatesAutoresizingMaskIntoConstraints = false
sendButton.translatesAutoresizingMaskIntoConstraints = false
viewAcc?.addConstraint(NSLayoutConstraint(item: inputTextField, attribute: .left, relatedBy: .equal, toItem: viewAcc, attribute: .left, multiplier: 1, constant: 8))
viewAcc?.addConstraint(NSLayoutConstraint(item: inputTextField, attribute: .top, relatedBy: .equal, toItem: viewAcc, attribute: .top, multiplier: 1, constant: 7.5))
viewAcc?.addConstraint(NSLayoutConstraint(item: inputTextField, attribute: .right, relatedBy: .equal, toItem: sendButton, attribute: .left, multiplier: 1, constant: -2))
viewAcc?.addConstraint(NSLayoutConstraint(item: inputTextField, attribute: .bottom, relatedBy: .equal, toItem: viewAcc, attribute: .bottom, multiplier: 1, constant: -8))
viewAcc?.addConstraint(NSLayoutConstraint(item: sendButton, attribute: .right, relatedBy: .equal, toItem: viewAcc, attribute: .right, multiplier: 1, constant: 0))
viewAcc?.addConstraint(NSLayoutConstraint(item: sendButton, attribute: .bottom, relatedBy: .equal, toItem: viewAcc, attribute: .bottom, multiplier: 1, constant: -4.5))
As your text view is not subview of view controller so it will work as expected
EDIT IPHONE X SUPPORT
lazy var viewAcc: SafeAreaInputAccessoryViewWrapperView = {
return SafeAreaInputAccessoryViewWrapperView(for: button)
}()
Hope it is helpful
I have been trying to implement interactive keyboard like in 'iMessages' app Something like this;
I need to continuously get exact frame of keyboard when sliding it up or down.
I have already tried; keyboardWillChangeFrame, keyboardDidChangeFrame, keyboardWillShowForResizing, keyboardWillHideForResizing, keyboardWillShow, keyboardWillBeHidden None of them continuously return the frame. What is the best way to catch that frame?
That's a tricky & Simple.
ViewController inherits UIResponder
open class UIViewController : UIResponder, NSCoding, UIAppearanceContainer, UITraitEnvironment, UIContentContainer, UIFocusEnvironment
So it can become first responder since it can have input accessory view too.
What you have to do is
1) Go to Storyboard -> ViewController and then select your tableview and change keyboard dismiss type to
2) In your Viewcontroller
Add following code
var viewAcc : UIView? // That contains TextFied and send button
override var inputAccessoryView: UIView? {
return viewAcc
}
override var canBecomeFirstResponder: Bool {
return true
}
For you I have created simple Textfield and send button as example you can modify it with your's
func setupView () {
self.tblChatDetail.contentInset = UIEdgeInsetsMake(0, 0, 20, 0)
viewAcc = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 44))
viewAcc?.backgroundColor = UIColor.white
textView = UITextView (frame: CGRect(x:0, y:0, width:0,height: 44 - 0.5))
textView.textContainerInset = UIEdgeInsetsMake(4, 3, 3, 3)
textView.delegate = self
viewAcc?.backgroundColor = .lightGray
viewAcc?.addSubview(textView);
sendButton = UIButton(type: .system)
sendButton.isEnabled = true
sendButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 17)
sendButton.setTitle("Send", for: .normal)
sendButton.contentEdgeInsets = UIEdgeInsets(top: 6, left: 6, bottom: 6, right: 6)
sendButton.addTarget(self, action: #selector(ChatDetailViewController.sendMessage), for: UIControlEvents.touchUpInside)
viewAcc?.addSubview(sendButton)
textView.translatesAutoresizingMaskIntoConstraints = false
sendButton.translatesAutoresizingMaskIntoConstraints = false
viewAcc?.addConstraint(NSLayoutConstraint(item: textView, attribute: .left, relatedBy: .equal, toItem: viewAcc, attribute: .left, multiplier: 1, constant: 8))
viewAcc?.addConstraint(NSLayoutConstraint(item: textView, attribute: .top, relatedBy: .equal, toItem: viewAcc, attribute: .top, multiplier: 1, constant: 7.5))
viewAcc?.addConstraint(NSLayoutConstraint(item: textView, attribute: .right, relatedBy: .equal, toItem: sendButton, attribute: .left, multiplier: 1, constant: -2))
viewAcc?.addConstraint(NSLayoutConstraint(item: textView, attribute: .bottom, relatedBy: .equal, toItem: viewAcc, attribute: .bottom, multiplier: 1, constant: -8))
viewAcc?.addConstraint(NSLayoutConstraint(item: sendButton, attribute: .right, relatedBy: .equal, toItem: viewAcc, attribute: .right, multiplier: 1, constant: 0))
viewAcc?.addConstraint(NSLayoutConstraint(item: sendButton, attribute: .bottom, relatedBy: .equal, toItem: viewAcc, attribute: .bottom, multiplier: 1, constant: -4.5))
}
Just run and Bang on !!
I am trying to put a UIActivityIndicatorView inside each collection view cell as it downloads its image. I have it appearing in each cell, but it refuses to center itself. It stays in the top left corner. How can I get it to center itself properly?
Here's how I'm doing it:
extension UIView {
func showActivityIndicator(onView: UIView, withIndicator: UIActivityIndicatorView) {
withIndicator.frame = CGRect(x: onView.frame.midX - 20, y: onView.frame.midY - 20, width: 40, height: 40)
withIndicator.activityIndicatorViewStyle = .whiteLarge
withIndicator.center = onView.center
onView.addSubview(withIndicator)
withIndicator.startAnimating()
}
}
I call that function inside cellForItemAtIndexPath like:
showActivityIndicator(onView: cell.contentView, withIndicator: activityInd)
But nothing I do will move it from the top left corner. Any advice?
Try this
withIndicator.center = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
You need to add contraints to center it. For example use NSLayoutAnchor.
You need to set translatesAutoresizingMaskIntoConstraints to false, to set your constraints. And after adding it to the view set the constraints (hints in the code comments):
extension UIView {
func showActivityIndicator(onView: UIView, withIndicator: UIActivityIndicatorView) {
withIndicator.frame = CGRect(x: onView.frame.midX - 20, y: onView.frame.midY - 20, width: 40, height: 40)
withIndicator.activityIndicatorViewStyle = .whiteLarge
// set translatesAutoresizingMaskIntoConstraints to false to set your constraints
withIndicator.translatesAutoresizingMaskIntoConstraints = false
onView.addSubview(withIndicator)
withIndicator.startAnimating()
// add the constraints to center the indicator
withIndicator.centerXAnchor.constraint(equalTo: onView.centerXAnchor).isActive = true
withIndicator.centerYAnchor.constraint(equalTo: onView.centerYAnchor).isActive = true
}
}
I suggest using constraints (aka auto layout):
indicator.translatesAutoresizingMaskIntoConstraints = false
"your cell".addSubview(indicator)
let widthConstraint = NSLayoutConstraint(item: indicator, attribute: .Width, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 250)
let heightConstraint = NSLayoutConstraint(item: indicator, attribute: .Height, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 100)
let xConstraint = NSLayoutConstraint(item: indicator, attribute: .CenterX, relatedBy: .Equal, toItem: self.tableView, attribute: .CenterX, multiplier: 1, constant: 0)
let yConstraint = NSLayoutConstraint(item: indicator, attribute: .CenterY, relatedBy: .Equal, toItem: self.tableView, attribute: .CenterY, multiplier: 1, constant: 0)
NSLayoutConstraint.activateConstraints([widthConstraint, heightConstraint, xConstraint, yConstraint])
I'm trying to get my head around how adding constraints programmatically works. So far I have my code like so:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//addViewStandard()
addConstraintsView()
}
func addConstraintsView() {
let someView = UIView(frame: CGRect.zero)
someView.backgroundColor = UIColor.blue
// I want to mimic a frame set of CGRect(x: 20, y: 50, width: 50, height: 50)
let widthConstraint = NSLayoutConstraint(item: someView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let heightConstraint = NSLayoutConstraint(item: someView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let leadingConstraint = NSLayoutConstraint(item: someView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 20)
someView.translatesAutoresizingMaskIntoConstraints = false
someView.addConstraints([widthConstraint, heightConstraint, leadingConstraint])
view.addSubview(someView)
}
}
Now when I run the app it crashes because of the leading constraint. The error message is "Impossible to set up layout with view hierarchy unprepared for constraint". What am I doing wrong here? Should I be adding the constraints to the object (the blue box on this case) or adding them to its superview?
EDIT:
After code changes I have:
func addConstraintsView() {
let someView = UIView(frame: CGRect.zero)
someView.backgroundColor = UIColor.blue
view.addSubview(someView)
someView.translatesAutoresizingMaskIntoConstraints = false
let widthConstraint = NSLayoutConstraint(item: someView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let heightConstraint = NSLayoutConstraint(item: someView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let leadingConstraint = NSLayoutConstraint(item: someView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: 20)
someView.addConstraints([widthConstraint, heightConstraint])
view.addConstraints([leadingConstraint])
}
First of all,
view.addSubview(someView)
someView.translatesAutoresizingMaskIntoConstraints = false
should come before the constraints phase; you have to apply the constraints AFTER someView is added to its superview.
Also, if you are targeting iOS 9, I'd advise you to use layout anchors like
let widthConstraint = someView.widthAnchor.constraint(equalToConstant: 50.0)
let heightConstraint = someView.heightAnchor.constraint(equalToConstant: 50.0)
let leadingConstraint = someView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0)
NSLayoutConstraint.activate([widthConstraint, heightConstraint, leadingConstraint])
This way you don't have to worry about which view to apply the constraints to.
Finally (and to clear up your doubt), if you can't use layout anchors, you should add the leading constraint to the superview, not the view.
I'm working with a iMessage application and have programmatically added a view. However I can't seem to work out the correct constraints for making it the correct size at all times. For example, the view moves down a few hundred px if I leave the extension for another and come back to it. I think this has something to do with the .isActive. My goal is to make the view automatically resize to always be the right size or take up the full available height and width.
func createBrowser() {
let controller = MSStickerBrowserViewController(stickerSize: .small)
addChildViewController(controller)
view.addSubview(controller.view)
controller.view.translatesAutoresizingMaskIntoConstraints = false
controller.stickerBrowserView.backgroundColor = UIColor.blue
controller.stickerBrowserView.dataSource = self
view.topAnchor.constraint(equalTo: controller.view.topAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: controller.view.bottomAnchor).isActive = true
view.leftAnchor.constraint(equalTo: controller.view.leftAnchor).isActive = true
view.rightAnchor.constraint(equalTo: controller.view.rightAnchor).isActive = true
view.centerXAnchor.constraint(equalTo: controller.view.centerXAnchor).isActive = true
view.centerYAnchor.constraint(equalTo: controller.view.centerYAnchor).isActive = true
}
Screenshot: https://d17oy1vhnax1f7.cloudfront.net/items/1F2B0s3v0s1k3E2L0Z07/Screen%20Shot%202016-09-19%20at%2011.42.51%20AM.png
to better explain things I've put together the following. This demonstrates two methods of fixing the layout for subviews. When using constraints, I prefer to create the constraints as an array and activate them all in one go, as you will see in the code for createredSquareWithConstraints. A constraint is simply a linear equation relating the features of one view to that of another. In "pseudocode", for example, the first constraint in my array could be written:
"Set the leading margin of the subview equal to 1 times the leading margin of the container view plus a constant of 0."
(This is why I was getting confused earlier as it looked to me as though you were setting the containing view's constraints based on the characteristics of one of its subviews.)
While it remains perfectly valid to use layout constraints, I think the preferred methodology these days is to override the viewWillTransitionToSize() delegate method, which simply asks you to specify, given a size for the containing view, what the frame of a view controller's subviews should be. As such, I've included an implementation of this too, creating a yellow square with an initial frame that is then modified whenever viewWillTransitionToSize is called. I personally find this a lot less fiddly that using layout constraints.
If you lay around with the buttons and rotate the screen you should see that either method achieves the same thing. [NB I have labelled one square as constrained and one as unconstrained, but in reality they are of course both constrained, just in different ways. I would add that this is clearly not how you would do things in practice - you should choose one methodology and stick to it otherwise your code will be all over the place!].
Hope that helps!
import UIKit
class ViewController: UIViewController {
var constrainedredSquare : UIView!
var unconstrainedRedSquare : UIView!
var methodOneButton : UIButton!
var methodTwoButton : UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = UIColor.blue
func getButton(name: String) -> UIButton {
let button : UIButton = UIButton()
button.backgroundColor = UIColor.white
button.layer.cornerRadius = 3
button.clipsToBounds = true
button.setTitle(name, for: UIControlState.normal)
button.setTitleColor(UIColor.black, for: UIControlState.normal)
return button
}
self.methodOneButton = getButton(name: "Red - Constraints")
self.methodTwoButton = getButton(name: "Yellow - viewWillTransitionToSize")
self.methodOneButton.addTarget(self, action: #selector(self.createRedSquareWithConstraints), for: .touchUpInside)
self.methodTwoButton.addTarget(self, action: #selector(self.createYellowSquareWithoutConstraints), for: .touchUpInside)
self.methodOneButton.frame = CGRect(origin: CGPoint(x: 200, y: 100), size: CGSize(width: 300, height: 300))
self.methodTwoButton.frame = CGRect(origin: CGPoint(x: self.view.frame.width - 500, y: 100), size: CGSize(width: 300, height: 300))
self.view.addSubview(self.methodOneButton)
self.view.addSubview(self.methodTwoButton)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if let _ = self.unconstrainedRedSquare {
self.unconstrainedRedSquare.frame = CGRect(origin: CGPoint.zero, size: size)
}
self.methodOneButton.frame = CGRect(origin: CGPoint(x: 200, y: 100), size: CGSize(width: 300, height: 300))
self.methodTwoButton.frame = CGRect(origin: CGPoint(x: size.width - 500, y: 100), size: CGSize(width: 300, height: 300))
}
func createYellowSquareWithoutConstraints() {
if let _ = self.unconstrainedRedSquare {
self.unconstrainedRedSquare.removeFromSuperview()
}
else
{
if let _ = constrainedredSquare {
self.constrainedredSquare.removeFromSuperview()
}
self.unconstrainedRedSquare = UIView()
self.unconstrainedRedSquare.backgroundColor = UIColor.yellow
self.unconstrainedRedSquare.frame = CGRect(origin: CGPoint.zero, size: self.view.frame.size)
self.view.addSubview(self.unconstrainedRedSquare)
self.view.bringSubview(toFront: self.methodOneButton)
self.view.bringSubview(toFront: self.methodTwoButton)
}
}
func createRedSquareWithConstraints() {
if let _ = self.constrainedredSquare {
self.constrainedredSquare.removeFromSuperview()
}
else
{
if let _ = self.unconstrainedRedSquare {
self.unconstrainedRedSquare.removeFromSuperview()
}
let redSquare : UIView = UIView()
redSquare.backgroundColor = UIColor.red
self.view.addSubview(redSquare)
self.view.bringSubview(toFront: self.methodOneButton)
self.view.bringSubview(toFront: self.methodTwoButton)
let rsConstraints : [NSLayoutConstraint] = [NSLayoutConstraint(item: redSquare, attribute: NSLayoutAttribute.leading, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.leading, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: redSquare, attribute: NSLayoutAttribute.trailing, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.trailing, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: redSquare, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.top, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: redSquare, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.bottom, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: redSquare, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.width, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: redSquare, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.height, multiplier: 1.0, constant: 0)]
redSquare.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(rsConstraints)
}
}
}
You can use my extension to UIView. It allows to add extra padding on any side (only if you want to):
public extension UIView {
typealias ConstraintsTupleStretched = (top:NSLayoutConstraint, bottom:NSLayoutConstraint, leading:NSLayoutConstraint, trailing:NSLayoutConstraint)
func addSubviewStretched(subview:UIView?, insets: UIEdgeInsets = UIEdgeInsets() ) -> ConstraintsTupleStretched? {
guard let subview = subview else {
return nil
}
subview.translatesAutoresizingMaskIntoConstraints = false
addSubview(subview)
let constraintLeading = NSLayoutConstraint(item: subview,
attribute: .Left,
relatedBy: .Equal,
toItem: self,
attribute: .Left,
multiplier: 1,
constant: insets.left)
addConstraint(constraintLeading)
let constraintTrailing = NSLayoutConstraint(item: self,
attribute: .Right,
relatedBy: .Equal,
toItem: subview,
attribute: .Right,
multiplier: 1,
constant: insets.right)
addConstraint(constraintTrailing)
let constraintTop = NSLayoutConstraint(item: subview,
attribute: .Top,
relatedBy: .Equal,
toItem: self,
attribute: .Top,
multiplier: 1,
constant: insets.top)
addConstraint(constraintTop)
let constraintBottom = NSLayoutConstraint(item: self,
attribute: .Bottom,
relatedBy: .Equal,
toItem: subview,
attribute: .Bottom,
multiplier: 1,
constant: insets.bottom)
addConstraint(constraintBottom)
return (constraintTop, constraintBottom, constraintLeading, constraintTrailing)
}
}
Usage:
view.addSubviewStretched(tableView)
let BorderedBackgroundInset = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1)
view?.addSubviewStretched(calendar.view, insets: BorderedBackgroundInset)