How to add padding inside UITextField using NSLayoutConstraint in Swift? - ios

I have a subclass of UIView in which I have UITextField and wants to move placeholder text little right. I am using NSLayoutConstraint for setting subviews position and size. I have created UIView and adding it as leftView of UITextField but it crashes stating "The view hierarchy is not prepared for the constraint". Below is my code :
class MasterView: UIView {
let searchTextField = UITextField()
let paddingView = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
searchTextField.placeholder = "Search videos"
searchTextField.translatesAutoresizingMaskIntoConstraints = false
paddingView.translatesAutoresizingMaskIntoConstraints = false
searchTextField.leftView = paddingView
searchTextField.leftViewMode = UITextFieldViewMode.Always
self.addSubview(searchTextField)
}
// Gets called when using storyboard
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
self.addConstraint(NSLayoutConstraint(item: searchTextField, attribute: NSLayoutAttribute.TopMargin, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.TopMargin, multiplier: 1.0, constant: 20))
self.addConstraint(NSLayoutConstraint(item: searchTextField, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Height, multiplier: 0.12, constant: 0))
self.addConstraint(NSLayoutConstraint(item: searchTextField, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.CenterX, multiplier: 1.0, constant: 0))
self.addConstraint(NSLayoutConstraint(item: searchTextField, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0))
self.addConstraint(NSLayoutConstraint(item: paddingView, attribute: NSLayoutAttribute.LeadingMargin, relatedBy: NSLayoutRelation.Equal, toItem: searchTextField, attribute: NSLayoutAttribute.LeadingMargin, multiplier: 1.0, constant: 0))
self.addConstraint(NSLayoutConstraint(item: paddingView, attribute: NSLayoutAttribute.TopMargin, relatedBy: NSLayoutRelation.Equal, toItem: searchTextField, attribute: NSLayoutAttribute.TopMargin, multiplier: 1.0, constant: 0))
self.addConstraint(NSLayoutConstraint(item: paddingView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: searchTextField, attribute: NSLayoutAttribute.Width, multiplier: 0.12, constant: 0))
self.addConstraint(NSLayoutConstraint(item: paddingView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: searchTextField, attribute: NSLayoutAttribute.Height, multiplier: 1.0, constant: 0))
}
}

Proper solution for this problem:
#IBDesignable
class FormTextField: UITextField {
#IBInspectable var paddingLeft: CGFloat = 0
#IBInspectable var paddingRight: CGFloat = 0
override func textRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: bounds.origin.x + paddingLeft, y: bounds.origin.y, width: bounds.size.width - paddingLeft - paddingRight, height: bounds.size.height)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return textRect(forBounds: bounds)
}
}
taken from here

Make UITextField custom class and implement this two method in that class and make your textfield as custom textfield.
class customTextField: UITextField {
override func textRectForBounds(bounds: CGRect) -> CGRect {
return super.textRectForBounds(UIEdgeInsetsInsetRect(bounds, UIEdgeInsetsMake(0, 5, 0, 0)))
}
override func editingRectForBounds(bounds: CGRect) -> CGRect {
return super.editingRectForBounds(UIEdgeInsetsInsetRect(bounds, UIEdgeInsetsMake(0, 5, 0, 0)))
}
}

let paddingForFirst = UIView(frame: CGRectMake(0, 0, 5, self.Email_Txt.frame.size.height-5))
Email_Txt.leftView = paddingForFirst
Email_Txt.leftViewMode = UITextFieldViewMode .Always

Try this code it will help you
override func viewDidLoad() {
super.viewDidLoad()
let paddingView = UIView(frame: CGRectMake(0, 0, 15, self.myTextField.frame.height))
myTextField.leftView = paddingView
myTextField.leftViewMode = UITextFieldViewMode.Always
}

yon should not set or give constraints to that padding view. remove all constraints of padding view. it's left view of textfield so it automatically adjust as per constraint of textfield. And set frame of padding view that what space you expect as padding.
Hope this will help :)

Just write below:- SWIFT 2.1.1
let tempView = UIView(frame:CGRectMake(6, 0, 22, 26))
tempView.backgroundColor = UIColor.clearColor()
searchTextField.leftView?.frame = CGRectMake(0, 0, tempView.frame.size.width, tempView.frame.height)
searchTextField.leftViewMode = UITextFieldViewMode.Always
searchTextField.leftView = tempView

You are facing this error "The view hierarchy is not prepared for the constraint", because paddingView is added as left/right view and won't required any constraints.
Note : It is not required to give constraint to a view which is added as a left/right view to UITextField. So you just need to set frame of a paddingView and assign it to the leftView. No need to give constraints to paddingView.

Related

Programmatically add full size view in swift

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)

Why add UIView in contentView center always not in center?

I want add a UIView in its contentView center.
#IBOutlet weak var contentView: UIView!
override func viewDidLoad() {
let view = UIView(frame: CGRectMake(0,0,100,100))
view.backgroundColor = UIColor.blueColor()
contentView.backgroundColor = UIColor.redColor()
contentView.addSubview(view)
view.center = contentView.center
}
But the result is
Did I forget something?
Update
thanks #Wain's tip, Constrain work.
According to https://stackoverflow.com/a/27624927/6006588
override func viewDidLoad() {
let view = UIView(frame: CGRectMake(0,0,100,100))
view.backgroundColor = UIColor.blueColor()
contentView.backgroundColor = UIColor.redColor()
view.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(view)
let widthConstraint = NSLayoutConstraint(item: view, attribute: .Width, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 100)
let heightConstraint = NSLayoutConstraint(item: view, attribute: .Height, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 100)
let xConstraint = NSLayoutConstraint(item: view, attribute: .CenterX, relatedBy: .Equal, toItem: contentView, attribute: .CenterX, multiplier: 1, constant: 0)
let yConstraint = NSLayoutConstraint(item: view, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 1, constant: 0)
NSLayoutConstraint.activateConstraints([heightConstraint, widthConstraint,xConstraint, yConstraint])
}
I think I use constraint in StoryBoard to contentView (centerX , centerY , Equal Width , Equal Height to superView).
The contentView will have incorrect frame size in ViewDidLoad.
And when I set view.center = contentView.center it don't work.
I need use constraint programmatically to set view's position.
Thanks.
You can use this code
#IBOutlet weak var contentView: UIView!
Then add this to viewDidLoad
let view = UIView(frame: CGRectMake(0,0,100,100))
let contentView = UIView(frame: self.view.frame)
view.backgroundColor = UIColor.blueColor()
contentView.backgroundColor = UIColor.redColor()
view.center = contentView.center
contentView.addSubview(view)
self.view.addSubview(contentView)
This is the result
Try this -
let view = UIView(frame: CGRectMake((contentView.frame.size.width-100)/2,(contentView.frame.size.height-100)/2,100,100))
view.backgroundColor = UIColor.blueColor()
contentView.backgroundColor = UIColor.redColor()
contentView.addSubview(view)

Line before and after text in UILabel

I would like to achieve the same result as this :
I already saw this : Draw line in UILabel before and after text , but I would like to know if there's a way to do this with only one UILabel ?
This is custom view as you requested
import UIKit
class CustomizedUILabel: UIView
{
let label = UILabel()
var lineInsideOffset: CGFloat = 20
var lineOutsideOffset: CGFloat = 4
var lineHeight: CGFloat = 1
var lineColor = UIColor.grayColor()
//MARK: - init
override init(frame: CGRect)
{
super.init(frame: frame)
initLabel()
}
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
initLabel()
}
convenience init() {self.init(frame: CGRect.zero)}
func initLabel()
{
label.textAlignment = .Center
label.translatesAutoresizingMaskIntoConstraints = false
let top = NSLayoutConstraint(item: self, attribute: .Top, relatedBy: .Equal, toItem: label, attribute: .Top, multiplier: 1, constant: 0)
let bot = NSLayoutConstraint(item: self, attribute: .Bottom, relatedBy: .Equal, toItem: label, attribute: .Bottom, multiplier: 1, constant: 0)
let lead = NSLayoutConstraint(item: self, attribute: .Leading, relatedBy: .LessThanOrEqual, toItem: label, attribute: .Leading, multiplier: 1, constant: 0)
let trail = NSLayoutConstraint(item: self, attribute: .Trailing, relatedBy: .GreaterThanOrEqual, toItem: label, attribute: .Trailing, multiplier: 1, constant: 0)
let centerX = NSLayoutConstraint(item: self, attribute: .CenterX, relatedBy: .Equal, toItem: label, attribute: .CenterX, multiplier: 1, constant: 0)
addSubview(label)
addConstraints([top, bot, lead, trail, centerX])
//... if the opaque property of your view is set to YES, your drawRect: method must totally fill the specified rectangle with opaque content.
//http://stackoverflow.com/questions/11318987/black-background-when-overriding-drawrect-in-uiscrollview
opaque = false
}
//MARK: - drawing
override func drawRect(rect: CGRect)
{
let lineWidth = label.frame.minX - rect.minX - lineInsideOffset - lineOutsideOffset
if lineWidth <= 0 {return}
let lineLeft = UIBezierPath(rect: CGRectMake(rect.minX + lineOutsideOffset, rect.midY, lineWidth, 1))
let lineRight = UIBezierPath(rect: CGRectMake(label.frame.maxX + lineInsideOffset, rect.midY, lineWidth, 1))
lineLeft.lineWidth = lineHeight
lineColor.set()
lineLeft.stroke()
lineRight.lineWidth = lineHeight
lineColor.set()
lineRight.stroke()
}
}
Usage in code: in usual case you should provide top, leading and trailing/width constraints and let CustomizedUILabel to determine height by its internal UILabel. Lets show our label in debug mode in some view controller's viewDidLoad method:
override func viewDidLoad()
{
super.viewDidLoad()
let customizedLabel = CustomizedUILabel()
customizedLabel.label.text = "RATE YOUR RIDE"
customizedLabel.label.textColor = UIColor.grayColor()
customizedLabel.label.font = customizedLabel.label.font.fontWithSize(25)
customizedLabel.backgroundColor = UIColor.redColor()
view.addSubview(customizedLabel)
customizedLabel.translatesAutoresizingMaskIntoConstraints = false
let top = NSLayoutConstraint(item: view, attribute: .Top, relatedBy: .Equal, toItem: customizedLabel, attribute: .Top, multiplier: 1, constant: -100)
let trail = NSLayoutConstraint(item: view, attribute: .Trailing, relatedBy: .Equal, toItem: customizedLabel, attribute: .Trailing, multiplier: 1, constant: 0)
let lead = NSLayoutConstraint(item: view, attribute: .Leading, relatedBy: .Equal, toItem: customizedLabel, attribute: .Leading, multiplier: 1, constant: 0)
view.addConstraints([top, trail, lead])
}
Usage in xib/storyboard: as soon as you implemented init with coder initializer you can use this view in xib/storyboard. You need add UIView element to your superview, assign Class for this element to CustomizedUILabel, perform constraints and make outlet. Then you can use it with pretty same way:
#IBOutlet weak var customizedLabel: CustomizedUILabel!
override func viewDidLoad()
{
super.viewDidLoad()
customizedLabel.label.text = "RATE YOUR RIDE"
customizedLabel.label.textColor = UIColor.grayColor()
customizedLabel.label.font = customizedLabel.label.font.fontWithSize(25)
customizedLabel.backgroundColor = UIColor.redColor()
}
UILabel * label = [UILabel new];
label.frame = CGRectMake(0,0,0,20);
label.text = #"Rate Your Ride";
[self.view addSubview:label];
[label sizeToFit];
float w = self.view.frame.size.width;
float lw = label.frame.size.width;
float offset = (w-lw)/2;
float padding = 20.0f;
//center the label
[label setFrame:CGRectMake(offset, 0, lw, 20)];
//make the borders
UIImageView * left = [UIImageView new];
left.frame = CGRectMake(0, 9, offset-padding, 2);
left.backgroundColor = [UIColor blackColor];
[self.view addSubview:left];
UIImageView * right = [UIImageView new];
right.frame = CGRectMake(w-offset+padding, 9, offset-padding, 2);
right.backgroundColor = [UIColor blackColor];
[self.view right];
OR
make the label background the same colour as its parent and put a single line behind it.

How can I create an extension with a function which creates a UIView which can be placed at different positions?

I have to create a custom UIView which is actually a circle having the same size and appearance which is placed at different positions in a view. I created an extension which works fine for one circle, but I want to use that same function to place the circle at different positions. Here's my extension:
extension UIView{
func helptip(hotSpot: HelpTips, parentView: UIView){
hotSpot.tag = 1
hotSpot.userInteractionEnabled = true
hotSpot.backgroundColor = UIColor.clearColor()
hotSpot.translatesAutoresizingMaskIntoConstraints = false
parentView.addSubview(hotSpot)
hotSpot.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
UIView.animateWithDuration(4.0, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity:1, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
//self.hotSpotOne.center.x += self.view.bounds.width
hotSpot.alpha = 1.0
hotSpot.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
hotSpot.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "showPopover"))
}, completion: nil)
let horizontalConstraint = NSLayoutConstraint(item: hotSpot, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: parentView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: -1)
let verticalConstraint = NSLayoutConstraint(item: hotSpot
, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: parentView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 16)
let widthConstraint = NSLayoutConstraint(item: hotSpot, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 50)
let heightConstraint = NSLayoutConstraint(item: hotSpot, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 50)
parentView.addConstraints([verticalConstraint, horizontalConstraint, widthConstraint, heightConstraint])
}
}
This is how i call the extension:
hotSpotOne.helptip(self.hotSpotOne, parentView: self.view)// first circle
Now what I want to do is something like this:
hotSpotTwo.helptip(self.hotSpotTwo, parentView: self.view) //second circle
my helptips class looks like this:
class HelpTips: UIView {
var selectedColor: UIColor = UIColor.TRLMHelpTipYellowColor(){
didSet{
self.setNeedsDisplay()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
CGContextSetLineWidth(context, 1)
CGContextSetStrokeColorWithColor(context, UIColor.TRLMHelpTipStrokeColor().CGColor)
let circle = CGRectMake(5, 5, 40, 40)
CGContextAddEllipseInRect(context, circle)
CGContextStrokePath(context)
selectedColor.setFill()
CGContextFillEllipseInRect(context, circle)
}
}
I want to use the same extension with different constraints for the above circle as it is placed in a different position in the view. Any help is appreciated. Thanks!
My current thought is to add something like the following to your HelpTips class:
var constraintList: [NSLayoutConstraint] = []
func helptip(parentView: UIView){
self.tag = 1
self.userInteractionEnabled = true
self.backgroundColor = UIColor.clearColor()
self.translatesAutoresizingMaskIntoConstraints = false
parentView.addSubview(self)
UIView.animateWithDuration(4.0, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity:1, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
//self.hotSpotOne.center.x += self.view.bounds.width
self.alpha = 1.0
self.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "showPopover"))
}, completion: nil)
if constraintList.count > 3 {
parentView.addConstraints([constraintList[0], constraintList[1], constraintList[2], constraintList[3]])
} else {
print("Must initialize constraints before displaying.")
}
}
The caller would then be responsible for supplying the constraints and the calling sequence would be:
var hsp1 = HelpTips(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
hsp1.constraintList.append(NSLayoutConstraint(item: hsp1, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: parentView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: -1))
hsp1.constraintList.append(NSLayoutConstraint(item: hsp1
, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: parentView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 16))
hsp1.constraintList.append(NSLayoutConstraint(item: hsp1, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 50))
hsp1.constraintList.append(NSLayoutConstraint(item: hsp1, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 50))
hsp1.helptip(parentView)
(Better still would be an addConstraint method in the class so that the caller didn't need to access the constraintList array.)

Swift inputAccessoryView override bug

I'm experiencing a weird bug with the appearance of my inputAccessoryView. While in the middle of a transition, it appears like so:
After the transition, it appears as it should:
I override the property like so:
override var inputAccessoryView: UIView! {
get {
if composeView == nil {
composeView = CommentComposeView(frame: CGRectMake(0, 0, 0, MinimumToolbarHeight - 0.5))
self.setupSignals()
}
return composeView
}
}
I'm wondering if anyone can point out any obvious flaw in what I'm doing or provide some more information on how to ensure my view appears as it should, before, during, and after transitions.
Thanks!
EDIT
Here's my CommentComposeView:
import UIKit
class CommentComposeView: UIToolbar {
var textView: SAMTextView!
var sendButton: UIButton!
private var didSetConstraints: Bool = false
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.initialize()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.initialize()
}
private func initialize() {
textView = SAMTextView(frame: CGRectZero)
sendButton = UIButton.buttonWithType(.System) as UIButton
self.barStyle = .Black
self.translucent = true
textView.backgroundColor = UIColor.presentOffWhite()
textView.font = UIFont.presentLightMedium()
textView.layer.borderWidth = 0.5
textView.layer.cornerRadius = 5
textView.placeholder = "Comment"
textView.scrollsToTop = false
textView.textContainerInset = UIEdgeInsetsMake(4, 3, 3, 3)
textView.keyboardAppearance = .Dark
textView.keyboardType = .Twitter
self.addSubview(textView)
sendButton = UIButton.buttonWithType(.System) as UIButton
sendButton.enabled = false
sendButton.titleLabel!.font = UIFont.presentBoldLarge()
sendButton.setTitle("Send", forState: .Normal)
sendButton.setTitleColor(UIColor.whiteColor(), forState: .Normal)
sendButton.setTitleColor(UIColor.presentCyan(), forState: .Highlighted)
sendButton.setTitleColor(UIColor.presentLightGray(), forState: .Disabled)
sendButton.contentEdgeInsets = UIEdgeInsetsMake(6, 6, 6, 6)
self.addSubview(sendButton)
RAC(self.sendButton, "enabled") <~ self.textView.rac_textSignal()
.map { text in
return (text as NSString).length > 0
}
textView.setTranslatesAutoresizingMaskIntoConstraints(false)
sendButton.setTranslatesAutoresizingMaskIntoConstraints(false)
}
override func updateConstraints() {
super.updateConstraints()
if !didSetConstraints {
// TODO: Replace raw constraints with a friendlier looking DSL
self.addConstraint(
NSLayoutConstraint(item: textView, attribute: .Left, relatedBy: .Equal, toItem: self, attribute: .Left, multiplier: 1, constant: 8)
)
self.addConstraint(
NSLayoutConstraint(item: textView, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: 7.5)
)
self.addConstraint(
NSLayoutConstraint(item: textView, attribute: .Right, relatedBy: .Equal, toItem: sendButton, attribute: .Left, multiplier: 1, constant: -2)
)
self.addConstraint(
NSLayoutConstraint(item: textView, attribute: .Bottom, relatedBy: .Equal, toItem: self, attribute: .Bottom, multiplier: 1, constant: -8)
)
self.addConstraint(
NSLayoutConstraint(item: sendButton, attribute: .Right, relatedBy: .Equal, toItem: self, attribute: .Right, multiplier: 1, constant: 0)
)
self.addConstraint(
NSLayoutConstraint(item: sendButton, attribute: .Bottom, relatedBy: .Equal, toItem: self, attribute: .Bottom, multiplier: 1, constant: -4.5)
)
}
}
}
This is iOS8 issue with inputAccessoryView autolayout. Issue is that UIToolbar's subview of clas _UIToolbarBackground is not positioned properly during initial layout. Try to do next things:
Make CommentComposeView subclassing UIView, not UIToolbar, add instance of UIToolbar as subview.
Use autolayout masks (not actual constraints) inside your CommentComposeView
Override -layoutSubviews in your CommentComposeView like this:
- (void)layoutSubviews
{
[super layoutSubviews];
contentToolbar.frame = self.bounds;
sendButton.frame = CGRectMake(0.f, 0.f, 44.f, self.bounds.size.height);
textView.frame = CGRectMake(44.f, 0.f, self.bounds.size.width - 44.f, self.bounds.size.height);
}

Resources