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

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

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)

Blend UIView (Overlay) with app background

I would like to blend a UIView with my app's background, using a special blend mode (in my case, the Overlay mode). However, the view to blend is contained in a complex hierarchy of views.
Blending a view with its direct siblings can be achieved using view.layer.compositingFilter = "overlayBlendMode", but the view won't blend with non-siblings views, like the app background.
To recreate the problem, I made the following playground:
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let parentView = UIView()
parentView.backgroundColor = .purple
// Child view
let childView = UIView(frame: CGRect(x: 50, y: 50, width: 200, height: 200))
childView.layer.borderColor = UIColor.orange.cgColor
childView.layer.borderWidth = 3
parentView.addSubview(childView)
// Child child view
let childChildView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 50))
childChildView.backgroundColor = .white
childChildView.layer.compositingFilter = "overlayBlendMode"
childView.addSubview(childChildView)
self.view = parentView
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
We can see here that the child child view, in white, is not blended:
Whereas the view should appear blended like this (the border should not change color):
To create the second picture, I applied the compositing filter on the childView instead of the childChildView, which will blend all the other subviews — therefore it's not what I want. I just want this specific view to be blended.
Note: this view is supposed to move, because it's inside a UIScrollView.
EDIT: More complex example with image background and scrollviews
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let parentView = UIView()
// Background image
let backgroundImageView = UIImageView(image: UIImage(named: "image.jpg")!)
backgroundImageView.frame = UIScreen.main.bounds
parentView.addSubview(backgroundImageView)
// Page view (horizontal scrollview)
let pageView = UIScrollView(frame: CGRect(x: 50, y: 50, width: 200, height: 200))
pageView.contentSize = CGSize(width: 600, height: 200)
pageView.flashScrollIndicators()
pageView.layer.borderColor = UIColor.orange.cgColor
pageView.layer.borderWidth = 3
parentView.addSubview(pageView)
// Child view (vertical scrollview)
let childView = UIScrollView(frame: CGRect(x: 20, y: 20, width: 100, height: 150))
childView.contentSize = CGSize(width: 100, height: 300)
childView.layer.borderColor = UIColor.green.cgColor
childView.layer.borderWidth = 3
pageView.addSubview(childView)
// Child child view
let childChildView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 50))
childChildView.backgroundColor = .white
childChildView.layer.compositingFilter = "overlayBlendMode"
childView.addSubview(childChildView)
self.view = parentView
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
UPDATE 2:
I've tried several ways including adding layers or creating custom image filters that use the background image as input image but none of these solutions got the desired result. The main problem was always the view hierarchy.
I may have found a solution by using a generated image of the views or the actual background image as the content background of the childView once the childChildView is being created but before being displayed. I've changed your example code a bit to add a scroll view and background image in the parentView. See if this works for you / is your desired result:
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let parentView = UIView()
parentView.backgroundColor = .purple
let imageName = "image.jpg"
let image = UIImage(named: imageName)
let imageWidth = Int((image?.size.width)!)
let imageheight = Int((image?.size.height)!)
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: 50, y: 50, width: imageWidth , height: imageheight)
parentView.addSubview(imageView)
// Child view as UIScrollView
let childView = UIScrollView(frame: CGRect(x: 55, y: 55, width: imageWidth - 10, height: imageheight - 10 ))
childView.contentSize = CGSize(width: imageWidth - 10, height: 5000)
childView.layer.borderColor = UIColor.orange.cgColor
childView.flashScrollIndicators()
childView.layer.borderWidth = 10
parentView.addSubview(childView)
// ChildChild view
let childChildView = UIView(frame: CGRect(x: 15, y: 100, width: 85, height: imageheight - 180))
childChildView.layer.compositingFilter = "overlayBlendMode"
childChildView.backgroundColor = .white
//Creating a static image of the background views BEFORE adding the childChildView.
let format = UIGraphicsImageRendererFormat()
format.scale = 1
format.preferredRange = .standard ///color profile
///Change the imageView to the parentView size of the app. Not available if not set in the playground.
let renderer = UIGraphicsImageRenderer(size: imageView.bounds.size, format: format)
let imageBG = renderer.image { context in
///This draws all subviews of the parentView one after the other.
///Because the background image is not a parent of our current view, otherwise childView.drawHierachy would have been enough
for subview in parentView.subviews {
///Skip specific views or view classes you don't want to be added to the image. or if you only need the parentView itself rendered remove the for in loop.
subview.drawHierarchy(in: imageView.bounds, afterScreenUpdates: true)
}
}
//Adding the static background image. This could simply also be the actual image: UIImage if no other views are supposed to be used.
childView.layer.contents = imageBG.cgImage
childView.addSubview(childChildView)
self.view = parentView
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
It results in the following:
UPDATE:
The colors in the images are misleading, as you could assume a normal transparency effect would be the same. But the overlayBlendMode is quite different as Coconuts has pointed out. I assume the issue is that the compositingFilter only works with the view below, even if this view is transparent.
I tried finding a workaround by using a mask that cuts out a square of the size of the childchild from the childview. But this also didn't work as the mask is also applied to all subviews. The only way I got it to work is by making the childchildview a sibling of childview instead, or a direct subview of the background view. But not sure if this will be possible in the complex view hierarchy mentioned by Coconuts.
// Sibling view with adjusted x and y
let childChildView = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
childChildView.backgroundColor = .white
childChildView.layer.compositingFilter = "overlayBlendMode"
parentView.addSubview(childChildView)
MISC:
To only get the visual result of the sample images, not actually using the overlayBlendMode filter as asked by Coconut.
If you only need to blend the color you could change the alpha value of the color.
// Child child view
let childChildView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 50))
childChildView.backgroundColor = UIColor(white: 1, alpha: 0.5)
//childChildView.layer.compositingFilter = "overlayBlendMode"
childView.addSubview(childChildView)
Or try this:
// Child child view
let childChildView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 50))
childChildView.backgroundColor = .white
childChildView.layer.opacity = 0.5
childView.addSubview(childChildView)
ADDITIONAL ATTEMPTS WHEN HAVING SEVERAL SCROLL VIEWS:
This is an attempt to solve the from Coconut added more complicated view hierarchy with multiple scroll views. The performance needs to be improved or the part that adjusts the background image of the background image layer needs to run in sync when the app is updating (redrawing) its views. At the moment it's lagging behind a bit.
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let parentView = UIView()
// Background image
let backgroundImageView = UIImageView(image: UIImage(named: "image.jpg")!)
backgroundImageView.frame = UIScreen.main.bounds
parentView.addSubview(backgroundImageView)
// Page view (horizontal scrollview)
let pageView = UIScrollView(frame: CGRect(x: 50, y: 50, width: 200, height: 200))
pageView.contentSize = CGSize(width: 600, height: 200)
pageView.flashScrollIndicators()
pageView.layer.borderColor = UIColor.yellow.cgColor
pageView.layer.borderWidth = 3
pageView.clipsToBounds = true
parentView.addSubview(pageView)
// Child view (vertical scrollview)
let childView = UIScrollView(frame: CGRect(x: 20, y: 20, width: 100, height: 150))
childView.contentSize = CGSize(width: 100, height: 300)
childView.layer.borderColor = UIColor.red.cgColor
childView.layer.borderWidth = 3
pageView.addSubview(childView)
// Child child view
let childChildView = UIView(frame: CGRect(x: 50, y: 50, width: 50, height: 50))
//Child child view foreground sublayer
let childChildFrontLayer = CALayer()
childChildFrontLayer.frame = childChildView.frame.offsetBy(dx: -75, dy: -50)
childChildFrontLayer.backgroundColor = UIColor.white.cgColor
childChildFrontLayer.compositingFilter = "overlayBlendMode"
//Child child view background sublayer
let childChildBackLayer = CALayer()
childChildBackLayer.contents = UIImage(named: "image.jpg")?.cgImage
var absolutFrame = parentView.convert(childChildView.frame, from: childView)
childChildBackLayer.frame = CGRect(x: -absolutFrame.minX, y: -absolutFrame.minY, width: backgroundImageView.frame.width, height: backgroundImageView.frame.height)
childChildView.layer.addSublayer(childChildBackLayer)
childChildView.layer.addSublayer(childChildFrontLayer)
childView.addSubview(childChildView)
//Checking for any scrolling. Is slightly faster then the scollview delegate methods but might cause main thread checker warning.
DispatchQueue.global(qos: .userInteractive).async {
while true {
if pageView.isDragging || pageView.isTracking || pageView.isDecelerating || childView.isDragging || childView.isTracking || childView.isDecelerating {
absolutFrame = parentView.convert(childChildView.frame, from: childView)
DispatchQueue.main.async {
childChildBackLayer.frame = CGRect(x: -absolutFrame.minX, y: -absolutFrame.minY, width: backgroundImageView.frame.width, height: backgroundImageView.frame.height)
}
}
}
}
self.view = parentView
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
Is the issue related to the child view background being clear and therefore 'blending' to give the white colour. Could set the child view background colour to be equal to the app background and then then just blend the childView within the childChildView?

Auto Resizing CAShapeLayer while Animating UIView

Apologies, if this question has already been answered elsewhere. I tried searching in multiple places but could not find a good solution. I am a beginner to Swift development.
As per the code below, I am creating a SubView, adding an oval ShapeLayer to it and then animating the SubView by moving its center and increasing its size.
The SubView is animating correctly, however the ShapeLayer inside the SubView is not changing size. I would like the Red Oval to increase in size, similar to the SubView. I would really appreciate it if could let me know what I am missing.
class playGroundView: UIView {
override func draw(_ rect: CGRect) {
// Add a blue rectangle as subview
let startFrame = CGRect(x: self.frame.midX, y: self.frame.midY, width: 10, height: 20)
self.addSubview(UIView(frame: startFrame))
self.subviews[0].backgroundColor = UIColor.blue
// Create red oval shape that is bounded by blue rectangle
// Add red oval shape as sub-layer to blue rectangle view
let subView = self.subviews[0]
let ovalSymbol = UIBezierPath(ovalIn: subView.bounds)
let shapeLayer = CAShapeLayer()
shapeLayer.path = ovalSymbol.cgPath
shapeLayer.fillColor = UIColor.red.cgColor
shapeLayer.strokeColor = UIColor.red.cgColor
subView.layer.addSublayer(shapeLayer)
// Animate movement and increase in size of blue rectangle view
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 3, delay: 0, options: [], animations: {
let endFrame = CGRect(x:self.frame.midX - 50, y:self.frame.midY - 50, width: 20, height: 40)
self.subviews[0].frame = endFrame
self.setNeedsLayout()
self.layoutIfNeeded()
})
}
}
Image of Incorrect Output
Ok, after spending more time researching and getting a better understanding of what goes inside a ViewController and UIView class, entering the following in those classes works for me:
Inside class ViewController: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
let startFrame = CGRect(x: playGround.frame.midX, y: playGround.frame.midY, width: 30, height: 60)
cardSubView = cardView(frame: startFrame)
playGround.addSubview(cardSubView!)
let endFrame = CGRect(x: playGround.frame.midX - 100, y: playGround.frame.midY - 100, width: 60, height: 120)
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 10, delay: 0, options: [], animations: {
self.cardSubView?.frame = endFrame
self.cardSubView?.layoutIfNeeded()
})
}
Inside a UIView class:
class cardView: UIView {
override func draw(_ rect: CGRect) {
let backGroundPath = UIBezierPath(rect: rect)
UIColor.blue.setFill()
backGroundPath.fill()
let ovalSymbol = UIBezierPath(ovalIn: rect)
UIColor.red.setFill()
ovalSymbol.fill()
}
}
This still results in a weird frame shadowing towards the end of the animation in my iPhone simulator, however when running on device there is no issue.

Text in UIView Border

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

Custom progress view

I want to do a custom progress view for my iOS app, with 2 dots. Here is my code:
import UIKit
#IBDesignable
class StepProgressView: UIView {
#IBInspectable var progress: Float = 0
var progressColor = UIColor.blackColor()
var bgColor = UIColor.whiteColor()
override func layoutSubviews() {
self.backgroundColor = UIColor.clearColor()
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
let height = frame.height-8
let circle1 = UIView(frame: CGRect(x: frame.width*(1/3), y: 0, width: frame.height, height: frame.height))
circle1.backgroundColor = bgColor
circle1.layer.cornerRadius = frame.height/2
addSubview(circle1)
let circle2 = UIView(frame: CGRect(x: frame.width*(2/3), y: 0, width: frame.height, height: frame.height))
circle2.backgroundColor = bgColor
circle2.layer.cornerRadius = frame.height/2
addSubview(circle2)
let bgView = UIView(frame: CGRect(x: height/2, y: 4, width: frame.width-height/2, height: height))
bgView.backgroundColor = bgColor
bgView.layer.cornerRadius = height/2
addSubview(bgView)
let progressView = UIView(frame: CGRect(x: 0, y: 4, width: frame.width*CGFloat(progress), height: height))
progressView.backgroundColor = progressColor
progressView.layer.cornerRadius = height/2
addSubview(progressView)
}
}
The result:
However, as you can see, the circles aren't "filled" when the progression pass over one of them, and I don't know how to do that. I could create another view but I don't know how to handle the corner radius.
Can you help me ?
Thanks

Resources