UITextfield leftView/rightView padding on iOS7 - ios

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.

Related

Add UILabel below the thumb of UISlider [duplicate]

How can i put an UILabel over the thumb of UISlider...so that when i move the thumb....UILabel will also move....as it is on the thumb...
Any idea??
Try this
yourLabel = [[UILabel alloc]initWithFrame:....];
//Call this method on Slider value change event
-(void)sliderValueChanged{
CGRect trackRect = [self.slider trackRectForBounds:self.slider.bounds];
CGRect thumbRect = [self.slider thumbRectForBounds:self.slider.bounds
trackRect:trackRect
value:self.slider.value];
yourLabel.center = CGPointMake(thumbRect.origin.x + self.slider.frame.origin.x, self.slider.frame.origin.y - 20);
}
I could get most accurate value by using this snippet.
The "knob" isn't available per public API, so bad chances for hooking it up - if it is a subview at all and not just drawn directly.
So you should add you label to the same view as the slider (make sure you add it later so that appears over it). You can then listen for the value change events and place your label accordingly. It is linear scaling between the endpoints that you need to figure out at first, but it shouldn't be too difficult.
Edit with code:
yourLabel = [[UILabel alloc]initWithFrame:....];
// .. configure label
[[yourSlider superview] addSubview:yourLabel];
[yourSlider addTarget:self action:#selector(adjustLabelForSlider:) forControlEvents:UIControlEventValueChanged];
-(void)adjustLabelForSlider:(id)slider
{
float value = slider.value;
float min = slider.minimumValue;
float max = slider.maximumValue;
CGFloat newX = ...; // Calculate based on yourSlider.frame and value, min, and max
CGFloat newY = ...;
[yourLabel setCenter:CGPointMake(newX,newY)];
}
Note: untested code ;-)
Same answer with swift3:
let trackRect: CGRect = slider.trackRect(forBounds: slider.bounds)
let thumbRect: CGRect = slider.thumbRect(forBounds: slider.bounds , trackRect: trackRect, value: slider.value)
let x = thumbRect.origin.x + slider.frame.origin.x
let y = slider.frame.origin.y - 20
sliderLabel.center = CGPoint(x: x, y: y)
extension UIImage {
class func imageWithLabel(_ label: UILabel) -> UIImage {
UIGraphicsBeginImageContextWithOptions(label.bounds.size, false, 0)
defer { UIGraphicsEndImageContext() }
label.layer.render(in: UIGraphicsGetCurrentContext()!)
return UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
}
}
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate, UITextViewDelegate, UIPickerViewDelegate, UIPickerViewDataSource, UIScrollViewDelegate
{
func maskRoundedImage(image: UIImage, radius: CGFloat) -> UIImage {
let imageView: UIImageView = UIImageView(image: image)
let layer = imageView.layer
layer.masksToBounds = true
layer.cornerRadius = radius
UIGraphicsBeginImageContext(imageView.bounds.size)
layer.render(in: UIGraphicsGetCurrentContext()!)
let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return roundedImage!
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 28, height: 28))
label.backgroundColor = .black
label.textAlignment = .center
label.font = label.font.withSize(12)
label.text = String(Int(round( backlightSlider.value * 100 )))
label.textColor = .white
var image = UIImage.imageWithLabel(label)
image = maskRoundedImage(image: image, radius: 14.0)
backlightSlider.setThumbImage(image, for: .normal)
}
Just add an imageview on the thumb of slider add a label on imageview
- (IBAction)valueChangedSlider:(id)sender {
handleView = [_slider.subviews lastObject];
label = [[UILabel alloc] initWithFrame:handleView.bounds];
label = (UILabel*)[handleView viewWithTag:1000];
if (label==nil) {
label = [[UILabel alloc] initWithFrame:handleView.bounds];
label.tag = 1000;
[label setFont:[UIFont systemFontOfSize:12]];
label.textColor = [UIColor redColor];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
[handleView addSubview:label];
}
label.text = [NSString stringWithFormat:#"%0.2f", self.slider.value];
}
If anybody is looking for answer in Swift, please take a look of my answer here:- Put Label over UISlider Thumb
It'll work like a charm :)
This could be very helpful...
How to get the center of the thumb image of UISlider

Can't set titleView in the center of navigation bar because back button

I'm using an image view to display an image in my nav bar. The problem is that I can't set it to the center correctly because of the back button. I checked the related questions and had almost the same problem earlier that I solved, but this time I have no idea.
Earlier I solved this problem with fake bar buttons, so I tried to add a fake bar button to the right (and left) side, but it doesn't helped.
- (void) searchButtonNavBar {
CGRect imageSizeDummy = CGRectMake(0, 0, 25,25);
UIButton *dummy = [[UIButton alloc] initWithFrame:imageSizeDummy];
UIBarButtonItem
*searchBarButtonDummy =[[UIBarButtonItem alloc] initWithCustomView:dummy];
self.navigationItem.rightBarButtonItem = searchBarButtonDummy;
}
- (void)setNavBarLogo {
[self setNeedsStatusBarAppearanceUpdate];
CGRect myImageS = CGRectMake(0, 0, 44, 44);
UIImageView *logo = [[UIImageView alloc] initWithFrame:myImageS];
[logo setImage:[UIImage imageNamed:#"color.png"]];
logo.contentMode = UIViewContentModeScaleAspectFit;
self.navigationItem.titleView = logo;
[[UIBarButtonItem appearance] setTitlePositionAdjustment:UIOffsetMake(0.0f, 0.0f) forBarMetrics:UIBarMetricsDefault];
}
I think it should be workin fine because in this case the titleView has bar buttons on the same side. Is there any explanation why it worked with bar buttons that was created programmatically but doesn't works with the common back button?
UINavigationBar automatically centers its titleView as long as there is enough room. If the title isn't centered that means that the title view is too wide to be centered, and if you set the backgroundColor if your UIImageView you'll see that's exactly what is happening.
The title view is too wide because that navigation bar will automatically resize the title to hold its content, using -sizeThatFits:. This means that your title view will always be resized to the size of your image.
Two possible fixes:
The image you're using is way too big. Use a properly sized 44x44 pt image with 2x and 3x versions.
Wrap UIImageView inside of a regular UIView to avoid resizing.
Example:
UIImageView* imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"test.jpeg"]];
imageView.contentMode = UIViewContentModeScaleAspectFit;
UIView* titleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
imageView.frame = titleView.bounds;
[titleView addSubview:imageView];
self.navigationItem.titleView = titleView;
An example in Swift 3 version of Darren's second way:
let imageView = UIImageView(image: UIImage(named: "test"))
imageView.contentMode = UIViewContentMode.scaleAspectFit
let titleView = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
imageView.frame = titleView.bounds
titleView.addSubview(imageView)
self.navigationItem.titleView = titleView
I suggest you Override the function - (void)setFrame:(CGRect)fram
like this:
- (void)setFrame:(CGRect)frame {
[super setFrame:frame]; //systom function
self.center = CGPointMake(self.superview.center.x, self.center.y); //rewrite function
}
so that the titleView.center always the right location
Don't use titleView.
Just add your image to navigationController.navigationBar
CGRect myImageS = CGRectMake(0, 0, 44, 44);
UIImageView *logo = [[UIImageView alloc] initWithFrame:myImageS];
[logo setImage:[UIImage imageNamed:#"color.png"]];
logo.contentMode = UIViewContentModeScaleAspectFit;
logo.center = CGPointMake(self.navigationController.navigationBar.width / 2.0, self.navigationController.navigationBar.height / 2.0);
logo.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
[self.navigationController.navigationBar addSubview:logo];
Qun Li's worked perfectly for me. Here's the swift 2.3 code:
override var frame: CGRect {
set(newValue) {
super.frame = newValue
if let superview = self.superview {
self.center = CGPoint(x: superview.center.x, y: self.center.y)
}
}
get {
return super.frame
}
}
If you're using a custom view from a nib, be sure to disable auto-layout on the nib file.
I created a custom UINavigationController that after dropping in, the only thing you have to do is call showNavBarTitle(title:font:) when you want to show and removeNavBarTitle() when you want to hide:
class NavigationController: UINavigationController {
private static var mTitleFont = UIFont(name: <your desired font (String)> , size: <your desired font size -- however, font size will automatically adjust so the text fits in the label>)!
private static var mNavBarLabel: UILabel = {
let x: CGFloat = 60
let y: CGFloat = 7
let label = UILabel(frame: CGRect(x: x, y: y, width: UIScreen.main.bounds.size.width - 2 * x, height: 44 - 2 * y))
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.5
label.font = NavigationController.mTitleFont
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
func showNavBarLabel(title: String, font: UIFont = mTitleFont) {
NavigationController.mNavBarLabel.text = title
NavigationController.mNavBarLabel.font = font
navigationBar.addSubview(NavigationController.mNavBarLabel)
}
func removeNavBarLabel() {
NavigationController.mNavBarLabel.removeFromSuperview()
}
}
I find the best place to call showNavBarTitle(title:font:) and removeNavBarTitle() are in the view controller's viewWillAppear() and viewWillDisappear() methods, respectively:
class YourViewController: UIViewController {
func viewWillAppear() {
(navigationController as! NavigationController).showNavBarLabel(title: "Your Title")
}
func viewWillDisappear() {
(navigationController as! NavigationController).removeNavBarLabel()
}
}
1) You can try setting your image as UINavigationBar's background image by calling
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"color.png"] forBarMetrics:UIBarMetricsDefault];
inside the viewDidLoad method.
That way it will be always centered, but if you have back button with long title as left navigation item, it can appear on top of your logo. And you should probably at first create another image with the same size as the navigation bar, then draw your image at its center, and after that set it as the background image.
2) Or instead of setting your image view as titleView, you can try simply adding at as a subview, so it won't have the constraints related to right and left bar button items.
In Swift, this is what worked for me however it´s not the best solution (basically, add it up to navigationBar):
let titleIV = UIImageView(image: UIImage(named:"some"))
titleIV.contentMode = .scaleAspectFit
titleIV.translatesAutoresizingMaskIntoConstraints = false
if let navigationController = self.navigationController{
navigationController.navigationBar.addSubview(titleIV)
titleIV.centerXAnchor.constraint(equalTo:
navigationController.navigationBar.centerXAnchor).isActive = true
titleIV.centerYAnchor.constraint(equalTo: navigationController.navigationBar.centerYAnchor).isActive = true
}
else{
view.addSubview(titleIV)
titleIV.topAnchor.constraint(equalTo: view.topAnchor, constant: UIApplication.shared.statusBarFrame.height).isActive = true
titleIV.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
}
Extending Darren's answer, a fix for me was to return a sizeThatFits with the UILabel size. It turns out that this is called after layoutSubViews so the label has a size.
override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: titleLabel.frame.width + titleInset*2, height: titleLabel.frame.height)
}
Also note that I have + titleInset*2 because Im setting the horizontal constraints like so:
titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: titleInset),
titleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -titleInset)

Padding Placeholder text ios

I want to make the placeholder text display in middle of the textfield (Padding placeholder text). The size of the placeholder text also needs to increase. My code is as follows, how can i solve this ?
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(30, 220,250,55)];
textField.placeholder=#"iiiiiii";
UIView *padView = [[UIView alloc] initWithFrame:CGRectMake(10, 110, 10, 0)];
textField.leftView = padView;
textField.leftViewMode = UITextFieldViewModeAlways;
[self.view addSubview:textField];
UPDATE
I want the font size of the placeholder text to increase your name, and it should have a Left padding to it.
You could subclass your UITextFiled and override methods:
MyTextField.m
- (CGRect)textRectForBounds:(CGRect)bounds {
return [self rectForBounds:bounds];
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self rectForBounds:bounds];
}
- (CGRect)placeholderRectForBounds:(CGRect)bounds {
return [self rectForBounds:bounds];
}
//here 40 - is your x offset
- (CGRect)rectForBounds:(CGRect)bounds {
return CGRectInset(bounds, 40, 3);
}
upd:
also set
textFiled.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
because it could some problems with io6 vs ios 7 vertical positionning
Same question was being asked and answered as below Set padding for UITextField with UITextBorderStyleNone
UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, 20)];
textField.leftView = paddingView;
textField.leftViewMode = UITextFieldViewModeAlways;
You can set the starting text alignment (of the textField from xib or via code) to be center aligned.
Then in the -textFieldShouldBeginEditing, you can set the textField to be left aligned.
Similarly, on the -textFieldDidEndEditing, check if the textField is empty and if it is then set textField back to center aligned.
basically:
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
[textField setTextAlignment:NSTextAlignmentLeft];
return YES;
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
if(textField.text.length == 0) {
[textField setTextAlignment:NSTextAlignmentCenter];
}
}
EDIT::
the .h of your ViewController class should look like:
#interface ViewController : UIViewController <UITextFieldDelegate>
{
UITextField *myTextField;
}
now, replace your other code with this:
myTextField = [[UITextField alloc] initWithFrame:CGRectMake(30, 220,250,55)];
myTextField.placeholder=#"iiiiiii";
//important
[myTextField setDelegate: self];
//commented lines not really needed
//UIView *padView = [[UIView alloc] initWithFrame:CGRectMake(10, 110, 10, 0)];
//textField.leftView = padView;
//textField.leftViewMode = UITextFieldViewModeAlways;
[self.view addSubview:textField];
sampleTextfield.leftView = UIView(frame: CGRectMake(0, 0, 20, self.sampleTextfield.frame.height))
sampleTextfield.leftViewMode = UITextFieldViewMode.Always
The easiest way that I found to do this task on swift 2 and Xcode 7:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var emailTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
let paddingView = UIView(frame: CGRectMake(0, 0, 25, self.emailTextField.frame.height))
emailTextField.leftView = paddingView
emailTextField.leftViewMode = UITextFieldViewMode.Always
}
}
func placeholderPadding(textField:UITextField, leftPadding:CGFloat) {
textField.leftView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: leftPadding, height: textField.frame.height))
textField.leftViewMode = UITextFieldViewMode.always
}
While I have not specifically tested it, this should work:
self.YourTextField.attributedPlaceholder = NSAttributedString(string: " YourText")
Add the amount of blank spaces (padding) to your string

Position of rightView UITextField

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

How do I put the image on the right side of the text in a UIButton?

I don't want to use a subview if I can avoid it. I want a UIButton with a background image, text, and an image in it. Right now, when I do that, the image is on the left side of the text. The background image, text, and image all have different highlight states.
Simplest solution:
iOS 10 & up, Swift:
button.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
button.titleLabel?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
button.imageView?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
Before iOS 10, Swift/Obj-C:
button.transform = CGAffineTransformMakeScale(-1.0, 1.0);
button.titleLabel.transform = CGAffineTransformMakeScale(-1.0, 1.0);
button.imageView.transform = CGAffineTransformMakeScale(-1.0, 1.0);
iOS 9 & up, Swift: (Recommended)
button.semanticContentAttribute = .forceRightToLeft
Despite some of the suggested answers being very creative and extremely clever, the simplest solution is as follows:
button.semanticContentAttribute = UIApplication.shared
.userInterfaceLayoutDirection == .rightToLeft ? .forceLeftToRight : .forceRightToLeft
As simple as that. As a bonus, the image will be at the left side in right-to-left locales.
EDIT: as the question has been asked a few times, this is iOS 9 +.
UPDATED FOR XCODE 9 (Via Interface Builder)
There's an easier way from the Interface Builder.
Select the UIButton and select this option in the View Utilities > Semantic:
That's it! Nice and simple!
OPTIONAL - 2nd step:
If you want to adjust the spacing between the image and the title you can change the Image Inset here:
Subclassing UIButton is completely unnecessary. Instead you can simply set a high left inset value for the image insets, and a small right inset for the title. Something like this:
button.imageEdgeInsets = UIEdgeInsetsMake(0., button.frame.size.width - (image.size.width + 15.), 0., 0.);
button.titleEdgeInsets = UIEdgeInsetsMake(0., 0., 0., image.size.width);
I'm giving Inspire48 the credit for this one. Based on his suggestion and looking at that other question I came up with this. Subclass UIButton and override these methods.
#implementation UIButtonSubclass
- (CGRect)imageRectForContentRect:(CGRect)contentRect
{
CGRect frame = [super imageRectForContentRect:contentRect];
frame.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(frame) - self.imageEdgeInsets.right + self.imageEdgeInsets.left;
return frame;
}
- (CGRect)titleRectForContentRect:(CGRect)contentRect
{
CGRect frame = [super titleRectForContentRect:contentRect];
frame.origin.x = CGRectGetMinX(frame) - CGRectGetWidth([self imageRectForContentRect:contentRect]);
return frame;
}
#end
Just update the insets when the title is changed. You need to compensate for the inset with an equal and opposite inset on the other side.
[thebutton setTitle:title forState:UIControlStateNormal];
thebutton.titleEdgeInsets = UIEdgeInsetsMake(0, -thebutton.imageView.frame.size.width, 0, thebutton.imageView.frame.size.width);
thebutton.imageEdgeInsets = UIEdgeInsetsMake(0, thebutton.titleLabel.frame.size.width, 0, -thebutton.titleLabel.frame.size.width);
All of these answers, as of January 2016, are unnecessary. In Interface Builder, set the View Semantic to Force Right-to-Left, or if you prefer programmatic way, semanticContentAttribute = .forceRightToLeft That will cause the image to appear on the right of your text.
In interface builder you can configure options Edge Insets for UIButton, separately each of three parts: content, image, title
Xcode 8:
I decided not to use the standard button image view because the proposed solutions to move it around felt hacky. This got me the desired aesthetic, and it is intuitive to reposition the button by changing the constraints:
extension UIButton {
func addRightIcon(image: UIImage) {
let imageView = UIImageView(image: image)
imageView.translatesAutoresizingMaskIntoConstraints = false
addSubview(imageView)
let length = CGFloat(15)
titleEdgeInsets.right += length
NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: self.titleLabel!.trailingAnchor, constant: 10),
imageView.centerYAnchor.constraint(equalTo: self.titleLabel!.centerYAnchor, constant: 0),
imageView.widthAnchor.constraint(equalToConstant: length),
imageView.heightAnchor.constraint(equalToConstant: length)
])
}
}
Update: Swift 3
class ButtonIconRight: UIButton {
override func imageRect(forContentRect contentRect:CGRect) -> CGRect {
var imageFrame = super.imageRect(forContentRect: contentRect)
imageFrame.origin.x = super.titleRect(forContentRect: contentRect).maxX - imageFrame.width
return imageFrame
}
override func titleRect(forContentRect contentRect:CGRect) -> CGRect {
var titleFrame = super.titleRect(forContentRect: contentRect)
if (self.currentImage != nil) {
titleFrame.origin.x = super.imageRect(forContentRect: contentRect).minX
}
return titleFrame
}
}
Original answer for Swift 2:
A solution that handles all horizontal alignments, with a Swift implementation example. Just translate to Objective-C if needed.
class ButtonIconRight: UIButton {
override func imageRectForContentRect(contentRect:CGRect) -> CGRect {
var imageFrame = super.imageRectForContentRect(contentRect)
imageFrame.origin.x = CGRectGetMaxX(super.titleRectForContentRect(contentRect)) - CGRectGetWidth(imageFrame)
return imageFrame
}
override func titleRectForContentRect(contentRect:CGRect) -> CGRect {
var titleFrame = super.titleRectForContentRect(contentRect)
if (self.currentImage != nil) {
titleFrame.origin.x = CGRectGetMinX(super.imageRectForContentRect(contentRect))
}
return titleFrame
}
}
Also worth noting that it handles quite well image & title insets.
Inspired from jasongregori answer ;)
If this need to be done in UIBarButtonItem, additional wrapping in view should be used
This will work
let view = UIView()
let button = UIButton()
button.setTitle("Skip", for: .normal)
button.setImage(#imageLiteral(resourceName:"forward_button"), for: .normal)
button.semanticContentAttribute = .forceRightToLeft
button.sizeToFit()
view.addSubview(button)
view.frame = button.bounds
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: view)
This won't work
let button = UIButton()
button.setTitle("Skip", for: .normal)
button.setImage(#imageLiteral(resourceName:"forward_button"), for: .normal)
button.semanticContentAttribute = .forceRightToLeft
button.sizeToFit()
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button)
Do Yourself. Xcode10, swift4,
For programmatically UI design
lazy var buttonFilter : ButtonRightImageLeftTitle = {
var button = ButtonRightImageLeftTitle()
button.setTitle("Playfir", for: UIControl.State.normal)
button.setImage(UIImage(named: "filter"), for: UIControl.State.normal)
button.backgroundColor = UIColor.red
button.contentHorizontalAlignment = .left
button.titleLabel?.font = UIFont.systemFont(ofSize: 16)
return button
}()
Edge inset values are applied to a rectangle to shrink or expand the
area represented by that rectangle. Typically, edge insets are used
during view layout to modify the view’s frame. Positive values cause
the frame to be inset (or shrunk) by the specified amount. Negative
values cause the frame to be outset (or expanded) by the specified
amount.
class ButtonRightImageLeftTitle: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
guard imageView != nil else { return }
imageEdgeInsets = UIEdgeInsets(top: 5, left: (bounds.width - 35), bottom: 5, right: 5)
titleEdgeInsets = UIEdgeInsets(top: 0, left: -((imageView?.bounds.width)! + 10), bottom: 0, right: 0 )
}
}
for StoryBoard UI design
iOS 15 brought an update where you can now handle image placements in buttons in a simpler non messier way, ie. without insets.
In XIB/Storyboards:
Simply set the button 'placement' property to leading/training/top/bottom after adding an image property to button. Since it's leading/training, there is an added advantage of it supporting RTL
**In code (Programmatically): **
Use Button Configuration property programmatically
This is not a backward compatible feature, and will work only in iOS15+ as was demonstrated in WWDC '21 - https://developer.apple.com/videos/play/wwdc2021/10064/?time=236
Developer documentation: https://developer.apple.com/documentation/uikit/uibutton/configuration?changes=_4
Here is solution for UIButton with center aligned content.
This code make image right aligned and allows to use imageEdgeInsets and titleEdgeInsets for precious positioning.
Subclass UIButton with your custom class and add:
- (CGRect)imageRectForContentRect:(CGRect)contentRect {
CGRect frame = [super imageRectForContentRect:contentRect];
CGFloat imageWidth = frame.size.width;
CGRect titleRect = CGRectZero;
titleRect.size = [[self titleForState:self.state] sizeWithAttributes:#{NSFontAttributeName: self.titleLabel.font}];
titleRect.origin.x = (self.frame.size.width - (titleRect.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
frame.origin.x = titleRect.origin.x + titleRect.size.width - self.imageEdgeInsets.right + self.imageEdgeInsets.left;
return frame;
}
- (CGRect)titleRectForContentRect:(CGRect)contentRect {
CGFloat imageWidth = [self imageForState:self.state].size.width;
CGRect frame = [super titleRectForContentRect:contentRect];
frame.origin.x = (self.frame.size.width - (frame.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
return frame;
}
Extension Way
Using extension to set image on the right side with custom offset
extension UIButton {
func addRightImage(image: UIImage, offset: CGFloat) {
self.setImage(image, for: .normal)
self.imageView?.translatesAutoresizingMaskIntoConstraints = false
self.imageView?.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0.0).isActive = true
self.imageView?.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -offset).isActive = true
}
}
Being that the transform solution doesn't work in iOS 11 I decided to write a new approach.
Adjusting the buttons semanticContentAttribute gives us the image nicely to the right without having to relayout if the text changes. Because of this it's the ideal solution. However I still need RTL support. The fact that an app can not change it's layout direction in the same session resolves this issue easily.
With that said, it's pretty straight forward.
extension UIButton {
func alignImageRight() {
if UIApplication.shared.userInterfaceLayoutDirection == .leftToRight {
semanticContentAttribute = .forceRightToLeft
}
else {
semanticContentAttribute = .forceLeftToRight
}
}
}
Swift -Extend the UiButton and put these lines
if let imageWidth = self.imageView?.frame.width {
self.titleEdgeInsets = UIEdgeInsetsMake(0, -imageWidth, 0, imageWidth);
}
if let titleWidth = self.titleLabel?.frame.width {
let spacing = titleWidth + 20
self.imageEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, -spacing);
}
Building on Piotr Tomasik's elegant solution: if you want to have a bit of spacing between the button label and image as well, then include that in your edge insets as follows (copying my code here that works perfectly for me):
CGFloat spacing = 3;
CGFloat insetAmount = 0.5 * spacing;
// First set overall size of the button:
button.contentEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, insetAmount);
[button sizeToFit];
// Then adjust title and image insets so image is flipped to the right and there is spacing between title and image:
button.titleEdgeInsets = UIEdgeInsetsMake(0, -button.imageView.frame.size.width - insetAmount, 0, button.imageView.frame.size.width + insetAmount);
button.imageEdgeInsets = UIEdgeInsetsMake(0, button.titleLabel.frame.size.width + insetAmount, 0, -button.titleLabel.frame.size.width - insetAmount);
Thanks Piotr for your solution!
Erik
Took #Piotr's answer and made it into a Swift extension. Make sure to set the image and title before calling this, so that the button sizes properly.
extension UIButton {
/// Makes the ``imageView`` appear just to the right of the ``titleLabel``.
func alignImageRight() {
if let titleLabel = self.titleLabel, imageView = self.imageView {
// Force the label and image to resize.
titleLabel.sizeToFit()
imageView.sizeToFit()
imageView.contentMode = .ScaleAspectFit
// Set the insets so that the title appears to the left and the image appears to the right.
// Make the image appear slightly off the top/bottom edges of the button.
self.titleEdgeInsets = UIEdgeInsets(top: 0, left: -1 * imageView.frame.size.width,
bottom: 0, right: imageView.frame.size.width)
self.imageEdgeInsets = UIEdgeInsets(top: 4, left: titleLabel.frame.size.width,
bottom: 4, right: -1 * titleLabel.frame.size.width)
}
}
}
With Xcode 13.3 I solved in the following few steps and as well adding padding to the image.
After creating the button then do this as listed below:
First define the image:
let symbol = UIImage(named: "put name of your symbol here")
Then in viewDidLoad where you created the button, initialise the above defined image in 1, to add the image to the button & set the properties:
button.setImage(symbol, for: .normal)
button.semanticContentAttribute = .forceRightToLeft
button.configuration?.imagePadding = 2
And don't forget to add your button to the view.
Subclassing and over-riding layoutSubviews is probably your best way to go.
Referenced from: iPhone UIButton - image position
A swift option that does what you want without playing with any insets:
class RightImageButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
if let textSize = titleLabel?.intrinsicContentSize(),
imageSize = imageView?.intrinsicContentSize() {
let wholeWidth = textSize.width + K.textImageGap + imageSize.width
titleLabel?.frame = CGRect(
x: round(bounds.width/2 - wholeWidth/2),
y: 0,
width: ceil(textSize.width),
height: bounds.height)
imageView?.frame = CGRect(
x: round(bounds.width/2 + wholeWidth/2 - imageSize.width),
y: RoundRetina(bounds.height/2 - imageSize.height/2),
width: imageSize.width,
height: imageSize.height)
}
}
struct K {
static let textImageGap: CGFloat = 5
}
}
Solutions mentioned here stopped working, once I enabled Auto Layout. I had to come up with my own:
Subclass UIButton and override layoutSubviews method:
//
// MIThemeButtonImageAtRight.m
// Created by Lukasz Margielewski on 7/9/13.
//
#import "MIThemeButtonImageAtRight.h"
static CGRect CGRectByApplyingUIEdgeInsets(CGRect frame, UIEdgeInsets insets);
#implementation MIThemeButtonImageAtRight
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect contentFrame = CGRectByApplyingUIEdgeInsets(self.bounds, self.contentEdgeInsets);
CGRect frameIcon = self.imageView.frame;
CGRect frameText = self.titleLabel.frame;
frameText.origin.x = CGRectGetMinX(contentFrame) + self.titleEdgeInsets.left;
frameIcon.origin.x = CGRectGetMaxX(contentFrame) - CGRectGetWidth(frameIcon);
self.imageView.frame = frameIcon;
self.titleLabel.frame = frameText;
}
#end
static CGRect CGRectByApplyingUIEdgeInsets(CGRect frame, UIEdgeInsets insets){
CGRect f = frame;
f.origin.x += insets.left;
f.size.width -= (insets.left + insets.right);
f.origin.y += (insets.top);
f.size.height -= (insets.top + insets.bottom);
return f;
}
Result:
swift 3.0 Migration
solution given by jasongregori
class ButtonIconRight: UIButton {
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
var imageFrame = super.imageRect(forContentRect: contentRect)
imageFrame.origin.x = super.titleRect(forContentRect: contentRect).maxX - imageFrame.width
return imageFrame
}
override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
var titleFrame = super.titleRect(forContentRect: contentRect)
if (self.currentImage != nil) {
titleFrame.origin.x = super.imageRect(forContentRect: contentRect).minX
}
return titleFrame
}
Xcode 11.4 Swift 5.2
For anyone trying to mirror the Back button style with the chevron like this:
import UIKit
class NextBarButton: UIBarButtonItem {
convenience init(target: Any, selector: Selector) {
// Create UIButton
let button = UIButton(frame: .zero)
// Set Title
button.setTitle("Next", for: .normal)
button.setTitleColor(.systemBlue, for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 17)
// Configure Symbol
let config = UIImage.SymbolConfiguration(pointSize: 19.0, weight: .semibold, scale: .large)
let image = UIImage(systemName: "chevron.right", withConfiguration: config)
button.setImage(image, for: .normal)
// Add Target
button.addTarget(target, action: selector, for: .touchUpInside)
// Put the Image on the right hand side of the button
// Credit to liau-jian-jie for this part
button.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
button.titleLabel?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
button.imageView?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
// Customise spacing to match system Back button
button.imageEdgeInsets = UIEdgeInsets(top: 0.0, left: -18.0, bottom: 0.0, right: 0.0)
button.titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -12.0, bottom: 0.0, right: 0.0)
self.init(customView: button)
}
}
Implementation:
override func viewDidLoad() {
super.viewDidLoad()
let nextButton = NextBarButton(target: self, selector: #selector(nextTapped))
navigationItem.rightBarButtonItem = nextButton
}
#objc func nextTapped() {
// your code
}
Swift 3:
open override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
var frame = super.imageRect(forContentRect: contentRect)
let imageWidth = frame.size.width
var titleRect = CGRect.zero
titleRect.size = self.title(for: self.state)!.size(attributes: [NSFontAttributeName: self.titleLabel!.font])
titleRect.origin.x = (self.frame.size.width - (titleRect.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
frame.origin.x = titleRect.origin.x + titleRect.size.width - self.imageEdgeInsets.right + self.imageEdgeInsets.left;
return frame
}
open override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
var frame = super.titleRect(forContentRect: contentRect)
if let imageWidth = self.image(for: self.state)?.size.width {
frame.origin.x = (self.frame.size.width - (frame.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
}
return frame
}
How about Constraints? Unlike semanticContentAttribute, they don't change semantics. Something like this perhaps:
button.rightAnchorconstraint(equalTo: button.rightAnchor).isActive = true
or in Objective-C:
[button.imageView.rightAnchor constraintEqualToAnchor:button.rightAnchor].isActive = YES;
Caveats: Untested, iOS 9+
After trying multiple solutions from around the internet, I was not achieving the exact requirement. So I ended up writing custom utility code. Posting to help someone in future.
Tested on swift 4.2
// This function should be called in/after viewDidAppear to let view render
func addArrowImageToButton(button: UIButton, arrowImage:UIImage = #imageLiteral(resourceName: "my_image_name") ) {
let btnSize:CGFloat = 32
let imageView = UIImageView(image: arrowImage)
let btnFrame = button.frame
imageView.frame = CGRect(x: btnFrame.width-btnSize-8, y: btnFrame.height/2 - btnSize/2, width: btnSize, height: btnSize)
button.addSubview(imageView)
//Imageview on Top of View
button.bringSubviewToFront(imageView)
}
for this issue you can create UIView inside "label with UIImage view" and set UIView class as a UIControl and create IBAction as tuch up in side
Swift 4 & 5
Change the direction of UIButton image (RTL and LTR)
extension UIButton {
func changeDirection(){
isArabic ? (self.contentHorizontalAlignment = .right) : (self.contentHorizontalAlignment = .left)
// left-right margin
self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
self.titleEdgeInsets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
}
}

Resources