UITextField custom underline in Swift - ios

I have a question about making an underline on UITextField.
I am trying to make an underline with bar on each end as shown below.
I tried the following and got this one. There is no bar on the right end.
extension UITextField {
func underline() {
let borderWidth = CGFloat(1.0)
let endBorderHeight = CGFloat(10.0)
let bottom = CALayer()
bottom.frame = CGRect(
x: 1,
y: self.frame.height - borderWidth,
width: self.frame.width - 2,
height: borderWidth)
bottom.borderWidth = borderWidth
bottom.borderColor = UIColor.lightGrayColor().CGColor
let leftEndBorder = CALayer()
leftEndBorder.frame = CGRect(
x: 0,
y: self.frame.height - endBorderHeight,
width: borderWidth,
height: endBorderHeight)
leftEndBorder.borderWidth = borderWidth
leftEndBorder.borderColor = UIColor.lightGrayColor().CGColor
print(bottom.frame.width)
let rightEndBorder = CALayer()
rightEndBorder.frame = CGRect(
x: self.frame.width - 1,
y: self.frame.height - endBorderHeight,
width: borderWidth,
height: endBorderHeight)
rightEndBorder.borderWidth = borderWidth
rightEndBorder.borderColor = UIColor.lightGrayColor().CGColor
self.layer.addSublayer(leftEndBorder)
self.layer.addSublayer(bottom)
self.layer.addSublayer(rightEndBorder)
self.layer.masksToBounds = true
}
}
I could make the bar on left side but having trouble making the right side because of the wrong x position of the rightEndBorder probably?
Can anyone tell me what I am doing wrong??
----- edit
I tried to set the x-position of the rightEndBorder to 200 and it gave me the following.
But if I tried to set it to 300, I don't see it anymore.
----- edit
Checked if the entire textfield was shown on the screen.
----- edit
It was that the leading and trailing constraints that changed the width of the textField I guess.
----- Solution
The problem was that I had leading and trailing constraints on the textField and those constraints changed the width after the unline was inserted. After searching google, I figured that I had to make the underline after the constraints were applied which is in the function viewDidLayoutSubviews().

Re-typing from comments section as an answer.
The problem seems to be either that the view is getting cut off (thus you will not see the right border) or the right end border is being shifted farther right after setting. Reason for suspecting this is from your picture where the right border is placed at x = 200 because it is well past half along the bottom border line but should be only be 40% of the way along the line.
Update:
The correct answer to this question was the constraints imposed caused the textfield width to change.

Related

StackView - center buttons with text below image

I am using this class for center text below icon for UIButton:
#IBDesignable
class TopIconButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
let kTextTopPadding:CGFloat = 3.0;
var titleLabelFrame = self.titleLabel!.frame;
let labelSize = titleLabel!.sizeThatFits(CGSize(width: self.contentRect(forBounds: self.bounds).width, height: CGFloat.greatestFiniteMagnitude))
var imageFrame = self.imageView!.frame;
let fitBoxSize = CGSize(width: max(imageFrame.size.width, labelSize.width), height: labelSize.height + kTextTopPadding + imageFrame.size.height)
let fitBoxRect = self.bounds.insetBy(dx: (self.bounds.size.width - fitBoxSize.width)/2, dy: (self.bounds.size.height - fitBoxSize.height) / 2)
imageFrame.origin.y = fitBoxRect.origin.y
imageFrame.origin.x = fitBoxRect.midX - (imageFrame.size.width/2)
self.imageView!.frame = imageFrame;
// Adjust the label size to fit the text, and move it below the image
titleLabelFrame.size.width = labelSize.width
titleLabelFrame.size.height = labelSize.height
titleLabelFrame.origin.x = (self.frame.size.width / 2) - (labelSize.width / 2)
titleLabelFrame.origin.y = fitBoxRect.origin.y + imageFrame.size.height + kTextTopPadding
self.titleLabel!.frame = titleLabelFrame
self.titleLabel!.textAlignment = .center
}
}
It's working fine. My problem is when I start using in UIStackView. I am creating something similar to UITabBar so I have 5 buttons next to each other. I want to middle button be in exact center of screen but it's always several pixels to side. I check my constraints for UIStackView and it's okay. I remove all buttons exect middle one and then it's in exact middle. I tried different options for UIStackView (Fill, Centered, Fill Equally, etc.). Nothing helps. Do anyone has any idea? Each button has different icon (with different size) and texts are different too (free, premium, etc. - sometimes bigger then icon sometimes smaller), is this relevant?
Example of my StackView (red line shows where is middle):
The image size is going to be relevant here. Once you get all 5 images to be the same exact size you won't experience this issue anymore. Using 'fill equally' after doing that and you will get the result you want

How to draw a partial border?

Just as the following image, how to draw that black line?
I want the line to be drawn on layer, not on another view.
The problem I got is how to locate the bottom. Thanks for any suggestion.
Here i have used textfield same way you can use for button also
let borderOld = CALayer()
let width = CGFloat(1.5)
borderOld.frame = CGRect(x: 0, y: txtField.frame.size.height - width, width: txtField.frame.size.width, height: txtField.frame.size.height)
borderOld.borderWidth = width
txtField.layer.masksToBounds = true
txtField.layer.addSublayer(borderOld)

iOS - UITextField: extend the bottom line for ANY resolution

I created a UITextFiled with a bottom line using this:
let Bottomline CALayer = ()
bottomLine.frame CGRect = (x: 0, y: usernameTextField.frame.height-7, width: usernameTextField.frame.width, height: 1)
bottomLine.backgroundColor = UIColor.black.cgColor
TextField.borderStyle = UITextBorderStyle.none
TextField.layer.addSublayer (Bottomline)
and the result of an iPhone 6 (right) is this:
Ok.
✄------------------------
The problem is to run the same application on a Pro iPad, because the
bottom line does not extend following the UITextField, but is shorter
This is the result on iPad Pro:
I do not understand why the bottom line does not follow the UITextField. When I called the bottom line I defined as:
bottomLine.frame CGRect = (x: 0, y: usernameTextField.frame.height-7, width: usernameTextField.frame.width, height: 1)
I have specified that the length of the line at the bottom must be:
width: usernameTextField.frame.width
What's the problem?
EDIT 1: The contrains are correct, because the UITextField adapts to
all types of resolution
EDIT:2 Thanks Matt! Now work!!!
I do not understand why the bottom line does not follow the UITextField
Because it's a layer. Layers do not automatically change size when their superlayer (the text field) changes size.
So, you need to redraw the bottom line every time the text field changes size.
At the moment, though, you are configuring the "bottom line" layer in viewDidLoad. So you are basing it on the frame that the text field has at that moment. But the text field has not yet attained its real size. Then it does change size, and meanwhile your "bottom line" layer just sits there — so now it is the wrong size.
An easy solution is to subclass UITextField and redraw the line every time layoutSubviews is called:
class MyTextField : UITextField {
var lineLayer : CALayer?
override func layoutSubviews() {
super.layoutSubviews()
self.lineLayer?.removeFromSuperlayer()
let bottomLine = CALayer()
bottomLine.frame = CGRect(x: 0, y: self.bounds.height-7, width: self.bounds.width, height: 1)
bottomLine.backgroundColor = UIColor.black.cgColor
self.layer.addSublayer(bottomLine)
self.lineLayer = bottomLine
}
}
If your text field is a MyTextField, it will behave exactly as you desire.

UIScrollView starts out halfway down content [duplicate]

This question already has answers here:
Why is UIScrollView leaving space on top and does not scroll to the bottom
(13 answers)
Closed 6 years ago.
I am trying to implement a UIScrollView in a similar fashion to the featured banner at the top of the App Store. I am adding 3 views and then paging through them using the code below. When the controller loads however, it is started with the content down a bit. If I tap on the view the content goes back into where it should be. How can I fix this? I've trying setting the content offset to 0, I've tried manually scrolling to the origin rect, and I've tried putting my views in a content view, but nothing has worked.
featuredScrollView.alwaysBounceVertical = false
featuredScrollView.contentSize = CGSize(width: 3 * featuredScrollView.frame.size.width, height: featuredScrollView.frame.size.height)
let contentView = UIView(frame: CGRect(x: 0, y: 0, width: 3 * featuredScrollView.frame.size.width, height: featuredScrollView.frame.size.height))
featuredScrollView.addSubview(contentView)
for i in 0..<3
{
let testView = UIView(frame: CGRect(x: CGFloat(i) * featuredScrollView.bounds.size.width, y: 0, width: featuredScrollView.bounds.size.width, height: featuredScrollView.bounds.size.height))
testView.backgroundColor = .blue
testView.layer.borderColor = UIColor.white.cgColor
testView.layer.borderWidth = 1
contentView.addSubview(testView)
}
Did you try set automaticallyAdjustsScrollViewInsets to NO?

Swift Dynamically created UILabel shows up twice

I have written a custom graph UIView subclass, and I use it to graph some basic data, and insert some user-defined data. As a final step, I'd like to add a UILabel on top of the graph with the user-defined data-point called out.
I highlight the point, and then create and add the UILabel:
if(graphPoints[i] == highlightPoint){
var point2 = CGPoint(x:columnXPoint(i), y:columnYPoint(graphPoints[i]))
point2.x -= 8.0/2
point2.y -= 8.0/2
let circle2 = UIBezierPath(ovalInRect:
CGRect(origin: point2,
size: CGSize(width: 8.0, height: 8.0)))
highlightColor.setFill()
highlightColor.setStroke()
circle2.fill()
let circle = UIBezierPath(ovalInRect:
CGRect(origin: point,
size: CGSize(width: 5.0, height: 5.0)))
UIColor.whiteColor().setFill()
UIColor.whiteColor().setStroke()
circle.fill()
var pointLabel : UILabel = UILabel()
pointLabel.text = "Point = \(graphPoints[i])"
pointLabel.frame = CGRectMake(point2.x, point2.y, 100, 50)
self.addSubview(pointLabel)
} else {
let circle = UIBezierPath(ovalInRect:
CGRect(origin: point,
size: CGSize(width: 5.0, height: 5.0)))
UIColor.whiteColor().setFill()
UIColor.whiteColor().setStroke()
circle.fill()
}
This looks like it should work, but the UILabel is added twice. What am I doing wrong?
This code is probably in drawRect. You're doing subview-adding in drawRect which is incorrect. drawRect gets called at least twice as the view appears, and perhaps many times after that.
You should be overriding some early lifecycle method, like awakeFromNib() or the constructor. In that, construct your label and add it as a child view. This way the addSubView() happens once as it should. The label will be invisible having no contents, so don't worry about that.
In drawRect, just update all the necessary attributes of the label including the frame.
If you find you're actually needing lots of text "labels" that come and go, different quantity for every graph, you don't really want UILabels as subviews after all. Consider directly drawing text on screen with someString.drawAtPoint(...)

Resources