You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

390 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import SwiftUI
import Foundation
// MARK: -
// MARK: -
extension String {
///
var isValidEmail: Bool {
let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailRegex)
return emailPredicate.evaluate(with: self)
}
///
var isValidPhone: Bool {
let phoneRegex = "^[+]?[0-9\\s\\-\\(\\)]{7,}$"
let phonePredicate = NSPredicate(format: "SELF MATCHES %@", phoneRegex)
return phonePredicate.evaluate(with: self)
}
/// URL
var isValidURL: Bool {
guard let url = URL(string: self) else { return false }
return UIApplication.shared.canOpenURL(url)
}
///
var trimmed: String {
self.trimmingCharacters(in: .whitespacesAndNewlines)
}
///
var isEmptyOrWhitespace: Bool {
self.trimmed.isEmpty
}
///
var characterCount: Int {
self.trimmed.count
}
///
func truncated(to length: Int, suffix: String = "...") -> String {
if self.count <= length {
return self
}
let index = self.index(self.startIndex, offsetBy: length - suffix.count)
return String(self[..<index]) + suffix
}
/// URL
func withURLProtocol() -> String {
if self.hasPrefix("http://") || self.hasPrefix("https://") {
return self
}
return "https://" + self
}
}
// MARK: -
extension Date {
///
func formattedString(style: DateFormatter.Style = .medium) -> String {
let formatter = DateFormatter()
formatter.dateStyle = style
formatter.locale = Locale(identifier: "zh_CN")
return formatter.string(from: self)
}
///
func formattedTimeString() -> String {
let formatter = DateFormatter()
formatter.timeStyle = .short
formatter.locale = Locale(identifier: "zh_CN")
return formatter.string(from: self)
}
///
func formattedFullString() -> String {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
formatter.locale = Locale(identifier: "zh_CN")
return formatter.string(from: self)
}
///
var isToday: Bool {
Calendar.current.isDateInToday(self)
}
///
var isYesterday: Bool {
Calendar.current.isDateInYesterday(self)
}
///
var relativeTimeDescription: String {
let now = Date()
let components = Calendar.current.dateComponents([.minute, .hour, .day], from: self, to: now)
if let day = components.day, day > 0 {
if day == 1 {
return "昨天"
} else if day < 7 {
return "\(day)天前"
} else {
return self.formattedString(style: .short)
}
} else if let hour = components.hour, hour > 0 {
return "\(hour)小时前"
} else if let minute = components.minute, minute > 0 {
return "\(minute)分钟前"
} else {
return "刚刚"
}
}
}
// MARK: -
extension Color {
///
static func random() -> Color {
Color(
red: Double.random(in: 0...1),
green: Double.random(in: 0...1),
blue: Double.random(in: 0...1)
)
}
///
static let systemBackground = Color(.systemBackground)
static let systemGroupedBackground = Color(.systemGroupedBackground)
///
static let label = Color(.label)
static let secondaryLabel = Color(.secondaryLabel)
static let tertiaryLabel = Color(.tertiaryLabel)
static let quaternaryLabel = Color(.quaternaryLabel)
///
static let systemBlue = Color(.systemBlue)
static let systemGreen = Color(.systemGreen)
static let systemIndigo = Color(.systemIndigo)
static let systemOrange = Color(.systemOrange)
static let systemPink = Color(.systemPink)
static let systemPurple = Color(.systemPurple)
static let systemRed = Color(.systemRed)
static let systemTeal = Color(.systemTeal)
static let systemYellow = Color(.systemYellow)
///
static let systemGray = Color(.systemGray)
static let systemGray2 = Color(.systemGray2)
static let systemGray3 = Color(.systemGray3)
static let systemGray4 = Color(.systemGray4)
static let systemGray5 = Color(.systemGray5)
static let systemGray6 = Color(.systemGray6)
}
// MARK: -
extension View {
///
func roundedCorners(_ radius: CGFloat, corners: UIRectCorner = .allCorners) -> some View {
clipShape(RoundedCorner(radius: radius, corners: corners))
}
///
func customShadow(
color: Color = .black.opacity(0.1),
radius: CGFloat = 8,
x: CGFloat = 0,
y: CGFloat = 4
) -> some View {
self.shadow(color: color, radius: radius, x: x, y: y)
}
///
func customBorder(
_ color: Color,
width: CGFloat = 1,
cornerRadius: CGFloat = 0
) -> some View {
self.overlay(
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(color, lineWidth: width)
)
}
///
func customBackground(_ color: Color, cornerRadius: CGFloat = 0) -> some View {
self.background(
RoundedRectangle(cornerRadius: cornerRadius)
.fill(color)
)
}
///
func hideKeyboard() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
// MARK: -
struct RoundedCorner: Shape {
var radius: CGFloat = .infinity
var corners: UIRectCorner = .allCorners
func path(in rect: CGRect) -> Path {
let path = UIBezierPath(
roundedRect: rect,
byRoundingCorners: corners,
cornerRadii: CGSize(width: radius, height: radius)
)
return Path(path.cgPath)
}
}
// MARK: -
struct ValidationHelper {
///
static func isValidEmail(_ email: String) -> Bool {
email.isValidEmail
}
///
static func isValidPhone(_ phone: String) -> Bool {
phone.isValidPhone
}
/// URL
static func isValidURL(_ url: String) -> Bool {
url.isValidURL
}
///
static func isRequiredFieldValid(_ field: String) -> Bool {
!field.isEmptyOrWhitespace
}
///
static func isLengthValid(_ text: String, min: Int, max: Int) -> Bool {
let count = text.characterCount
return count >= min && count <= max
}
///
static func getPasswordStrength(_ password: String) -> PasswordStrength {
var score = 0
if password.count >= 8 { score += 1 }
if password.range(of: "[a-z]", options: .regularExpression) != nil { score += 1 }
if password.range(of: "[A-Z]", options: .regularExpression) != nil { score += 1 }
if password.range(of: "[0-9]", options: .regularExpression) != nil { score += 1 }
if password.range(of: "[^a-zA-Z0-9]", options: .regularExpression) != nil { score += 1 }
switch score {
case 0...1:
return .weak
case 2...3:
return .medium
case 4...5:
return .strong
default:
return .weak
}
}
}
// MARK: -
enum PasswordStrength {
case weak
case medium
case strong
var description: String {
switch self {
case .weak:
return ""
case .medium:
return ""
case .strong:
return ""
}
}
var color: Color {
switch self {
case .weak:
return .red
case .medium:
return .orange
case .strong:
return .green
}
}
}
// MARK: -
struct FormatHelper {
///
static func formatFileSize(_ bytes: Int64) -> String {
let formatter = ByteCountFormatter()
formatter.allowedUnits = [.useKB, .useMB, .useGB]
formatter.countStyle = .file
return formatter.string(fromByteCount: bytes)
}
///
static func formatNumber(_ number: Int) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter.string(from: NSNumber(value: number)) ?? "\(number)"
}
///
static func formatPercentage(_ value: Double) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .percent
formatter.minimumFractionDigits = 1
formatter.maximumFractionDigits = 1
return formatter.string(from: NSNumber(value: value)) ?? "\(value * 100)%"
}
///
static func formatCurrency(_ amount: Double, locale: Locale = .current) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.locale = locale
return formatter.string(from: NSNumber(value: amount)) ?? "\(amount)"
}
}
// MARK: -
struct AnimationHelper {
///
static let spring = Animation.spring(response: 0.5, dampingFraction: 0.8, blendDuration: 0)
///
static let easeIn = Animation.easeIn(duration: 0.3)
///
static let easeOut = Animation.easeOut(duration: 0.3)
///
static let easeInOut = Animation.easeInOut(duration: 0.3)
/// 线
static let linear = Animation.linear(duration: 0.3)
}
// MARK: -
struct FeedbackHelper {
///
static func lightImpact() {
let impactFeedback = UIImpactFeedbackGenerator(style: .light)
impactFeedback.impactOccurred()
}
///
static func mediumImpact() {
let impactFeedback = UIImpactFeedbackGenerator(style: .medium)
impactFeedback.impactOccurred()
}
///
static func heavyImpact() {
let impactFeedback = UIImpactFeedbackGenerator(style: .heavy)
impactFeedback.impactOccurred()
}
///
static func success() {
let notificationFeedback = UINotificationFeedbackGenerator()
notificationFeedback.notificationOccurred(.success)
}
///
static func warning() {
let notificationFeedback = UINotificationFeedbackGenerator()
notificationFeedback.notificationOccurred(.warning)
}
///
static func error() {
let notificationFeedback = UINotificationFeedbackGenerator()
notificationFeedback.notificationOccurred(.error)
}
}