Swift inputAccessoryView override bug - ios

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);
}

Related

UITabBar Transition Issue Below iOS 11 Swift

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

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)

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 to add padding inside UITextField using NSLayoutConstraint in Swift?

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.

Custom keyboard for iOS

I've built keyboard this way. (code below)
But, there are some problems like:
when you click on some button, it's click animation taking long to get back.
there is no way to place some common keys as globe symbol for language changing or caps lock
What I want to do is, to modify original iOS keyboard and add some other buttons.
Is it possible? Any suggestions?
import UIKit
class KeyboardViewController: UIInputViewController {
#IBOutlet var nextKeyboardButton: UIButton!
override func updateViewConstraints() {
super.updateViewConstraints()
// Add custom view sizing constraints here
}
override func viewDidLoad() {
super.viewDidLoad()
let buttonTitles1 = ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"]
let buttonTitles2 = ["A", "S", "D", "F", "G", "H", "J", "K", "L"]
let buttonTitles3 = ["CP", "Z", "X", "C", "V", "B", "N", "M", "BP"]
let buttonTitles4 = ["CHG", "SPACE", "RETURN"]
var row1 = createRowOfButtons(buttonTitles1)
var row2 = createRowOfButtons(buttonTitles2)
var row3 = createRowOfButtons(buttonTitles3)
var row4 = createRowOfButtons(buttonTitles4)
self.view.addSubview(row1)
self.view.addSubview(row2)
self.view.addSubview(row3)
self.view.addSubview(row4)
row1.setTranslatesAutoresizingMaskIntoConstraints(false)
row2.setTranslatesAutoresizingMaskIntoConstraints(false)
row3.setTranslatesAutoresizingMaskIntoConstraints(false)
row4.setTranslatesAutoresizingMaskIntoConstraints(false)
addConstraintsToInputView(self.view, rowViews: [row1, row2, row3, row4])
}
func createRowOfButtons(buttonTitles: [NSString]) -> UIView {
var buttons = [UIButton]()
var keyboardRowView = UIView(frame: CGRectMake(0, 0, 320, 50))
for buttonTitle in buttonTitles{
let button = createButtonWithTitle(buttonTitle)
buttons.append(button)
keyboardRowView.addSubview(button)
}
addIndividualButtonConstraints(buttons, mainView: keyboardRowView)
return keyboardRowView
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated
}
override func textWillChange(textInput: UITextInput) {
// The app is about to change the document's contents. Perform any preparation here.
}
override func textDidChange(textInput: UITextInput) {
// The app has just changed the document's contents, the document context has been updated.
var textColor: UIColor
var proxy = self.textDocumentProxy as UITextDocumentProxy
if proxy.keyboardAppearance == UIKeyboardAppearance.Dark {
textColor = UIColor.whiteColor()
} else {
textColor = UIColor.blackColor()
}
}
func createButtonWithTitle(title: String) -> UIButton {
let button = UIButton.buttonWithType(.System) as UIButton
button.frame = CGRectMake(0, 0, 20, 20)
button.setTitle(title, forState: .Normal)
button.sizeToFit()
button.titleLabel?.font = UIFont.systemFontOfSize(15)
button.setTranslatesAutoresizingMaskIntoConstraints(false)
button.backgroundColor = UIColor(white: 1.0, alpha: 1.0)
button.setTitleColor(UIColor.darkGrayColor(), forState: .Normal)
button.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
return button
}
func didTapButton(sender: AnyObject?) {
let button = sender as UIButton
var proxy = textDocumentProxy as UITextDocumentProxy
if let title = button.titleForState(.Normal) {
switch title {
case "BP" :
proxy.deleteBackward()
case "RETURN" :
proxy.insertText("\n")
case "SPACE" :
proxy.insertText(" ")
case "CHG" :
self.advanceToNextInputMode()
default :
proxy.insertText(title)
}
}
}
func addIndividualButtonConstraints(buttons: [UIButton], mainView: UIView){
for (index, button) in enumerate(buttons) {
var topConstraint = NSLayoutConstraint(item: button, attribute: .Top, relatedBy: .Equal, toItem: mainView, attribute: .Top, multiplier: 1.0, constant: 1)
var bottomConstraint = NSLayoutConstraint(item: button, attribute: .Bottom, relatedBy: .Equal, toItem: mainView, attribute: .Bottom, multiplier: 1.0, constant: -1)
var rightConstraint : NSLayoutConstraint!
if index == buttons.count - 1 {
rightConstraint = NSLayoutConstraint(item: button, attribute: .Right, relatedBy: .Equal, toItem: mainView, attribute: .Right, multiplier: 1.0, constant: -1)
}else{
let nextButton = buttons[index+1]
rightConstraint = NSLayoutConstraint(item: button, attribute: .Right, relatedBy: .Equal, toItem: nextButton, attribute: .Left, multiplier: 1.0, constant: -1)
}
var leftConstraint : NSLayoutConstraint!
if index == 0 {
leftConstraint = NSLayoutConstraint(item: button, attribute: .Left, relatedBy: .Equal, toItem: mainView, attribute: .Left, multiplier: 1.0, constant: 1)
}else{
let prevtButton = buttons[index-1]
leftConstraint = NSLayoutConstraint(item: button, attribute: .Left, relatedBy: .Equal, toItem: prevtButton, attribute: .Right, multiplier: 1.0, constant: 1)
let firstButton = buttons[0]
var widthConstraint = NSLayoutConstraint(item: firstButton, attribute: .Width, relatedBy: .Equal, toItem: button, attribute: .Width, multiplier: 1.0, constant: 0)
widthConstraint.priority = 800
mainView.addConstraint(widthConstraint)
}
mainView.addConstraints([topConstraint, bottomConstraint, rightConstraint, leftConstraint])
}
}
func addConstraintsToInputView(inputView: UIView, rowViews: [UIView]){
for (index, rowView) in enumerate(rowViews) {
var rightSideConstraint = NSLayoutConstraint(item: rowView, attribute: .Right, relatedBy: .Equal, toItem: inputView, attribute: .Right, multiplier: 1.0, constant: -1)
var leftConstraint = NSLayoutConstraint(item: rowView, attribute: .Left, relatedBy: .Equal, toItem: inputView, attribute: .Left, multiplier: 1.0, constant: 1)
inputView.addConstraints([leftConstraint, rightSideConstraint])
var topConstraint: NSLayoutConstraint
if index == 0 {
topConstraint = NSLayoutConstraint(item: rowView, attribute: .Top, relatedBy: .Equal, toItem: inputView, attribute: .Top, multiplier: 1.0, constant: 0)
}else{
let prevRow = rowViews[index-1]
topConstraint = NSLayoutConstraint(item: rowView, attribute: .Top, relatedBy: .Equal, toItem: prevRow, attribute: .Bottom, multiplier: 1.0, constant: 0)
let firstRow = rowViews[0]
var heightConstraint = NSLayoutConstraint(item: firstRow, attribute: .Height, relatedBy: .Equal, toItem: rowView, attribute: .Height, multiplier: 1.0, constant: 0)
heightConstraint.priority = 800
inputView.addConstraint(heightConstraint)
}
inputView.addConstraint(topConstraint)
var bottomConstraint: NSLayoutConstraint
if index == rowViews.count - 1 {
bottomConstraint = NSLayoutConstraint(item: rowView, attribute: .Bottom, relatedBy: .Equal, toItem: inputView, attribute: .Bottom, multiplier: 1.0, constant: 0)
}else{
let nextRow = rowViews[index+1]
bottomConstraint = NSLayoutConstraint(item: rowView, attribute: .Bottom, relatedBy: .Equal, toItem: nextRow, attribute: .Top, multiplier: 1.0, constant: 0)
}
inputView.addConstraint(bottomConstraint)
}
}
}
Use the github repo below. It's nearly 1:1 immitation of iOS 8 original keyboard and works like a charm
https://github.com/archagon/tasty-imitation-keyboard
when you click on some button, it's click animation taking long to get back.
Your keys are nothing more than UIButtons. The fade-out animation you're seeing is the normal animation for a UIButton—you can see it for yourself on most buttons across the system. Instead of initializing it with UIButtonType.System, use .Custom and set your own appearance for the desired states.
As an example (there are many possibilities):
let button = UIButton(.Custom)
button.setTitleColor(UIColor.redColor(), forState: .Highlighted)
Note that you don't even have to restrict yourself to using UIButton—custom keyboards are a blank slate.
there is no way to place some common keys as globe symbol for language changing or caps lock
From the Custom Keyboard Guide:
The system picks the appropriate “next” keyboard; there is no API to
obtain a list of enabled keyboards or for picking a particular
keyboard to switch to.
So these "special" keys are also up to you to provide, most likely with your own custom icons. If you're using UIButton, that probably means calling setImage:forState:. It looks like most third-party keyboards use a globe icon almost identicial to the system one for the "next" key.
There is no API to mod the system keyboard—you must build one yourself from the ground-up.
For slow animation, you can study these github repo on how they do their animation:
https://github.com/YuAo/WUEmoticonsKeyboard (See the popview when the button gets pressed.)
https://github.com/ayushgoel/AGEmojiKeyboard
https://github.com/kulpreetchilana/Custom-iOS-Keyboards
or search github:https://github.com/search?utf8=✓&q=iOS+keyboard
Note: They are mostly objective-c repo.
Lastly, it might be cheaper to install a custom keyboard than to make one:
http://www.imore.com/best-custom-keyboards-ios-8

Resources