How to get left padding on UITextField leftView image? - ios

I am setting up a UIImageView as a leftView on a UITextField like so:
UIImageView *envelopeView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.height*.1, self.height*.1)];
envelopeView.image = [UIImage imageNamed:#"envelope.png"];
envelopeView.contentMode = UIViewContentModeScaleAspectFit;
envelopeView.bounds = CGRectInset(envelopeView.frame, 15, 10);
self.emailAddress.leftView = envelopeView;
self.emailAddress.leftViewMode = UITextFieldViewModeAlways;
which gets me the following:
As you can see the left size of the image goes right up to the left edge of the button even though I tried to set an inset. How can I move this envelope in so that it's got padding on all sides?
Update: I tried the proposed answer of changing the UIImageView frame like so, but the envelope is still lined up on the left side at the border of the UITextField:
CGFloat padding = 20;
UIImageView *envelopeView = [[UIImageView alloc] initWithFrame:CGRectMake(3*padding, padding, self.height*.1-padding, self.height*.1-padding)];

For Swift 3 Users
Here is what worked for me:
extension UITextField {
/// set icon of 20x20 with left padding of 8px
func setLeftIcon(_ icon: UIImage) {
let padding = 8
let size = 20
let outerView = UIView(frame: CGRect(x: 0, y: 0, width: size+padding, height: size) )
let iconView = UIImageView(frame: CGRect(x: padding, y: 0, width: size, height: size))
iconView.image = icon
outerView.addSubview(iconView)
leftView = outerView
leftViewMode = .always
}
}
test:
txOrigin.setLeftIcon(icon_location)
result:

For Swift 4.2 +
You can use this extension:
extension UITextField {
func leftImage(_ image: UIImage?, imageWidth: CGFloat, padding: CGFloat) {
let imageView = UIImageView(image: image)
imageView.frame = CGRect(x: padding, y: 0, width: imageWidth, height: frame.height)
imageView.contentMode = .center
let containerView = UIView(frame: CGRect(x: 0, y: 0, width: imageWidth + 2 * padding, height: frame.height))
containerView.addSubview(imageView)
leftView = containerView
leftViewMode = .always
}
}

you can simply try this:
UIImageView *envelopeView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 0, 30, 30)];
envelopeView.image = [UIImage imageNamed:#"comment-128.png"];
envelopeView.contentMode = UIViewContentModeScaleAspectFit;
UIView *test= [[UIView alloc]initWithFrame:CGRectMake(20, 0, 30, 30)];
[test addSubview:envelopeView];
[self.textField.leftView setFrame:envelopeView.frame];
self.textField.leftView =test;
self.textField.leftViewMode = UITextFieldViewModeAlways;

You can use this. Change your frame according to your need.
NSTextAttachment* placeholderImageTextAttachment = [[NSTextAttachment alloc] init];
placeholderImageTextAttachment.image = [UIImage imageNamed:#"Search"];
placeholderImageTextAttachment.bounds = CGRectMake(0, -2, 16, 16);
NSMutableAttributedString* placeholderImageString = [[NSAttributedString attributedStringWithAttachment:placeholderImageTextAttachment] mutableCopy];
NSMutableAttributedString* placeholderString = [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(#" Search", nil)];
[placeholderImageString appendAttributedString:placeholderString];
_txtFieldSearch.attributedPlaceholder = placeholderImageString;
_txtFieldSearch.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;

Related

How can I add padding to UIImageView that is inside of a rightView of a UITextField?

So I added a UIImageView to the right of my UITextField, but I need to add a little bit of padding to the right side so that it doesn't anchor all the way to the right. I tried adding a custom frame but that didn't work, so I'm not too sure how to go about getting that padding. Any help would be much appreaciated.
See TextField Example Here
let titleField : UITextField = {
let titleField = UITextField()
titleField.placeholder = "Title"
titleField.textAlignment = .center
titleField.backgroundColor = .white
titleField.addDoneCancelToolbar()
var imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 10, height: 10))
let image = UIImage(systemName: "exclamationmark.circle")?.withTintColor(.systemRed, renderingMode: .alwaysOriginal)
imageView.image = image
titleField.rightView = imageView
titleField.rightViewMode = .always
// titleField.rightView?.isHidden = true
return titleField
}()
Subclass UITextField and override https://developer.apple.com/documentation/uikit/uitextfield/1619638-rightviewrect.
Just add the extension :
extension UITextField {
func rightImage(_ image: UIImage?, imageWidth: CGFloat, padding: CGFloat) {
let imageView = UIImageView()
imageView.frame = CGRect(x: padding + 2, y: 0, width: imageWidth, height: frame.height)
imageView.contentMode = .scaleAspectFit
imageView.image = image
let containerView = UIView(frame: CGRect(x: 0, y: 0, width: imageWidth + padding , height: frame.height))
containerView.addSubview(imageView)
rightView = containerView
rightViewMode = .always
}
}
To use it :
if let image = UIImage(named: imagename + ".png") {
titlefield.rightImage(image, imageWidth: 30, padding: 5)
}

UINavigationItem is behind UINavigationBar

I've set up a UINavigationBar to have rounded bottom corners with shadow, but it removed my UINavigationItem. I've tried to set it back programatically but it sets it behind the top bar item.
class RoundedShadowCorners {
func shadowTopBar(_ topBar: UINavigationBar,_ offset: CGFloat,_ navigationItem: UINavigationItem){
topBar.isTranslucent = false
topBar.tintColor = UIColor.orange
topBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
topBar.shadowImage = UIImage()
topBar.backgroundColor = UIColor.white
let shadowView = UIView(frame: CGRect(x: 0, y: -offset, width: (topBar.bounds.width), height: (topBar.bounds.height) + offset))
shadowView.backgroundColor = UIColor.white
topBar.insertSubview(shadowView, at: 1)
let shadowLayer = CAShapeLayer()
shadowLayer.path = UIBezierPath(roundedRect: shadowView.bounds, byRoundingCorners: [.bottomLeft , .bottomRight , .topLeft], cornerRadii: CGSize(width: 20, height: 20)).cgPath
shadowLayer.fillColor = UIColor.white.cgColor
shadowLayer.shadowColor = UIColor.darkGray.cgColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowOffset = CGSize(width: 2.0, height: 2.0)
shadowLayer.shadowOpacity = 0.8
shadowLayer.shadowRadius = 2
shadowView.layer.insertSublayer(shadowLayer, at: 0)
topBar.prefersLargeTitles = true
topBar.topItem?.title = "HJFSKDJKA"
}
}
offset is view.safeAreaInsets.top! Picture attached, as you can see, the title is behind the layer.
Text is behind
As you can see, layer works
I have edited #Daljeet's code and thanks to that I've found the solution:
let titleLabelView = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 40))
titleLabelView.backgroundColor = UIColor.clear
titleLabelView.textAlignment = .center
titleLabelView.textColor = UIColor.black
titleLabelView.font = UIFont.systemFont(ofSize: 17)
titleLabelView.adjustsFontSizeToFitWidth = true
titleLabelView.text = navigationItem.title
let labelView: UIView = titleLabelView
topBar.insertSubview(labelView, aboveSubview: shadowView)
let yCoordPlaceholder = labelView.center.y
labelView.center = CGPoint(x: safeArea.width/2, y: yCoordNavPlaceholder)
Thanks again #Daljeet!
You can try below code:
// First create custom label
UILabel *titleLabelView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 40)];
[titleLabelView setBackgroundColor:[UIColor clearColor]];
[titleLabelView setTextAlignment: NSTextAlignmentCenter];
[titleLabelView setTextColor:[UIColor whiteColor]];
[titleLabelView setFont:[UIFont systemFontOfSize: 27]];
[titleLabelView setAdjustsFontSizeToFitWidth:YES];
titleLabelView.text = #"You text here";
// here set in navBar titleView
topBar.topItem.titleView = titleLabelView;
topBar.bringSubviewToFront(topBar.topItem!.titleView!)
Add this line of code after setting the title.

make everything clear except the fake border in swift

I'm implementing a "fake" border using a view with a background color so that the border doesn't cover another view. (as per this answer and the following code)
UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
backgroundView.backgroundColor = [UIColor blackColor]; /* I want this to be clear except for the part outside bView */
backgroundView.clipsToBounds = NO;
UIView *bView = [[UIView alloc] initWithFrame:CGRectInset(backgroundView.bounds, 3, 3)];
bView.backgroundColor = [UIColor redColor]; /* I want this to be clear */
UIView *cView = [[UIView alloc] initWithFrame:CGRectMake(-50, -50, 100, 100)];
cView.backgroundColor = [UIColor yellowColor];
[bView addSubview:cView];
[backgroundView addSubview:bView];
[self.window addSubview:backgroundView];
How can I have backgroundView be clear(transparent) except for the border and bview be completely transparent? If I set both color to clear, I will lose my border. I am using swift unlike the code example.
Thanks
This is what I actually want. The big box needs to be transparent except for the fake black border around it so that the text (and everything behind it) can show up.
You say you want this:
So now I will easily construct it, but I will make the third view white so we can see it (with an annotation that it should be clear):
self.view.backgroundColor = .gray
let borderView = UIView(frame:CGRect(x: 150, y: 150, width: 200, height: 200))
borderView.backgroundColor = .clear
borderView.layer.borderWidth = 3
self.view.addSubview(borderView)
let yellowView = UIView(frame:CGRect(x: 100, y: 100, width: 100, height: 100))
yellowView.backgroundColor = .yellow
self.view.addSubview(yellowView)
let clearView = UIView(frame:borderView.frame.insetBy(dx: 3, dy: 3))
clearView.backgroundColor = .white // should be .clear
self.view.addSubview(clearView)
Result:
Substitute .clear for .white to get the desired outcome.
Well, I can't give you definitive performance data, but I would expect this to give you better performance than multiple views to create a "fake" border...
class myView: UIView {
override func draw(_ rect: CGRect) {
UIColor.black.set()
let context = UIGraphicsGetCurrentContext()
context?.stroke(rect.insetBy(dx: 1.5, dy: 1.5), width: 3.0)
}
}
let backgroundView = myView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
backgroundView.backgroundColor = UIColor.clear
let cView = UIView(frame: CGRect(x: -50, y: -50, width: 100, height: 100))
cView.backgroundColor = UIColor.yellow
backgroundView.addSubview(cView)
self.view.addSubview(backgroundView)
Of course, if you really want to do this with subviews to create the frame, this will also do the job. It adds 4 subviews to the background view, one for each side of the rectangle:
let backgroundView = UIView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
backgroundView.backgroundColor = UIColor.clear
backgroundView.clipsToBounds = false
let bgvFrame = backgroundView.bounds
let leftEdge = UIView(frame: CGRect(x: 0, y: 0, width: 3, height: bgvFrame.size.height))
leftEdge.backgroundColor = UIColor.black
let topEdge = UIView(frame: CGRect(x: 0, y: 0, width: bgvFrame.size.width, height: 3))
topEdge.backgroundColor = UIColor.black
let rightEdge = UIView(frame: CGRect(x: bgvFrame.size.width - 3, y: 0, width: 3, height: bgvFrame.size.height))
rightEdge.backgroundColor = UIColor.black
let bottomEdge = UIView(frame: CGRect(x: 0, y: bgvFrame.size.height - 3, width: bgvFrame.size.width, height: 3))
bottomEdge.backgroundColor = UIColor.black
backgroundView.addSubview(leftEdge)
backgroundView.addSubview(topEdge)
backgroundView.addSubview(rightEdge)
backgroundView.addSubview(bottomEdge)
let cView = UIView(frame: CGRect(x: -50, y: -50, width: 100, height: 100))
cView.backgroundColor = UIColor.yellow
backgroundView.addSubview(cView)
self.view.addSubview(backgroundView)

UIscrollview not scrolling swift

I have the following problem, I have created view1 above view2
Then added scrollview to view1 , the scrollview contains some ui elements added by code
The problem that my scrollview is not scrolling, and the content of it appear above view 2 while I need it to be behind it and to be able to scroll it
screenshot is following
my code of adding UI elements is following
mainScrollView = UIScrollView(frame: CGRectMake(0, 0, self.view.frame.size.width, 1000))
let awesomeView2: UIImageView = UIImageView(frame: CGRectMake(0, 0, self.view.frame.size.width, 180))
awesomeView2.load(selectedItem.listingImage);
mainScrollView.addSubview(awesomeView2);
let title: UILabel = UILabel(frame: CGRectMake(0, 180, self.view.frame.size.width, 44));
title.text = selectedItem.listingTitle;
title.textAlignment = .Right;
mainScrollView.addSubview(title);
let catName: UILabel = UILabel(frame: CGRectMake(200, 224, 120, 21));
catName.text = selectedItem.catName
catName.textAlignment = .Right;
mainScrollView.addSubview(catName);
let datePublished: UILabel = UILabel(frame: CGRectMake(0, 224, 200, 21));
datePublished.text = selectedItem.dateCreated
datePublished.textAlignment = .Left
mainScrollView.addSubview(datePublished);
let grayColor = UIColor(red: 1, green: 165/255, blue: 0, alpha: 1)
let iconsView: UIView = UIView(frame: CGRectMake(0, 250, self.view.frame.size.width, 32));
iconsView.backgroundColor = grayColor
mainScrollView.addSubview(iconsView);
if (KhawaterDataManager.instance.banners.itemBanners.count > 0) {
let awesomeView3: UIImageView = UIImageView(frame: CGRectMake(0, 282, self.view.frame.size.width, 50))
awesomeView3.load(KhawaterDataManager.instance.banners.itemBanners[0].image);
mainScrollView.addSubview(awesomeView3);
}
// test
let catName1: UILabel = UILabel(frame: CGRectMake(0, 335, self.view.frame.size.width, 50));
catName1.text = selectedItem.catName
mainScrollView.addSubview(catName1);
let catName2: UILabel = UILabel(frame: CGRectMake(0, 385, self.view.frame.size.width, 50));
catName2.text = selectedItem.catName
mainScrollView.addSubview(catName2);
let catName3: UILabel = UILabel(frame: CGRectMake(0, 435, self.view.frame.size.width, 50));
catName3.text = selectedItem.catName
mainScrollView.addSubview(catName3);
mainScrollView.contentSize = CGSizeMake(self.view.frame.size.width, 1000)
mainScrollView.scrollEnabled = true;
mainView.addSubview(mainScrollView);
please anyone tell me what is the problem here ?
Please check this line
mainScrollView.contentSize = CGSizeMake(self.view.frame.size.width, 1000)
Why content height 1000. This is too much. This should be
mainScrollView.contentSize = CGSizeMake(self.view.frame.size.width, 435+50)
And another issue is that why set your scollView as sub view of mainView rather than self.view.
Also you might want to add all subviews (awesomeView2, title, catName, etc)
to container view, and then add container view to scroll view.
something like:
let containerView: UIView = {
let view = UIView()
view.addSubview(self.awesomeView2)
view.addSubview(self.title)
view.addSubview(self.catName)
view.addSubview(self.datePublished)
return view
}()
mainScrollView.addSubview(containerView)

Insets to UIImageView?

I would like to achieve :
Scale to fill mode of image view
Insets to UIImageView so that image
does not meet the edges of the UIImageView
Bottomline: I want to increase the touch area of the UIImageView without stretching the image beyond certain size.
Is it possible through storyboard ? If not can anyone tell how to do it programmatically ?
I can achieve similar functionality through a UIButton and setting image inset through storyboard but I can't find any such thing with UIImageView.
You can add inset to UIImage instead of UIImageView.
Swift 4
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.image = UIImage(named: "example")?.withAlignmentRectInsets(UIEdgeInsets(top: -4, left: 0, bottom: -4, right: 0))
Using the method given here :
UIImage *image= [MyUtil imageWithImage:[UIImage imageNamed:#"MyImage"] scaledToSize:CGSizeMake(80, 80)];
UIImageView* imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
imgView.contentMode = UIViewContentModeCenter;
[imgView setImage:image];
Now your insets become 100-80 i.e. 20 .
This is a workaround to provide insets to an UIImageView. Better answers are welcomed.
It looks like you want to have a touchable imageView, so why not using an UIButton with image and imageEdgeInsets?
UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom];
[b setFrame:someFrame];
[b setImage:img forState:UIControlStateNormal];
b.imageEdgeInsets = UIEdgeInsetsMake(20, 20, 20, 20);
[b addTarget:self action:#selector(imageClicked:) forControlEvents:UIControlEventTouchUpInside];
My Code
minus inset: smaller image
plus inset: larger image
Swift4
private lazy var imageView: UIImageView = {
let view = UIImageView()
view.contentMode = .scaleAspectFit
let insetValue: CGFloat = -8
view.image = image.withAlignmentRectInsets(UIEdgeInsets(top: insetValue, left: insetValue, bottom: insetValue, right: insetValue))
return view
}()
Do not suffer, try this:
After trying for a while I found this solution:
extension UIImage {
func withInset(_ insets: UIEdgeInsets) -> UIImage? {
let cgSize = CGSize(width: self.size.width + insets.left * self.scale + insets.right * self.scale,
height: self.size.height + insets.top * self.scale + insets.bottom * self.scale)
UIGraphicsBeginImageContextWithOptions(cgSize, false, self.scale)
defer { UIGraphicsEndImageContext() }
let origin = CGPoint(x: insets.left * self.scale, y: insets.top * self.scale)
self.draw(at: origin)
return UIGraphicsGetImageFromCurrentImageContext()?.withRenderingMode(self.renderingMode)
}
}
Usage example:
let margin:CGFloat = 16
self.imageView.image = image?.withInset(UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin))
Source
One option is to use withAlignmentRectInsets, but it doesn't work with the layer border.
Another way is to use resizableImage(withCapInsets: resizingMode: .stretch) where you'd want to use positive inset values for smaller images.
Sample code:
private func setupQRImageView() -> UIImageView {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.layer.borderColor = Default.imageBorderColor.cgColor
imageView.layer.borderWidth = 4.0
imageView.layer.cornerRadius = 6.0
let imageInset: CGFloat = 8.0
imageView.image = UIImage(named: "QR")?.resizableImage(withCapInsets: UIEdgeInsets(top: imageInset, left: imageInset, bottom: imageInset, right: imageInset), resizingMode: .stretch)
return imageView
}
Produces the following result:
You can add a UIView first and then you add your UIImageView inside the UIView with a padding of whatever you like, and if you want your gesture to get affected only in UIImageView, make it UserInteractionEnable to yes or if you want the gesture to be in the whole then you can add the gesture to the whole UIView
Try work around with UIImage within UIView like this ;

Resources