Position of UIView in ScrollView is wrong - swift

I tried to add 4 views to scrollview to create horizontal paging view.
But I don't understand why between each view have white space which width equal width of each view. They should be close together.
walkthroughItems = {
let width = UIScreen.mainScreen().bounds.size.width
let height = UIScreen.mainScreen().bounds.size.height
return [
WalkthroughItem0(frame: CGRect(x: 0, y: 0, width: width,height: height)),
WalkthroughItem1(frame: CGRect(x: width, y: 0, width: width,height: height)),
WalkthroughItem2(frame: CGRect(x: width * 2, y: 0, width: width,height: height)),
WalkthroughItem3(frame: CGRect(x: width * 3, y: 0, width: width,height: height))
]
}()
for item in walkthroughItems {
scrollView.addSubview(item)
}
scrollView.contentSize = CGSize(width: UIScreen.mainScreen().bounds.size.width*4, height: UIScreen.mainScreen().bounds.size.height)

Related

Why the max value of cornerRadius is not half of the rectangle? Drawing roundedRect with UIBezierPath in swift

as the document says the cornerRadius can not larger than half width, but in my code it only 1/3 of the width the round rect become a circle, can't know why? need help
document: https://developer.apple.com/documentation/uikit/uibezierpath/1624356-init
cornerRadius:
The radius of each corner oval. A value of 0 results in a rectangle without rounded corners. Values larger than half the rectangle’s width or height are clamped appropriately to half the width or height.
var path = UIBezierPath(roundedRect: CGRect(x: 50, y: 200, width: 94, height: 94), cornerRadius: 31)
path.stroke()
path = UIBezierPath(roundedRect: CGRect(x: 50, y: 300, width: 94, height: 94), cornerRadius: 30.5)
path.stroke()
path = UIBezierPath(roundedRect: CGRect(x: 50, y: 400, width: 94, height: 94), cornerRadius: 31.5)
path.stroke()
I think it is because of a type casting error.
Try this.
let xCor: CGFloat = 50.0
let width: CGFloat = 94.0
let height: CGFloat = 94.0
let cornerRadius: CGFloat = width / 3.0
var path = UIBezierPath(roundedRect: CGRect(x: xCor, y: 200.0, width: width, height: height), cornerRadius: cornerRadius)
path.stroke()
path = UIBezierPath(roundedRect: CGRect(x: xCor, y: 300.0, width: width, height: height), cornerRadius: cornerRadius)
path.stroke()
path = UIBezierPath(roundedRect: CGRect(x: xCor, y: 400.0, width: width, height: height), cornerRadius: cornerRadius)
path.stroke()
PS: didnt check by running this code let me know if there is any problem here.

UILabel - custom borders on rounded corners left, top and right

I am looking at adding borders top left and top right on UILabels.
What working solution is best ?
I have tried :
func draw(_ rect: CGRect) {
let cgContext = UIGraphicsGetCurrentContext()
cgContext?.move(to: CGPoint(x: rect.minX, y: rect.minY))
cgContext?.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
cgContext?.setStrokeColor(UIColor.red.cgColor)
cgContext?.setLineWidth(2.0)
cgContext?.strokePath()
}
func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
let border = CALayer()
switch edge {
case .top:
border.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: thickness)
case .left:
border.frame = CGRect(x:0, y: self.frame.height - thickness, width: self.frame.width, height: thickness)
case .bottom:
border.frame = CGRect(x: 0, y: 0, width: thickness, height: self.frame.height)
case .right:
border.frame = CGRect(x: self.frame.width - thickness, y: 0, width: thickness, height: self.frame.height)
default: break
}
border.backgroundColor = color.cgColor
self.addSublayer(border)
}
func addBorder2(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
let border = CALayer()
border.backgroundColor = color.cgColor
//top
border.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: 2)
//left
border.frame = CGRect(x: 0, y: 0, width: thickness, height: 2)
//right
border.frame = CGRect(x: self.frame.width - thickness, y: 0, width: thickness, height: 2)
self.addSublayer(border)
/* SWITCH CASE NOT WORKING !!!
switch edge {
case .top:
border.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: 2)
case .bottom:
border.frame = CGRect(x: 0, y: self.frame.height - thickness, width: self.frame.width, height:2)
case .left:
border.frame = CGRect(x: 0, y: 0, width: thickness, height: 2)
case .right:
border.frame = CGRect(x: self.frame.width - thickness, y: 0, width: thickness, height: 2)
default: break
}
*/
}
Final result should be something like this below :
A border is needed around top left and top right. The bottom remains with no border.
You can set roundness to corners of any UIView using UIBezierPath.
setRoundness(ToCorners: [.topLeft, .bottomLeft], for: yourView, withRadius: customRadius)
Function:
func setRoundness(ToCorners corners: UIRectCorner, for view: UIView, withRadius radius: CGFloat) {
let path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.cgPath
view.layer.mask = mask
}

Swift UIView "Empty Image"

Could someone explain me the difference between these two pictures please?
Code with preview of the UIView:
Code without the preview of the UIView:
What's the difference? And why can't I get a preview on the second code example? It only shows "empty image"....
Thanks for helping me.
This seems to be a "feature" (bug) of a Swift playground. If you don't create the view instance using a non-zero frame width and height, you will get "empty image".
This works:
let rect = UIView(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
rect.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
rect.backgroundColor = .green
But this doesn't:
let rect = UIView(frame: .zero)
// also bad: let rect = UIView(frame: CGRect(x: 0, y: 0, width: 1, height: 0))
rect.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
rect.backgroundColor = .green
And remember that:
let rect = UIView()
is essentially the same as doing:
let rect = UIView(frame: .zero)
So when using a playground, create a view with a non-zero frame width and height in the initializer if you don't want to see "empty image".

swift drawing borders around textview or labels

myLabel.layer.borderWidth = 0.5
myLabel.layer.borderColor = UIColor.green.cgColor
Thats a simple answer of drawing a border. However the issue is I need to draw the border only to the right and bottom. I also need another label to draw border on top and right. How can I draw border to specific sides, not all 4 sides?
Add a shape layer and draw the lines in that layer. Note that layers do not participate in auto layout so you need to put your code in viewDidLayoutSubviews or subclass uilabel and do this in layout subviews. Here is a playground example using the UILabel subclass:
import PlaygroundSupport
import UIKit
class L: UILabel {
var strokeColor = UIColor.blue
var strokeWidth = CGFloat(0.5)
private lazy var labelBorderLayer:CAShapeLayer = {
let shapeLayer = CAShapeLayer()
self.layer.addSublayer(shapeLayer)
return shapeLayer
}()
override func layoutSubviews() {
super.layoutSubviews()
let path = CGMutablePath()
path.move(to: CGPoint(x: 0, y: bounds.size.height))
path.addLine(to: CGPoint(x: bounds.size.width, y: bounds.size.height))
path.addLine(to: CGPoint(x: bounds.size.width, y: 0))
labelBorderLayer.path = path
labelBorderLayer.strokeColor = strokeColor.cgColor
labelBorderLayer.lineWidth = strokeWidth
labelBorderLayer.fillColor = UIColor.clear.cgColor
}
}
let v = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
let l = L(frame: v.frame.insetBy(dx: 50, dy: 80))
v.addSubview(l)
l.textColor = .white
l.textAlignment = .center
l.text = "gjgkjgjgjgj"
v.backgroundColor = .red
PlaygroundPage.current.liveView = v
Im not sure if you can do that with the actual label border. But but you can do something similar to this
public enum UIButtonBorderSide {
case Top, Bottom, Left, Right
}
extension UIButton {
public func addBorder(side: UIButtonBorderSide, color: UIColor, width: CGFloat) {
let border = CALayer()
border.backgroundColor = color.CGColor
switch side {
case .Top:
border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: width)
case .Bottom:
border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: width)
case .Left:
border.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height)
case .Right:
border.frame = CGRect(x: self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height)
}
self.layer.addSublayer(border)
}
}
Thats for UIButton, But you can probably adapt it easily.
Source: https://gist.github.com/Isuru-Nanayakkara/496d5713e61125bddcf5
Hope this helps

swift label only border left

good morning together,
i have a tableview like this:
Example:
in cell one i have got an red text label on the right side.
left from it i include an image like a grey line.
with this code i can set a complete green border:
cell.Label.layer.borderWidth = 0.5
cell.Label.layer.borderColor = UIColor.greenColor().CGColor
can i set only a border on the left side from this text label?
i use swift ios8 - so, i need a swift solution
Here is an extension you can add to your project:
extension CALayer {
func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
var border = CALayer()
switch edge {
case UIRectEdge.Top:
border.frame = CGRectMake(0, 0, CGRectGetHeight(self.frame), thickness)
break
case UIRectEdge.Bottom:
border.frame = CGRectMake(0, CGRectGetHeight(self.frame) - thickness, UIScreen.mainScreen().bounds.width, thickness)
break
case UIRectEdge.Left:
border.frame = CGRectMake(0, 0, thickness, CGRectGetHeight(self.frame))
break
case UIRectEdge.Right:
border.frame = CGRectMake(CGRectGetWidth(self.frame) - thickness, 0, thickness, CGRectGetHeight(self.frame))
break
default:
break
}
border.backgroundColor = color.CGColor;
self.addSublayer(border)
}
}
And the use it like this:
cell.Label.layer.addBorder(UIRectEdge.Top, color: UIColor.greenColor(), thickness: 0.5)
FOR SWIFT 3, 4 & 5:
extension CALayer {
func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
let border = CALayer()
switch edge {
case UIRectEdge.top:
border.frame = CGRect(x: 0, y: 0, width: self.frame.height, height: thickness)
break
case UIRectEdge.bottom:
border.frame = CGRect(x: 0, y: self.frame.height - thickness, width: UIScreen.main.bounds.width, height: thickness)
break
case UIRectEdge.left:
border.frame = CGRect(x: 0, y: 0, width: thickness, height: self.frame.height)
break
case UIRectEdge.right:
border.frame = CGRect(x: self.frame.width - thickness, y: 0, width: thickness, height: self.frame.height)
break
default:
break
}
border.backgroundColor = color.cgColor;
self.addSublayer(border)
}
}
Swift 3 version:
extension CALayer {
func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
let border = CALayer()
switch edge {
case UIRectEdge.top:
border.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: thickness)
break
case UIRectEdge.bottom:
border.frame = CGRect(x: 0, y: self.frame.height - thickness, width: self.frame.width, height: thickness)
break
case UIRectEdge.left:
border.frame = CGRect(x: 0, y: 0, width: thickness, height: self.frame.height)
break
case UIRectEdge.right:
border.frame = CGRect(x: self.frame.width - thickness, y: 0, width: thickness, height: self.frame.height)
break
default:
break
}
border.backgroundColor = color.cgColor;
self.addSublayer(border)
}
}
Or use IBDesignable (see answer below)
Swift 4 Version:
extension CALayer {
func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
let border = CALayer()
switch edge {
case UIRectEdge.top:
border.frame = CGRect(x: 0, y: 0, width: self.bounds.width, height: thickness)
case UIRectEdge.bottom:
border.frame = CGRect(x: 0, y: self.bounds.height - thickness, width: self.bounds.width, height: thickness)
case UIRectEdge.left:
border.frame = CGRect(x: 0, y: 0, width: thickness, height: self.bounds.height)
case UIRectEdge.right:
border.frame = CGRect(x: self.bounds.width - thickness, y: 0, width: thickness, height: self.bounds.height)
default:
break
}
border.backgroundColor = color.cgColor;
self.addSublayer(border)
}
}
These solutions worked for me, however I was using tableview rows and the height of the row was dynamically set based on how much content was in the rows.
For the above solutions I found that if the size of the row was dynamically increased because of the content within the row, the border's size didn't increase. So I had an incomplete border on rows that were larger than normal.
I came across this solution from #Debaprio B at this link (Oct 11, 2017 post):
UIView set only side borders
His solution worked for me, and the border's sized changed appropriately when my row's size increased dynamically. I'm sharing #Debaprio's answer below for brevity:
public extension UIView {
// Border type and arbitrary tag values to identify UIView borders as subviews
public enum BorderType: Int {
case left = 20000
case right = 20001
case top = 20002
case bottom = 20003
}
public func addBorder(borderType: BorderType, width: CGFloat, color: UIColor {
// figure out frame and resizing based on border type
var autoresizingMask: UIViewAutoresizing
var layerFrame: CGRect
switch borderType {
case .left:
layerFrame = CGRect(x: 0, y: 0, width: width, height: self.bounds.height)
autoresizingMask = [ .flexibleHeight, .flexibleRightMargin ]
case .right:
layerFrame = CGRect(x: self.bounds.width - width, y: 0, width: width, height: self.bounds.height)
autoresizingMask = [ .flexibleHeight, .flexibleLeftMargin ]
case .top:
layerFrame = CGRect(x: 0, y: 0, width: self.bounds.width, height: width)
autoresizingMask = [ .flexibleWidth, .flexibleBottomMargin ]
case .bottom:
layerFrame = CGRect(x: 0, y: self.bounds.height - width, width: self.bounds.width, height: width)
autoresizingMask = [ .flexibleWidth, .flexibleTopMargin ]
}
// look for the existing border in subviews
var newView: UIView?
for eachSubview in self.subviews {
if eachSubview.tag == borderType.rawValue {
newView = eachSubview
break
}
}
Apart from using an extension, you can also use #IBDesignable / #IBInspectable to create a subclass from UILabel and draw the border there. This has some advantages, because you can then use the Interface Builder to style your label and it is shown directly in the Storyboard.
#IBDesignable
class LeftBorderedLabel: UILabel {
#IBInspectable var blockColor: UIColor = UIColor.black {
didSet{
let border = CALayer()
border.frame = CGRect(x: 0, y: 0, width: 15, height: self.frame.height)
border.backgroundColor = blockColor.cgColor;
self.layer.addSublayer(border)
}
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
}
}
Example in interface builder (example is for a tableViewCell):
Here is an extension you can add to your project.
SWIFT 3 :-
extension CALayer {
func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
let border = CALayer()
switch edge {
case UIRectEdge.top:
border.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: thickness)
break
case UIRectEdge.bottom:
border.frame = CGRect(x: 0, y: self.frame.height - thickness, width: self.frame.width, height: thickness)
break
case UIRectEdge.left:
border.frame = CGRect(x: 0, y: 0, width: thickness, height: self.frame.height)
break
case UIRectEdge.right:
border.frame = CGRect(x: self.frame.width - thickness, y: 0, width: thickness, height: self.frame.height)
break
default:
//For Center Line
border.frame = CGRect(x: self.frame.width/2 - thickness, y: 0, width: thickness, height: self.frame.height)
break
}
border.backgroundColor = color.cgColor;
self.addSublayer(border)
}
}
And the use it like this:
labelName.layer.addBorder(edge: UIRectEdge.right, color: UIColor.black, thickness: 1.5)
If you want to draw a line on Center, then you can set following frame in any of the scenario :-
border.frame = CGRect(x: self.frame.width/2 - thickness, y: 0, width: thickness, height: self.frame.height)