The context menu with the standard text actions (cut, paste, copy, look up) is not showing up when I long press or double tap the text field. Instead, both of these actions seem to select the content of the text field and allow me to drag the selection elsewhere.
I am using the following custom UITextField subclass:
class BMTextField: UITextField {
required init(placeholder: String? = nil, text: String? = nil) {
super.init(frame: CGRect.zero)
font = UIFont(name: Fonts.Light, size: 18)
textColor = Colors.Gray51
layer.borderColor = Colors.Gray51Transparent.cgColor
layer.borderWidth = 2
layer.cornerRadius = 0
self.placeholder = placeholder
self.text = text
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let padding = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 0);
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
}
Here is how I'm adding it to my superview (using SnapKit):
lastNameTextField.textContentType = .familyName
lastNameTextField.autocorrectionType = .yes
showingSuperview.addSubview(lastNameTextField)
lastNameTextField.snp.makeConstraints {
make in
make.height.equalTo(M.standardControlHeight)
make.top.equalTo(firstNameTextField.snp.bottom).offset(S.thirtyTwo)
make.left.right.equalToSuperview()
}
I tried creating a brand new project with a text field, and there, everything works smoothly.
Related
Introduction:
I have a class, which inherits from UIButton. In this class I want to update properties, like titleEdgeInsets, imageEdgeInsets, contentHorizontalAlignment.
My first approach was to use layoutSubviews:
override func layoutSubviews() {
super.layoutSubviews()
// update properties
}
The layoutSubviews creates an infinity loop, so that I've searched for an alternative method.
My Question:
Is it a common way, to use the willMove method for updating UIButton properties?
override func willMove(toWindow newWindow: UIWindow?) {
super.willMove(toWindow: newWindow)
// update properties
}
If not, why?
My goal is to align the imageView of the button left (with padding) and center the text.
UPDATE:
I need the button frame.size and the bounds.width to calculate the position of the text and the image view
All the properties you mentioned above can be set in the init of the UIButton there is absolutely no need to set them in layoutSubviews or willMove(toWindow.
layoutSubviews will be called multiple times so setting these properties again n agin in here makes no sense. willMove(toWindow will be called when button is added to some view and button is loaded but you dont have to wait till then to set these properties. Because you already have a subclass of button, so I would suggest doing
class SomeButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
self.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.contentHorizontalAlignment = .center
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
By the way creating a subclass of UIButton is not recommended, so if you wanna simply assign these properties to your button you can rather have a extension to UIButton
extension UIButton {
func applyStyle() {
self.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.contentHorizontalAlignment = .center
}
}
EDIT:
Is this what you want??
No matter what the text is, text is always in centre and image is to its left with 10 pixel padding
EDIT 2:
As OP has confirmed that, he wants the button to be styled as showed in images above, posting the code to achieve the same
class SomeButton: UIButton {
var titleFont: UIFont! = nil
var textSize: CGFloat = 0
let imageWidth: CGFloat = 20
let buttonHeight: CGFloat = 30
override init(frame: CGRect) {
super.init(frame: frame)
self.titleFont = titleLabel!.font
self.setTitle("here", for: .normal)
self.setTitleColor(UIColor.red, for: .normal)
self.setImage(UIImage(named: "hand"), for: .normal)
self.backgroundColor = UIColor.green
}
override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
if let string = self.title(for: .normal) {
textSize = string.widthOfString(usingFont: self.titleFont)
//30 because imageWidth + 10 padding
return CGRect(origin: CGPoint(x: 30, y: 0), size: CGSize(width: textSize + 30, height: buttonHeight))
}
return CGRect.zero
}
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
return CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: imageWidth, height: buttonHeight))
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override var intrinsicContentSize: CGSize {
//60 because you need eauql padding on both side 30 + 30 = 60
return CGSize(width: textSize + 60, height: buttonHeight)
}
}
extension String {
func widthOfString(usingFont font: UIFont) -> CGFloat {
let fontAttributes = [NSAttributedString.Key.font: font]
let size = self.size(withAttributes: fontAttributes)
return size.width
}
}
Hope it helps
I have a grid of custom "text below image" UIButtons inside a static UITableViewCell.
To make 8 UIButtons form 4x2 grid, I put them into 2 horizontal UIStackViews and wrapped them with a vertical UIStackView.
You can see details in the screenshots below.
It runs perfect on simulator and real devices, but not rendered properly in Storyboard. I'd like to know if these are something missing in my code or some settings I should check/uncheck in Storyboard.
Runtime behavior(works well)
In Storyboard(chaos)
Outline
Custom Button:
import UIKit
#IBDesignable class YPTextBelowImageButton: UIButton {
#IBInspectable var heightRatio: CGFloat = 0.8 {
didSet {
self.setNeedsDisplay()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.frame = frame
self.imageView?.contentMode = .scaleAspectFit
self.titleLabel?.textAlignment = .center
self.setNeedsDisplay()
self.setNeedsLayout()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.imageView?.contentMode = .scaleAspectFit
self.titleLabel?.textAlignment = .center
self.setNeedsDisplay()
self.setNeedsLayout()
}
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
return CGRect(x: 0, y: 0, width: contentRect.size.width, height: contentRect.size.height * heightRatio)
}
override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
return CGRect(x: 0, y: contentRect.size.height * heightRatio, width: contentRect.size.width, height: contentRect.size.height * (1 - heightRatio))
}
}
Preview:
func prepareForInterfaceBuilder not working
override func prepareForInterfaceBuilder() {
self.imageView?.contentMode = .scaleAspectFit
self.titleLabel?.textAlignment = .center
self.setNeedsDisplay()
self.setNeedsLayout()
}
I was using this post as reference: Create space at the beginning of a UITextField. And in this post, there is a very helpful class adding padding in a textfield. However, the only way that I know how to use this class is for me to programmatically create a textfield. But instead, I would like to use this class with an IBOutlet. Here is the TextField Class:
class TextField: UITextField {
let padding = UIEdgeInsets(top: 0, left: 35, bottom: 0, right: 5);
override func textRectForBounds(bounds: CGRect) -> CGRect {
return self.newBounds(bounds)
}
override func placeholderRectForBounds(bounds: CGRect) -> CGRect {
return self.newBounds(bounds)
}
override func editingRectForBounds(bounds: CGRect) -> CGRect {
return self.newBounds(bounds)
}
private func newBounds(bounds: CGRect) -> CGRect {
print("paisjdpfij")
var newBounds = bounds
newBounds.origin.x += padding.left
newBounds.origin.y += padding.top
newBounds.size.height -= padding.top + padding.bottom
newBounds.size.width -= padding.left + padding.right
return newBounds
}
}
And here is my attempt to use it with my IBOutlet:
#IBOutlet weak var firstNameTextField: TextField!
override func viewDidLoad() {
super.viewDidLoad()
firstNameTextField = TextField()
}
However, the there is still no padding in the textfield. Anybody have a solution to this problem?
Now, I used this code:
let paddingView = UIView(frame: CGRect(x: 0.0, y: 10.0, width: 5.0, height: 20.0))
firstNameTextField.leftView = paddingView
firstNameTextField.leftViewMode = .Always
to add padding on the left side of the textfield. However, I would also like some padding on the bottom as well. And there doesn't seem to be a simple solution for adding a bottom padding.
you can create category/extension or create custom text field as you did. And implement this 2 methods and play with it by changing different bounds.
import UIKit
class CustomTextField: UITextField
{
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
}
override func textRectForBounds(bounds: CGRect) -> CGRect
{
return CGRectMake(bounds.origin.x + 10, bounds.origin.y + 8, bounds.size.width - 20, bounds.size.height - 16);
}
override func editingRectForBounds(bounds: CGRect) -> CGRect
{
return self.textRectForBounds(bounds);
}
}
For more detail you can refer this Nate Flink's answer at here:Set padding for UITextField with UITextBorderStyleNone
By default, the type-able area of a UITextField get positioned "vertical-center", like so
How can I get the type-able area to be positioned to the bottom but with a padding/offset from the bottom ?
You can override textRectForBounds(_:), placeholderRectForBounds(_:) and editingRectForBounds(_:) methods of UITextField to customize the text position and combine with contentVerticalAlignment set to .Bottom.
Say that you want your text to have 10 points padding:
class CustomTextField: UITextField {
#IBInspectable var padding: CGFloat = 10.0
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
contentVerticalAlignment = .Bottom
}
override func textRectForBounds(bounds: CGRect) -> CGRect {
return CGRectInset(bounds, padding, padding)
}
override func placeholderRectForBounds(bounds: CGRect) -> CGRect {
return self.textRectForBounds(bounds)
}
override func editingRectForBounds(bounds: CGRect) -> CGRect {
return self.textRectForBounds(bounds)
}
}
To start off I already looked at this question and tried to implement it but it didn't change anything so I'll just show my own attempt in this code.
Like the question says I'm trying to change the height of the UITextField inside the searchbar. I've made a custom searchbar as well as a custom searchcontroller.
The searchbar class looks like this
class CustomSearchBar: UISearchBar {
var preferredFont: UIFont!
var preferredTextColor: UIColor!
override func drawRect(rect: CGRect) {
// Drawing code
// Find the index of the search field in the search bar subviews.
if let index = indexOfSearchFieldInSubviews() {
// Access the search field
let searchField: UITextField = (subviews[0]).subviews[index] as! UITextField
// Set its frame.
//searchField.frame = CGRectMake(5.0, 5.0, frame.size.width - 10.0, frame.size.height - 10.0)
searchField.frame = CGRectMake(5.0, 5.0, frame.size.width, 100)
// Set the font and text color of the search field.
searchField.font = preferredFont
searchField.textColor = preferredTextColor
// Set the background color of the search field.
searchField.backgroundColor = barTintColor
}
let startPoint = CGPointMake(0.0, frame.size.height)
let endPoint = CGPointMake(frame.size.width, frame.size.height)
let path = UIBezierPath()
path.moveToPoint(startPoint)
path.addLineToPoint(endPoint)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.CGPath
shapeLayer.strokeColor = preferredTextColor.CGColor
shapeLayer.lineWidth = 2.5
layer.addSublayer(shapeLayer)
super.drawRect(rect)
}
init(frame: CGRect, font: UIFont, textColor: UIColor){
super.init(frame: frame)
self.frame = frame
preferredFont = font
preferredTextColor = textColor
searchBarStyle = UISearchBarStyle.Prominent
translucent = false
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func indexOfSearchFieldInSubviews() -> Int! {
var index: Int!
let searchBarView = subviews[0]
for var i=0; i<searchBarView.subviews.count; ++i {
if searchBarView.subviews[i].isKindOfClass(UITextField) {
index = i
break
}
}
return index
}
}
My Custom searchController looks like this
class CustomSearchController: UISearchController, UISearchBarDelegate {
var customSearchBar: CustomSearchBar!
var customDelegate: CustomSearchControllerDelegate!
init(searchResultsController: UIViewController!, searchBarFrame: CGRect, searchBarFont: UIFont, searchBarTextColor: UIColor, searchBarTintColor: UIColor){
super.init(searchResultsController: searchResultsController)
configureSearchBar(searchBarFrame, font: searchBarFont, textColor: searchBarTextColor, bgColor: searchBarTintColor)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?){
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
func configureSearchBar(frame: CGRect, font: UIFont, textColor:UIColor, bgColor: UIColor){
customSearchBar = CustomSearchBar(frame: frame, font: font, textColor: textColor)
customSearchBar.barTintColor = bgColor
customSearchBar.tintColor = textColor
customSearchBar.showsBookmarkButton = true
customSearchBar.setImage(UIImage(named: "recording.png"), forSearchBarIcon: UISearchBarIcon.Bookmark, state: UIControlState.Normal)
customSearchBar.setImage(UIImage(named:"record_tap.png"), forSearchBarIcon: UISearchBarIcon.Bookmark, state: UIControlState.Selected)
customSearchBar.showsCancelButton = false
customSearchBar.delegate = self
}
In my view controller where I'm creating this searchbar I use the following piece of code. that is being called in my viewDidLoad()
func configureCustomSearchController() {
customSearchController = CustomSearchController(searchResultsController: self, searchBarFrame: CGRectMake(0.0, 0.0, UIScreen.mainScreen().bounds.width, 100.0), searchBarFont: UIFont(name: "Futura", size: 18.0)!, searchBarTextColor: UIColor(red: 0.612, green: 0.984, blue: 0.533, alpha: 1), searchBarTintColor: UIColor.blackColor())
customSearchController.customSearchBar.placeholder = "Search.."
tblSearchResults.tableHeaderView = customSearchController.customSearchBar
customSearchController.customDelegate = self
}
I'm able to change the frame of the entire searchbar see above where I set it too 100. But I'm unable to change the height of the UITextField which I want to make bigger. I'm also sure I'm reaching the UITextView. When I change the color in the drawRect function it changes. Just the height is the problem here. Does someone have any clue what I'm doing wrong or what I need to do to achieve this.
I find if I move your sizing code into layoutSubviews(), I think it works as you expect.
override func layoutSubviews() {
// Find the index of the search field in the search bar subviews.
if let index = indexOfSearchFieldInSubviews() {
... //Added red background
}
}
override func drawRect(rect: CGRect) {...
I believe by the time drawRect() is called, the time for layout has passed. I cannot really comment on if this is the best implementation though.