Position of rightView UITextField - ios

Is there a way to adjust the position of a rightView on UITextField? I tried setting the frame on the view (set as rightView) but it didn't change anything.
I'd like to avoid making two views, one as the rightView and one as the rightView's subview where I change the subview's position, if possible.

The right overlay view is placed in the rectangle returned by the rightViewRectForBounds: method of the receiver.
So I suggest you subclass UITextField and override this method, something like this:
#interface CustomTextField: UITextField
#end
#implementation CustomTextField
// override rightViewRectForBounds method:
- (CGRect)rightViewRectForBounds:(CGRect)bounds{
CGRect rightBounds = CGRectMake(bounds.origin.x + 10, 0, 30, 44);
return rightBounds ;
}

#Puneet Sharma's answer was great but that would give the need to create a class that would subclass UITextField, what I did instead was create a UIView that would act as a padding.
This code works without the need to subclass
Here's my code, although it's written in Swift 3
// this is the view I want to see on the rightView
let checkImageView = UIImageView(image: UIImage(named: "check24.png"))
checkImageView.frame = CGRect(x: 0, y: 0, width: 24, height: 24)
checkImageView.curveEdges(12)
checkImageView.contentMode = .scaleAspectFit
// declare how much padding I want to have
let padding: CGFloat = 6
// create the view that would act as the padding
let rightView = UIView(frame: CGRect(
x: 0, y: 0, // keep this as 0, 0
width: checkImageView.frame.width + padding, // add the padding
height: checkImageView.frame.height))
rightView.addSubview(checkImageView)
// set the rightView UIView as the textField's rightView
self.textField.rightViewMode = .whileEditing
self.textField.rightView = rightView
What happened here is, that the rightView which is a UIView that has a transparent colored background which then gave the illusion that there is a padding whereas there is not.

Right Padding you can use as
let imageview = UIImageView(image: UIImage(named: "image name"))
imageview.contentMode = .center
let rightPadding: CGFloat = 14 //--- change right padding
imageview.frame = CGRect(x: 0, y: 0, width: imageview.frame.size.width + rightPadding , height:imageview.frame.size.height)
textField.rightViewMode = .always
textFieldd.rightView = imageview

Related

Auto-sizing a UILabel without setting an explicit height

How do I get a multi-line label to size itself? I don't want to set an explicit height for it but I do need to place it in view.
The way my app is built, we explicitly set frames and origins rather than using NSLayoutConstraints. It's a mature app so this isn't up for discussion.
I'd like to be able to give my UILabel an origin and a width and let it figure its own height out.
How can I do this? This is my playground code:
let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 180))
view.backgroundColor = .white
let l = UILabel()
l.text = "this is a really long label that should wrap around and stuff. it should maybe wrap 2 or three times i dunno"
l.textColor = .black
l.lineBreakMode = .byWordWrapping
l.numberOfLines = 0
l.textAlignment = .center
l.sizeToFit()
let margin: CGFloat = 60
view
view.addSubview(l)
l.frame = CGRect(x: margin, y: 0, width: view.bounds.width - (margin * 2), height: 100)
// I don't want to do this ^^
This may do what you want...
As requested, you want to set the .origin and .width of a UILabel and have it set its own .height based on the text.
class ZackLabel: UILabel {
override public func layoutSubviews() {
super.layoutSubviews()
let h = sizeThatFits(CGSize(width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude))
self.frame.size.height = h.height
}
}
class ViewController: UIViewController {
var testLabel: ZackLabel!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .yellow
// instantiate a 300 x 180 UIView at 20, 80
let myView = UIView(frame: CGRect(x: 20, y: 80, width: 300, height: 180))
myView.backgroundColor = .white
// instantiate a ZackLabel
testLabel = ZackLabel()
testLabel.text = "this is a really long label that should wrap around and stuff. it should maybe wrap 2 or three times i dunno"
testLabel.textColor = .black
testLabel.lineBreakMode = .byWordWrapping
testLabel.numberOfLines = 0
testLabel.textAlignment = .center
// set background color so we can see its frame
testLabel.backgroundColor = .cyan
let margin: CGFloat = 60
// set label's origin
testLabel.frame.origin = CGPoint(x: margin, y: 0)
// set label's width (label will set its own height)
testLabel.frame.size.width = myView.bounds.width - margin * 2
// add the view
view.addSubview(myView)
// add the label to the view
myView.addSubview(testLabel)
// add a tap recognizer so we can change the label's text at run-time
let rec = UITapGestureRecognizer(target: self, action: #selector(tapFunc(_:)))
view.addGestureRecognizer(rec)
}
#objc func tapFunc(_ sender: UITapGestureRecognizer) -> Void {
testLabel.text = "This is dynamic text being set."
}
}
Result (on an iPhone 8):
and, after tapping on the (yellow) view, dynamically changing the text:
label.sizeThatFits(CGSize(width: <your required width>, height: CGFloat.greatestFiniteMagnitude))
This returns the labels needed size, growing infinitely in height, but fitted to your required width. I've occasionally noticed minor inaccuracies with this function (rounding error?), so I tend to bump the width and height by 1 just to be safe.
UILabel comes with an intrinsic size that should be calculated based on the text and the label's .font property. You may need to add a margin to it...
var height = l.intrinsicContentSize.height
height += margin
l.frame = CGRect(x: margin, y: 0, width: view.bounds.width - (margin * 2), height: height)
Failing that, maybe you can try something like:
let size = CGSize(width: view.bounds.width - (margin * 2), height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
var estimatedFrame = CGRect()
if let font = l.font {
estimatedFrame = NSString(string: l.text).boundingRect(with: size, options: options, attributes: [NSAttributedString.Key.font: font], context: nil)
}
//if you need a margin:
estimatedFrame.height += margin
l.frame = estimatedFrame
Give your UILabel as a UIScrollview or UITableView cell subview.
Then you setup UILabel leading, tralling, top, bottom constrain.
If you give UITableview then set table view hight auto dynamic. If you give UIScrollview
just set UILabel bottom constrain priority low

Getting details about the pinch gesture in ARKit

In my project I have a pinch to resize option for the object that has been placed in scene view. But when someone pinch the screen to reduce or enlarge the actual size of the object I need to get that scale. I need to display the scale in which the object is being changed in the screen. How do I get the scale when the action is being performed?
Thank you
Within your main ViewController Class for the ARSCNView
declare the label view, and the label itself at the top.
let scaleLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, 70))
let labelView = UIView(frame: CGRect(x: 300, y: 300, width: 300, height: 70))
Now within LoadView or ViewDidLoad you can set the attributes for the label such backgroundColor, textColor etc... and also add the view and label to sceneView.
// add your attributes for label,view
labelView.backgroundColor = .clear
scaleLabel.textColor = .white
scaleLabel.adjustsFontSizeToFitWidth
// add you views to sceneView
labelView.addSubview(scaleLabel)
sceneView.addSubview(labelView)
Lastly, with the pinch gesture function for scaling.. which should look something like this.
#objc func pinchGesture(_ gesture: UIPinchGestureRecognizer) {
if nodeYouScale != nil {
let action = SCNAction.scale(by: gesture.scale, duration: 0.1)
nodeYouScale.runAction(action)
gesture.scale = 1
// this part updates the label with the current scale factor
scaleLabel.text = "X: \(nodeYouScale.scale.x) Y: \(nodeYouScale.scale.y) Z:\(nodeYouScale.scale.z)"
} else {
return
}

Add shape to a UIButton in Swift

I have a UIButton and I wanna add a circle shape in the middle of it.
how can I do that?
Here's what I want to achieve.
Thanks/
Insert a subview in your button
let circleFrame = CGRect(x: 0, y: 0, width: circleDimension, height: circleDimension)
let circle = UIView(frame: circleFrame)
circle.backgroundColor = UIColor.clear
circle.layer.borderWidth = circleWidth
circle.layer.borderColor = UIColor.white.cgColor
circle.layer.cornerRadius = circleDimension/2
Just add that as a subview to your button and set centerX and centerY anchors equal to your button. Then, if you want to make it square, just do
circle.layer.cornerRadius = 0
There are many ways to do this including using CoreGraphics. The easiest way is to just set an image on the button
How to set image of UIButton in Swift 3?
If you need it clickable ofcourse, otherwise just use a UIImageView(which you can also make clickable using UITapGestureRecognizer)

padding setting for password field

I am using Swift 3. I have two txtFields, one of them for email, the other one for password. I use the following code to give the email field a padding:
txtEmail.leftView = paddingView;
txtEmail.leftViewMode = .always
However, the app seems to run into an infinity loop when I apply the same to the password field:
txtPassword.leftView = paddingView;
txtPassword.leftViewMode = .always
What am I missing?
This UITextField extension will ease your implementation later on and should solve any problems related to using the same UIView on multiple textfields:
extension UITextField {
func pad(_ amount: Int) {
self.leftView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: self.frame.height))
self.leftViewMode = UITextFieldViewMode.always
}
}
Then you can use it like this in viewDidLoad or anywhere lower in the view controller's call hierarchy
txtEmail.pad(15)
txtPassword.pad(15)
As your code display you are using same view object for both textfield at the same time and may be that's why your app is looping. you can not add same view object as a subview for two diffrent textfield at the same time. so create new view instance and set it as a padding for individual textfiled.
let paddingForEmail = UIView(frame: CGRectMake(0, 0, 15,txtEmail.frame.size.height))
//Adding the padding to the Email textField
txtEmail.leftView = paddingForFirst
txtEmail.leftViewMode = UITextFieldViewMode .Always
//Create new padding object of view for second textField
let paddingForPassword = UIView(frame: CGRectMake(0, 0, 15,txtPassword.frame.size.height))
//Add padding to the Password textField
txtPassword.leftView = paddingForPassword
txtPassword.leftViewMode = UITextFieldViewMode .Always
hope this will help you.

UITextfield leftView/rightView padding on iOS7

The leftView and rightView views of an UITextField on iOS7 are really close to the textfield border.
How may I add some (horizontal) padding to those items?
I tried modifying the frame, but did not work
uint padding = 10;//padding for iOS7
UIImageView * iconImageView = [[UIImageView alloc] initWithImage:iconImage];
iconImageView.frame = CGRectMake(0 + padding, 0, 16, 16);
textField.leftView = iconImageView;
Please, note that I'm not interested in adding padding to the textfield's text, like this Set padding for UITextField with UITextBorderStyleNone
A much simpler solution, which takes advantage of contentMode:
arrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"down_arrow"]];
arrow.frame = CGRectMake(0.0, 0.0, arrow.image.size.width+10.0, arrow.image.size.height);
arrow.contentMode = UIViewContentModeCenter;
textField.rightView = arrow;
textField.rightViewMode = UITextFieldViewModeAlways;
In Swift 3,
let arrow = UIImageView(image: UIImage(named: "arrowDrop"))
if let size = arrow.image?.size {
arrow.frame = CGRect(x: 0.0, y: 0.0, width: size.width + 10.0, height: size.height)
}
arrow.contentMode = UIViewContentMode.center
self.textField.rightView = arrow
self.textField.rightViewMode = UITextFieldViewMode.always
Was just working on this myself and used this solution:
- (CGRect) rightViewRectForBounds:(CGRect)bounds {
CGRect textRect = [super rightViewRectForBounds:bounds];
textRect.origin.x -= 10;
return textRect;
}
This will move the image over from the right by 10 instead of having the image squeezed up against the edge in iOS 7.
Additionally, this was in a subclass of UITextField, which can be created by:
Create a new file that's a subclass of UITextField instead of the default NSObject
Add a new method named - (id)initWithCoder:(NSCoder*)coder to set the image
- (id)initWithCoder:(NSCoder*)coder {
self = [super initWithCoder:coder];
if (self) {
self.clipsToBounds = YES;
[self setRightViewMode:UITextFieldViewModeUnlessEditing];
self.leftView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"textfield_edit_icon.png"]];
}
return self;
}
You may have to import #import <QuartzCore/QuartzCore.h>
Add the rightViewRectForBounds method above
In Interface Builder, click on the TextField you would like to subclass and change the class attribute to the name of this new subclass
Easiest way is add a UIView to leftView/righView and add an ImageView to UIView , adjust the origin of ImageView inside UIView anywhere you like , this worked for me like a charm. It needs only few lines of code
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 5, 26, 26)];
imgView.image = [UIImage imageNamed:#"img.png"];
UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 32, 32)];
[paddingView addSubview:imgView];
[txtField setLeftViewMode:UITextFieldViewModeAlways];
[txtField setLeftView:paddingView];
This works great for Swift:
let imageView = UIImageView(image: UIImage(named: "image.png"))
imageView.contentMode = UIViewContentMode.Center
imageView.frame = CGRectMake(0.0, 0.0, imageView.image!.size.width + 20.0, imageView.image!.size.height)
textField.rightViewMode = UITextFieldViewMode.Always
textField.rightView = imageView
This works for me
UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 20)];
self.passwordTF.leftView = paddingView;
self.passwordTF.leftViewMode = UITextFieldViewModeAlways;
May it helps you.
I like this solution because it solves the problem with a single line of code
myTextField.layer.sublayerTransform = CATransform3DMakeTranslation(10.0f, 0.0f, 0.0f);
Note: .. or 2 if you consider including QuartzCore a line :)
Swift 5
class CustomTextField: UITextField {
func invalidate() {
let errorImage = UIImageView(image: UIImage(named: "errorImage"))
errorImage.frame = CGRect(x: 8, y: 8, width: 16, height: 16)
rightView = UIView(frame: CGRect(x: 0, y: 0, width: 32, height: 32))
rightView?.addSubview(errorImage)
rightViewMode = .always
}
}
You'll want to:
Subclass UITextField
Write an invalidate method inside the
subclassed text field
In the invalidate method, create a UIView
larger than your image
Place your image inside the view
Assign the
view to UITextField.rightView
Instead of manipluating imageView or image we can override a method provided by apple for rightView.
class CustomTextField : UITextField {
override func rightViewRect(forBounds bounds: CGRect) -> CGRect {
let offset = 5
let width = 20
let height = width
let x = Int(bounds.width) - width - offset
let y = offset
let rightViewBounds = CGRect(x: x, y: y, width: width, height: height)
return rightViewBounds
}}
and same way we can override below func for left view.
override func leftViewRect(forBounds bounds: CGRect) -> CGRect {
/*return as per requirement*/
}
The best way to do this is simply make a class using subclass of UITextField and in .m file
#import "CustomTextField.h"
#import <QuartzCore/QuartzCore.h>
#implementation CustomTextField
- (id)initWithCoder:(NSCoder*)coder
{
self = [super initWithCoder:coder];
if (self) {
//self.clipsToBounds = YES;
//[self setRightViewMode:UITextFieldViewModeUnlessEditing];
self.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,15,46)];
self.leftViewMode=UITextFieldViewModeAlways;
}
return self;
}
by doing this go to your storyboard or xib and click on identity inspector and replace UITextfield with your own "CustomTextField" in class option.
Note: If you simply give padding with auto layout for textfield then your application will not run and show only blank screen.
I found this somewhere...
UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, 20)];
paddingView.backgroundColor = [UIColor clearColor];
itemDescription.leftView = paddingView;
itemDescription.leftViewMode = UITextFieldViewModeAlways;
[self addSubview:itemDescription];
Since iOS 13 and Xcode 11 this is the only solution that works for us.
// Init of custom UITextField
override init(frame: CGRect) {
super.init(frame: frame)
if let size = myButton.imageView?.image?.size {
myButton.frame = CGRect(x:0, y: 0, width: size.width, height: size.height)
let padding: CGFloat = 5
let container = UIView(frame: CGRect(x:0, y: 0, width: size.width + padding, height: size.height))
container.addSubview(myButton)
myButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
myButton.topAnchor.constraint(equalTo: container.topAnchor),
myButton.leftAnchor.constraint(equalTo: container.leftAnchor),
myButton.bottomAnchor.constraint(equalTo: container.bottomAnchor),
myButton.rightAnchor.constraint(equalTo: container.rightAnchor, constant: -padding),
])
textField.rightViewMode = .always
textField.rightView = container
}
}
Maybe you might set up an empty view and embed your view as a subview:
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 50.0, height: 50.0))
imageView.contentMode = .center
imageView.image = UIImage(named: "ic_dropdown")
let emptyView = UIView(frame: CGRect(x: 0, y: 0, width: 50.0, height: 50.0))
emptyView.backgroundColor = .clear
emptyView.addSubview(imageView)
self.documentTypeTextLabel.rightView = emptyView
self.documentTypeTextLabel.rightViewMode = .always
Happy coding
Create a custom UITextField class and use that class instead of UITextField. Override - (CGRect) textRectForBounds:(CGRect)bounds to set the rect that you need
Example
- (CGRect)textRectForBounds:(CGRect)bounds{
CGRect textRect = [super textRectForBounds:bounds];
textRect.origin.x += 10;
textRect.size.width -= 10;
return textRect;
}
Here is one solution:
UIView *paddingTxtfieldView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 20, 42)]; // what ever you want
txtfield.leftView = paddingTxtfieldView;
txtfield.leftViewMode = UITextFieldViewModeAlways;
Below example is for adding horizontal padding to a left view that happens to be an icon - you can use the similar approach for adding padding to any UIView that you would like to use as the textfield's left view.
Inside UITextField subclass:
static CGFloat const kLeftViewHorizontalPadding = 10.0f;
#implementation TextFieldWithLeftIcon
{
UIImage *_image;
UIImageView *_imageView;
}
- (instancetype)initWithFrame:(CGRect)frame image:(UIImage *)image
{
self = [super initWithFrame:frame];
if (self) {
if (image) {
_image = image;
_imageView = [[UIImageView alloc] initWithImage:image];
_imageView.contentMode = UIViewContentModeCenter;
self.leftViewMode = UITextFieldViewModeAlways;
self.leftView = _imageView;
}
}
return self;
}
#pragma mark - Layout
- (CGRect)leftViewRectForBounds:(CGRect)bounds
{
CGFloat widthWithPadding = _image.size.width + kLeftViewHorizontalPadding * 2.0f;
return CGRectMake(0, 0, widthWithPadding, CGRectGetHeight(bounds));
}
Although we are a subclassing UITextField here, I believe this is the cleanest approach.
- (CGRect)rightViewRectForBounds:(CGRect)bounds
{
return CGRectMake(bounds.size.width - 40, 0, 40, bounds.size.height);
}
thank you guys for your answers, to my surprise none of them really fitted the right view image to my textfield while still providing the needed padding. then i thought of using the AspectFill mode and miracles happened. for future seekers, here's what i used:
UIImageView *emailRightView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 35, 35)];
emailRightView.contentMode = UIViewContentModeScaleAspectFill;
emailRightView.image = [UIImage imageNamed:#"icon_email.png"];
emailTextfield.rightViewMode = UITextFieldViewModeAlways;
emailTextfield.rightView = emailRightView;
the 35 in the frame of my imageview represents the height of my emailTextfield, feel free to adjust it to your needs.
If you are using a UIImageView as leftView then you have to use this code :
Caution : Don't use inside viewWillAppear or viewDidAppear
-(UIView*)paddingViewWithImage:(UIImageView*)imageView andPadding:(float)padding
{
float height = CGRectGetHeight(imageView.frame);
float width = CGRectGetWidth(imageView.frame) + padding;
UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
[paddingView addSubview:imageView];
return paddingView;
}
I created a custom method in my ViewController class, like shown bellow:
- (void) modifyTextField:(UITextField *)textField
{
// Prepare the imageView with the required image
uint padding = 10;//padding for iOS7
UIImageView * iconImageView = [[UIImageView alloc] initWithImage:iconImage];
iconImageView.frame = CGRectMake(0 + padding, 0, 16, 16);
// Set the imageView to the left of the given text field.
textField.leftView = iconImageView;
textField.leftViewMode = UITextFieldViewModeAlways;
}
Now I can call that method inside (viewDidLoad method) and send any of my TextFields to that method and add padding for both right and left, and give text and background colors by writing just one line of code, as follows:
[self modifyTextField:self.firstNameTxtFld];
This Worked perfectly on iOS 7! Hope this still works on iOS 8 and 9 too!
I know that adding too much Views might make this a bit heavier object to be loaded. But when concerned about the difficulty in other solutions, I found myself more biased to this method and more flexible with using this way. ;)
Hope this answer might be helpful or useful to figure out another solution to someone else.
Cheers!
This works for me just like I looking for:
func addImageViewInsideMyTextField() {
let someView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 24))
let imageView = UIImageView(image: UIImage(named: "accountImage"))
imageView.frame = CGRect(x: 16, y: 0, width: 24, height: 24)
imageView.contentMode = .scaleAspectFit
someView.addSubview(imageView)
self.myTextField.leftView = someView
self.myTextField.leftViewMode = .always
}
Set Rightview of UITextField using swift 4.2
TxtPass.rightViewMode = UITextField.ViewMode.always
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 18, height: 18))
imageView.contentMode = UIView.ContentMode.scaleAspectFit
let image = UIImage(named: "hidepass")
imageView.image = image
let rightView = UIView(frame: CGRect(x: 0, y: 0, width: 28, height: 18))
rightView.addSubview(imageView)
rightView.contentMode = UIView.ContentMode.left
TxtPass.rightView = rightView
One trick: Add a UIView containing UIImageView to UITextField as rightView. This UIView must be larger in size, now place the UIImageView to left of it. So there will be a padding of space from right.
// Add a UIImageView to UIView and now this UIView to UITextField - txtFieldDate
UIView *viewRightIntxtFieldDate = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 20, 30)];
// (Height of UITextField is 30px so height of viewRightIntxtFieldDate = 30px)
UIImageView *imgViewCalendar = [[UIImageView alloc] initWithFrame:CGRectMake(0, 10, 10, 10)];
[imgViewCalendar setImage:[UIImage imageNamed:#"calendar_icon.png"]];
[viewRightIntxtFieldDate addSubview:imgViewCalendar];
txtFieldDate.rightViewMode = UITextFieldViewModeAlways;
txtFieldDate.rightView = viewRightIntxtFieldDate;
I have had this problem myself, and by far the easiest solution is to modify your image to simply add padding to each side of the image!
I just altered my png image to add 10 pixels transparent padding, and it works well, with no coding at all!
Easiest way is just change the Textfield as RoundRect instead of Custom and see the magic. :)
for Swift2 , I use
...
self.mSearchTextField.leftViewMode = UITextFieldViewMode.Always
let searchImg = UIImageView(image: UIImage(named: "search.png"))
let size = self.mSearchTextField.frame.height
searchImg.frame = CGRectMake(0, 0, size,size)
searchImg.contentMode = UIViewContentMode.ScaleAspectFit
self.mSearchTextField.leftView = searchImg
...
...
textField.rightView = UIImageView(image: ...)
textField.rightView?.contentMode = .top
textField.rightView?.bounds.size.height += 10
textField.rightViewMode = .always
...
I realize this an old post and this answer is a bit specific to my use case, but I posted it in case others are seeking a similar solution. I want to move a UITextField's leftView or rightView but I am not putting images in them and do not want any hard coded constants.
My UI calls for hiding the text field's clear button and displaying a UIActivityIndicatorView where the clear button was located.
I add a spinner to the rightView, but out of the box (on iOS 13) it is shifted 20 pixels to the right of the clearButton. I don't like to use magic numbers since the position of the clearButton and rightView are subject to change at any time by Apple. The UI design intent is "spinner where the clear button is" so my solution was to subclass UITextField and override rightViewRect(forBounds).
override func rightViewRect(forBounds bounds: CGRect) -> CGRect {
// Use clearButton's rectangle
return self.clearButtonRect(forBounds: bounds)
}
Below is a working example (sans Storyboard):
//------------------------------------------------------------------------------
class myCustomTextField: UITextField {
override func rightViewRect(forBounds bounds: CGRect) -> CGRect {
// Use clearButton rectangle
return self.clearButtonRect(forBounds: bounds)
}
}
//------------------------------------------------------------------------------
class myViewController: UIViewController {
var activityView: UIActivityIndicatorView = {
let activity = UIActivityIndicatorView()
activity.startAnimating()
return activity
}()
#IBOutlet weak var searchTextField: myCustomTextField!
//------------------------------------------------------------------------------
// MARK: - Lifecycle
//------------------------------------------------------------------------------
override func viewDidLoad() {
super.viewDidLoad()
searchTextField.rightView = activityView
searchTextField.rightViewMode = .never // Hide spinner
searchTextField.clearButtonMode = .never // Hide clear button
setupUIForTextEntry()
}
// ...
// More code to switch between user text entry and "search progress"
// by calling setupUI... functions below
// ...
//------------------------------------------------------------------------------
// MARK: - UI
//------------------------------------------------------------------------------
func setupUIForTextEntry() {
// Hide spinner
searchTextField.rightViewMode = .never
// Show clear button
searchTextField.clearButtonMode = .whileEditing
searchTextField.becomeFirstResponder()
}
//------------------------------------------------------------------------------
func setupUIForSearching() {
// Show spinner
searchTextField.rightViewMode = .always
// Hide clear button
searchTextField.clearButtonMode = .never
searchTextField.resignFirstResponder()
}
//------------------------------------------------------------------------------
}
//------------------------------------------------------------------------------
Simple approach:
textField.rightViewMode = .always
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 25, height: 15))
textField.contentMode = .scaleAspectFit
imageView = UIImage(named: "imageName")
textField.rightView = imageView
Note: Height should be smaller than the width to allow horizontal padding.

Resources