diff --git a/Package.swift b/Package.swift index 82b349f..a03b160 100644 --- a/Package.swift +++ b/Package.swift @@ -5,6 +5,9 @@ import PackageDescription let package = Package( name: "SwiftExtensions", + platforms: [ + .iOS(.v14) + ], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( @@ -21,8 +24,32 @@ let package = Package( .target( name: "SwiftExtensions", dependencies: []), - .testTarget( - name: "SwiftExtensionsTests", - dependencies: ["SwiftExtensions"]), +// .testTarget( +// name: "SwiftExtensionsTests", +// dependencies: ["SwiftExtensions"]), ] ) + +/* + name: "textStickersSwiftUIViews", + platforms: [ + .iOS(.v14) + ], + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "textStickersSwiftUIViews", + targets: ["textStickersSwiftUIViews"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "textStickersSwiftUIViews", + dependencies: []) + ] + */ diff --git a/README.md b/README.md index febedfc..b5c3243 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # SwiftExtensions -A description of this package. +Handy extensions for use in different projects. diff --git a/Sources/SwiftExtensions/AnyTransition+scaleAndFade.swift b/Sources/SwiftExtensions/AnyTransition+scaleAndFade.swift new file mode 100644 index 0000000..c61bd10 --- /dev/null +++ b/Sources/SwiftExtensions/AnyTransition+scaleAndFade.swift @@ -0,0 +1,18 @@ +// +// AnyTransition+scaleAndFade.swift +// kaomojiEmoticonKeyboard +// +// Created by Robert McGovern on 16/02/2021. +// + +import SwiftUI + +extension AnyTransition { + static var scaleAndFade: AnyTransition { + let insertion = AnyTransition.scale + .combined(with: .opacity) + let removal = AnyTransition.scale + .combined(with: .opacity).animation(Animation.easeOut.speed(10)) + return .asymmetric(insertion: insertion, removal: removal) + } +} diff --git a/Sources/SwiftExtensions/Array+removeDuplicates.swift b/Sources/SwiftExtensions/Array+removeDuplicates.swift new file mode 100644 index 0000000..3e8a672 --- /dev/null +++ b/Sources/SwiftExtensions/Array+removeDuplicates.swift @@ -0,0 +1,27 @@ +// +// Array+removeDuplicates.swift +// kaomojiEmoticonKeyboard +// +// Created by Robert McGovern on 17/02/2021. +// +// extensoin from Paul Hudson - https://www.hackingwithswift.com/example-code/language/how-to-remove-duplicate-items-from-an-array + +import Foundation + +/* + provides two methods: one called removingDuplicates() that returns an array with duplicates removed, and one called removeDuplicates() that changes the array in place. + */ + +extension Array where Element: Hashable { + func removingDuplicates() -> [Element] { + var addedDict = [Element: Bool]() + + return filter { + addedDict.updateValue(true, forKey: $0) == nil + } + } + + mutating func removeDuplicates() { + self = self.removingDuplicates() + } +} diff --git a/Sources/SwiftExtensions/Binding+didSet.swift b/Sources/SwiftExtensions/Binding+didSet.swift new file mode 100644 index 0000000..0c91916 --- /dev/null +++ b/Sources/SwiftExtensions/Binding+didSet.swift @@ -0,0 +1,23 @@ +// +// Bindig+didSet.swift +// kaomojiEmoticonKeyboard +// +// Created by Robert McGovern on 26/11/2020. +// + +import SwiftUI + +extension Binding { + func didSet(execute: @escaping (Value) -> Void) -> Binding { + return Binding( + get: { + return self.wrappedValue + }, + set: { + self.wrappedValue = $0 + execute($0) + } + ) + } +} + diff --git a/Sources/SwiftExtensions/Color+fromString+ToString.swift b/Sources/SwiftExtensions/Color+fromString+ToString.swift new file mode 100644 index 0000000..6e45bac --- /dev/null +++ b/Sources/SwiftExtensions/Color+fromString+ToString.swift @@ -0,0 +1,31 @@ +// +// Color+fromString+ToString.swift +// kaomojiEmoticonKeyboard +// +// Created by Robert McGovern on 25/11/2020. +// + +import SwiftUI + +extension Color { + func toString() -> String { + let uiColor = UIColor(self) + var red: CGFloat = 0 + var green: CGFloat = 0 + var blue: CGFloat = 0 + var alpha: CGFloat = 0 + + uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) + + return "\(red),\(green),\(blue),\(alpha)" + } + + static func fromString(_ string: String) -> Color { + let rgbArray = string.components(separatedBy: ",") + if let red = Double(rgbArray[0]), let green = Double(rgbArray[1]), let blue = Double(rgbArray[2]), let alpha = Double(rgbArray[3]) { + return Color(.sRGB, red: red, green: green, blue: blue, opacity: alpha) + } else { + return Color.pink + } + } +} diff --git a/Sources/SwiftExtensions/Color+random.swift b/Sources/SwiftExtensions/Color+random.swift new file mode 100644 index 0000000..fa6077e --- /dev/null +++ b/Sources/SwiftExtensions/Color+random.swift @@ -0,0 +1,14 @@ +// +// Color+random.swift +// kaomojiEmoticonKeyboard (iOS) +// +// Created by Robert McGovern on 24/11/2020. +// + +import SwiftUI + +extension Color { + static var random: Color { + [Color.red, Color.orange, Color.yellow, Color.blue, Color.purple, Color.pink, Color.green].randomElement()! + } +} diff --git a/Sources/SwiftExtensions/Directories.swift b/Sources/SwiftExtensions/Directories.swift new file mode 100644 index 0000000..ea5c891 --- /dev/null +++ b/Sources/SwiftExtensions/Directories.swift @@ -0,0 +1,102 @@ +// +// View+directories.swift +// kaomojiEmoticonKeyboard (iOS) +// +// Created by Robert McGovern on 24/11/2020. +// + +import SwiftUI + +class Directories { + + // Get user's documents directory path + static func getDocumentsDirectoryPath() -> URL { + let arrayPaths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) + let docDirectoryPath = arrayPaths[0] + return docDirectoryPath + } + + // Get user's cache directory path + static func getCacheDirectoryPath() -> URL { + let arrayPaths = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask) + let cacheDirectoryPath = arrayPaths[0] + return cacheDirectoryPath + } + + // Get user's temp directory path + static func getTempDirectoryPath() -> URL { + let tempDirectoryPath = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) + return tempDirectoryPath + } + + // Get user's temp directory path + static func getAppSharedGroupPath() -> URL? { + let sharedContainerURL :URL? = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.net.tarasis.kaomojiEmoticonKeyboard") + + return sharedContainerURL + } + + static func clearCache(){ + let cacheURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! + let fileManager = FileManager.default + do { + // Get the directory contents urls (including subfolders urls) + let directoryContents = try FileManager.default.contentsOfDirectory( at: cacheURL, includingPropertiesForKeys: nil, options: []) + for file in directoryContents { + do { + try fileManager.removeItem(at: file) + } + catch let error as NSError { + debugPrint("Ooops! Something went wrong: \(error)") + } + + } + } catch let error as NSError { + print(error) + } + } + + static func clearAppGroup(){ + guard let cacheURL = getAppSharedGroupPath() else { fatalError("Error getting app group url") } + let fileManager = FileManager.default + do { + // Get the directory contents urls (including subfolders urls) + let directoryContents = try FileManager.default.contentsOfDirectory( at: cacheURL, includingPropertiesForKeys: nil, options: []) + for file in directoryContents { + do { + try fileManager.removeItem(at: file) + } + catch let error as NSError { + debugPrint("Ooops! Something went wrong: \(error)") + } + + } + } catch let error as NSError { + print(error) + } + } + + + static func clearCachedStickers(){ + guard let cacheURL = getAppSharedGroupPath() else { fatalError("Error getting app group url") } + + let stickerDirectoryPath = cacheURL.appendingPathComponent("Stickers") + + let fileManager = FileManager.default + do { + // Get the directory contents urls (including subfolders urls) + let directoryContents = try FileManager.default.contentsOfDirectory( at: stickerDirectoryPath, includingPropertiesForKeys: nil, options: []) + for file in directoryContents { + do { + try fileManager.removeItem(at: file) + } + catch let error as NSError { + debugPrint("Ooops! Something went wrong: \(error)") + } + + } + } catch let error as NSError { + print(error) + } + } +} diff --git a/Sources/SwiftExtensions/String+subscript.swift b/Sources/SwiftExtensions/String+subscript.swift new file mode 100644 index 0000000..1cf3ba5 --- /dev/null +++ b/Sources/SwiftExtensions/String+subscript.swift @@ -0,0 +1,14 @@ +// +// String+subscript.swift +// kaomojiEmoticonKeyboard +// +// Created by Robert McGovern on 16/02/2021. +// + +import Foundation + +extension String { + subscript(idx: Int) -> String { + String(self[index(startIndex, offsetBy: idx)]) + } +} diff --git a/Sources/SwiftExtensions/SwiftExtensions.swift b/Sources/SwiftExtensions/SwiftExtensions.swift deleted file mode 100644 index 7024042..0000000 --- a/Sources/SwiftExtensions/SwiftExtensions.swift +++ /dev/null @@ -1,3 +0,0 @@ -struct SwiftExtensions { - var text = "Hello, World!" -} diff --git a/Sources/SwiftExtensions/UIHostingController+capture.swift b/Sources/SwiftExtensions/UIHostingController+capture.swift new file mode 100644 index 0000000..963e280 --- /dev/null +++ b/Sources/SwiftExtensions/UIHostingController+capture.swift @@ -0,0 +1,39 @@ +// +// File.swift +// kaomojiEmoticonKeyboard +// +// Created by Robert McGovern on 26/11/2020. +// + +import SwiftUI + +extension UIHostingController { + + func capture(transparentBackground: Bool = true) -> UIImage { + let size = sizeThatFits(in: UIScreen.main.bounds.size) + // makes background transparent + if (transparentBackground) { + view.backgroundColor = UIColor.clear + view.isOpaque = false + view.layer.isOpaque = false + view.layer.backgroundColor = UIColor.clear.cgColor + } + + view.bounds.size = size + view.sizeToFit() + + //suggested version + // UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0) + // view.drawHierarchy(in: view.bounds, afterScreenUpdates: true) + // let snapshotImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()! + // UIGraphicsEndImageContext() + + //cleaner ver + let renderer = UIGraphicsImageRenderer(size: self.view.bounds.size) + let snapshotImage = renderer.image { ctx in + self.view.drawHierarchy(in: self.view.bounds, afterScreenUpdates: true) + } + + return snapshotImage + } +} diff --git a/Sources/SwiftExtensions/UIImage+scaleImage.swift b/Sources/SwiftExtensions/UIImage+scaleImage.swift new file mode 100644 index 0000000..232b5d6 --- /dev/null +++ b/Sources/SwiftExtensions/UIImage+scaleImage.swift @@ -0,0 +1,87 @@ +// +// UIImage+scaleImage.swift +// kaomojiEmoticonKeyboard +// +// Created by Robert McGovern on 28/11/2020. +// + +import UIKit + +extension UIImage { + func resizeImage(newWidth: CGFloat) -> UIImage? { + + let scale = newWidth / self.size.width + let newHeight = self.size.height * scale + UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight)) + self.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight)) + + let newImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return newImage + } + + func scaleToSize(_ newSize: CGSize) -> UIImage? { + var newImage: UIImage? + let newRect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height).integral + UIGraphicsBeginImageContextWithOptions(newSize, false, 0) + if let context = UIGraphicsGetCurrentContext(), let cgImage = self.cgImage { + context.interpolationQuality = .high + let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: newSize.height) + context.concatenate(flipVertical) + context.draw(cgImage, in: newRect) + if let img = context.makeImage() { + // newImage = UIImage(cgImage: img) -- orig + let scale = UIScreen.main.scale + newImage = UIImage(cgImage: img, scale: scale, orientation: .up) + } + UIGraphicsEndImageContext() + } + return newImage + } + + func scaleToWidth(_ newWidth: CGFloat) -> UIImage? { + var newImage: UIImage? + let scale = newWidth / self.size.width + let newHeight = self.size.height * scale + + let newRect = CGRect(x: 0, y: 0, width: newWidth, height: newHeight).integral + UIGraphicsBeginImageContextWithOptions(CGSize(width: newWidth, height: newHeight), false, 0) + if let context = UIGraphicsGetCurrentContext(), let cgImage = self.cgImage { + context.interpolationQuality = .high + let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: newHeight) + context.concatenate(flipVertical) + context.draw(cgImage, in: newRect) + if let img = context.makeImage() { + // newImage = UIImage(cgImage: img) -- orig + let scale = UIScreen.main.scale + newImage = UIImage(cgImage: img, scale: scale, orientation: .up) + } + UIGraphicsEndImageContext() + } + return newImage + } + + // don't need, kept because erm +// static func scaleImage(_ image: UIImage, toNewWidth newWidth: CGFloat) -> UIImage? { +// var newImage: UIImage? +// let scale = newWidth / image.size.width +// let newHeight = image.size.height * scale +// +// let newRect = CGRect(x: 0, y: 0, width: newWidth, height: newHeight).integral +// UIGraphicsBeginImageContextWithOptions(CGSize(width: newWidth, height: newHeight), false, 0) +// if let context = UIGraphicsGetCurrentContext(), let cgImage = image.cgImage { +// context.interpolationQuality = .high +// let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: newHeight) +// context.concatenate(flipVertical) +// context.draw(cgImage, in: newRect) +// if let img = context.makeImage() { +// // newImage = UIImage(cgImage: img) -- orig +// let scale = UIScreen.main.scale +// newImage = UIImage(cgImage: img, scale: scale, orientation: .up) +// } +// UIGraphicsEndImageContext() +// } +// return newImage +// } +} diff --git a/Sources/SwiftExtensions/View+glow.swift b/Sources/SwiftExtensions/View+glow.swift new file mode 100644 index 0000000..8b0d3aa --- /dev/null +++ b/Sources/SwiftExtensions/View+glow.swift @@ -0,0 +1,18 @@ +// +// View+glow.swift +// kaomojiEmoticonKeyboard +// +// Created by Robert McGovern on 18/11/2020. +// + +import SwiftUI + +extension View { + func glow(color: Color = .red, radius: CGFloat = 20) -> some View { + self + .overlay(self.blur(radius: radius / 6)) + .shadow(color: color, radius: radius / 3) + .shadow(color: color, radius: radius / 3) + .shadow(color: color, radius: radius / 3) + } +} diff --git a/Sources/SwiftExtensions/View+hideKeyboard.swift b/Sources/SwiftExtensions/View+hideKeyboard.swift new file mode 100644 index 0000000..8dbfb03 --- /dev/null +++ b/Sources/SwiftExtensions/View+hideKeyboard.swift @@ -0,0 +1,14 @@ +// +// View+hideKeyboard.swift +// kaomojiEmoticonKeyboard +// +// Created by Robert McGovern on 15/11/2020. +// + +import SwiftUI + +extension View { + func hideKeyboard() { + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) + } +} diff --git a/Sources/SwiftExtensions/View+multicolorGlow.swift b/Sources/SwiftExtensions/View+multicolorGlow.swift new file mode 100644 index 0000000..f8baadf --- /dev/null +++ b/Sources/SwiftExtensions/View+multicolorGlow.swift @@ -0,0 +1,21 @@ +// +// View+multicolorGlow.swift +// kaomojiEmoticonKeyboard +// +// Created by Robert McGovern on 18/11/2020. +// + +import SwiftUI + +extension View { + func multicolorGlow() -> some View { + ForEach(0..<2) { i in + Rectangle() + .fill(AngularGradient(gradient: Gradient(colors: [.red, .yellow, .green, .blue, .purple, .red]), center: .center)) + .frame(width: 50, height: 50) + .mask(self.blur(radius: 20)) + .overlay(self.blur(radius: 5 - CGFloat(i * 5))) + } + } +} + diff --git a/Tests/SwiftExtensionsTests/SwiftExtensionsTests.swift b/Tests/SwiftExtensionsTests/SwiftExtensionsTests.swift deleted file mode 100644 index 9ac7b90..0000000 --- a/Tests/SwiftExtensionsTests/SwiftExtensionsTests.swift +++ /dev/null @@ -1,11 +0,0 @@ - import XCTest - @testable import SwiftExtensions - - final class SwiftExtensionsTests: XCTestCase { - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct - // results. - XCTAssertEqual(SwiftExtensions().text, "Hello, World!") - } - }