123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323 |
- //
- // StringUtil.swift
- // Runner
- //
- // Created by Yayan Dwi on 20/04/20.
- // Copyright © 2020 The Chromium Authors. All rights reserved.
- //
- import Foundation
- import UIKit
- import SDWebImage
- extension Date {
-
- public func currentTimeMillis() -> Int {
- return Int(self.timeIntervalSince1970 * 1000)
- }
-
- func format(dateFormat: String) -> String {
- let formatter = DateFormatter()
- formatter.dateFormat = dateFormat
- return formatter.string(from: self)
- }
-
- var millisecondsSince1970:Int64 {
- return Int64((self.timeIntervalSince1970 * 1000.0).rounded())
- }
-
- public init(milliseconds:Int64) {
- self = Date(timeIntervalSince1970: TimeInterval(milliseconds) / 1000)
- }
- }
- extension String {
-
- func toNormalString() -> String {
- let _source = self.replacingOccurrences(of: "+", with: "%20")
- if var result = _source.removingPercentEncoding {
- result = result.replacingOccurrences(of: "<NL>", with: "\n")
- result = result.replacingOccurrences(of: "<CR>", with: "\r")
- return decrypt(source: result)
- }
- return self
- }
-
- func toStupidString() -> String {
- if var result = self.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
- result = result.replacingOccurrences(of: "\n", with: "<NL>")
- result = result.replacingOccurrences(of: "\r", with: "<CR>")
- result = result.replacingOccurrences(of: "+", with: "%2B")
- return result
- }
- return self
- }
-
- private func decrypt(source : String) -> String {
- if let result = source.removingPercentEncoding {
- return result
- }
- return source
- }
-
- public func matches(_ regex: String) -> Bool {
- return self.range(of: regex, options: .regularExpression, range: nil, locale: nil) != nil
- }
-
- }
- extension Int {
-
- func toHex() -> String {
- return String(format: "%02X", self)
- }
-
- }
- extension UIApplication {
- public static var appVersion: String? {
- return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
- }
-
- var rootViewController: UIViewController? {
- return UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController
- }
-
- public var visibleViewController: UIViewController? {
- let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
- if var topController = keyWindow?.rootViewController {
- while let presentedViewController = topController.presentedViewController {
- topController = presentedViewController
- }
- return topController
- }
- return nil
- }
- }
- extension UIView {
-
- public func anchor(top: NSLayoutYAxisAnchor? = nil,
- left: NSLayoutXAxisAnchor? = nil,
- bottom: NSLayoutYAxisAnchor? = nil,
- right: NSLayoutXAxisAnchor? = nil,
- paddingTop: CGFloat = 0,
- paddingLeft: CGFloat = 0,
- paddingBottom: CGFloat = 0,
- paddingRight: CGFloat = 0,
- centerX: NSLayoutXAxisAnchor? = nil,
- centerY: NSLayoutYAxisAnchor? = nil,
- width: CGFloat = 0,
- height: CGFloat = 0,
- minHeight: CGFloat = 0,
- maxHeight: CGFloat = 0,
- minWidth: CGFloat = 0,
- maxWidth: CGFloat = 0,
- dynamicLeft: Bool = false,
- dynamicRight: Bool = false) {
-
- translatesAutoresizingMaskIntoConstraints = false
-
- if let top = top {
- topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true
- }
- if let left = left {
- if dynamicLeft {
- leftAnchor.constraint(greaterThanOrEqualTo: left, constant: paddingLeft).isActive = true
- } else {
- leftAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
- }
- }
- if let right = right {
- if dynamicRight {
- leftAnchor.constraint(lessThanOrEqualTo: right, constant: -paddingRight).isActive = true
- } else {
- rightAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
- }
- }
- if let bottom = bottom {
- bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom).isActive = true
- }
- if let centerX = centerX {
- centerXAnchor.constraint(equalTo: centerX).isActive = true
- }
- if let centerY = centerY {
- centerYAnchor.constraint(equalTo: centerY).isActive = true
- }
- if height != 0 || minHeight != 0 || maxHeight != 0 {
- if minHeight != 0 && maxHeight != 0 {
- heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight).isActive = true
- heightAnchor.constraint(lessThanOrEqualToConstant: maxHeight).isActive = true
- } else if minHeight != 0 && maxHeight == 0 {
- heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight).isActive = true
- } else if minHeight == 0 && maxHeight != 0 {
- heightAnchor.constraint(lessThanOrEqualToConstant: maxHeight).isActive = true
- } else {
- heightAnchor.constraint(equalToConstant: height).isActive = true
- }
- }
- if width != 0 || minWidth != 0 || maxWidth != 0 {
- if minWidth != 0 && maxWidth != 0 {
- heightAnchor.constraint(greaterThanOrEqualToConstant: minWidth).isActive = true
- heightAnchor.constraint(lessThanOrEqualToConstant: maxWidth).isActive = true
- } else if minWidth != 0 && maxWidth == 0 {
- heightAnchor.constraint(greaterThanOrEqualToConstant: minWidth).isActive = true
- } else if minWidth == 0 && maxWidth != 0 {
- heightAnchor.constraint(lessThanOrEqualToConstant: maxWidth).isActive = true
- } else {
- widthAnchor.constraint(equalToConstant: width).isActive = true
- }
- }
- }
-
- public func addTopBorder(with color: UIColor?, andWidth borderWidth: CGFloat, view: UIView = UIView()) {
- let border = view
- border.backgroundColor = color
- border.autoresizingMask = [.flexibleWidth, .flexibleBottomMargin]
- border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: borderWidth)
- addSubview(border)
- }
- public func addBottomBorder(with color: UIColor?, andWidth borderWidth: CGFloat, x: CGFloat = 0, view: UIView = UIView()) {
- let border = view
- border.backgroundColor = color
- border.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
- border.frame = CGRect(x: x, y: frame.size.height - borderWidth, width: frame.size.width, height: borderWidth)
- addSubview(border)
- }
- public func addLeftBorder(with color: UIColor?, andWidth borderWidth: CGFloat) {
- let border = UIView()
- border.backgroundColor = color
- border.frame = CGRect(x: 0, y: 0, width: borderWidth, height: frame.size.height)
- border.autoresizingMask = [.flexibleHeight, .flexibleRightMargin]
- addSubview(border)
- }
- public func addRightBorder(with color: UIColor?, andWidth borderWidth: CGFloat) {
- let border = UIView()
- border.backgroundColor = color
- border.autoresizingMask = [.flexibleHeight, .flexibleLeftMargin]
- border.frame = CGRect(x: frame.size.width - borderWidth, y: 0, width: borderWidth, height: frame.size.height)
- addSubview(border)
- }
-
- }
- extension UIViewController {
-
- var previousViewController: UIViewController? {
- guard let navigationController = navigationController else { return nil }
- let count = navigationController.viewControllers.count
- return count < 2 ? nil : navigationController.viewControllers[count - 2]
- }
-
- }
- extension UIImage {
- func resize(target: CGSize) -> UIImage {
- // Determine the scale factor that preserves aspect ratio
- let widthRatio = target.width / size.width
- let heightRatio = target.height / size.height
-
- let scaleFactor = min(widthRatio, heightRatio)
-
- // Compute the new image size that preserves aspect ratio
- let scaledImageSize = CGSize(
- width: size.width * scaleFactor,
- height: size.height * scaleFactor
- )
-
- // Draw and return the resized UIImage
- let renderer = UIGraphicsImageRenderer(
- size: scaledImageSize
- )
-
- let scaledImage = renderer.image { _ in
- self.draw(in: CGRect(
- origin: .zero,
- size: scaledImageSize
- ))
- }
-
- return scaledImage
- }
- }
- extension UIImage {
-
- var isPortrait: Bool { size.height > size.width }
- var isLandscape: Bool { size.width > size.height }
- var breadth: CGFloat { min(size.width, size.height) }
- var breadthSize: CGSize { .init(width: breadth, height: breadth) }
- var breadthRect: CGRect { .init(origin: .zero, size: breadthSize) }
- public var circleMasked: UIImage? {
- guard let cgImage = cgImage?
- .cropping(to: .init(origin: .init(x: isLandscape ? ((size.width-size.height)/2).rounded(.down) : 0,
- y: isPortrait ? ((size.height-size.width)/2).rounded(.down) : 0),
- size: breadthSize)) else { return nil }
- let format = imageRendererFormat
- format.opaque = false
- return UIGraphicsImageRenderer(size: breadthSize, format: format).image { _ in
- UIBezierPath(ovalIn: breadthRect).addClip()
- UIImage(cgImage: cgImage, scale: format.scale, orientation: imageOrientation)
- .draw(in: .init(origin: .zero, size: breadthSize))
- }
- }
-
- }
- extension NSObject {
-
- private static var urlStore = [String:String]()
- public func getImage(name url: String, placeholderImage: UIImage? = nil, isCircle: Bool = false, tableView: UITableView? = nil, indexPath: IndexPath? = nil, completion: @escaping (Bool, Bool, UIImage?)->()) {
- let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
- type(of: self).urlStore[tmpAddress] = url
- if url.isEmpty {
- completion(false, false, placeholderImage)
- return
- }
- do {
- let documentDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
- let file = documentDir.appendingPathComponent(url)
- if FileManager().fileExists(atPath: file.path) {
- let image = UIImage(contentsOfFile: file.path)?.sd_resizedImage(with: CGSize(width: 400, height: 400), scaleMode: .aspectFill)
- completion(true, false, isCircle ? image?.circleMasked : image)
- } else {
- completion(false, false, placeholderImage)
- Download().startHTTP(forKey: url) { (name, progress) in
- guard progress == 100 else {
- return
- }
-
- DispatchQueue.main.async {
- if tableView != nil {
- tableView!.beginUpdates()
- tableView!.reloadRows(at: [indexPath!], with: .none)
- tableView!.endUpdates()
- }
- if type(of: self).urlStore[tmpAddress] == name {
- let image = UIImage(contentsOfFile: file.path)?.sd_resizedImage(with: CGSize(width: 400, height: 400), scaleMode: .aspectFill)
- completion(true, true, isCircle ? image?.circleMasked : image)
- }
- }
- }
- }
- } catch {}
- }
-
- func loadImage(named: String, placeholderImage: UIImage?, completion: @escaping (UIImage?, Bool) -> ()) {
- guard !named.isEmpty else {
- completion(placeholderImage, true)
- return
- }
- SDWebImageManager.shared.loadImage(with: URL.palioImage(named: named), options: .highPriority, progress: .none) { image, data, error, type, finish, url in
- completion(image, finish)
- }
- }
-
- public func deleteAllRecordDatabase() {
- Database.shared.database?.inTransaction({ fmdb, rollback in
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "BUDDY", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "GROUPZ", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "GROUPZ_MEMBER", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "DISCUSSION_FORUM", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "POST", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE_STATUS", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE_SUMMARY", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "OUTGOING", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "FOLLOW", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE_FAVORITE", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "LINK_PREVIEW", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "PULL_DB", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "PREFS", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "CALL_CENTER_HISTORY", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "FORM_DATA", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "TASK_PIC", _where: "")
- _ = Database.shared.deleteRecord(fmdb: fmdb, table: "TASK_DETAIL", _where: "")
- })
- }
-
- }
- extension URL {
-
- static func palioImage(named: String) -> URL? {
- return URL(string: "https://newuniverse.io/filepalio/image/\(named)")
- }
-
- }
- extension UIColor {
- public static var mainColor: UIColor {
- renderColor(hex: "#3669ad")
- }
-
- public static var secondaryColor: UIColor {
- return renderColor(hex: "#FAFAFF")
- }
-
- public static var orangeColor: UIColor {
- return renderColor(hex: "#FFA03E")
- }
-
- public static var orangeBNI: UIColor {
- return renderColor(hex: "#EE6600")
- }
-
- public static var greenColor: UIColor {
- return renderColor(hex: "#C7EA46")
- }
-
- public static var grayColor: UIColor {
- return renderColor(hex: "#F5F5F5")
- }
-
- public static var docColor: UIColor {
- return renderColor(hex: "#798F9A")
- }
-
- public static var mentionColor: UIColor {
- return renderColor(hex: "#53bdea")
- }
-
- public static var blueBubbleColor: UIColor {
- return renderColor(hex: "#C5D1E1")
- }
-
- public static var officialColor: UIColor {
- return renderColor(hex: "#4c87ef")
- }
-
- public static var verifiedColor: UIColor {
- return renderColor(hex: "#00b333")
- }
-
- public static var ccColor: UIColor {
- return renderColor(hex: "#FFF1A353")
- }
-
- public static var internalColor: UIColor {
- return renderColor(hex: "#ff0000")
- }
-
- public class func renderColor(hex: String) -> UIColor {
- var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
- if (cString.hasPrefix("#")) {
- cString.remove(at: cString.startIndex)
- }
- if ((cString.count) != 6) {
- return UIColor.gray
- }
- var rgbValue:UInt64 = 0
- Scanner(string: cString).scanHexInt64(&rgbValue)
- return UIColor(
- red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
- green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
- blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
- alpha: CGFloat(1.0)
- )
- }
- }
- extension UIView {
-
- public func circle() {
- layer.cornerRadius = 0.5 * bounds.size.width
- clipsToBounds = true
- }
-
- public func maxCornerRadius() -> CGFloat {
- return (self.frame.width > self.frame.height) ? self.frame.height / 2 : self.frame.width / 2
- }
-
- }
- extension String {
-
- public func localized(uppercased: Bool = false) -> String {
- if let _ = UserDefaults.standard.string(forKey: "i18n_language") {} else {
- // we set a default, just in case
- let langDefault = UserDefaults.standard.stringArray(forKey: "AppleLanguages")
- if langDefault![0].contains("id") {
- UserDefaults.standard.set("id", forKey: "i18n_language")
- UserDefaults.standard.synchronize()
- } else {
- UserDefaults.standard.set("en", forKey: "i18n_language")
- UserDefaults.standard.synchronize()
- }
- }
- let lang = UserDefaults.standard.string(forKey: "i18n_language")
- let bundle = Bundle.resourceBundle(for: Nexilis.self).path(forResource: lang, ofType: "lproj")
- let bundlePath = Bundle(path: bundle!)
- let result = NSLocalizedString(
- self,
- tableName: "Localizable",
- bundle: bundlePath!,
- value: self,
- comment: self)
- if uppercased {
- return result.uppercased()
- }
- return result
- }
-
- }
- extension UIViewController {
-
- public func showToast(message : String, font: UIFont = UIFont.systemFont(ofSize: 12, weight: .medium), controller: UIViewController) {
-
- let toastContainer = UIView(frame: CGRect())
- toastContainer.backgroundColor = UIColor.mainColor.withAlphaComponent(0.6)
- toastContainer.alpha = 0.0
- toastContainer.layer.cornerRadius = 25;
- toastContainer.clipsToBounds = true
-
- let toastLabel = UILabel(frame: CGRect())
- toastLabel.textColor = UIColor.white
- toastLabel.textAlignment = .center;
- toastLabel.font = font
- toastLabel.text = message
- toastLabel.clipsToBounds = true
- toastLabel.numberOfLines = 0
-
- toastContainer.addSubview(toastLabel)
- controller.view.addSubview(toastContainer)
- controller.view.bringSubviewToFront(toastContainer)
-
- toastLabel.translatesAutoresizingMaskIntoConstraints = false
- toastContainer.translatesAutoresizingMaskIntoConstraints = false
-
- let a1 = NSLayoutConstraint(item: toastLabel, attribute: .leading, relatedBy: .equal, toItem: toastContainer, attribute: .leading, multiplier: 1, constant: 15)
- let a2 = NSLayoutConstraint(item: toastLabel, attribute: .trailing, relatedBy: .equal, toItem: toastContainer, attribute: .trailing, multiplier: 1, constant: -15)
- let a3 = NSLayoutConstraint(item: toastLabel, attribute: .bottom, relatedBy: .equal, toItem: toastContainer, attribute: .bottom, multiplier: 1, constant: -15)
- let a4 = NSLayoutConstraint(item: toastLabel, attribute: .top, relatedBy: .equal, toItem: toastContainer, attribute: .top, multiplier: 1, constant: 15)
- toastContainer.addConstraints([a1, a2, a3, a4])
-
- let c1 = NSLayoutConstraint(item: toastContainer, attribute: .leading, relatedBy: .equal, toItem: controller.view, attribute: .leading, multiplier: 1, constant: 65)
- let c2 = NSLayoutConstraint(item: toastContainer, attribute: .trailing, relatedBy: .equal, toItem: controller.view, attribute: .trailing, multiplier: 1, constant: -65)
- let c3 = NSLayoutConstraint(item: toastContainer, attribute: .bottom, relatedBy: .equal, toItem: controller.view, attribute: .bottom, multiplier: 1, constant: -75)
- controller.view.addConstraints([c1, c2, c3])
-
- UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveEaseIn, animations: {
- toastContainer.alpha = 1.0
- }, completion: { _ in
- UIView.animate(withDuration: 0.5, delay: 1.5, options: .curveEaseOut, animations: {
- toastContainer.alpha = 0.0
- }, completion: {_ in
- toastContainer.removeFromSuperview()
- })
- })
- }
-
- public func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
- UIGraphicsBeginImageContextWithOptions(targetSize, false, 0.0);
- image.draw(in: CGRect(x: 0, y: 0, width: targetSize.width, height: targetSize.height))
- let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
- UIGraphicsEndImageContext()
- return newImage
- }
-
- }
- extension UITextView {
- enum ShouldChangeCursor {
- case incrementCursor
- case preserveCursor
- }
- func preserveCursorPosition(withChanges mutatingFunction: (UITextPosition?) -> (ShouldChangeCursor)) {
- //save the cursor positon
- var cursorPosition: UITextPosition? = nil
- if let selectedRange = self.selectedTextRange {
- let offset = self.offset(from: self.beginningOfDocument, to: selectedRange.start)
- cursorPosition = self.position(from: self.beginningOfDocument, offset: offset)
- }
- //make mutaing changes that may reset the cursor position
- let shouldChangeCursor = mutatingFunction(cursorPosition)
- //restore the cursor
- if var cursorPosition = cursorPosition {
- if shouldChangeCursor == .incrementCursor {
- cursorPosition = self.position(from: cursorPosition, offset: 1) ?? cursorPosition
- }
- if let range = self.textRange(from: cursorPosition, to: cursorPosition) {
- self.selectedTextRange = range
- }
- }
- }
- }
- extension String {
-
- public func substring(from: Int?, to: Int?) -> String {
- if let start = from {
- guard start < self.count else {
- return ""
- }
- }
-
- if let end = to {
- guard end >= 0 else {
- return ""
- }
- }
-
- if let start = from, let end = to {
- guard end - start >= 0 else {
- return ""
- }
- }
-
- let startIndex: String.Index
- if let start = from, start >= 0 {
- startIndex = self.index(self.startIndex, offsetBy: start)
- } else {
- startIndex = self.startIndex
- }
-
- let endIndex: String.Index
- if let end = to, end >= 0, end < self.count {
- endIndex = self.index(self.startIndex, offsetBy: end + 1)
- } else {
- endIndex = self.endIndex
- }
-
- return String(self[startIndex ..< endIndex])
- }
-
- func countEmojiCharacter() -> Int {
-
- func isEmoji(s:NSString) -> Bool {
-
- let high:Int = Int(s.character(at: 0))
- if 0xD800 <= high && high <= 0xDBFF {
- let low:Int = Int(s.character(at: 1))
- let codepoint: Int = ((high - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000
- return (0x1D000 <= codepoint && codepoint <= 0x1F9FF)
- }
- else {
- return (0x2100 <= high && high <= 0x27BF)
- }
- }
-
- let nsString = self as NSString
- var length = 0
-
- nsString.enumerateSubstrings(in: NSMakeRange(0, nsString.length), options: NSString.EnumerationOptions.byComposedCharacterSequences) { (subString, substringRange, enclosingRange, stop) -> Void in
-
- if isEmoji(s: subString! as NSString) {
- length+=1
- }
- }
-
- return length
- }
-
- public func richText(isEditing: Bool = false, isSearching: Bool = false, textSearch: String = "", group_id: String = "", listMentionInTextField: [User] = []) -> NSMutableAttributedString {
- let font = UIFont.systemFont(ofSize: 12)
- let boldFont = UIFont.boldSystemFont(ofSize: 12)
- let italicFont = UIFont.italicSystemFont(ofSize: 12)
- let boldItalicFont = UIFont.systemFont(ofSize: 12, weight: .semibold)
- let textUTF8 = String(self.utf8)
- let finalText = NSMutableAttributedString(string: textUTF8, attributes: [NSAttributedString.Key.font: font])
- let boldSign: Character = "*"
- let italicSign: Character = "_"
- let underlineSign: Character = "^"
- let strikethroughSign: Character = "~"
- var locationBold: [NSRange] = []
-
- //Bold
- let rangeBold = getRangeOfWordWithSign(sentence: textUTF8, sign: boldSign)
- if rangeBold.count > 0 {
- var lastFirstRange = -1
- var countRemoveBoldSign = 0
- var continueCheckingBold = false
- var totalEmoji = 0
- for i in 0..<rangeBold.count {
- if rangeBold[i].startIndex > lastFirstRange {
- let charStart: Character = rangeBold[i].startIndex != 0 ? Array(textUTF8.substring(from: rangeBold[i].startIndex - 1, to: rangeBold[i].startIndex - 1))[0] : Array("0")[0]
- if (rangeBold[i].startIndex == 0 || (!charStart.isLetter && !charStart.isNumber)) {
- lastFirstRange = rangeBold[i].startIndex
- continueCheckingBold = true
- } else {
- continueCheckingBold = false
- }
- }
- if !continueCheckingBold {
- continue
- }
- if rangeBold[i].endIndex != (textUTF8.count-1) {
- let char: Character = Array(textUTF8.substring(from: rangeBold[i].endIndex + 1, to: rangeBold[i].endIndex + 1))[0]
- if char.isLetter || char.isNumber {
- continue
- }
- }
- let countEmojiBefore = finalText.string.substring(from: 0, to: lastFirstRange - (2*countRemoveBoldSign)).countEmojiCharacter()
- let countEmoji = finalText.string.substring(from: lastFirstRange - (2*countRemoveBoldSign), to: rangeBold[i].endIndex - (2*countRemoveBoldSign)).countEmojiCharacter()
- totalEmoji = countEmoji + countEmojiBefore
- locationBold.append(NSRange(location: lastFirstRange - (2*countRemoveBoldSign) + countEmojiBefore, length: (rangeBold[i].endIndex + countEmoji + 1) - lastFirstRange))
- finalText.addAttribute(.font, value: boldFont, range: NSRange(location: lastFirstRange - (2*countRemoveBoldSign) + countEmojiBefore, length: (rangeBold[i].endIndex + countEmoji + 1) - lastFirstRange))
- if !isEditing{
- finalText.mutableString.replaceOccurrences(of: "\(boldSign)", with: "", options: .literal, range: NSRange(location: lastFirstRange + countEmojiBefore - (2*countRemoveBoldSign), length: 1))
- finalText.mutableString.replaceOccurrences(of: "\(boldSign)", with: "", options: .literal, range: NSRange(location: rangeBold[i].endIndex + totalEmoji - (2*countRemoveBoldSign) - 1, length: 1))
- countRemoveBoldSign += 1
- } else {
- finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(location: lastFirstRange + countEmojiBefore, length: 1))
- finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(location: rangeBold[i].endIndex + totalEmoji, length: 1))
- }
- lastFirstRange = rangeBold[i].endIndex
- continueCheckingBold = false
- }
- }
-
- //Italic
- let textAfterbold = finalText.string
- let rangeItalic = getRangeOfWordWithSign(sentence: textAfterbold, sign: italicSign)
- if rangeItalic.count > 0 {
- var lastFirstRange = -1
- var countRemoveItalicSign = 0
- var continueCheckingItalic = false
- var totalEmoji = 0
- for i in 0..<rangeItalic.count {
- if rangeItalic[i].startIndex > lastFirstRange {
- let charStart: Character = rangeItalic[i].startIndex != 0 ? Array(textUTF8.substring(from: rangeItalic[i].startIndex - 1, to: rangeItalic[i].startIndex - 1))[0] : Array("0")[0]
- if (rangeItalic[i].startIndex == 0 || (!charStart.isLetter && !charStart.isNumber)) {
- lastFirstRange = rangeItalic[i].startIndex
- continueCheckingItalic = true
- } else {
- continueCheckingItalic = false
- }
- }
- if !continueCheckingItalic {
- continue
- }
- if rangeItalic[i].endIndex != (textAfterbold.count-1) {
- let char: Character = Array(textAfterbold.substring(from: rangeItalic[i].endIndex + 1, to: rangeItalic[i].endIndex + 1))[0]
- if char.isLetter || char.isNumber {
- continue
- }
- }
- let countEmojiBefore = finalText.string.substring(from: 0, to: lastFirstRange - (2*countRemoveItalicSign)).countEmojiCharacter()
- let countEmoji = finalText.string.substring(from: lastFirstRange - (2*countRemoveItalicSign), to: rangeItalic[i].endIndex - (2*countRemoveItalicSign)).countEmojiCharacter()
- totalEmoji = countEmoji + countEmojiBefore
- if isIntInRangeList((rangeItalic[i].endIndex + countEmoji + 1) - lastFirstRange - lastFirstRange - (2*countRemoveItalicSign) + countEmojiBefore, rangeList: locationBold) {
- finalText.addAttribute(.font, value: boldItalicFont, range: NSRange(location: lastFirstRange - (2*countRemoveItalicSign) + countEmojiBefore, length: (rangeItalic[i].endIndex + countEmoji + 1) - lastFirstRange))
- } else {
- finalText.addAttribute(.font, value: italicFont, range: NSRange(location: lastFirstRange - (2*countRemoveItalicSign) + countEmojiBefore, length: (rangeItalic[i].endIndex + countEmoji + 1) - lastFirstRange))
- }
- if !isEditing{
- finalText.mutableString.replaceOccurrences(of: "\(italicSign)", with: "", options: .literal, range: NSRange(location: lastFirstRange + countEmojiBefore - (2*countRemoveItalicSign), length: 1))
- finalText.mutableString.replaceOccurrences(of: "\(italicSign)", with: "", options: .literal, range: NSRange(location: rangeItalic[i].endIndex + totalEmoji - (2*countRemoveItalicSign) - 1, length: 1))
- countRemoveItalicSign += 1
- } else {
- finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(location: lastFirstRange + countEmojiBefore, length: 1))
- finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(location: rangeItalic[i].endIndex + totalEmoji, length: 1))
- }
- lastFirstRange = rangeItalic[i].endIndex
- continueCheckingItalic = false
- }
- }
-
- //Underline
- let textAfterItalic = finalText.string
- let rangeUnderline = getRangeOfWordWithSign(sentence: textAfterItalic, sign: underlineSign)
- if rangeUnderline.count > 0 {
- var lastFirstRange = -1
- var countRemoveUnderlineSign = 0
- var continueCheckingUnderline = false
- var totalEmoji = 0
- for i in 0..<rangeUnderline.count {
- if rangeUnderline[i].startIndex > lastFirstRange {
- let charStart: Character = rangeUnderline[i].startIndex != 0 ? Array(textUTF8.substring(from: rangeUnderline[i].startIndex - 1, to: rangeUnderline[i].startIndex - 1))[0] : Array("0")[0]
- if (rangeUnderline[i].startIndex == 0 || (!charStart.isLetter && !charStart.isNumber)) {
- lastFirstRange = rangeUnderline[i].startIndex
- continueCheckingUnderline = true
- } else {
- continueCheckingUnderline = false
- }
- }
- if !continueCheckingUnderline {
- continue
- }
- if rangeUnderline[i].endIndex != (textAfterItalic.count-1) {
- let char: Character = Array(textAfterItalic.substring(from: rangeUnderline[i].endIndex + 1, to: rangeUnderline[i].endIndex + 1))[0]
- if char.isLetter || char.isNumber {
- continue
- }
- }
- let countEmojiBefore = finalText.string.substring(from: 0, to: lastFirstRange - (2*countRemoveUnderlineSign)).countEmojiCharacter()
- let countEmoji = finalText.string.substring(from: lastFirstRange - (2*countRemoveUnderlineSign), to: rangeUnderline[i].endIndex - (2*countRemoveUnderlineSign)).countEmojiCharacter()
- totalEmoji = countEmoji + countEmojiBefore
- finalText.addAttribute(.underlineStyle, value: NSUnderlineStyle.thick.rawValue, range: NSRange(location: lastFirstRange - (2*countRemoveUnderlineSign) + countEmojiBefore + 1, length: (rangeUnderline[i].endIndex + countEmoji - 1) - lastFirstRange))
- if !isEditing{
- finalText.mutableString.replaceOccurrences(of: "\(underlineSign)", with: "", options: .literal, range: NSRange(location: lastFirstRange + countEmojiBefore - (2*countRemoveUnderlineSign), length: 1))
- finalText.mutableString.replaceOccurrences(of: "\(underlineSign)", with: "", options: .literal, range: NSRange(location: rangeUnderline[i].endIndex + totalEmoji - (2*countRemoveUnderlineSign) - 1, length: 1))
- countRemoveUnderlineSign += 1
- } else {
- finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(location: lastFirstRange + countEmojiBefore, length: 1))
- finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(location: rangeUnderline[i].endIndex + totalEmoji, length: 1))
- }
- lastFirstRange = rangeUnderline[i].endIndex
- continueCheckingUnderline = false
- }
- }
-
- //Strikethrough
- let textAfterUnderline = finalText.string
- let rangeStrikethrough = getRangeOfWordWithSign(sentence: textAfterUnderline, sign: strikethroughSign)
- if rangeStrikethrough.count > 0 {
- var lastFirstRange = -1
- var countRemoveStrikethroughSign = 0
- var continueCheckingStrikethrough = false
- var totalEmoji = 0
- for i in 0..<rangeStrikethrough.count {
- if rangeStrikethrough[i].startIndex > lastFirstRange {
- let charStart: Character = rangeStrikethrough[i].startIndex != 0 ? Array(textUTF8.substring(from: rangeStrikethrough[i].startIndex - 1, to: rangeStrikethrough[i].startIndex - 1))[0] : Array("0")[0]
- if (rangeStrikethrough[i].startIndex == 0 || (!charStart.isLetter && !charStart.isNumber)) {
- lastFirstRange = rangeStrikethrough[i].startIndex
- continueCheckingStrikethrough = true
- } else {
- continueCheckingStrikethrough = false
- }
- }
- if !continueCheckingStrikethrough {
- continue
- }
- if rangeStrikethrough[i].endIndex != (textAfterUnderline.count-1) {
- let char: Character = Array(textAfterUnderline.substring(from: rangeStrikethrough[i].endIndex + 1, to: rangeStrikethrough[i].endIndex + 1))[0]
- if char.isLetter || char.isNumber {
- continue
- }
- }
- let countEmojiBefore = finalText.string.substring(from: 0, to: lastFirstRange - (2*countRemoveStrikethroughSign)).countEmojiCharacter()
- let countEmoji = finalText.string.substring(from: lastFirstRange - (2*countRemoveStrikethroughSign), to: rangeStrikethrough[i].endIndex - (2*countRemoveStrikethroughSign)).countEmojiCharacter()
- totalEmoji = countEmoji + countEmojiBefore
- finalText.addAttribute(.strikethroughStyle, value: 2, range: NSRange(location: lastFirstRange - (2*countRemoveStrikethroughSign) + countEmojiBefore + 1, length: (rangeStrikethrough[i].endIndex + countEmoji - 1) - lastFirstRange))
- if !isEditing{
- finalText.mutableString.replaceOccurrences(of: "\(strikethroughSign)", with: "", options: .literal, range: NSRange(location: lastFirstRange + countEmojiBefore - (2*countRemoveStrikethroughSign), length: 1))
- finalText.mutableString.replaceOccurrences(of: "\(strikethroughSign)", with: "", options: .literal, range: NSRange(location: rangeStrikethrough[i].endIndex + totalEmoji - (2*countRemoveStrikethroughSign) - 1, length: 1))
- countRemoveStrikethroughSign += 1
- } else {
- finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(location: lastFirstRange + countEmojiBefore, length: 1))
- finalText.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(location: rangeStrikethrough[i].endIndex + totalEmoji, length: 1))
- }
- lastFirstRange = rangeStrikethrough[i].endIndex
- continueCheckingStrikethrough = false
- }
- }
-
- //Check Mention
- let finalTextAfterRichText = finalText.string
- if finalTextAfterRichText.contains("@") {
- let listTextEnter = finalText.string.split(separator: "\n")
- for j in 0...listTextEnter.count - 1 {
- let listText = listTextEnter[j].split(separator: " ")
- let listMention = listText.filter({ $0.starts(with: "@")})
- if listMention.count > 0 {
- for i in 0..<listMention.count {
- let f_pin = (String(listMention[i])).substring(from: 1, to: listMention[i].count)
- let member = Member.getMember(f_pin: f_pin)
- if member != nil {
- let name = (member!.firstName + " " + member!.lastName).trimmingCharacters(in: .whitespaces)
- finalText.mutableString.replaceOccurrences(of: f_pin, with: name, options: .literal, range: NSString(string: finalText.string).range(of: f_pin))
- if !group_id.isEmpty && Member.getMemberInGroup(f_pin: f_pin, group_id: group_id) != nil {
- finalText.addAttribute(.foregroundColor, value: UIColor.mentionColor, range: NSString(string: finalText.string).range(of: "@\(name)"))
- }
- }
- }
- }
- }
- }
- if isSearching {
- let range = NSString(string: finalText.string).range(of: textSearch, options: .caseInsensitive) // 2
- let highlightColor = UIColor.systemYellow // 3
- let highlightedAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.backgroundColor: highlightColor] // 4
-
- finalText.addAttributes(highlightedAttributes, range: range) // 5
- }
-
- return finalText
- }
-
- func isIntInRangeList(_ intValue: Int, rangeList: [NSRange]) -> Bool {
- for range in rangeList {
- if intValue >= range.location && intValue < range.location + range.length {
- return true
- }
- }
- return false
- }
-
- func checkCharBefore(char: String) -> Bool {
- return char == " " || char == "\n"
- }
-
- func checkCharRich(char: String) -> Bool {
- return char == "*" || char == "_" || char == "^" || char == "~"
- }
-
- func checkStartWithLink() -> Bool {
- return self.starts(with: "https://") || self.starts(with: "http://") || self.starts(with: "www.")
- //|| self.starts(with: "*https://") || self.starts(with: "*http://") || self.starts(with: "*www.") || self.starts(with: "_https://") || self.starts(with: "_http://") || self.starts(with: "_www.") || self.starts(with: "^https://") || self.starts(with: "^http://") || self.starts(with: "^www.") || self.starts(with: "~https://") || self.starts(with: "~http://") || self.starts(with: "~www.")
- }
-
- func getRangeOfWordWithSign(sentence: String, sign: Character) -> [Range<Int>] {
- let asterisk: Character = sign
- var ranges = [Range<Int>]()
-
- var startIndex = sentence.startIndex
-
- while let startRange = sentence[startIndex...].firstIndex(of: asterisk),
- let endRange = sentence[startRange...].dropFirst().firstIndex(of: asterisk) {
-
- let range = startRange..<endRange
- let word = sentence[range].replacingOccurrences(of: "\(sign)", with: "")
-
- if !word.isEmpty {
- let lower = sentence.distance(from: sentence.startIndex, to: startRange)
- let upper = sentence.distance(from: sentence.startIndex, to: endRange)
- ranges.append(Range(uncheckedBounds: (lower: lower, upper: upper)))
- }
-
- startIndex = endRange
- }
-
- return ranges
- }
-
- }
- extension UIFont {
- var bold: UIFont {
- return with(traits: .traitBold)
- } // bold
-
- var italic: UIFont {
- return with(traits: .traitItalic)
- } // italic
-
- func with(traits: UIFontDescriptor.SymbolicTraits) -> UIFont {
- guard let descriptor = self.fontDescriptor.withSymbolicTraits(traits) else {
- return self
- } // guard
-
- return UIFont(descriptor: descriptor, size: 0)
- } // with(traits:)
- }
- extension UILabel {
- public func set(image: UIImage, with text: String, size: CGFloat, y: CGFloat) {
- let attachment = NSTextAttachment()
- attachment.image = image
- attachment.bounds = CGRect(x: 0, y: y, width: size, height: size)
- let attachmentStr = NSAttributedString(attachment: attachment)
-
- let mutableAttributedString = NSMutableAttributedString()
- mutableAttributedString.append(attachmentStr)
-
- let textString = NSAttributedString(string: text, attributes: [.font: self.font!])
- mutableAttributedString.append(textString)
-
- self.attributedText = mutableAttributedString
- }
-
- public func setAttributeText(image: UIImage, with textMutable: NSAttributedString, size: CGFloat, y: CGFloat) {
- let attachment = NSTextAttachment()
- attachment.image = image
- attachment.bounds = CGRect(x: 0, y: y, width: size, height: size)
- let attachmentStr = NSAttributedString(attachment: attachment)
-
- let mutableAttributedString = NSMutableAttributedString()
- mutableAttributedString.append(attachmentStr)
-
- mutableAttributedString.append(textMutable)
-
- self.attributedText = mutableAttributedString
- }
- }
- extension Bundle {
- public static func resourceBundle(for frameworkClass: AnyClass) -> Bundle {
- let frameworkBundle = Bundle(for: frameworkClass)
- guard let resourceBundleURL = frameworkBundle.url(forResource: "NexilisLite", withExtension: "bundle"),
- let resourceBundle = Bundle(url: resourceBundleURL) else {
- return frameworkBundle
- }
- return resourceBundle
- }
-
- public static func resourcesMediaBundle(for frameworkClass: AnyClass) -> Bundle {
- let frameworkBundle = Bundle(for: frameworkClass)
- guard let resourceBundleURL = frameworkBundle.url(forResource: "NexilisLiteResources", withExtension: "bundle"),
- let resourceBundle = Bundle(url: resourceBundleURL) else {
- //print("\(moduleName).bundle not found in \(frameworkBundle)")
- return frameworkBundle
- }
- return resourceBundle
- }
-
- }
- //extension UIFont {
- //
- // static func register(from url: URL) throws {
- // guard let fontDataProvider = CGDataProvider(url: url as CFURL) else {
- // throw fatalError("Could not create font data provider for \(url).")
- // }
- // let font = CGFont(fontDataProvider)
- // var error: Unmanaged<CFError>?
- // guard CTFontManagerRegisterGraphicsFont(font!, &error) else {
- // throw error!.takeUnretainedValue()
- // }
- // }
- //
- //}
- extension UIButton {
- private func actionHandleBlock(action:(() -> Void)? = nil) {
- struct __ {
- static var action :(() -> Void)?
- }
- if action != nil {
- __.action = action
- } else {
- __.action?()
- }
- }
-
- @objc private func triggerActionHandleBlock() {
- self.actionHandleBlock()
- }
-
- public func actionHandle(controlEvents control :UIControl.Event, ForAction action:@escaping () -> Void) {
- self.actionHandleBlock(action: action)
- self.addTarget(self, action: #selector(self.triggerActionHandleBlock), for: control)
- }
- }
- extension UINavigationController {
- func replaceAllViewController(with viewController: UIViewController, animated: Bool) {
- pushViewController(viewController, animated: animated)
- viewControllers.removeSubrange(1...viewControllers.count - 2)
- }
-
- var rootViewController : UIViewController? {
- return viewControllers.first
- }
-
- func popViewController(animated: Bool, completion: @escaping () -> Void) {
- popViewController(animated: animated)
- if animated, let coordinator = transitionCoordinator {
- coordinator.animate(alongsideTransition: nil) { _ in
- completion()
- }
- } else {
- completion()
- }
- }
- }
- extension UIImageView {
- private static var taskKey = 0
- private static var urlKey = 0
- private var currentTask: URLSessionTask? {
- get { return objc_getAssociatedObject(self, &UIImageView.taskKey) as? URLSessionTask }
- set { objc_setAssociatedObject(self, &UIImageView.taskKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
- }
- private var currentURL: URL? {
- get { return objc_getAssociatedObject(self, &UIImageView.urlKey) as? URL }
- set { objc_setAssociatedObject(self, &UIImageView.urlKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
- }
- func loadImageAsync(with urlString: String?) {
- // cancel prior task, if any
- weak var oldTask = currentTask
- currentTask = nil
- oldTask?.cancel()
- // reset imageview's image
- self.image = nil
- // allow supplying of `nil` to remove old image and then return immediately
- guard let urlString = urlString else { return }
- // check cache
- if let cachedImage = ImageCache.shared.image(forKey: urlString) {
- self.image = cachedImage
- return
- }
- // download
- let url = URL(string: urlString)!
- currentURL = url
- let task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
- self?.currentTask = nil
- //error handling
- if let error = error {
- // don't bother reporting cancelation errors
- if (error as NSError).domain == NSURLErrorDomain && (error as NSError).code == NSURLErrorCancelled {
- return
- }
- //print(error)
- return
- }
- guard let data = data, let downloadedImage = UIImage(data: data) else {
- //print("unable to extract image")
- return
- }
- ImageCache.shared.save(image: downloadedImage, forKey: urlString)
- if url == self?.currentURL {
- DispatchQueue.main.async {
- self?.image = downloadedImage
- }
- }
- }
- // save and start new task
- currentTask = task
- task.resume()
- }
-
- private func actionHandleBlock(action:(() -> Void)? = nil) {
- struct __ {
- static var action :(() -> Void)?
- }
- if action != nil {
- __.action = action
- } else {
- __.action?()
- }
- }
-
- @objc private func triggerActionHandleBlock() {
- self.actionHandleBlock()
- }
-
- func actionHandle(controlEvents control :UIControl.Event, ForAction action:@escaping () -> Void) {
- self.actionHandleBlock(action: action)
- self.isUserInteractionEnabled = true
- self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.triggerActionHandleBlock)))
- }
- }
- extension UITextField {
- public enum PaddingSide {
- case left(CGFloat)
- case right(CGFloat)
- case both(CGFloat)
- }
- public func addPadding(_ padding: PaddingSide) {
- self.leftViewMode = .always
- self.layer.masksToBounds = true
- switch padding {
- case .left(let spacing):
- let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
- self.leftView = paddingView
- self.rightViewMode = .always
- case .right(let spacing):
- let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
- self.rightView = paddingView
- self.rightViewMode = .always
- case .both(let spacing):
- let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
- // left
- self.leftView = paddingView
- self.leftViewMode = .always
- // right
- self.rightView = paddingView
- self.rightViewMode = .always
- }
- }
- }
- class ImageCache {
- private let cache = NSCache<NSString, UIImage>()
- private var observer: NSObjectProtocol!
- static let shared = ImageCache()
- private init() {
- // make sure to purge cache on memory pressure
- observer = NotificationCenter.default.addObserver(forName: UIApplication.didReceiveMemoryWarningNotification, object: nil, queue: nil) { [weak self] notification in
- self?.cache.removeAllObjects()
- }
- }
- deinit {
- NotificationCenter.default.removeObserver(observer as Any)
- }
- func image(forKey key: String) -> UIImage? {
- return cache.object(forKey: key as NSString)
- }
- func save(image: UIImage, forKey key: String) {
- cache.setObject(image, forKey: key as NSString)
- }
- }
- public class LibAlertController: UIAlertController {
- public override func viewWillAppear(_ animated: Bool) {
- super.viewWillAppear(animated)
-
- // Customize the title's font
- let titleFont = UIFont.boldSystemFont(ofSize: 16)
- let titleAttributes = [NSAttributedString.Key.font: titleFont]
- setValue(NSAttributedString(string: self.title ?? "", attributes: titleAttributes), forKey: "attributedTitle")
-
- // Change the font for the message
- let messageFont = UIFont.systemFont(ofSize: 14)
- let messageAttributes = [NSAttributedString.Key.font: messageFont]
- setValue(NSAttributedString(string: self.message ?? "", attributes: messageAttributes), forKey: "attributedMessage")
-
- for i in self.actions {
- let attributedText = NSAttributedString(string: i.title ?? "", attributes: [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)])
- guard let label = (i.value(forKey: "__representer") as AnyObject).value(forKey: "label") as? UILabel else { return }
- label.attributedText = attributedText
- }
- }
- }
- extension UISearchBar
- {
- func setMagnifyingGlassColorTo(color: UIColor)
- {
- // Search Icon
- let textFieldInsideSearchBar = self.value(forKey: "searchField") as? UITextField
- let glassIconView = textFieldInsideSearchBar?.leftView as? UIImageView
- glassIconView?.image = glassIconView?.image?.withRenderingMode(.alwaysTemplate)
- glassIconView?.tintColor = color
- }
- func setClearButtonColorTo(color: UIColor)
- {
- // Clear Button
- let textFieldInsideSearchBar = self.value(forKey: "searchField") as? UITextField
- let crossIconView = textFieldInsideSearchBar?.value(forKey: "clearButton") as? UIButton
- crossIconView?.setImage(crossIconView?.currentImage?.withRenderingMode(.alwaysTemplate), for: .normal)
- crossIconView?.tintColor = color
- }
- func setPlaceholderTextColorTo(color: UIColor)
- {
- let textFieldInsideSearchBar = self.value(forKey: "searchField") as? UITextField
- textFieldInsideSearchBar?.textColor = color
- let textFieldInsideSearchBarLabel = textFieldInsideSearchBar!.value(forKey: "placeholderLabel") as? UILabel
- textFieldInsideSearchBarLabel?.textColor = color
- }
- }
- extension String {
- init(unicodeScalar: UnicodeScalar) {
- self.init(Character(unicodeScalar))
- }
- init?(unicodeCodepoint: Int) {
- if let unicodeScalar = UnicodeScalar(unicodeCodepoint) {
- self.init(unicodeScalar: unicodeScalar)
- } else {
- return nil
- }
- }
- static func +(lhs: String, rhs: Int) -> String {
- return lhs + String(unicodeCodepoint: rhs)!
- }
- static func +=(lhs: inout String, rhs: Int) {
- lhs = lhs + rhs
- }
- }
- extension UIGraphicsRenderer {
- static func renderImagesAt(urls: [NSURL], size: CGSize, scale: CGFloat = 1) -> UIImage {
- let renderer = UIGraphicsImageRenderer(size: size)
- let options: [NSString: Any] = [
- kCGImageSourceThumbnailMaxPixelSize: max(size.width * scale, size.height * scale),
- kCGImageSourceCreateThumbnailFromImageAlways: true
- ]
- let thumbnails = try urls.map { url -> CGImage in
- let imageSource = CGImageSourceCreateWithURL(url, nil)
- let scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource!, 0, options as CFDictionary)
-
- return scaledImage!
- }
- // Translate Y-axis down because cg images are flipped and it falls out of the frame (see bellow)
- let rect = CGRect(x: 0,
- y: -size.height,
- width: size.width,
- height: size.height)
- let resizedImage = renderer.image { ctx in
- let context = ctx.cgContext
- context.scaleBy(x: 1, y: -1) //Flip it ( cg y-axis is flipped)
- for image in thumbnails {
- context.draw(image, in: rect)
- }
- }
- return resizedImage
- }
-
- static func renderImageAt(url: NSURL, size: CGSize, scale: CGFloat = 1) -> UIImage {
- return renderImagesAt(urls: [url], size: size, scale: scale)
- }
- }
|