Text in UIView Border - uiview

I am currently working on a project in iOS (using XCode and Swift). I am trying to implement the following UITextFields for the login view:
I was thinking of different ways to go about doing this and they all seem complicated. It would be amazing if someone knows of a super easy way to do this or if there is already a cocoapod that can be used to create this TextView.
Here are a few ways I was thinking of doing it:
Just make a UITextField with a border and put a UILabel with a background matching the parent view's background, blocking out the part where "Login" and "Password" would show up. This would hide the border at these parts and would solve the issue. The problem with this approach is if the background is a gradient, pattern, or image. This can be seen in the following images:
If the user looks closely at the "EMAIL" and "PASSWORD" UILabels here it can be seen that it does not have a transparent background and that it has an set background color in order to block out the border of the UITextField.
Instead of doing this, I would like to actually stop the drawing of the border which brings me to a second possible method of implementation.
Using core graphics to manually draw the border of the UITextField, this would have to be dynamic since there can be different length strings ("Login) is 5 characters, "Password" is 8). This approach seems complicated because dealing with CoreGraphics can be annoying.
I wasn't able to come up with any other ways of implementing this but I'd appreciate it if there was a less cumbersome solution.

Try this extension. I have tried this and is working good.
extension UITextField {
func leftBorder() {
let leftBorder = CALayer()
leftBorder.frame = CGRect(x: CGFloat(0.0), y: CGFloat(0.0), width: CGFloat(1.0), height: CGFloat(self.frame.size.height))
leftBorder.backgroundColor = UIColor.black.cgColor
self.layer.addSublayer(leftBorder)
}
func rightBorder() {
let rightBorder = CALayer()
rightBorder.frame = CGRect(x: CGFloat(self.frame.size.width - 1), y: CGFloat(0.0), width: CGFloat(1.0), height: CGFloat(self.frame.size.height))
rightBorder.backgroundColor = UIColor.black.cgColor
self.layer.addSublayer(rightBorder)
}
func bottomBorder() {
let bottomBorder = CALayer()
bottomBorder.frame = CGRect(x: CGFloat(0.0), y: CGFloat(self.frame.size.height - 1), width: CGFloat(self.frame.size.width), height: CGFloat(1.0))
bottomBorder.backgroundColor = UIColor.black.cgColor
self.layer.addSublayer(bottomBorder)
}
func topBorder1() {
let topBorder = CALayer()
topBorder.frame = CGRect(x: CGFloat(0.0), y: CGFloat(0.0), width: CGFloat(25.0), height: CGFloat(1.0))
topBorder.backgroundColor = UIColor.black.cgColor
self.layer.addSublayer(topBorder)
}
func topBorder2(position: CGFloat) {
let width = CGFloat(self.frame.size.width - position)
let topBorder2 = CALayer()
topBorder2.frame = CGRect(x: position, y: CGFloat(0), width: width, height: CGFloat(1.0))
topBorder2.backgroundColor = UIColor.black.cgColor
self.layer.addSublayer(topBorder2)
}
}
Call those extension methods in viewDidLayoutSubviews method like this..
override func viewDidLayoutSubviews() {
loginTextField.leftBorder()
loginTextField.rightBorder()
loginTextField.bottomBorder()
loginTextField.topBorder1()
let position = CGFloat(25 + loginLabel.frame.size.width + 10)
loginTextField.topBorder2(position: position)
}
This is how the initial story board looks like. I used a textfield and then placed a label above that textfield.
Note: I have used the label's width for some calculation.
And the result in the simulator is

I believe one method of doing this is by drawing the individual borders:
extension UITextField {
func border(position: CGFloat) {
let leftBorder = CALayer()
leftBorder.frame = CGRect(x: CGFloat(0.0), y: CGFloat(0.0), width: CGFloat(1.0), height: CGFloat(self.frame.size.height))
leftBorder.backgroundColor = UIColor.black.cgColor
self.layer.addSublayer(leftBorder)
let rightBorder = CALayer()
rightBorder.frame = CGRect(x: CGFloat(self.frame.size.width - 1), y: CGFloat(0.0), width: CGFloat(1.0), height: CGFloat(self.frame.size.height))
rightBorder.backgroundColor = UIColor.black.cgColor
self.layer.addSublayer(rightBorder)
let bottomBorder = CALayer()
bottomBorder.frame = CGRect(x: CGFloat(0.0), y: CGFloat(self.frame.size.height - 1), width: CGFloat(self.frame.size.width), height: CGFloat(1.0))
bottomBorder.backgroundColor = UIColor.black.cgColor
self.layer.addSublayer(bottomBorder)
let topBorder = CALayer()
topBorder.frame = CGRect(x: CGFloat(0.0), y: CGFloat(0.0), width: CGFloat(25.0), height: CGFloat(1.0))
topBorder.backgroundColor = UIColor.black.cgColor
self.layer.addSublayer(topBorder)
let width = CGFloat(self.frame.size.width - position)
let topBorder2 = CALayer()
topBorder2.frame = CGRect(x: position, y: CGFloat(0), width: width, height: CGFloat(1.0))
topBorder2.backgroundColor = UIColor.black.cgColor
self.layer.addSublayer(topBorder2)
}

I do not think it is possible to do this with a corner radius. The only way that I can imagine doing this is by going into CoreGraphics, which is usually more work than it is worth. Take a look here

Related

how to programmatically show a colored disk behind a UIImageView, in swift on an iOS device

I have an icon that I programmatically display.
How can I display a solid, colored disk behind the icon, at the same screen position?
Icon is opaque.
I will sometimes programmatically change screen position and disk's diameter and color.
Here's how I programmatically display the icon now.............
DispatchQueue.main.async
{
ViewController.wrist_band_UIImageView.frame = CGRect( x: screen_position_x,
y: screen_position_y,
width: App_class.screen_width,
height: App_class.screen_height)
}
UPDATE #1 for D.Mika below...
UPDATE #2 for D.Mika below...
import UIKit
import Foundation
class ViewController: UIViewController
{
static var circleView: UIView!
static let wrist_band_UIImageView: UIImageView = {
let theImageView = UIImageView()
theImageView.image = UIImage( systemName: "applewatch" )
theImageView.translatesAutoresizingMaskIntoConstraints = false
return theImageView
}()
override func viewDidLoad()
{
super.viewDidLoad()
view.addSubview( ViewController.wrist_band_UIImageView )
// Init disk image:
ViewController.circleView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
ViewController.circleView.backgroundColor = .red
ViewController.circleView.layer.cornerRadius = view.bounds.size.height / 2.0
ViewController.circleView.clipsToBounds = true
// Add view to view hierarchy below image view
ViewController.circleView.insertSubview(view, belowSubview: ViewController.wrist_band_UIImageView)
App_class.display_single_wearable()
}
}
class App_class
{
static var is_communication_established = false
static var total_packets = 0
static func display_single_wearable()
{
ViewController.wrist_band_UIImageView.frame = CGRect(x: 0, y: 0,
width: 100, height: 100)
ViewController.circleView.frame = CGRect( x: 0,
y: 0,
width: 100,
height: 100)
ViewController.circleView.layer.cornerRadius = 100
}
static func process_location_xy(text_location_xyz: String)
{}
} // App_class
You can display a colored circle using the following view:
circleView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
circleView.backgroundColor = .red
circleView.layer.cornerRadius = view.bounds.size.height / 2.0
circleView.clipsToBounds = true
// Add view to view hierarchy below image view
view.insertSubview(circleView, belowSubview: wrist_band_UIImageView)
You can change it's position and size by applying a new frame. But you have to change the layer's cornerRadius accordingly.
This is a simple example in a playground:
Edit:
I've modified the sample code to add the view the view hierarchy.
BUT, you still need to adjust the circle's frame, when you modify the image view's frame. (and the cornerRadius accordingly). For example
DispatchQueue.main.async
{
ViewController.wrist_band_UIImageView.frame = CGRect( x: screen_position_x,
y: screen_position_y,
width: App_class.screen_width,
height: App_class.screen_height)
circleView.frame = CGRect( x: screen_position_x,
y: screen_position_y,
width: App_class.screen_width,
height: App_class.screen_height)
circleView.layer.cornerRadius = App_class.screen_width
}
And you need to add a variable to the viewController holding the reference to the circle view, like:
var circleView: UIView!
The following answer works.
The answers from d.mika above did not work. Plus, he was insulting. ;-)
// Show red circle
let circlePath = UIBezierPath(arcCenter: CGPoint(x: 200, y: 400), radius: CGFloat(40), startAngle: CGFloat(0), endAngle: CGFloat(Double.pi * 2), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
// Change the fill color
shapeLayer.fillColor = UIColor.red.cgColor
// You can change the stroke color
shapeLayer.strokeColor = UIColor.red.cgColor
// You can change the line width
shapeLayer.lineWidth = 3.0
view.layer.addSublayer(shapeLayer)

draw a top border on UIScrollView

I have a horizontally scrollable collectionView and the thing I want to do is to draw a simple top border.I tried to do it like so but with no success and cant figure out why it doesn't work
var hourlyCollection: UICollectionView!
let border = CALayer()
border.backgroundColor = UIColor.lightGray.cgColor
border.frame = CGRect(x: 0, y: 0, width: hourlyCollection.contentSize.width, height: 1)
hourlyCollection.layer.addSublayer(border)
override func viewDidLoad() {
super.viewDidLoad()
//TOP BORDER ON COLLECTION VIEW
let border = CALayer()
border.frame = CGRect(x: 0, y: 0, width: self.collectionVIEW.frame.width,
height: 1)
border.backgroundColor = UIColor.black.cgColor
collectionVIEW.layer.addSublayer(border)
}

IOS - How to create Facebook reaction bar with blur background?

Although it may not be the week to replicate some design of Facebook, I would like to be able to design my own version of the reaction indicator view below.
I have three UIImageViews lined in the same positions as above. The problem is that, unlike Facebook, the background color may change (i.e is on top of a UIBlurEffect) and therefore I am unable to set the border color to white.
I thought it would make sense to set the borderColor like so:
imageViewOne.layer.borderColor = UIColor.clear.cgColor
imageViewOne.layer.borderWidth = 2
However, the underlying imageViewTwo is displayed in the border instead of the background color.
So far, I have this:
Would appreciate some help/ideas on how to make this work - I'm thinking of masks but not sure whether a. this is the correct solution and b. how to achieve the desired effect. To clarify, I am not able to set the border color as a constant as it will change with the UIBlurEffect.
In my opinion, there are 2 way to resolve your problem.
Create and use clipped image for Wow and Love like below Love image.
Another way is using mask property of UIView. Creating a mask image and apply it for mask property.
Mask image looks like.
Code for applying mask.
let imvLoveMask = UIImageView.init(image: UIImage.init(named: "MASK_IMAGE_NAME"));
imvLoveMask.frame = CGRect.init(x: 0, y: 0, width: imvLove.frame.size.width, height: imvLove.frame.size.height);
imvLove.mask = imvLoveMask;
Both of 2 above way can help you achieve what you want in the question. Background of icons in below image is an UIVisualEffectView.
In my opinion, the first way with clipped image is better and faster because you don't need to apply mask for your imageView. But if you don't want to create a clipped image for some reason, you can use the second way.
For more detail, you can take a look at my demo repo
You need to clip part of the image in order to let underlying content be visible in the gaps between images. See playground sample.
Add smile_1, smile_2, smile_3 images to playground resources. I took emoji images from https://emojipedia.org/facebook/.
import UIKit
import PlaygroundSupport
class EmojiView: UIView {
var imageView = UIImageView()
var imageInset = UIEdgeInsets(top: 3.0, left: 3.0, bottom: 3.0, right: 3.0)
var shapeLayer = CAShapeLayer()
var overlap: CGFloat = 0.0 {
didSet {
self.updateShape()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// MARK: UIView
override func awakeFromNib() {
super.awakeFromNib()
self.setup()
}
override func layoutSubviews() {
super.layoutSubviews()
self.imageView.frame = UIEdgeInsetsInsetRect(self.bounds, self.imageInset)
self.shapeLayer.frame = self.bounds
self.updateShape()
}
// MARK: Private
private func setup() {
self.addSubview(self.imageView)
self.layer.mask = self.shapeLayer
}
private func updateShape() {
let frame = self.bounds
let path = UIBezierPath(rect: frame)
// Cut off part of the image if overlap more then > 0
if 0 < self.overlap {
path.usesEvenOddFillRule = true
path.append(UIBezierPath(ovalIn: CGRect(x: -frame.width + self.overlap, y: 0.0, width: frame.width, height: frame.height)).reversing())
}
self.shapeLayer.path = path.cgPath
}
}
let overlap: CGFloat = 10 // Amount of pixels emojis overlap each other
// Create emoji views
let emojiView_1 = EmojiView(frame: CGRect(x: 5.0, y: 5.0, width: 40.0, height: 40.0))
emojiView_1.imageView.image = UIImage(named: "smile_1")
let emojiView_2 = EmojiView(frame: CGRect(x: emojiView_1.frame.maxX - overlap, y: 5.0, width: 40.0, height: 40.0))
emojiView_2.imageView.image = UIImage(named: "smile_2")
emojiView_2.overlap = overlap
let emojiView_3 = EmojiView(frame: CGRect(x: emojiView_2.frame.maxX - overlap, y: 5.0, width: 40.0, height: 40.0))
emojiView_3.imageView.image = UIImage(named: "smile_3")
emojiView_3.overlap = overlap
let holderView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: emojiView_3.frame.maxX + 5, height: 50.0))
// Add gradient layer
let gradientLayer = CAGradientLayer()
gradientLayer.frame = holderView.bounds
gradientLayer.colors = [UIColor.red.cgColor, UIColor.green.cgColor]
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
holderView.layer.addSublayer(gradientLayer)
// Add emoji views
holderView.addSubview(emojiView_1)
holderView.addSubview(emojiView_2)
holderView.addSubview(emojiView_3)
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = holderView
use this :
self.imageViewOne.layer.cornerRadius = self.imageViewOne.layer.bounds.width/2
self.imageViewOne.layer.masksToBounds = true
Simple suggestion: As you are setting border color programatically, you have a control to change it, according to background color (if background color is solid (not a gradient)).
imageViewOne.layer.borderColor = imageViewOne.superview?.backgroundColor ?? UIColor.white
imageViewOne.layer.borderWidth = 2.0
Actually instead of masking, you can put your images in a view which has white background and round(set corner radius). Then you can put these views (which has white background and images in it) via settings their zPosition or on storyboard with view hierarchy.
I've prepared a little playground for you. You can see the result in the screenshot. I've put a view inside the containerViews instead you can use uiimageview etc. It's a bit ugly but solves your issue I guess it's up to you to decide how use it.
Here is the code, you can just copy and paste it to a new playground and test it.
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
var mainContainerView = UIView(frame: CGRect(x: 0, y: 0, width: 140, height: 80))
mainContainerView.backgroundColor = UIColor.white
var containerView = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
containerView.backgroundColor = UIColor.white
containerView.layer.cornerRadius = containerView.frame.width / 2
var innerView = UIView(frame: CGRect(x: 10, y: 10, width: 60, height: 60))
innerView.backgroundColor = UIColor.red
innerView.layer.cornerRadius = innerView.frame.width / 2
containerView.addSubview(innerView)
var containerView2 = UIView(frame: CGRect(x: 60, y: 0, width: 80, height: 80))
containerView2.backgroundColor = UIColor.yellow
containerView2.layer.cornerRadius = containerView2.frame.width / 2
var innerView2 = UIView(frame: CGRect(x: 10, y: 10, width: 60, height: 60))
innerView2.backgroundColor = UIColor.green
innerView2.layer.cornerRadius = innerView2.frame.width / 2
containerView2.addSubview(innerView2)
containerView.layer.zPosition = 2
containerView2.layer.zPosition = 1
mainContainerView.addSubview(containerView)
mainContainerView.addSubview(containerView2)
PlaygroundPage.current.liveView = mainContainerView

Creating two textfields with only bottom border swift

I'm trying to create two UITextFields that both only have the bottom border, I got some code from another StackOverflow and it worked. However, when I copy and pasted the code again for my second TextField it only shows one TextField with the bottom border.
Here is the code:
let border = CALayer()
let width = CGFloat(2.0)
border.borderColor = UIColor.gray.cgColor
border.frame = CGRect(x: 0, y: emailText.frame.size.height -
width, width: emailText.frame.size.width, height:
emailText.frame.size.height)
border.frame = CGRect(x: 0, y: passwordText.frame.size.height
- width, width: passwordText.frame.size.width, height:
passwordText.frame.size.height)
border.borderWidth = width
passwordText.layer.addSublayer(border)
passwordText.layer.masksToBounds = true
border.borderWidth = width
emailText.layer.addSublayer(border)
emailText.layer.masksToBounds = true
Thanks for the help!
CALayers are instance-based, that means if you added the layer to a view, you cannot add it to a new one without removing it from the first view. Create two instances of CALayers or better yet, make an extension that produces the desired result.
Create separate border object for email textfield and username textfield.
let emailborder = CALayer()
let passwordborder = CALayer()
let width = CGFloat(2.0)
emailborder.borderColor = UIColor.gray.cgColor
emailborder.frame = CGRect(x: 0, y: emailText.frame.size.height -
width, width: emailText.frame.size.width, height:
emailText.frame.size.height)
passwordborder.frame = CGRect(x: 0, y: passwordText.frame.size.height
- width, width: passwordText.frame.size.width, height:
passwordText.frame.size.height)
passwordborder.borderWidth = width
passwordText.layer.addSublayer(passwordborder)
passwordText.layer.masksToBounds = true
emailborder.borderWidth = width
emailText.layer.addSublayer(emailborder)
emailText.layer.masksToBounds = true
You cant use the same view for both textfield. Use a copy of border for the second one. Easiest way to make this work is to refine a variable as border2 and set it up and sublayer it. Don't forget to remove your duplicate frame assign for boarder.
let border = CALayer()
let width = CGFloat(2.0)
border.borderColor = UIColor.gray.cgColor
border.frame = CGRect(x: 0, y: emailText.frame.size.height - width, width: emailText.frame.size.width, height: emailText.frame.size.height)
let border2 = CALayer() border2.borderColor = border.borderColor border2.frame = border.frame;
border.borderWidth = width
passwordText.layer.addSublayer(border)
passwordText.layer.masksToBounds = true
border.borderWidth = width
emailText.layer.addSublayer(border2)
emailText.layer.masksToBounds = true
The best you could without writing redundant code is that you create a function, pass the textfields you want to border as parameters and keep the styling code in there like so:
func setBorder(tf: UITextField) {
let border = CALayer()
let width = CGFloat(2.0)
border.borderColor = UIColor.gray.cgColor
border.frame = CGRect(x: 0, y: tf.frame.size.height - width,
width: tf.frame.size.width, height:
tf.frame.size.height)
border.borderWidth = width
tf.layer.addSublayer(border)
tf.layer.masksToBounds = true
}
Call this function in viewDidLoad:
setBorder(tf: emailText)
setBorder(tf: passwordText)
Hope this helps!

Circular transition (mask) in iOS and Objective C

We were wondering how we can easily build a circular mask, that blends out the background and transitions into a new view, with new buttons? See example here (watch the red planet gets triggered):
//Swift 4
This is a simple static method for circular bubble transition from a point screen.
//as shown on this link
https://github.com/andreamazz/BubbleTransition
import UIKit
class AnimationUtility: UIViewController {
static func animateBubbleTrnsitionView( selfView: UIView, point : CGPoint) {
//let button = CGRect.init(x: 30, y: selfView.frame.size.height - 15, width: 45, height: 45)
let button = CGRect.init(x: point.x, y: point.y, width: 0, height: 0)
let circleMaskPathInitial = UIBezierPath(ovalIn: CGRect.init(x: point.x, y: point.y, width: 1, height: 1))
let extremePoint = CGPoint(x: point.x, y: 15 - selfView.frame.size.height - 200)
let radius = sqrt((extremePoint.x*extremePoint.x) + (extremePoint.y*extremePoint.y))
let circleMaskPathFinal = UIBezierPath(ovalIn: (button.insetBy(dx: -radius, dy: -radius)))
let maskLayer = CAShapeLayer()
maskLayer.path = circleMaskPathFinal.cgPath
selfView.layer.mask = maskLayer
let maskLayerAnimation = CABasicAnimation(keyPath: "path")
maskLayerAnimation.fromValue = circleMaskPathInitial.cgPath
maskLayerAnimation.toValue = circleMaskPathFinal.cgPath
maskLayerAnimation.duration = 0.9
maskLayer.add(maskLayerAnimation, forKey: "path")
}
}
Use this code in following way
Perform segue or push without animation.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
switch segue.identifier! {
case "navToHomeWithoutanimation":
self.navigationController?.view.backgroundColor = APP_ORANGE_COLOR //which ever color you want
let vc = segue.destination as! MapViewController
AnimationUtility.animateBubbleTrnsitionView(selfView: vc.view, point: self.view.center)
break
}
}
//The animation will start to animate from given point.
Swift code :
I writted all code appdelegate file. Used a screenshot of timeline rather than a full-blown UITableView.
First, let’s add the screenshot on the window:
let imageView = UIImageView(frame: self.window!.frame)
imageView.image = UIImage(named: "twitterscreen")
self.window!.addSubview(imageView)
self.mask = CALayer()
self.mask!.contents = UIImage(named: "twitter logo mask").CGImage
self.mask!.bounds = CGRect(x: 0, y: 0, width: 100, height: 100)
self.mask!.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.mask!.position = CGPoint(x: imageView.frame.size.width/2, y: imageView.frame.size.height/2)
imageView.layer.mask = mask
animating the mask. You’ll observe that the bird reduces in size for a bit, and then increases in size, so to do that using just one animation, let’s use CAKeyframeAnimation.
let keyFrameAnimation = CAKeyframeAnimation(keyPath: "bounds")
keyFrameAnimation.duration = 1
keyFrameAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut), CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)]
let initalBounds = NSValue(CGRect: mask!.bounds)
let secondBounds = NSValue(CGRect: CGRect(x: 0, y: 0, width: 90, height: 90))
let finalBounds = NSValue(CGRect: CGRect(x: 0, y: 0, width: 1500, height: 1500))
keyFrameAnimation.values = [initalBounds, secondBounds, finalBounds]
keyFrameAnimation.keyTimes = [0, 0.3, 1]
self.mask!.addAnimation(keyFrameAnimation, forKey: "bounds")
If you see result please visit the github repo. https://github.com/rounak/TwitterBirdAnimation/

Resources