From be4b7e68137e0363bbd9f434ba0aae219292f0e3 Mon Sep 17 00:00:00 2001 From: ReshabCT Date: Wed, 22 Apr 2026 16:56:40 +0530 Subject: [PATCH 1/8] SDK-5737: Add UI components for Timer template view for border color, radius and gradient effects --- .../project.pbxproj | 12 ++ .../CTTimerTemplateController.swift | 98 +++++++++++---- .../Timer/Model/TimerTemplateProperties.swift | 44 ++++++- .../Templates/Timer/View/CTTimerBoxView.swift | 117 ++++++++++++++++++ 4 files changed, 246 insertions(+), 25 deletions(-) create mode 100644 CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift diff --git a/CTNotificationContent.xcodeproj/project.pbxproj b/CTNotificationContent.xcodeproj/project.pbxproj index 8fa421b..22da571 100644 --- a/CTNotificationContent.xcodeproj/project.pbxproj +++ b/CTNotificationContent.xcodeproj/project.pbxproj @@ -46,6 +46,7 @@ 525715EE1E67B8C0000E455B /* CTNotificationContent.h in Headers */ = {isa = PBXBuildFile; fileRef = 525715EC1E67B8C0000E455B /* CTNotificationContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; 525715F71E67B990000E455B /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 525715F51E67B990000E455B /* UserNotifications.framework */; }; 525715F81E67B990000E455B /* UserNotificationsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 525715F61E67B990000E455B /* UserNotificationsUI.framework */; }; + 58614E962F98B4BA000D1C8A /* CTTimerBoxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58614E942F98B4BA000D1C8A /* CTTimerBoxView.swift */; }; A1B2C3D4E5F60001A1B2C3D4 /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = A1B2C3D4E5F60002A1B2C3D4 /* SDWebImage */; }; /* End PBXBuildFile section */ @@ -91,6 +92,7 @@ 525715EC1E67B8C0000E455B /* CTNotificationContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTNotificationContent.h; sourceTree = ""; }; 525715F51E67B990000E455B /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; }; 525715F61E67B990000E455B /* UserNotificationsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotificationsUI.framework; path = System/Library/Frameworks/UserNotificationsUI.framework; sourceTree = SDKROOT; }; + 58614E942F98B4BA000D1C8A /* CTTimerBoxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CTTimerBoxView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -222,6 +224,7 @@ 32DFD82F28BCCCBB00E72588 /* Timer */ = { isa = PBXGroup; children = ( + 58614E952F98B4BA000D1C8A /* View */, 32DFD83028BCCCBB00E72588 /* Controller */, 32DFD83228BCCCBB00E72588 /* Model */, ); @@ -426,6 +429,14 @@ name = Frameworks; sourceTree = ""; }; + 58614E952F98B4BA000D1C8A /* View */ = { + isa = PBXGroup; + children = ( + 58614E942F98B4BA000D1C8A /* CTTimerBoxView.swift */, + ); + path = View; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -548,6 +559,7 @@ 3270BFDA28E4D80E003528ED /* CTProductDisplayVerticalViewController.swift in Sources */, 32DFD84628BCCCBB00E72588 /* CTZeroBezelController.swift in Sources */, 488F31C12817012500AE3AC8 /* CTNotificationViewController.m in Sources */, + 58614E962F98B4BA000D1C8A /* CTTimerBoxView.swift in Sources */, 32DFD84B28BCCCBB00E72588 /* CTCarouselController.swift in Sources */, 3270BFF128E4D89C003528ED /* RatingProperties.swift in Sources */, ); diff --git a/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift b/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift index ddc23c6..6c774b2 100644 --- a/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift +++ b/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift @@ -3,6 +3,7 @@ import UserNotificationsUI import SDWebImage @objc public class CTTimerTemplateController: BaseCTNotificationContentViewController { + var contentView: UIView = UIView(frame: .zero) @objc public var data: String = "" @objc public var templateCaption: String = "" @@ -54,16 +55,43 @@ import SDWebImage subcaptionLabel.translatesAutoresizingMaskIntoConstraints = false return subcaptionLabel }() - private var timerLabel: UILabel = { - let timerLabel = UILabel() - timerLabel.textAlignment = .center - timerLabel.adjustsFontSizeToFitWidth = false - timerLabel.font = UIFont.boldSystemFont(ofSize: 18.0) - timerLabel.textColor = UIColor.black - timerLabel.translatesAutoresizingMaskIntoConstraints = false - return timerLabel - }() - + private var timerBoxView: CTTimerBoxView? + private var timerLabel: UILabel? + + private enum CTTimerStyleCapability { + static var supportsRichTimerBox: Bool { + if #available(iOS 13.0, *) { return true } + return false + } + } + + private func setTimerText(_ text: String) { + timerBoxView?.timerLabel.text = text + timerLabel?.text = text + } + + private func hideTimerDisplay() { + timerBoxView?.isHidden = true + timerLabel?.isHidden = true + } + + private func isDarkMode() -> Bool { + if #available(iOS 12.0, *) { + return traitCollection.userInterfaceStyle == .dark + } + return false + } + + private func hasRichTimerStyling(_ props: TimerTemplateProperties) -> Bool { + return props.pt_timer_bg != nil || props.pt_timer_bg_dark != nil + || props.pt_timer_bg_gradient_start != nil || props.pt_timer_bg_gradient_end != nil + || props.pt_timer_bg_gradient_start_dark != nil || props.pt_timer_bg_gradient_end_dark != nil + || props.pt_timer_bg_gradient_angle != nil + || props.pt_timer_border_color != nil || props.pt_timer_border_color_dark != nil + || props.pt_timer_border_width != nil || props.pt_timer_border_radius != nil + || props.pt_timer_text_clr != nil || props.pt_timer_text_clr_dark != nil + } + @objc public override func viewDidLoad() { super.viewDidLoad() @@ -95,7 +123,22 @@ import SDWebImage contentView.addSubview(imageView) contentView.addSubview(captionLabel) contentView.addSubview(subcaptionLabel) - contentView.addSubview(timerLabel) + + if CTTimerStyleCapability.supportsRichTimerBox, + let props = jsonContent, hasRichTimerStyling(props) { + let box = CTTimerBoxView() + timerBoxView = box + contentView.addSubview(box) + } else { + let label = UILabel() + label.textAlignment = .center + label.adjustsFontSizeToFitWidth = false + label.font = UIFont.boldSystemFont(ofSize: 18.0) + label.textColor = UIColor.black + label.translatesAutoresizingMaskIntoConstraints = false + timerLabel = label + contentView.addSubview(label) + } captionLabel.setHTMLText(templateCaption) subcaptionLabel.setHTMLText(templateSubcaption) @@ -160,7 +203,11 @@ import SDWebImage } updateInterfaceColors() - + + if let box = timerBoxView { + box.applyStyle(properties: jsonContent, isDarkMode: isDarkMode()) + } + // Handle image loading // Load image only if timer is not ended. if thresholdSeconds > 0 { @@ -206,27 +253,32 @@ import SDWebImage imageView.backgroundColor = UIColor(hex: isDarkMode ? bgColorDark : bgColor) captionLabel.textColor = UIColor(hex: isDarkMode ? captionColorDark : captionColor) subcaptionLabel.textColor = UIColor(hex: isDarkMode ? subcaptionColorDark : subcaptionColor) - timerLabel.textColor = UIColor(hex: isDarkMode ? timerColorDark : timerColor) + timerLabel?.textColor = UIColor(hex: isDarkMode ? timerColorDark : timerColor) + + if let box = timerBoxView, let props = jsonContent { + box.applyStyle(properties: props, isDarkMode: isDarkMode) + } } func setupConstraints() { + let activeTimerView: UIView = timerBoxView ?? timerLabel! NSLayoutConstraint.activate([ captionLabel.topAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -(CTUtiltiy.getCaptionHeight() - Constraints.kCaptionTopPadding)), captionLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: Constraints.kCaptionLeftPadding), captionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kTimerLabelWidth), captionLabel.heightAnchor.constraint(equalToConstant: Constraints.kCaptionHeight), - + subcaptionLabel.topAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -(Constraints.kSubCaptionHeight + Constraints.kSubCaptionTopPadding)), subcaptionLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: Constraints.kCaptionLeftPadding), subcaptionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kTimerLabelWidth), subcaptionLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -Constraints.kSubCaptionTopPadding), subcaptionLabel.heightAnchor.constraint(equalToConstant: Constraints.kSubCaptionHeight), - - timerLabel.topAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -CTUtiltiy.getCaptionHeight()), - timerLabel.leadingAnchor.constraint(equalTo: captionLabel.trailingAnchor, constant: Constraints.kCaptionLeftPadding), - timerLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kCaptionLeftPadding), - timerLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -Constraints.kSubCaptionTopPadding), - timerLabel.heightAnchor.constraint(equalToConstant: CTUtiltiy.getCaptionHeight()) + + activeTimerView.topAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -CTUtiltiy.getCaptionHeight()), + activeTimerView.leadingAnchor.constraint(equalTo: captionLabel.trailingAnchor, constant: Constraints.kCaptionLeftPadding), + activeTimerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kCaptionLeftPadding), + activeTimerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -Constraints.kSubCaptionTopPadding), + activeTimerView.heightAnchor.constraint(equalToConstant: CTUtiltiy.getCaptionHeight()) ]) } @@ -236,15 +288,15 @@ import SDWebImage let sec = thresholdSeconds % 60 if thresholdSeconds > 0 { if hr < 1 { - self.timerLabel.text = String(format: "%02i:%02i", min, sec) + setTimerText(String(format: "%02i:%02i", min, sec)) } else { - self.timerLabel.text = String(format: "%02i:%02i:%02i", hr, min, sec) + setTimerText(String(format: "%02i:%02i:%02i", hr, min, sec)) } thresholdSeconds -= 1 } else { timer.invalidate() - self.timerLabel.isHidden = true + hideTimerDisplay() updateViewForExpiredTime() } } diff --git a/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift b/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift index ec480b7..d298899 100644 --- a/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift +++ b/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift @@ -30,9 +30,35 @@ struct TimerTemplateProperties: Decodable { let pt_big_img_alt_alt_text: String? let pt_gif: String? let pt_gif_alt: String? - + + // Timer box background + let pt_timer_bg: String? + let pt_timer_bg_dark: String? + let pt_timer_bg_gradient_start: String? + let pt_timer_bg_gradient_end: String? + let pt_timer_bg_gradient_start_dark: String? + let pt_timer_bg_gradient_end_dark: String? + let pt_timer_bg_gradient_angle: String? + + // Timer box border + let pt_timer_border_color: String? + let pt_timer_border_color_dark: String? + let pt_timer_border_width: String? + let pt_timer_border_radius: String? + + // Timer text color + let pt_timer_text_clr: String? + let pt_timer_text_clr_dark: String? + enum CodingKeys: String, CodingKey { case pt_title, pt_title_alt, pt_msg, pt_msg_alt, pt_msg_summary, pt_dl1, pt_big_img, pt_big_img_alt, pt_bg, pt_bg_dark, pt_chrono_title_clr, pt_chrono_title_clr_dark, pt_timer_threshold, pt_timer_end, pt_title_clr, pt_title_clr_dark, pt_msg_clr, pt_msg_clr_dark, pt_big_img_alt_text, pt_big_img_alt_alt_text, pt_gif, pt_gif_alt + case pt_timer_bg, pt_timer_bg_dark + case pt_timer_bg_gradient_start, pt_timer_bg_gradient_end + case pt_timer_bg_gradient_start_dark, pt_timer_bg_gradient_end_dark + case pt_timer_bg_gradient_angle + case pt_timer_border_color, pt_timer_border_color_dark + case pt_timer_border_width, pt_timer_border_radius + case pt_timer_text_clr, pt_timer_text_clr_dark } init(from decoder: Decoder) throws { @@ -58,7 +84,21 @@ struct TimerTemplateProperties: Decodable { pt_big_img_alt_alt_text = try container.decodeIfPresent(String.self, forKey: .pt_big_img_alt_alt_text) pt_gif = try container.decodeIfPresent(String.self, forKey: .pt_gif) pt_gif_alt = try container.decodeIfPresent(String.self, forKey: .pt_gif_alt) - + + pt_timer_bg = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg) + pt_timer_bg_dark = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_dark) + pt_timer_bg_gradient_start = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_start) + pt_timer_bg_gradient_end = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_end) + pt_timer_bg_gradient_start_dark = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_start_dark) + pt_timer_bg_gradient_end_dark = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_end_dark) + pt_timer_bg_gradient_angle = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_angle) + pt_timer_border_color = try container.decodeIfPresent(String.self, forKey: .pt_timer_border_color) + pt_timer_border_color_dark = try container.decodeIfPresent(String.self, forKey: .pt_timer_border_color_dark) + pt_timer_border_width = try container.decodeIfPresent(String.self, forKey: .pt_timer_border_width) + pt_timer_border_radius = try container.decodeIfPresent(String.self, forKey: .pt_timer_border_radius) + pt_timer_text_clr = try container.decodeIfPresent(String.self, forKey: .pt_timer_text_clr) + pt_timer_text_clr_dark = try container.decodeIfPresent(String.self, forKey: .pt_timer_text_clr_dark) + // Value for pt_timer_threshold and pt_timer_end key can be Int or String if received from JSON data or individual keys respectively, so checked for both case if present or else nil. var thresholdValue: Int? = nil do { diff --git a/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift b/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift new file mode 100644 index 0000000..cee8710 --- /dev/null +++ b/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift @@ -0,0 +1,117 @@ +import UIKit + +class CTTimerBoxView: UIView { + + var timerLabel: UILabel = { + let label = UILabel() + label.textAlignment = .center + label.adjustsFontSizeToFitWidth = false + label.font = UIFont.boldSystemFont(ofSize: 18.0) + label.textColor = UIColor.black + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + private var gradientLayer: CAGradientLayer? + + override init(frame: CGRect) { + super.init(frame: frame) + translatesAutoresizingMaskIntoConstraints = false + setupLabel() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupLabel() { + addSubview(timerLabel) + NSLayoutConstraint.activate([ + timerLabel.topAnchor.constraint(equalTo: topAnchor), + timerLabel.leadingAnchor.constraint(equalTo: leadingAnchor), + timerLabel.trailingAnchor.constraint(equalTo: trailingAnchor), + timerLabel.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) + } + + override func layoutSubviews() { + super.layoutSubviews() + gradientLayer?.frame = bounds + gradientLayer?.cornerRadius = layer.cornerRadius + } + + func applyStyle(properties: TimerTemplateProperties, isDarkMode: Bool) { + // Corner radius + if let radiusStr = properties.pt_timer_border_radius, + let radius = Double(radiusStr) { + layer.cornerRadius = CGFloat(radius) + clipsToBounds = true + } + + // Border width + if let widthStr = properties.pt_timer_border_width, + let width = Double(widthStr) { + layer.borderWidth = CGFloat(width) + } + + // Border color + let borderClrHex = isDarkMode + ? (properties.pt_timer_border_color_dark ?? properties.pt_timer_border_color) + : properties.pt_timer_border_color + if let hex = borderClrHex { + layer.borderColor = UIColor(hex: hex)?.cgColor + } + + // Timer text color + let textClrHex = isDarkMode + ? (properties.pt_timer_text_clr_dark ?? properties.pt_timer_text_clr) + : properties.pt_timer_text_clr + if let hex = textClrHex { + timerLabel.textColor = UIColor(hex: hex) + } + + // Background: gradient takes priority over solid color + let gradStartHex = isDarkMode + ? (properties.pt_timer_bg_gradient_start_dark ?? properties.pt_timer_bg_gradient_start) + : properties.pt_timer_bg_gradient_start + let gradEndHex = isDarkMode + ? (properties.pt_timer_bg_gradient_end_dark ?? properties.pt_timer_bg_gradient_end) + : properties.pt_timer_bg_gradient_end + + if let startHex = gradStartHex, let endHex = gradEndHex, + let c1 = UIColor(hex: startHex), let c2 = UIColor(hex: endHex) { + gradientLayer?.removeFromSuperlayer() + let grad = CAGradientLayer() + grad.frame = bounds + grad.cornerRadius = layer.cornerRadius + grad.colors = [c1.cgColor, c2.cgColor] + + // CSS linear-gradient angle convention: 0 = bottom-to-top, 90 = left-to-right. + let angleStr = properties.pt_timer_bg_gradient_angle ?? "90" + if let degrees = Double(angleStr.trimmingCharacters(in: .whitespaces)) { + let radians = degrees * .pi / 180.0 + let endX = 0.5 + 0.5 * sin(radians) + let endY = 0.5 - 0.5 * cos(radians) + grad.startPoint = CGPoint(x: 1.0 - endX, y: 1.0 - endY) + grad.endPoint = CGPoint(x: endX, y: endY) + } else { + grad.startPoint = CGPoint(x: 0, y: 0.5) + grad.endPoint = CGPoint(x: 1, y: 0.5) + } + + layer.insertSublayer(grad, at: 0) + gradientLayer = grad + backgroundColor = .clear + } else { + gradientLayer?.removeFromSuperlayer() + gradientLayer = nil + + let bgHex = isDarkMode + ? (properties.pt_timer_bg_dark ?? properties.pt_timer_bg) + : properties.pt_timer_bg + if let hex = bgHex { + backgroundColor = UIColor(hex: hex) + } + } + } +} From 6fa1455c932cb01b6911763178d712a53dac9c55 Mon Sep 17 00:00:00 2001 From: ReshabCT Date: Thu, 23 Apr 2026 13:16:47 +0530 Subject: [PATCH 2/8] SDK-5737: Added support for radial style gradient --- .../Timer/Model/TimerTemplateProperties.swift | 5 ++++ .../Templates/Timer/View/CTTimerBoxView.swift | 26 ++++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift b/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift index d298899..e8d602b 100644 --- a/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift +++ b/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift @@ -40,6 +40,9 @@ struct TimerTemplateProperties: Decodable { let pt_timer_bg_gradient_end_dark: String? let pt_timer_bg_gradient_angle: String? + // Timer box gradient type ("linear" | "radial", default "linear") + let pt_timer_bg_gradient_type: String? + // Timer box border let pt_timer_border_color: String? let pt_timer_border_color_dark: String? @@ -56,6 +59,7 @@ struct TimerTemplateProperties: Decodable { case pt_timer_bg_gradient_start, pt_timer_bg_gradient_end case pt_timer_bg_gradient_start_dark, pt_timer_bg_gradient_end_dark case pt_timer_bg_gradient_angle + case pt_timer_bg_gradient_type case pt_timer_border_color, pt_timer_border_color_dark case pt_timer_border_width, pt_timer_border_radius case pt_timer_text_clr, pt_timer_text_clr_dark @@ -92,6 +96,7 @@ struct TimerTemplateProperties: Decodable { pt_timer_bg_gradient_start_dark = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_start_dark) pt_timer_bg_gradient_end_dark = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_end_dark) pt_timer_bg_gradient_angle = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_angle) + pt_timer_bg_gradient_type = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_type) pt_timer_border_color = try container.decodeIfPresent(String.self, forKey: .pt_timer_border_color) pt_timer_border_color_dark = try container.decodeIfPresent(String.self, forKey: .pt_timer_border_color_dark) pt_timer_border_width = try container.decodeIfPresent(String.self, forKey: .pt_timer_border_width) diff --git a/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift b/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift index cee8710..d2c96fc 100644 --- a/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift +++ b/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift @@ -86,17 +86,23 @@ class CTTimerBoxView: UIView { grad.cornerRadius = layer.cornerRadius grad.colors = [c1.cgColor, c2.cgColor] - // CSS linear-gradient angle convention: 0 = bottom-to-top, 90 = left-to-right. - let angleStr = properties.pt_timer_bg_gradient_angle ?? "90" - if let degrees = Double(angleStr.trimmingCharacters(in: .whitespaces)) { - let radians = degrees * .pi / 180.0 - let endX = 0.5 + 0.5 * sin(radians) - let endY = 0.5 - 0.5 * cos(radians) - grad.startPoint = CGPoint(x: 1.0 - endX, y: 1.0 - endY) - grad.endPoint = CGPoint(x: endX, y: endY) + let gradientType = properties.pt_timer_bg_gradient_type?.lowercased() ?? "linear" + if gradientType == "radial" { + grad.type = .radial + grad.startPoint = CGPoint(x: 0.5, y: 0.5) + grad.endPoint = CGPoint(x: 1.0, y: 1.0) } else { - grad.startPoint = CGPoint(x: 0, y: 0.5) - grad.endPoint = CGPoint(x: 1, y: 0.5) + let angleStr = properties.pt_timer_bg_gradient_angle ?? "90" + if let degrees = Double(angleStr.trimmingCharacters(in: .whitespaces)) { + let radians = degrees * .pi / 180.0 + let endX = 0.5 + 0.5 * sin(radians) + let endY = 0.5 - 0.5 * cos(radians) + grad.startPoint = CGPoint(x: 1.0 - endX, y: 1.0 - endY) + grad.endPoint = CGPoint(x: endX, y: endY) + } else { + grad.startPoint = CGPoint(x: 0, y: 0.5) + grad.endPoint = CGPoint(x: 1, y: 0.5) + } } layer.insertSublayer(grad, at: 0) From 7f27bf4181a5839825c92fd2d0745ed75c21dcd4 Mon Sep 17 00:00:00 2001 From: ReshabCT Date: Thu, 23 Apr 2026 14:36:05 +0530 Subject: [PATCH 3/8] SDK-5737: Added top & bottom padding for timer box --- .../Timer/Controller/CTTimerTemplateController.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift b/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift index 6c774b2..4083391 100644 --- a/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift +++ b/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift @@ -274,11 +274,10 @@ import SDWebImage subcaptionLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -Constraints.kSubCaptionTopPadding), subcaptionLabel.heightAnchor.constraint(equalToConstant: Constraints.kSubCaptionHeight), - activeTimerView.topAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -CTUtiltiy.getCaptionHeight()), + activeTimerView.topAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -(CTUtiltiy.getCaptionHeight() - Constraints.kCaptionTopPadding)), activeTimerView.leadingAnchor.constraint(equalTo: captionLabel.trailingAnchor, constant: Constraints.kCaptionLeftPadding), activeTimerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kCaptionLeftPadding), - activeTimerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -Constraints.kSubCaptionTopPadding), - activeTimerView.heightAnchor.constraint(equalToConstant: CTUtiltiy.getCaptionHeight()) + activeTimerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -Constraints.kSubCaptionTopPadding) ]) } From da95a16bfa5d2a51152b618c710ada24dfc55837 Mon Sep 17 00:00:00 2001 From: ReshabCT Date: Tue, 12 May 2026 11:35:04 +0530 Subject: [PATCH 4/8] SDK-5737: Fixed correct payload keys for the timer background, border color, border radius and style --- .../CTTimerTemplateController.swift | 13 ++-- .../Timer/Model/TimerTemplateProperties.swift | 62 +++++++------------ .../Templates/Timer/View/CTTimerBoxView.swift | 36 ++++------- 3 files changed, 42 insertions(+), 69 deletions(-) diff --git a/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift b/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift index 4083391..dcf08cc 100644 --- a/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift +++ b/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift @@ -83,13 +83,12 @@ import SDWebImage } private func hasRichTimerStyling(_ props: TimerTemplateProperties) -> Bool { - return props.pt_timer_bg != nil || props.pt_timer_bg_dark != nil - || props.pt_timer_bg_gradient_start != nil || props.pt_timer_bg_gradient_end != nil - || props.pt_timer_bg_gradient_start_dark != nil || props.pt_timer_bg_gradient_end_dark != nil - || props.pt_timer_bg_gradient_angle != nil - || props.pt_timer_border_color != nil || props.pt_timer_border_color_dark != nil - || props.pt_timer_border_width != nil || props.pt_timer_border_radius != nil - || props.pt_timer_text_clr != nil || props.pt_timer_text_clr_dark != nil + return props.pt_chrono_bg_clr != nil + || props.pt_chrono_grad_clr1 != nil || props.pt_chrono_grad_clr2 != nil + || props.pt_chrono_grad_dir != nil + || props.pt_chrono_style != nil + || props.pt_chrono_border_clr != nil + || props.pt_chrono_border_width != nil || props.pt_chrono_border_radius != nil } @objc public override func viewDidLoad() { diff --git a/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift b/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift index e8d602b..64f737b 100644 --- a/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift +++ b/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift @@ -32,37 +32,27 @@ struct TimerTemplateProperties: Decodable { let pt_gif_alt: String? // Timer box background - let pt_timer_bg: String? - let pt_timer_bg_dark: String? - let pt_timer_bg_gradient_start: String? - let pt_timer_bg_gradient_end: String? - let pt_timer_bg_gradient_start_dark: String? - let pt_timer_bg_gradient_end_dark: String? - let pt_timer_bg_gradient_angle: String? + let pt_chrono_bg_clr: String? + let pt_chrono_grad_clr1: String? + let pt_chrono_grad_clr2: String? + let pt_chrono_grad_dir: String? - // Timer box gradient type ("linear" | "radial", default "linear") - let pt_timer_bg_gradient_type: String? + // Timer box style ("solid" | "gradient_linear" | "gradient_radial", default "solid") + let pt_chrono_style: String? // Timer box border - let pt_timer_border_color: String? - let pt_timer_border_color_dark: String? - let pt_timer_border_width: String? - let pt_timer_border_radius: String? - - // Timer text color - let pt_timer_text_clr: String? - let pt_timer_text_clr_dark: String? + let pt_chrono_border_clr: String? + let pt_chrono_border_width: String? + let pt_chrono_border_radius: String? enum CodingKeys: String, CodingKey { case pt_title, pt_title_alt, pt_msg, pt_msg_alt, pt_msg_summary, pt_dl1, pt_big_img, pt_big_img_alt, pt_bg, pt_bg_dark, pt_chrono_title_clr, pt_chrono_title_clr_dark, pt_timer_threshold, pt_timer_end, pt_title_clr, pt_title_clr_dark, pt_msg_clr, pt_msg_clr_dark, pt_big_img_alt_text, pt_big_img_alt_alt_text, pt_gif, pt_gif_alt - case pt_timer_bg, pt_timer_bg_dark - case pt_timer_bg_gradient_start, pt_timer_bg_gradient_end - case pt_timer_bg_gradient_start_dark, pt_timer_bg_gradient_end_dark - case pt_timer_bg_gradient_angle - case pt_timer_bg_gradient_type - case pt_timer_border_color, pt_timer_border_color_dark - case pt_timer_border_width, pt_timer_border_radius - case pt_timer_text_clr, pt_timer_text_clr_dark + case pt_chrono_bg_clr + case pt_chrono_grad_clr1, pt_chrono_grad_clr2 + case pt_chrono_grad_dir + case pt_chrono_style + case pt_chrono_border_clr + case pt_chrono_border_width, pt_chrono_border_radius } init(from decoder: Decoder) throws { @@ -89,20 +79,14 @@ struct TimerTemplateProperties: Decodable { pt_gif = try container.decodeIfPresent(String.self, forKey: .pt_gif) pt_gif_alt = try container.decodeIfPresent(String.self, forKey: .pt_gif_alt) - pt_timer_bg = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg) - pt_timer_bg_dark = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_dark) - pt_timer_bg_gradient_start = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_start) - pt_timer_bg_gradient_end = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_end) - pt_timer_bg_gradient_start_dark = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_start_dark) - pt_timer_bg_gradient_end_dark = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_end_dark) - pt_timer_bg_gradient_angle = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_angle) - pt_timer_bg_gradient_type = try container.decodeIfPresent(String.self, forKey: .pt_timer_bg_gradient_type) - pt_timer_border_color = try container.decodeIfPresent(String.self, forKey: .pt_timer_border_color) - pt_timer_border_color_dark = try container.decodeIfPresent(String.self, forKey: .pt_timer_border_color_dark) - pt_timer_border_width = try container.decodeIfPresent(String.self, forKey: .pt_timer_border_width) - pt_timer_border_radius = try container.decodeIfPresent(String.self, forKey: .pt_timer_border_radius) - pt_timer_text_clr = try container.decodeIfPresent(String.self, forKey: .pt_timer_text_clr) - pt_timer_text_clr_dark = try container.decodeIfPresent(String.self, forKey: .pt_timer_text_clr_dark) + pt_chrono_bg_clr = try container.decodeIfPresent(String.self, forKey: .pt_chrono_bg_clr) + pt_chrono_grad_clr1 = try container.decodeIfPresent(String.self, forKey: .pt_chrono_grad_clr1) + pt_chrono_grad_clr2 = try container.decodeIfPresent(String.self, forKey: .pt_chrono_grad_clr2) + pt_chrono_grad_dir = try container.decodeIfPresent(String.self, forKey: .pt_chrono_grad_dir) + pt_chrono_style = try container.decodeIfPresent(String.self, forKey: .pt_chrono_style) + pt_chrono_border_clr = try container.decodeIfPresent(String.self, forKey: .pt_chrono_border_clr) + pt_chrono_border_width = try container.decodeIfPresent(String.self, forKey: .pt_chrono_border_width) + pt_chrono_border_radius = try container.decodeIfPresent(String.self, forKey: .pt_chrono_border_radius) // Value for pt_timer_threshold and pt_timer_end key can be Int or String if received from JSON data or individual keys respectively, so checked for both case if present or else nil. var thresholdValue: Int? = nil diff --git a/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift b/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift index d2c96fc..9065b21 100644 --- a/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift +++ b/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift @@ -42,43 +42,37 @@ class CTTimerBoxView: UIView { func applyStyle(properties: TimerTemplateProperties, isDarkMode: Bool) { // Corner radius - if let radiusStr = properties.pt_timer_border_radius, + if let radiusStr = properties.pt_chrono_border_radius, let radius = Double(radiusStr) { layer.cornerRadius = CGFloat(radius) clipsToBounds = true } // Border width - if let widthStr = properties.pt_timer_border_width, + if let widthStr = properties.pt_chrono_border_width, let width = Double(widthStr) { layer.borderWidth = CGFloat(width) } // Border color - let borderClrHex = isDarkMode - ? (properties.pt_timer_border_color_dark ?? properties.pt_timer_border_color) - : properties.pt_timer_border_color - if let hex = borderClrHex { + if let hex = properties.pt_chrono_border_clr { layer.borderColor = UIColor(hex: hex)?.cgColor } // Timer text color let textClrHex = isDarkMode - ? (properties.pt_timer_text_clr_dark ?? properties.pt_timer_text_clr) - : properties.pt_timer_text_clr + ? (properties.pt_chrono_title_clr_dark ?? properties.pt_chrono_title_clr) + : properties.pt_chrono_title_clr if let hex = textClrHex { timerLabel.textColor = UIColor(hex: hex) } - // Background: gradient takes priority over solid color - let gradStartHex = isDarkMode - ? (properties.pt_timer_bg_gradient_start_dark ?? properties.pt_timer_bg_gradient_start) - : properties.pt_timer_bg_gradient_start - let gradEndHex = isDarkMode - ? (properties.pt_timer_bg_gradient_end_dark ?? properties.pt_timer_bg_gradient_end) - : properties.pt_timer_bg_gradient_end + let style = properties.pt_chrono_style?.lowercased() ?? "solid" + let isGradient = style == "gradient_linear" || style == "gradient_radial" - if let startHex = gradStartHex, let endHex = gradEndHex, + if isGradient, + let startHex = properties.pt_chrono_grad_clr1, + let endHex = properties.pt_chrono_grad_clr2, let c1 = UIColor(hex: startHex), let c2 = UIColor(hex: endHex) { gradientLayer?.removeFromSuperlayer() let grad = CAGradientLayer() @@ -86,13 +80,12 @@ class CTTimerBoxView: UIView { grad.cornerRadius = layer.cornerRadius grad.colors = [c1.cgColor, c2.cgColor] - let gradientType = properties.pt_timer_bg_gradient_type?.lowercased() ?? "linear" - if gradientType == "radial" { + if style == "gradient_radial" { grad.type = .radial grad.startPoint = CGPoint(x: 0.5, y: 0.5) grad.endPoint = CGPoint(x: 1.0, y: 1.0) } else { - let angleStr = properties.pt_timer_bg_gradient_angle ?? "90" + let angleStr = properties.pt_chrono_grad_dir ?? "90" if let degrees = Double(angleStr.trimmingCharacters(in: .whitespaces)) { let radians = degrees * .pi / 180.0 let endX = 0.5 + 0.5 * sin(radians) @@ -112,10 +105,7 @@ class CTTimerBoxView: UIView { gradientLayer?.removeFromSuperlayer() gradientLayer = nil - let bgHex = isDarkMode - ? (properties.pt_timer_bg_dark ?? properties.pt_timer_bg) - : properties.pt_timer_bg - if let hex = bgHex { + if let hex = properties.pt_chrono_bg_clr { backgroundColor = UIColor(hex: hex) } } From 793cc253a3969449245babdd69b2fafc22eb0f3e Mon Sep 17 00:00:00 2001 From: ReshabCT Date: Thu, 14 May 2026 12:13:25 +0530 Subject: [PATCH 5/8] Added support for dark mode keys for timer bg, border and title color --- .../Timer/Model/TimerTemplateProperties.swift | 8 ++++++-- .../Templates/Timer/View/CTTimerBoxView.swift | 10 ++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift b/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift index 64f737b..2867727 100644 --- a/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift +++ b/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift @@ -33,6 +33,7 @@ struct TimerTemplateProperties: Decodable { // Timer box background let pt_chrono_bg_clr: String? + let pt_chrono_bg_clr_dark: String? let pt_chrono_grad_clr1: String? let pt_chrono_grad_clr2: String? let pt_chrono_grad_dir: String? @@ -42,16 +43,17 @@ struct TimerTemplateProperties: Decodable { // Timer box border let pt_chrono_border_clr: String? + let pt_chrono_border_clr_dark: String? let pt_chrono_border_width: String? let pt_chrono_border_radius: String? enum CodingKeys: String, CodingKey { case pt_title, pt_title_alt, pt_msg, pt_msg_alt, pt_msg_summary, pt_dl1, pt_big_img, pt_big_img_alt, pt_bg, pt_bg_dark, pt_chrono_title_clr, pt_chrono_title_clr_dark, pt_timer_threshold, pt_timer_end, pt_title_clr, pt_title_clr_dark, pt_msg_clr, pt_msg_clr_dark, pt_big_img_alt_text, pt_big_img_alt_alt_text, pt_gif, pt_gif_alt - case pt_chrono_bg_clr + case pt_chrono_bg_clr, pt_chrono_bg_clr_dark case pt_chrono_grad_clr1, pt_chrono_grad_clr2 case pt_chrono_grad_dir case pt_chrono_style - case pt_chrono_border_clr + case pt_chrono_border_clr, pt_chrono_border_clr_dark case pt_chrono_border_width, pt_chrono_border_radius } @@ -80,11 +82,13 @@ struct TimerTemplateProperties: Decodable { pt_gif_alt = try container.decodeIfPresent(String.self, forKey: .pt_gif_alt) pt_chrono_bg_clr = try container.decodeIfPresent(String.self, forKey: .pt_chrono_bg_clr) + pt_chrono_bg_clr_dark = try container.decodeIfPresent(String.self, forKey: .pt_chrono_bg_clr_dark) pt_chrono_grad_clr1 = try container.decodeIfPresent(String.self, forKey: .pt_chrono_grad_clr1) pt_chrono_grad_clr2 = try container.decodeIfPresent(String.self, forKey: .pt_chrono_grad_clr2) pt_chrono_grad_dir = try container.decodeIfPresent(String.self, forKey: .pt_chrono_grad_dir) pt_chrono_style = try container.decodeIfPresent(String.self, forKey: .pt_chrono_style) pt_chrono_border_clr = try container.decodeIfPresent(String.self, forKey: .pt_chrono_border_clr) + pt_chrono_border_clr_dark = try container.decodeIfPresent(String.self, forKey: .pt_chrono_border_clr_dark) pt_chrono_border_width = try container.decodeIfPresent(String.self, forKey: .pt_chrono_border_width) pt_chrono_border_radius = try container.decodeIfPresent(String.self, forKey: .pt_chrono_border_radius) diff --git a/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift b/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift index 9065b21..6827b4a 100644 --- a/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift +++ b/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift @@ -55,7 +55,10 @@ class CTTimerBoxView: UIView { } // Border color - if let hex = properties.pt_chrono_border_clr { + let borderClrHex = isDarkMode + ? (properties.pt_chrono_border_clr_dark ?? properties.pt_chrono_border_clr) + : properties.pt_chrono_border_clr + if let hex = borderClrHex { layer.borderColor = UIColor(hex: hex)?.cgColor } @@ -105,7 +108,10 @@ class CTTimerBoxView: UIView { gradientLayer?.removeFromSuperlayer() gradientLayer = nil - if let hex = properties.pt_chrono_bg_clr { + let bgClrHex = isDarkMode + ? (properties.pt_chrono_bg_clr_dark ?? properties.pt_chrono_bg_clr) + : properties.pt_chrono_bg_clr + if let hex = bgClrHex { backgroundColor = UIColor(hex: hex) } } From 9f93f5f4679b81bd9ca11c6ea46a1663ea57eb1b Mon Sep 17 00:00:00 2001 From: ReshabCT Date: Thu, 14 May 2026 12:21:22 +0530 Subject: [PATCH 6/8] Fallback support if no chrono config present --- .../Templates/Timer/View/CTTimerBoxView.swift | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift b/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift index 6827b4a..b85e9f8 100644 --- a/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift +++ b/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift @@ -62,13 +62,18 @@ class CTTimerBoxView: UIView { layer.borderColor = UIColor(hex: hex)?.cgColor } - // Timer text color - let textClrHex = isDarkMode - ? (properties.pt_chrono_title_clr_dark ?? properties.pt_chrono_title_clr) - : properties.pt_chrono_title_clr - if let hex = textClrHex { - timerLabel.textColor = UIColor(hex: hex) - } + // Timer text color — falls back to the title color so the timer is + // never left at the lazy-init UIColor.black against a dark backdrop. + let textClrHex: String = isDarkMode + ? (properties.pt_chrono_title_clr_dark + ?? properties.pt_chrono_title_clr + ?? properties.pt_title_clr_dark + ?? properties.pt_title_clr + ?? ConstantKeys.kHexWhiteColor) + : (properties.pt_chrono_title_clr + ?? properties.pt_title_clr + ?? ConstantKeys.kHexBlackColor) + timerLabel.textColor = UIColor(hex: textClrHex) let style = properties.pt_chrono_style?.lowercased() ?? "solid" let isGradient = style == "gradient_linear" || style == "gradient_radial" From 08b4e351cf25aefcc947d0a58acbb17f9dbfc847 Mon Sep 17 00:00:00 2001 From: ReshabCT Date: Tue, 19 May 2026 15:53:05 +0530 Subject: [PATCH 7/8] Fixed border radius and width to be flexible double --- .../Templates/Timer/Model/TimerTemplateProperties.swift | 8 ++++---- .../Templates/Timer/View/CTTimerBoxView.swift | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift b/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift index 2867727..47e2c39 100644 --- a/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift +++ b/CTNotificationContent/Templates/Timer/Model/TimerTemplateProperties.swift @@ -44,8 +44,8 @@ struct TimerTemplateProperties: Decodable { // Timer box border let pt_chrono_border_clr: String? let pt_chrono_border_clr_dark: String? - let pt_chrono_border_width: String? - let pt_chrono_border_radius: String? + let pt_chrono_border_width: FlexibleDouble? + let pt_chrono_border_radius: FlexibleDouble? enum CodingKeys: String, CodingKey { case pt_title, pt_title_alt, pt_msg, pt_msg_alt, pt_msg_summary, pt_dl1, pt_big_img, pt_big_img_alt, pt_bg, pt_bg_dark, pt_chrono_title_clr, pt_chrono_title_clr_dark, pt_timer_threshold, pt_timer_end, pt_title_clr, pt_title_clr_dark, pt_msg_clr, pt_msg_clr_dark, pt_big_img_alt_text, pt_big_img_alt_alt_text, pt_gif, pt_gif_alt @@ -89,8 +89,8 @@ struct TimerTemplateProperties: Decodable { pt_chrono_style = try container.decodeIfPresent(String.self, forKey: .pt_chrono_style) pt_chrono_border_clr = try container.decodeIfPresent(String.self, forKey: .pt_chrono_border_clr) pt_chrono_border_clr_dark = try container.decodeIfPresent(String.self, forKey: .pt_chrono_border_clr_dark) - pt_chrono_border_width = try container.decodeIfPresent(String.self, forKey: .pt_chrono_border_width) - pt_chrono_border_radius = try container.decodeIfPresent(String.self, forKey: .pt_chrono_border_radius) + pt_chrono_border_width = try container.decodeIfPresent(FlexibleDouble.self, forKey: .pt_chrono_border_width) + pt_chrono_border_radius = try container.decodeIfPresent(FlexibleDouble.self, forKey: .pt_chrono_border_radius) // Value for pt_timer_threshold and pt_timer_end key can be Int or String if received from JSON data or individual keys respectively, so checked for both case if present or else nil. var thresholdValue: Int? = nil diff --git a/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift b/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift index b85e9f8..2a2ca86 100644 --- a/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift +++ b/CTNotificationContent/Templates/Timer/View/CTTimerBoxView.swift @@ -42,15 +42,13 @@ class CTTimerBoxView: UIView { func applyStyle(properties: TimerTemplateProperties, isDarkMode: Bool) { // Corner radius - if let radiusStr = properties.pt_chrono_border_radius, - let radius = Double(radiusStr) { + if let radius = properties.pt_chrono_border_radius?.value { layer.cornerRadius = CGFloat(radius) clipsToBounds = true } // Border width - if let widthStr = properties.pt_chrono_border_width, - let width = Double(widthStr) { + if let width = properties.pt_chrono_border_width?.value { layer.borderWidth = CGFloat(width) } From 8639113cab5fb3260356b80dce47a942bc8bfab3 Mon Sep 17 00:00:00 2001 From: ReshabCT Date: Wed, 20 May 2026 15:52:04 +0530 Subject: [PATCH 8/8] Fixed big image appeared in timer box in collapsed state --- .../CTTimerTemplateController.swift | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift b/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift index dcf08cc..d75560e 100644 --- a/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift +++ b/CTNotificationContent/Templates/Timer/Controller/CTTimerTemplateController.swift @@ -57,6 +57,8 @@ import SDWebImage }() private var timerBoxView: CTTimerBoxView? private var timerLabel: UILabel? + private var captionTrailingConstraint: NSLayoutConstraint? + private var subcaptionTrailingConstraint: NSLayoutConstraint? private enum CTTimerStyleCapability { static var supportsRichTimerBox: Bool { @@ -75,6 +77,13 @@ import SDWebImage timerLabel?.isHidden = true } + private func showTimerForExpandedState() { + timerBoxView?.isHidden = false + timerLabel?.isHidden = false + captionTrailingConstraint?.constant = -Constraints.kTimerLabelWidth + subcaptionTrailingConstraint?.constant = -Constraints.kTimerLabelWidth + } + private func isDarkMode() -> Bool { if #available(iOS 12.0, *) { return traitCollection.userInterfaceStyle == .dark @@ -138,6 +147,7 @@ import SDWebImage timerLabel = label contentView.addSubview(label) } + hideTimerDisplay() captionLabel.setHTMLText(templateCaption) subcaptionLabel.setHTMLText(templateSubcaption) @@ -261,15 +271,20 @@ import SDWebImage func setupConstraints() { let activeTimerView: UIView = timerBoxView ?? timerLabel! + let captionTrailing = captionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kCaptionLeftPadding) + let subcaptionTrailing = subcaptionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kCaptionLeftPadding) + captionTrailingConstraint = captionTrailing + subcaptionTrailingConstraint = subcaptionTrailing + NSLayoutConstraint.activate([ captionLabel.topAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -(CTUtiltiy.getCaptionHeight() - Constraints.kCaptionTopPadding)), captionLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: Constraints.kCaptionLeftPadding), - captionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kTimerLabelWidth), + captionTrailing, captionLabel.heightAnchor.constraint(equalToConstant: Constraints.kCaptionHeight), subcaptionLabel.topAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -(Constraints.kSubCaptionHeight + Constraints.kSubCaptionTopPadding)), subcaptionLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: Constraints.kCaptionLeftPadding), - subcaptionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constraints.kTimerLabelWidth), + subcaptionTrailing, subcaptionLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -Constraints.kSubCaptionTopPadding), subcaptionLabel.heightAnchor.constraint(equalToConstant: Constraints.kSubCaptionHeight), @@ -348,6 +363,10 @@ import SDWebImage view.frame = frame contentView.frame = frame preferredContentSize = CGSize(width: viewWidth, height: viewHeight) + + if thresholdSeconds > 0 { + showTimerForExpandedState() + } } func activateImageViewContraints() {