-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add new implementation of snackbar
- Loading branch information
1 parent
6c903d3
commit ec4448f
Showing
7 changed files
with
278 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
Sources/Purace/Common/Extensions/UIApplication+keyWindow.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// | ||
// UIApplication+keyWindow.swift | ||
// | ||
// | ||
// Created by Juan Hurtado on 21/11/22. | ||
// | ||
|
||
import UIKit | ||
|
||
extension UIApplication { | ||
var keyWindow: UIWindow? { | ||
return UIApplication.shared.connectedScenes | ||
.filter { $0.activationState == .foregroundActive } | ||
.first(where: { $0 is UIWindowScene }) | ||
.flatMap({ $0 as? UIWindowScene })?.windows | ||
.first(where: \.isKeyWindow) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// | ||
// File.swift | ||
// | ||
// | ||
// Created by Juan Hurtado on 19/11/22. | ||
// | ||
|
||
import Foundation | ||
import UIKit | ||
|
||
public protocol NibLoadable { | ||
static var nibName: String { get } | ||
} | ||
|
||
public extension NibLoadable where Self: UIView { | ||
static var nibName: String { | ||
return String(describing: Self.self) | ||
} | ||
|
||
static var nib: UINib { | ||
let bundle = Bundle.module | ||
return UINib(nibName: Self.nibName, bundle: bundle) | ||
} | ||
|
||
func setupFromNib() { | ||
guard let view = Self.nib.instantiate(withOwner: self, options: nil).first as? UIView else { fatalError("Error loading \(self) from nib") } | ||
addSubview(view) | ||
view.translatesAutoresizingMaskIntoConstraints = false | ||
view.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true | ||
view.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true | ||
view.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true | ||
view.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
Sources/Purace/Views/Basic/Snackbar v2/Component/PuraceSnackbar.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// | ||
// PuraceSnackbar.swift | ||
// | ||
// | ||
// Created by Juan Hurtado on 19/11/22. | ||
// | ||
|
||
import UIKit | ||
|
||
class PuraceSnackbar: UIView, NibLoadable { | ||
@IBOutlet var contentView: UIView! | ||
@IBOutlet weak var actionButton: UIButton! | ||
@IBOutlet weak var titleLabel: UILabel! | ||
|
||
override init(frame: CGRect) { | ||
super.init(frame: frame) | ||
setupFromNib() | ||
translatesAutoresizingMaskIntoConstraints = false | ||
setupSubviews() | ||
} | ||
|
||
required init?(coder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
|
||
private func setupSubviews() { | ||
titleLabel.font = UIFont(name: "Poppins-Regular", size: 14) | ||
titleLabel.textColor = .white | ||
actionButton.titleLabel?.textColor = .white | ||
actionButton.titleLabel?.font = UIFont(name: "Poppins-Medium", size: 14) | ||
contentView.layer.cornerRadius = 10 | ||
} | ||
|
||
func setup(with title: String, type: PuraceSnackbarType) { | ||
switch type { | ||
case .info: | ||
contentView.backgroundColor = .init(PuraceStyle.Color.G1) | ||
case .alert: | ||
contentView.backgroundColor = .init(PuraceStyle.Color.B1) | ||
case .error: | ||
contentView.backgroundColor = .init(PuraceStyle.Color.R1) | ||
} | ||
titleLabel.text = title | ||
} | ||
} | ||
|
||
// MARK: - Config constants | ||
extension PuraceSnackbar { | ||
static let height: CGFloat = 60 | ||
static let padding: CGFloat = 20 | ||
} |
47 changes: 47 additions & 0 deletions
47
Sources/Purace/Views/Basic/Snackbar v2/Component/PuraceSnackbar.xib
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> | ||
<device id="retina6_12" orientation="portrait" appearance="light"/> | ||
<dependencies> | ||
<deployment identifier="iOS"/> | ||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/> | ||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | ||
</dependencies> | ||
<objects> | ||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="PuraceSnackbar" customModuleProvider="target"> | ||
<connections> | ||
<outlet property="actionButton" destination="nek-Ra-1ia" id="c3m-MM-Pc2"/> | ||
<outlet property="contentView" destination="fTQ-8B-eqd" id="d4d-wB-YV5"/> | ||
<outlet property="titleLabel" destination="Vd3-Fo-KXQ" id="56M-lz-Bvd"/> | ||
</connections> | ||
</placeholder> | ||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> | ||
<view contentMode="scaleToFill" id="fTQ-8B-eqd"> | ||
<rect key="frame" x="0.0" y="0.0" width="393" height="134"/> | ||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> | ||
<subviews> | ||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Vd3-Fo-KXQ"> | ||
<rect key="frame" x="20" y="56.666666666666657" width="42" height="21"/> | ||
<fontDescription key="fontDescription" type="system" pointSize="17"/> | ||
<nil key="textColor"/> | ||
<nil key="highlightedColor"/> | ||
</label> | ||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nek-Ra-1ia"> | ||
<rect key="frame" x="319" y="50" width="54" height="34"/> | ||
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> | ||
<state key="normal" title="Button"/> | ||
</button> | ||
</subviews> | ||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | ||
<constraints> | ||
<constraint firstItem="Vd3-Fo-KXQ" firstAttribute="centerY" secondItem="fTQ-8B-eqd" secondAttribute="centerY" id="Icc-dL-4eH"/> | ||
<constraint firstAttribute="trailing" secondItem="nek-Ra-1ia" secondAttribute="trailing" constant="20" id="W4V-zZ-bMS"/> | ||
<constraint firstItem="nek-Ra-1ia" firstAttribute="centerY" secondItem="fTQ-8B-eqd" secondAttribute="centerY" id="YO9-ZV-LCy"/> | ||
<constraint firstItem="Vd3-Fo-KXQ" firstAttribute="leading" secondItem="fTQ-8B-eqd" secondAttribute="leading" constant="20" id="iaC-gl-QId"/> | ||
</constraints> | ||
<nil key="simulatedTopBarMetrics"/> | ||
<nil key="simulatedBottomBarMetrics"/> | ||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> | ||
<point key="canvasLocation" x="319.84732824427482" y="292.25352112676057"/> | ||
</view> | ||
</objects> | ||
</document> |
15 changes: 15 additions & 0 deletions
15
Sources/Purace/Views/Basic/Snackbar v2/Models/PuraceSnackbarConfig.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// | ||
// PuraceSnackbarConfig.swift | ||
// | ||
// | ||
// Created by Juan Hurtado on 21/11/22. | ||
// | ||
|
||
import Foundation | ||
|
||
struct PuraceSnackbarConfig { | ||
let title: String | ||
let type: PuraceSnackbarType = .info | ||
let action: (() -> Void)? | ||
let actionTitle: String? | ||
} |
110 changes: 110 additions & 0 deletions
110
Sources/Purace/Views/Basic/Snackbar v2/PuraceSnackbarManager.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// | ||
// PuraceSnackbarManager.swift | ||
// | ||
// | ||
// Created by Juan Hurtado on 18/11/22. | ||
// | ||
|
||
import Foundation | ||
import UIKit | ||
|
||
public class PuraceSnackbarManager { | ||
private static var _instance: PuraceSnackbarManager? = nil | ||
public static var instance: PuraceSnackbarManager { | ||
if _instance == nil { | ||
_instance = PuraceSnackbarManager() | ||
} | ||
return _instance! | ||
} | ||
|
||
private var verbose = false | ||
|
||
var isPresented = false | ||
var queue = [() -> ()]() | ||
var snackbar: PuraceSnackbar? | ||
|
||
private init() {} | ||
|
||
private func setupSnackbarView(withTitle title: String, type: PuraceSnackbarType) { | ||
guard let window = UIApplication.shared.keyWindow else { return } | ||
let bottomPadding = window.safeAreaInsets.bottom | ||
|
||
snackbar = PuraceSnackbar(frame: .init(x: 0, y: 0, width: 100, height: 0)) | ||
snackbar?.setup(with: title, type: type) | ||
window.addSubview(snackbar!) | ||
|
||
// Height constriant | ||
snackbar?.heightAnchor.constraint(equalToConstant: PuraceSnackbar.height).isActive = true | ||
|
||
// Bottom constriant | ||
snackbar?.topAnchor.constraint(equalTo: window.safeAreaLayoutGuide.bottomAnchor, constant: bottomPadding).isActive = true | ||
|
||
// Left constraint | ||
snackbar?.leadingAnchor.constraint(equalTo: window.leadingAnchor, constant: PuraceSnackbar.padding).isActive = true | ||
// Right constraint | ||
snackbar?.trailingAnchor.constraint(equalTo: window.trailingAnchor, constant: -PuraceSnackbar.padding).isActive = true | ||
} | ||
|
||
|
||
/// When called this function, some verbose logs will be printed on the console. | ||
/// | ||
/// Call this function **only** for debug purpuses. | ||
public func debug() -> PuraceSnackbarManager { | ||
verbose = true | ||
return self | ||
} | ||
|
||
public func show(withTitle title: String, type: PuraceSnackbarType) { | ||
if isPresented { | ||
enqueue(label: "show") { [weak self] in | ||
guard let self else { return } | ||
self.show(withTitle: title, type: type) | ||
} | ||
return | ||
} | ||
|
||
if verbose { | ||
print("showing snackbar") | ||
} | ||
|
||
setupSnackbarView(withTitle: title, type: type) | ||
UIView.animate(withDuration: 0.4, delay: 0, animations: { [weak self] in | ||
guard let self else { return } | ||
self.snackbar?.transform = .init(translationX: 0, y: -PuraceSnackbar.height - PuraceSnackbar.padding) | ||
}) { _ in | ||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in | ||
guard let self else { return } | ||
if self.isPresented { | ||
self.hide() | ||
} | ||
} | ||
} | ||
isPresented = true | ||
|
||
} | ||
|
||
public func hide() { | ||
if verbose { | ||
print("hiding snackbar") | ||
} | ||
guard isPresented else { return } | ||
UIView.animate(withDuration: 0.4, delay: 0, animations: { [weak self] in | ||
guard let self else { return } | ||
self.snackbar?.transform = .init(translationX: 0, y: PuraceSnackbar.height) | ||
}) { _ in | ||
self.snackbar?.removeFromSuperview() | ||
self.isPresented = false | ||
guard !self.queue.isEmpty else { return } | ||
let action = self.queue.removeFirst() | ||
action() | ||
} | ||
} | ||
|
||
private func enqueue(label: String, action: @escaping () -> ()) { | ||
queue.append(action) | ||
if verbose { | ||
print("Enqueuing action: \(label)") | ||
print("queue length: \(queue.count)") | ||
} | ||
} | ||
} |