func addUnderlineForSelectedSegment() {
removeBorder()
let underlineWidth: CGFloat = self.bounds.size.width / CGFloat(self.numberOfSegments)
let underlineHeight: CGFloat = 3.0
let underlineXPosition = CGFloat(selectedSegmentIndex * Int(underlineWidth))
let underLineYPosition = self.bounds.size.height + 10.0
let underlineFrame = CGRect(x: underlineXPosition, y: underLineYPosition, width: underlineWidth, height: underlineHeight)
let underline = UIView(frame: underlineFrame)
// underline.backgroundColor = UIColor(red: 67/255, green: 129/255, blue: 244/255, alpha: 1.0)
print("underline:\(underline)")
underline.backgroundColor = .red
underline.tag = 1
self.addSubview(underline)
didAddSubview(underline)
}
I got a bug of the same kind with you.
It's work in older version but doesn't work in currently iOS 13.
Spend all day for fixing this stupid thing, and one line fixing everything.
Try to set clipsToBounds to false after removeBorder().
func addUnderlineForSelectedSegment(){
removeBorder()
clipsToBounds = false
let underlineWidth: CGFloat = Size.Screen.width / CGFloat(numberOfSegments)
let underlineHeight: CGFloat = 1.0
let underlineXPosition = CGFloat(selectedSegmentIndex * Int(underlineWidth))
let underLineYPosition = bounds.size.height - 1.0
let underlineFrame = CGRect(x: underlineXPosition, y: underLineYPosition, width: underlineWidth, height: underlineHeight)
let underline = UIView(frame: underlineFrame)
underline.backgroundColor = Color.default80.withAlphaComponent(0.8)
underline.tag = 1
addSubview(underline)
}
Related
I can't seem to get the CAEmitterLayer display CAEmitterCells. I add the ConfettiEmitterView in Storyboard and the view is being displayed with a red background as intended. But the cells are not showing. No Errors or warinings either.
The code:
import UIKit
class ConfettiEmitterView: UIView {
override class var layerClass:AnyClass {
return CAEmitterLayer.self
}
func makeEmmiterCell(color:UIColor, velocity:CGFloat, scale:CGFloat)-> CAEmitterCell {
let cell = CAEmitterCell()
cell.birthRate = 10
cell.lifetime = 20.0
cell.lifetimeRange = 0
cell.velocity = velocity
cell.velocityRange = velocity / 4
cell.emissionLongitude = .pi
cell.emissionRange = .pi / 8
cell.scale = scale
cell.scaleRange = scale / 3
cell.contents = roundImage(with: .yellow)
return cell
}
override func layoutSubviews() {
let emitter = self.layer as! CAEmitterLayer
emitter.frame = self.bounds
emitter.masksToBounds = true
emitter.emitterShape = .line
emitter.emitterPosition = CGPoint(x: bounds.midX, y: 0)
emitter.emitterSize = CGSize(width: bounds.size.width, height: 1)
emitter.backgroundColor = UIColor.red.cgColor // Setting the background to red
let near = makeEmmiterCell(color: UIColor(white: 1, alpha: 1), velocity: 100, scale: 0.3)
let middle = makeEmmiterCell(color: UIColor(white: 1, alpha: 0.66), velocity: 80, scale: 0.2)
let far = makeEmmiterCell(color: UIColor(white: 1, alpha: 0.33), velocity: 60, scale: 0.1)
emitter.emitterCells = [near, middle, far]
}
func roundImage(with color: UIColor) -> UIImage {
let rect = CGRect(origin: .zero, size: CGSize(width: 12.0, height: 12.0))
return UIGraphicsImageRenderer(size: rect.size).image { context in
context.cgContext.setFillColor(color.cgColor)
context.cgContext.addPath(UIBezierPath(ovalIn: rect).cgPath)
context.cgContext.fillPath()
}
}
}
Adding a view of type ConfettiEmitterView to the UIViewController in Storyboard should be enough to recreate the issue.
Change
cell.contents = roundImage(with: .yellow)
To
cell.contents = roundImage(with: .yellow).cgImage
I have a view that's got some CAShapeLayers on it. I'm lazily instantiating it as follows:
lazy var myViewWithLayersOnIt: UIView = {
let view = UIView(frame: CGRect(origin: CGPoint(x: 14,
y: 2),
size: CGSize(width: 10,
height: 10)))
view.translatesAutoresizingMaskIntoConstraints = false
let outerCircleLayer = CAShapeLayer()
let outerPath = UIBezierPath(ovalIn: view.frame).cgPath
outerCircleLayer.path = outerPath
outerCircleLayer.position = view.center
outerCircleLayer.fillColor = UIColor.white.cgColor
view.layer.addSublayer(outerCircleLayer)
let innerFrame = CGRect(origin: CGPoint(x: 1.5,
y: 1.5),
size: CGSize(width: 8.5,
height: 8.5))
let innerCircleLayer = CAShapeLayer()
let innerPath = UIBezierPath(ovalIn: innerFrame).cgPath
innerCircleLayer.path = innerPath
innerCircleLayer.position = view.center
innerCircleLayer.fillColor = UIColor.red.cgColor
view.layer.addSublayer(innerCircleLayer)
return view
}()
While everything that's supposed to render on the screen is rendering, the CAShapeLayers aren't going in the center of the view. I thought it might be due to lazily instantiating, so I ditched lazy and I see the exact same issue. What did I forget to do?
This is how it's rendering:
Thank you for reading. I welcome your input.
You should use view.bounds instead of view.frame for outerPath. No need to set the position of outerCircleLayer, nor innerCircleLayer.
lazy var myViewWithLayersOnIt: UIView = {
let view = UIView(frame: CGRect(origin: CGPoint(x: 14,
y: 2),
size: CGSize(width: 10,
height: 10)))
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.black
let outerCircleLayer = CAShapeLayer()
let outerPath = UIBezierPath(ovalIn: view.bounds).cgPath /// bounds!
outerCircleLayer.path = outerPath
// outerCircleLayer.position = view.center
outerCircleLayer.fillColor = UIColor.white.cgColor
view.layer.addSublayer(outerCircleLayer)
let innerFrame = CGRect(origin: CGPoint(x: 1.5,
y: 1.5),
size: CGSize(width: 8.5,
height: 8.5))
let innerCircleLayer = CAShapeLayer()
let innerPath = UIBezierPath(ovalIn: innerFrame).cgPath
innerCircleLayer.path = innerPath
// innerCircleLayer.position = view.center
innerCircleLayer.fillColor = UIColor.red.cgColor
view.layer.addSublayer(innerCircleLayer)
return view
}()
Result:
The red circle is off center because
let innerFrame = CGRect(origin: CGPoint(x: 1.5,
y: 1.5),
size: CGSize(width: 8.5,
height: 8.5))
should be
let innerFrame = CGRect(origin: CGPoint(x: 0.75,
y: 0.75),
size: CGSize(width: 8.5,
height: 8.5))
8.5 + 0.75 + 0.75 = 10, not 8.5 + 1.5 + 1.5 = 11.5
I created a custom UIButton class and it causing the storyboard agent to fail.
I'm Including my extensions cause I really don't know what the problem is.
I tried to debug this view from the storyboard but it sends me straight to assembly code.
I tried to make it a without #IBDesignable, but it still cause a crash.
Also if you tips for improving how I'm writing my class I'll be glad to hear them.
I'll be glad if you can help me
This is my class:
#IBDesignable class customButton: UIButton{
private let imagesPadding: CGFloat = 2
private var ArrowSymbleImageView: UIImageView!
#IBInspectable var iconImageInspectable: UIImage = UIImage(systemName: "globe")!{
willSet {
if (ArrowSymbleImageView != nil) {
ArrowSymbleImageView.image = newValue
}
}
}
#IBInspectable var BackgroundColorInspectable: UIColor = .white {
willSet {
self.backgroundColor = newValue
if (ArrowSymbleImageView != nil) {
if (self.BackgroundColorInspectable.isDarkColor) {
ArrowSymbleImageView.tintColor = .white
}else{
ArrowSymbleImageView.tintColor = .black
}
}
}
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.generalInit()
}
private func generalDeinit() {
ArrowSymbleImageView.removeFromSuperview()
}
private func generalInit() {
self.backgroundColor = self.BackgroundColorInspectable
self.roundCorners(corners: [.bottomLeft], radius: self.width() / 2 * 0.7)
self.dropShadow()
let sizePartFromView: CGFloat = 4
ArrowSymbleImageView = UIImageView(frame: CGRect(x: self.width() / 2 - (self.width() / sizePartFromView / 2),
y: self.height() / 2 - (self.height() / sizePartFromView / 2),
width: self.width() / sizePartFromView,
height: self.height() / sizePartFromView))
ArrowSymbleImageView.image = self.iconImageInspectable
if (self.BackgroundColorInspectable.isDarkColor) {
ArrowSymbleImageView.tintColor = .white
}else{
ArrowSymbleImageView.tintColor = .black
}
ArrowSymbleImageView.contentMode = .scaleAspectFill
self.addSubview(ArrowSymbleImageView)
}
}
internal extension UIView {
func roundCorners(corners: UIRectCorner, radius: CGFloat) {
let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.cgPath
layer.mask = mask
}
}
internal extension UIView {
func dropShadow(scale: Bool = true, size: CGSize = CGSize(width: -2, height: 2)) {
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.5
layer.shadowOffset = size
layer.shadowRadius = 1
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
func dropShadow(color: UIColor, opacity: Float = 0.5, offSet: CGSize, radius: CGFloat = 1, scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = color.cgColor
layer.shadowOpacity = opacity
layer.shadowOffset = offSet
layer.shadowRadius = radius
layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
}
internal extension UIColor
{
var isDarkColor: Bool {
var r, g, b, a: CGFloat
(r, g, b, a) = (0, 0, 0, 0)
self.getRed(&r, green: &g, blue: &b, alpha: &a)
let lum = 0.2126 * r + 0.7152 * g + 0.0722 * b
return lum < 0.50 ? true : false
}
}
I noticed this:
UIImage(systemName: "globe")!
Is there a reason why you instantiate a custom image this way? Is "globe" an apple provided default image?
You should be really using, if this isn't a default image.
UIImage(named:"globe")!
Dont use Force unwrapping when you do not have confirmation about data always use optional binding and do change following line in code from this UIImage(systemName: "globe")! to UIImage(named:"globe")! .
I am trying to display a fraction as something like 1/2 but I want to line to be horizontal so the fraction looks more fraction like, some thing like this:
1
_
2
However with no massive space and in a way that I can use it inside one UILabel (which can have multiple lines). I also need to be able to put a fraction as the numerator/denominator of a fraction and so on. Any suggestions? (Fonts, characters...)
Thank you,
Gleb Koval
Here's a UIView subclass that draws a fraction:
class FractionView: UIView {
var font: UIFont = UIFont.systemFont(ofSize: 16) {
didSet { self.setNeedsDisplay() }
}
var numerator: Int = 1 {
didSet { self.setNeedsDisplay() }
}
var denominator: Int = 2{
didSet { self.setNeedsDisplay() }
}
var spacing: CGFloat = 5{
didSet { self.setNeedsDisplay() }
}
override func draw(_ rect: CGRect) {
let numString = "\(numerator)" as NSString
let numWidth = numString.size(withAttributes: [.font: font]).width
let denomString = "\(denominator)" as NSString
let denomWidth = denomString.size(withAttributes: [.font: font]).width
let numX: CGFloat
let denomX: CGFloat
if numWidth <= denomWidth {
denomX = 0
numX = (denomWidth - numWidth) / 2
} else {
denomX = (numWidth - denomWidth) / 2
numX = 0
}
numString.draw(at: CGPoint(x: numX, y: 0), withAttributes: [.font : font])
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: font.lineHeight + spacing))
path.addLine(to: CGPoint(x: self.frame.maxX, y: font.lineHeight + spacing))
UIColor.black.setStroke()
path.lineWidth = 1
path.stroke()
denomString.draw(at: CGPoint(x: denomX, y: font.lineHeight + spacing * 2), withAttributes: [.font: font])
}
}
// usage:
let width = ("12" as NSString).size(withAttributes: [.font: UIFont.systemFont(ofSize: 16)]).width
let view = FractionView(frame: CGRect(x: 0, y: 0, width: width, height: 48))
view.backgroundColor = .white
view.denominator = 12
As Sulthan in comments has suggested, using an NSAttributedString would be an easier approach:
let attrString = NSMutableAttributedString(string: "1", attributes: [NSAttributedStringKey.underlineStyle : 1])
attrString.append(NSAttributedString(string: "\n2"))
attrString
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
label.attributedText = attrString
label.numberOfLines = 2
I'm trying to animate the bounds of a UIButton using Facebook pop POPSpringAnimation, but I can't find what I should use in swift in replace of NSValue.valueWithCGRect
This is what I'm trying to do:
#IBAction func buttonTapped(sender : UIButton) {
var springAnimation = POPSpringAnimation()
springAnimation.property = POPAnimatableProperty.propertyWithName(kPOPLayerBounds) as POPAnimatableProperty
springAnimation.springBounciness = 12.0
springAnimation.springSpeed = 10.0
springAnimation.toValue = NSValue.valueWithCGRect(newBounds)
shutterButton.pop_addAnimation(springAnimation, forKey: "size")
}
This will compile:
let rect = CGRect(x: 0,y: 0,width: 1,height: 1)
let val = NSValue(CGRect: rect)
This is "initializer syntax"
Here's my solution:
func scaleView(view: UIView, magnitude: CGFloat) {
var springAnimation = POPSpringAnimation()
springAnimation.property = POPAnimatableProperty.propertyWithName(kPOPViewScaleXY) as POPAnimatableProperty
springAnimation.springBounciness = 12.0
springAnimation.springSpeed = 10.0
let newRect = CGRect(x: (view.bounds.origin.x + (view.bounds.size.width / 2.0)) - ((view.bounds.size.width / 2.0) * magnitude), y: (view.bounds.origin.y + (view.bounds.size.height / 2.0)) - ((view.bounds.size.height / 2.0) * magnitude), width: view.bounds.size.width * magnitude, height: view.bounds.size.height * magnitude)
springAnimation.fromValue = NSValue(CGRect: view.bounds)
springAnimation.toValue = NSValue(CGRect: newRect)
view.pop_addAnimation(springAnimation, forKey: "size")
}
In Swift:
let hover = CABasicAnimation(keyPath: "position")
hover.additive = true
hover.fromValue = NSValue(CGPoint: CGPointZero)
hover.toValue = NSValue(CGPoint: CGPointMake(0.0, -10.0))
hover.autoreverses = true
hover.duration = 0.2
hover.repeatCount = Float(Int.max)
button.layer.addAnimation(hover, forKey: "myHoverAnimation")