Fixing issues for model reuse.
This commit is contained in:
parent
e143c75215
commit
0c7a2940d9
@ -22,7 +22,20 @@ import UIKit
|
||||
return calendar
|
||||
}()
|
||||
|
||||
public var dateFormat: String?
|
||||
public var dateFormatter: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .medium
|
||||
formatter.timeZone = NSTimeZone.system
|
||||
formatter.locale = .current
|
||||
formatter.formatterBehavior = .default
|
||||
return formatter
|
||||
}()
|
||||
|
||||
public var dateFormat: String = "MMM d, y" {
|
||||
didSet {
|
||||
dateFormatter.dateFormat = dateFormat
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
@ -83,7 +96,7 @@ import UIKit
|
||||
if calendar.isDate(date, inSameDayAs: Date()) {
|
||||
text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string")
|
||||
} else {
|
||||
text = dateFormatter().string(from: date)
|
||||
text = dateFormatter.string(from: date)
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,29 +111,12 @@ import UIKit
|
||||
setTextWith(date: datePicker?.date)
|
||||
}
|
||||
|
||||
public func dateFormatter() -> DateFormatter {
|
||||
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .medium
|
||||
formatter.timeZone = NSTimeZone.system
|
||||
formatter.locale = .current
|
||||
formatter.formatterBehavior = .default
|
||||
|
||||
if let dateFormat = dateFormat {
|
||||
formatter.dateFormat = dateFormat
|
||||
}
|
||||
|
||||
return formatter
|
||||
}
|
||||
|
||||
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
|
||||
guard let model = model as? DateDropdownEntryFieldModel else { return }
|
||||
|
||||
if let dateFormat = model.dateFormat {
|
||||
self.dateFormat = dateFormat
|
||||
}
|
||||
self.dateFormat = model.dateFormat
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
return "dateDropdownEntryField"
|
||||
}
|
||||
|
||||
public var dateFormat: String?
|
||||
public var dateFormat: String = "MMM d, y"
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
@ -32,7 +32,7 @@
|
||||
required public init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
dateFormat = try typeContainer.decodeIfPresent(String.self, forKey: .dateFormat)
|
||||
dateFormat = try typeContainer.decodeIfPresent(String.self, forKey: .dateFormat) ?? "MMM d, y"
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
|
||||
@ -333,11 +333,9 @@ import UIKit
|
||||
|
||||
guard let model = model as? DigitEntryFieldModel else { return }
|
||||
|
||||
numberOfDigits = model.digits ?? 4
|
||||
numberOfDigits = model.digits
|
||||
|
||||
if let secureEntry = model.secureEntry, secureEntry {
|
||||
setAsSecureTextEntry(true)
|
||||
}
|
||||
setAsSecureTextEntry(model.secureEntry)
|
||||
|
||||
for digitBox in digitBoxes {
|
||||
MVMCoreUICommonViewsUtility.addDismissToolbar(digitBox.digitField, delegate: delegateObject as? UITextFieldDelegate)
|
||||
|
||||
@ -16,8 +16,8 @@
|
||||
return "digitTextField"
|
||||
}
|
||||
|
||||
public var digits: Int?
|
||||
public var secureEntry: Bool?
|
||||
public var digits: Int = 4
|
||||
public var secureEntry: Bool = false
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
@ -35,8 +35,8 @@
|
||||
required public init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
digits = try typeContainer.decodeIfPresent(Int.self, forKey: .digits)
|
||||
secureEntry = try typeContainer.decodeIfPresent(Bool.self, forKey: .secureEntry)
|
||||
digits = try typeContainer.decodeIfPresent(Int.self, forKey: .digits) ?? 4
|
||||
secureEntry = try typeContainer.decodeIfPresent(Bool.self, forKey: .secureEntry) ?? false
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
|
||||
@ -248,29 +248,12 @@ import UIKit
|
||||
|
||||
entryFieldContainer.setWithModel(model, delegateObject, additionalData)
|
||||
|
||||
if let title = model.title {
|
||||
self.title = title
|
||||
}
|
||||
|
||||
if let isDisabled = model.isDisabled {
|
||||
self.isEnabled = isDisabled
|
||||
}
|
||||
|
||||
if let feedback = model.feedback {
|
||||
self.feedback = feedback
|
||||
}
|
||||
|
||||
if let errorMessage = model.errorMessage {
|
||||
self.errorMessage = errorMessage
|
||||
}
|
||||
|
||||
if let isLocked = model.isLocked {
|
||||
self.isLocked = isLocked
|
||||
}
|
||||
|
||||
if let isSelected = model.isSelected {
|
||||
self.isSelected = isSelected
|
||||
}
|
||||
title = model.title
|
||||
isEnabled = model.isEnabled
|
||||
feedback = model.feedback
|
||||
errorMessage = model.errorMessage
|
||||
isLocked = model.isLocked
|
||||
isSelected = model.isSelected
|
||||
|
||||
if let fieldKey = model.fieldKey {
|
||||
self.fieldKey = fieldKey
|
||||
|
||||
@ -21,11 +21,10 @@ import Foundation
|
||||
public var backgroundColor: Color?
|
||||
public var title: String?
|
||||
public var feedback: String?
|
||||
public var errorMessage: String?
|
||||
public var isDisabled: Bool?
|
||||
public var isLocked: Bool?
|
||||
public var isSelected: Bool?
|
||||
|
||||
public var errorMessage: String = ""
|
||||
public var isEnabled: Bool = true
|
||||
public var isLocked: Bool = false
|
||||
public var isSelected: Bool = false
|
||||
public var fieldKey: String?
|
||||
public var isValid: Bool?
|
||||
public var isRequired: Bool?
|
||||
@ -37,7 +36,7 @@ import Foundation
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case backgroundColor
|
||||
case title
|
||||
case isDisabled
|
||||
case isEnabled
|
||||
case feedback
|
||||
case errorMessage = "errorMsg"
|
||||
case isLocked
|
||||
@ -56,10 +55,10 @@ import Foundation
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
title = try typeContainer.decodeIfPresent(String.self, forKey: .title)
|
||||
feedback = try typeContainer.decodeIfPresent(String.self, forKey: .feedback)
|
||||
errorMessage = try typeContainer.decodeIfPresent(String.self, forKey: .errorMessage)
|
||||
isDisabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .isDisabled)
|
||||
isLocked = try typeContainer.decodeIfPresent(Bool.self, forKey: .isLocked)
|
||||
isSelected = try typeContainer.decodeIfPresent(Bool.self, forKey: .isSelected)
|
||||
errorMessage = try typeContainer.decodeIfPresent(String.self, forKey: .errorMessage) ?? ""
|
||||
isEnabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .isEnabled) ?? true
|
||||
isLocked = try typeContainer.decodeIfPresent(Bool.self, forKey: .isLocked) ?? false
|
||||
isSelected = try typeContainer.decodeIfPresent(Bool.self, forKey: .isSelected) ?? false
|
||||
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
|
||||
isValid = try typeContainer.decodeIfPresent(Bool.self, forKey: .isValid)
|
||||
}
|
||||
@ -70,7 +69,7 @@ import Foundation
|
||||
try container.encodeIfPresent(title, forKey: .title)
|
||||
try container.encodeIfPresent(feedback, forKey: .feedback)
|
||||
try container.encodeIfPresent(errorMessage, forKey: .errorMessage)
|
||||
try container.encodeIfPresent(isDisabled, forKey: .isDisabled)
|
||||
try container.encodeIfPresent(isEnabled, forKey: .isEnabled)
|
||||
try container.encodeIfPresent(isLocked, forKey: .isLocked)
|
||||
try container.encodeIfPresent(isSelected, forKey: .isSelected)
|
||||
try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
|
||||
|
||||
@ -115,10 +115,14 @@ extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource {
|
||||
}
|
||||
|
||||
@objc public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
|
||||
guard !pickerData.isEmpty else { return nil }
|
||||
|
||||
return pickerData[row]
|
||||
}
|
||||
|
||||
@objc public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
|
||||
guard !pickerData.isEmpty else { return }
|
||||
|
||||
observeDropdownChange?(text ?? "", pickerData[row])
|
||||
text = pickerData[row]
|
||||
}
|
||||
|
||||
@ -16,28 +16,4 @@
|
||||
}
|
||||
|
||||
public var options: [String]?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case options
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
options = try typeContainer.decodeIfPresent([String].self, forKey: .options)
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(options, forKey: .options)
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,13 +286,9 @@ import UIKit
|
||||
textColor.disabled = disabledTextColor.uiColor
|
||||
}
|
||||
|
||||
if let text = model.text {
|
||||
self.text = text
|
||||
}
|
||||
text = model.text
|
||||
placeholder = model.placeholder
|
||||
|
||||
if let placeholder = model.placeholder {
|
||||
self.placeholder = placeholder
|
||||
}
|
||||
|
||||
switch model.type {
|
||||
case "password":
|
||||
|
||||
293
MVMCoreUI/Containers/views/EntryFieldContainer.swift
Normal file
293
MVMCoreUI/Containers/views/EntryFieldContainer.swift
Normal file
@ -0,0 +1,293 @@
|
||||
//
|
||||
// EntryFieldContainer.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 11/12/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
|
||||
@objcMembers open class EntryFieldContainer: View {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Drawing Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
/// The bottom border line. Height is dynamic based on scenario.
|
||||
public var bottomBar: CAShapeLayer? = {
|
||||
let layer = CAShapeLayer()
|
||||
layer.backgroundColor = UIColor.black.cgColor
|
||||
layer.drawsAsynchronously = true
|
||||
layer.anchorPoint = CGPoint(x: 0.5, y: 1.0);
|
||||
return layer
|
||||
}()
|
||||
|
||||
/// Total control over the drawn top, bottom, left and right borders.
|
||||
public var disableAllBorders = false {
|
||||
didSet {
|
||||
bottomBar?.isHidden = disableAllBorders
|
||||
}
|
||||
}
|
||||
|
||||
private(set) var fieldState: FieldState = .original {
|
||||
didSet (oldState) {
|
||||
// Will not update if new state is the same as old.
|
||||
if fieldState != oldState {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.fieldState.setStateUI(for: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if the top, left, and right borders should be drawn.
|
||||
private var hideBorders = false
|
||||
|
||||
public var borderStrokeColor: UIColor = .mfSilver()
|
||||
private var borderPath: UIBezierPath = UIBezierPath()
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Property Observers
|
||||
//--------------------------------------------------
|
||||
|
||||
private var _isEnabled: Bool = true
|
||||
private var _showError: Bool = false
|
||||
private var _isLocked: Bool = false
|
||||
private var _isSelected: Bool = false
|
||||
|
||||
public var isEnabled: Bool {
|
||||
get { return _isEnabled }
|
||||
set (enabled) {
|
||||
|
||||
_isEnabled = enabled
|
||||
_isLocked = false
|
||||
_isSelected = false
|
||||
_showError = false
|
||||
|
||||
fieldState = enabled ? .original : .disabled
|
||||
}
|
||||
}
|
||||
|
||||
public var showError: Bool {
|
||||
get { return _showError }
|
||||
set (error) {
|
||||
|
||||
_showError = error
|
||||
_isEnabled = true
|
||||
_isLocked = false
|
||||
_isSelected = false
|
||||
|
||||
fieldState = error ? .error : .original
|
||||
}
|
||||
}
|
||||
|
||||
public var isLocked: Bool {
|
||||
get { return _isLocked }
|
||||
set (locked) {
|
||||
|
||||
_isLocked = locked
|
||||
_isEnabled = true
|
||||
_isSelected = false
|
||||
_showError = false
|
||||
|
||||
fieldState = locked ? .locked : .original
|
||||
}
|
||||
}
|
||||
|
||||
public var isSelected: Bool {
|
||||
get { return _isSelected }
|
||||
set (selected) {
|
||||
|
||||
_isSelected = selected
|
||||
_isLocked = false
|
||||
_isEnabled = true
|
||||
|
||||
if _showError {
|
||||
fieldState = selected ? .selectedError : .error
|
||||
} else {
|
||||
fieldState = selected ? .selected : .original
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Delegate
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Holds reference to delegateObject to inform molecular tableView of an update.
|
||||
var delegateObject: MVMCoreUIDelegateObject?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Lifecycle
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
refreshUI(bottomBarSize: showError ? 4 : 1)
|
||||
}
|
||||
|
||||
open override func updateView(_ size: CGFloat) {
|
||||
super.updateView(size)
|
||||
|
||||
refreshUI()
|
||||
}
|
||||
|
||||
/// This handles the top, left, and right border lines.
|
||||
open override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
|
||||
borderPath.removeAllPoints()
|
||||
|
||||
if !disableAllBorders && !hideBorders {
|
||||
// Brings the other half of the line inside the view to prevent cropping.
|
||||
let origin = bounds.origin
|
||||
let size = frame.size
|
||||
let insetLean: CGFloat = 0.5
|
||||
borderPath.lineWidth = 1
|
||||
|
||||
borderPath.move(to: CGPoint(x: origin.x + insetLean, y: origin.y + size.height))
|
||||
borderPath.addLine(to: CGPoint(x: origin.x + insetLean, y: origin.y + insetLean))
|
||||
borderPath.addLine(to: CGPoint(x: origin.x + size.width - insetLean, y: origin.y + insetLean))
|
||||
borderPath.addLine(to: CGPoint(x: origin.x + size.width - insetLean, y: origin.y + size.height))
|
||||
|
||||
borderStrokeColor.setStroke()
|
||||
borderPath.stroke()
|
||||
}
|
||||
}
|
||||
|
||||
override open func setupView() {
|
||||
super.setupView()
|
||||
|
||||
isAccessibilityElement = false
|
||||
isOpaque = false
|
||||
|
||||
if let bottomBar = bottomBar {
|
||||
layer.addSublayer(bottomBar)
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Draw States
|
||||
//--------------------------------------------------
|
||||
|
||||
public enum FieldState {
|
||||
case original
|
||||
case error
|
||||
case selectedError
|
||||
case selected
|
||||
case locked
|
||||
case disabled
|
||||
|
||||
public func setStateUI(for formField: EntryFieldContainer) {
|
||||
|
||||
switch self {
|
||||
case .original:
|
||||
formField.originalUI()
|
||||
|
||||
case .error:
|
||||
formField.errorUI()
|
||||
|
||||
case .selectedError:
|
||||
formField.selectedErrorUI()
|
||||
|
||||
case .selected:
|
||||
formField.selectedUI()
|
||||
|
||||
case .locked:
|
||||
formField.lockedUI()
|
||||
|
||||
case .disabled:
|
||||
formField.disabledUI()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open func originalUI() {
|
||||
|
||||
isUserInteractionEnabled = true
|
||||
hideBorders = false
|
||||
borderStrokeColor = .mfSilver()
|
||||
bottomBar?.backgroundColor = UIColor.black.cgColor
|
||||
refreshUI(bottomBarSize: 1)
|
||||
}
|
||||
|
||||
open func errorUI() {
|
||||
|
||||
isUserInteractionEnabled = true
|
||||
hideBorders = false
|
||||
borderStrokeColor = .mfPumpkin()
|
||||
bottomBar?.backgroundColor = UIColor.mfPumpkin().cgColor
|
||||
refreshUI(bottomBarSize: 4)
|
||||
}
|
||||
|
||||
open func selectedErrorUI() {
|
||||
|
||||
isUserInteractionEnabled = true
|
||||
hideBorders = false
|
||||
borderStrokeColor = .black
|
||||
bottomBar?.backgroundColor = UIColor.mfPumpkin().cgColor
|
||||
refreshUI(bottomBarSize: 4)
|
||||
}
|
||||
|
||||
open func selectedUI() {
|
||||
|
||||
isUserInteractionEnabled = true
|
||||
hideBorders = false
|
||||
borderStrokeColor = .black
|
||||
bottomBar?.backgroundColor = UIColor.black.cgColor
|
||||
refreshUI(bottomBarSize: 1)
|
||||
}
|
||||
|
||||
open func lockedUI() {
|
||||
|
||||
isUserInteractionEnabled = false
|
||||
hideBorders = true
|
||||
borderStrokeColor = .clear
|
||||
bottomBar?.backgroundColor = UIColor.clear.cgColor
|
||||
refreshUI(bottomBarSize: 1)
|
||||
}
|
||||
|
||||
open func disabledUI() {
|
||||
|
||||
isUserInteractionEnabled = false
|
||||
hideBorders = false
|
||||
borderStrokeColor = .mfSilver()
|
||||
bottomBar?.backgroundColor = UIColor.mfSilver().cgColor
|
||||
refreshUI(bottomBarSize: 1)
|
||||
}
|
||||
|
||||
open func refreshUI(bottomBarSize: CGFloat? = nil) {
|
||||
|
||||
if !disableAllBorders {
|
||||
let size: CGFloat = bottomBarSize ?? (showError ? 4 : 1)
|
||||
bottomBar?.frame = CGRect(x: 0, y: bounds.height - size, width: bounds.width, height: size)
|
||||
|
||||
delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self)
|
||||
setNeedsDisplay()
|
||||
layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
self.delegateObject = delegateObject
|
||||
}
|
||||
}
|
||||
|
||||
extension EntryFieldContainer {
|
||||
|
||||
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
self.delegateObject = delegateObject
|
||||
|
||||
guard let dictionary = json, !dictionary.isEmpty else { return }
|
||||
}
|
||||
}
|
||||
@ -9,21 +9,25 @@
|
||||
import UIKit
|
||||
|
||||
@objcMembers public class DropDownFilterTableViewCell: TableViewCell {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
var dropDownListItemModel: DropDownListItemModel?
|
||||
let dropDown = ItemDropdownEntryField()
|
||||
var delegateObject: MVMCoreUIDelegateObject?
|
||||
var previousIndex = NSNotFound
|
||||
var dropDownSelectionObservation: NSKeyValueObservation?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - MFViewProtocol
|
||||
//--------------------------------------------------
|
||||
|
||||
override public func setupView() {
|
||||
super.setupView()
|
||||
|
||||
guard dropDown.superview == nil else { return }
|
||||
|
||||
dropDown.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(dropDown)
|
||||
|
||||
NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: dropDown, useMargins: true).values))
|
||||
|
||||
dropDown.observeDropdownChange = { [weak self] oldValue, newValue in
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
import Foundation
|
||||
|
||||
@objcMembers public class DropDownListItemModel: ContainerModel, ListItemModelProtocol {
|
||||
|
||||
public static var identifier: String = "dropDownListItem"
|
||||
public var molecules: [[ListItemModelProtocol]]
|
||||
public var dropDown: ItemDropdownEntryFieldModel
|
||||
@ -33,9 +34,11 @@ import Foundation
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
molecules = try typeContainer.decodeMolecules2D(codingKey: .molecules) as! [[ListItemModelProtocol]]
|
||||
dropDown = try typeContainer.decode(ItemDropdownEntryFieldModel.self, forKey: .dropDown)
|
||||
|
||||
if let lineModel = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) {
|
||||
line = lineModel
|
||||
}
|
||||
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
try super.init(from: decoder)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user