Reputation: 1446
I've written a model for my custom view. The view has caption with dropped shadow and in my example 4 strips bellow.
My problem is whenever I've created a model for view the shadow doesn't appear when I save the context - I want display shadow only bellow caption, not in each shape inside view.
Model :
import UIKit
protocol StripeDrawable {
func drawCaption<T: CGContext>(in rect: CGRect, using ctx: T)
func drawSripes<T: CGContext>(in rect: CGRect, using ctx: T)
}
struct Stripe {
var numberOfStripes: Int
var stripPattern: [UIColor]
}
struct Caption {
var captionFontSize: CGFloat
var captionHeight: CGFloat
var captionTitle: String
var captionBackgroundColor: UIColor
var captionTextColor: UIColor
var isCaptionShadowEnabled: Bool
}
class StripeAxis {
var stripe: Stripe
var caption: Caption
init(stripe: Stripe, caption: Caption) {
self.stripe = stripe
self.caption = caption
}
}
extension StripeAxis: StripeDrawable {
func drawCaption<T>(in rect: CGRect, using ctx: T) where T : CGContext {
let captionRect = CGRect(x: 0, y: 0, width: rect.width, height: caption.captionHeight)
ctx.addRect(captionRect)
ctx.saveGState()
if caption.isCaptionShadowEnabled {
ctx.setShadow(offset: CGSize(width: 0, height: 0), blur: 3, color: UIColor.black.cgColor)
}
ctx.setFillColor(caption.captionBackgroundColor.cgColor)
ctx.fill(captionRect)
let font = UIFont.boldSystemFont(ofSize: caption.captionFontSize)
let attribs = [NSAttributedStringKey.foregroundColor : caption.captionTextColor,
NSAttributedStringKey.font : font] as [NSAttributedStringKey : Any]
caption.captionTitle.center(in: captionRect, attribs: attribs as! [NSAttributedStringKey : NSObject])
ctx.restoreGState()
}
func drawSripes<T>(in rect: CGRect, using ctx: T) where T : CGContext {
let stripHeight: CGFloat = (rect.height - caption.captionHeight) / CGFloat(stripe.numberOfStripes)
for i in 0...stripe.numberOfStripes - 1 {
var color: UIColor!
let stripRect = CGRect(x: 0, y: stripHeight * CGFloat(i) + caption.captionHeight, width: rect.width, height: stripHeight)
if i % 2 == 0 {
color = stripe.stripPattern[0]
} else {
color = stripe.stripPattern[1]
}
ctx.setFillColor(color.cgColor)
ctx.fill(stripRect)
}
}
}
Custom view :
import UIKit
final class TimersStripesView: UIView {
var selectedIndex: Int = 0
var statusRects: [CGRect] = []
var onSelection: (() -> ())!
private let pad = UIDevice.current.userInterfaceIdiom == .pad
var stripeAxis: StripeAxis!
override init(frame: CGRect) {
super.init(frame: frame)
stripeAxis = StripeAxis(stripe: Stripe(numberOfStripes: 4, stripPattern: [.red, .blue]),
caption: Caption(captionFontSize: pad ? 15 : 11, captionHeight: pad ? 40 : 25, captionTitle: "TOTAL", captionBackgroundColor: .orange, captionTextColor: .white, isCaptionShadowEnabled: true))
contentMode = .redraw
// addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTouch(_:))))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let ctx = UIGraphicsGetCurrentContext() else { return }
stripeAxis.drawCaption(in: rect, using: ctx)
stripeAxis.drawSripes(in: rect, using: ctx)
}
}
As we can see shadow has dropped on caption titile, but it doesn't drop bellow rect.
Before changes when I had no model, simply when I did the same:
In draw:
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let ctx = UIGraphicsGetCurrentContext() else { return }
let captionRect = CGRect(x: 0, y: 0, width: rect.width, height: pad ? 40.0 : 25.0)
drawCaption(in: captionRect, using: ctx)
drawStrips(using: ctx, in: rect, captionHeight: pad ? 40.0 : 25.0)
}
And the method:
func drawCaption<T: CGContext>(in rect: CGRect, using ctx: T) {
ctx.addRect(rect)
ctx.saveGState()
ctx.setShadow(offset: CGSize(width: 0, height: 0), blur: 3, color: UIColor.black.cgColor)
ctx.setFillColor(UIColor.orange.cgColor)
ctx.fill(rect)
let statusText = "TOTAL"
let font = UIFont.boldSystemFont(ofSize: pad ? 15 : 11)
let attribs = [NSAttributedStringKey.foregroundColor : UIColor.white, NSAttributedStringKey.font: font]
statusText.center(in: rect, attribs: attribs)
ctx.restoreGState()
}
func drawStrips<T: CGContext>(using ctx: T, in rect: CGRect, captionHeight: CGFloat) {
let statusHeight: CGFloat = (rect.height - captionHeight) / 4
for i in 0...3 {
var color: UIColor!
let newRect = CGRect(x: 0, y: statusHeight * CGFloat(i) + captionHeight, width: rect.width, height: statusHeight)
if i % 2 == 0 {
color = .red
} else {
color = .blue
}
ctx.setFillColor(color.cgColor)
ctx.fill(newRect)
}
}
Was working with result :
I can't see where I've done mistake, any ideas?
Upvotes: 1
Views: 185
Reputation: 119242
Your previous code wasn't working (I ran it in a playground) - the shadow was also missing.
There's no problem with saving and restoring the graphics state. What's happening is simply that when you draw the stripes, they are drawn on top of the shadow from the orange box, so you can't see it any more. Simply change the order of the drawing calls so that the stripes are drawn before the caption, and it all works fine.
Upvotes: 1