Start version 2
This commit is contained in:
@ -1,30 +0,0 @@
|
||||
//
|
||||
// Array+Extensions.swift
|
||||
// CapCollector
|
||||
//
|
||||
// Created by Christoph on 12.05.20.
|
||||
// Copyright © 2020 CH. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Array {
|
||||
|
||||
func split(intoPartsOf maxElements: Int) -> [ArraySlice<Element>] {
|
||||
guard !isEmpty, maxElements > 0 else {
|
||||
return []
|
||||
}
|
||||
var result = [ArraySlice<Element>]()
|
||||
var currentIndex = 0
|
||||
while true {
|
||||
let nextIndex = currentIndex + maxElements
|
||||
if nextIndex >= count {
|
||||
result.append(self[currentIndex..<count])
|
||||
return result
|
||||
}
|
||||
result.append(self[currentIndex..<nextIndex])
|
||||
currentIndex += maxElements
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
30
Caps/Extensions/CGImage+Extensions.swift
Normal file
30
Caps/Extensions/CGImage+Extensions.swift
Normal file
@ -0,0 +1,30 @@
|
||||
import CoreGraphics
|
||||
import VideoToolbox
|
||||
|
||||
extension CGImage {
|
||||
|
||||
static func create(from cvPixelBuffer: CVPixelBuffer?) -> CGImage? {
|
||||
guard let pixelBuffer = cvPixelBuffer else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var image: CGImage?
|
||||
VTCreateCGImageFromCVPixelBuffer(pixelBuffer, options: nil, imageOut: &image)
|
||||
return image
|
||||
}
|
||||
|
||||
/**
|
||||
Crop an image to a square, centered around the middle of the frame
|
||||
- parameter size: The height and width of the resulting image
|
||||
- returns: The cropped image
|
||||
*/
|
||||
func crop(to size: CGFloat) -> CGImage? {
|
||||
let rect = CGRect(
|
||||
x: (CGFloat(height) - size) / 2,
|
||||
y: (CGFloat(width) - size) / 2,
|
||||
width: size,
|
||||
height: size)
|
||||
|
||||
return cropping(to: rect)
|
||||
}
|
||||
}
|
@ -1,11 +1,3 @@
|
||||
//
|
||||
// CGImagePropertyOrientation+Extensions.swift
|
||||
// CapCollector
|
||||
//
|
||||
// Created by Christoph on 13.05.20.
|
||||
// Copyright © 2020 CH. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension CGImagePropertyOrientation {
|
||||
|
59
Caps/Extensions/Data+Extensions.swift
Normal file
59
Caps/Extensions/Data+Extensions.swift
Normal file
@ -0,0 +1,59 @@
|
||||
import Foundation
|
||||
|
||||
extension Data {
|
||||
|
||||
public var hexEncoded: String {
|
||||
return map { String(format: "%02hhx", $0) }.joined()
|
||||
}
|
||||
|
||||
// Convert 0 ... 9, a ... f, A ...F to their decimal value,
|
||||
// return nil for all other input characters
|
||||
private func decodeNibble(_ u: UInt16) -> UInt8? {
|
||||
switch(u) {
|
||||
case 0x30 ... 0x39:
|
||||
return UInt8(u - 0x30)
|
||||
case 0x41 ... 0x46:
|
||||
return UInt8(u - 0x41 + 10)
|
||||
case 0x61 ... 0x66:
|
||||
return UInt8(u - 0x61 + 10)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public init?(fromHexEncodedString string: String) {
|
||||
let utf16 = string.utf16
|
||||
self.init(capacity: utf16.count/2)
|
||||
|
||||
var i = utf16.startIndex
|
||||
guard utf16.count % 2 == 0 else {
|
||||
return nil
|
||||
}
|
||||
while i != utf16.endIndex {
|
||||
guard let hi = decodeNibble(utf16[i]),
|
||||
let lo = decodeNibble(utf16[utf16.index(i, offsetBy: 1, limitedBy: utf16.endIndex)!]) else {
|
||||
return nil
|
||||
}
|
||||
var value = hi << 4 + lo
|
||||
self.append(&value, count: 1)
|
||||
i = utf16.index(i, offsetBy: 2, limitedBy: utf16.endIndex)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Data {
|
||||
|
||||
|
||||
func convert<T>(into value: T) -> T {
|
||||
withUnsafeBytes {
|
||||
$0.baseAddress!.load(as: T.self)
|
||||
}
|
||||
}
|
||||
|
||||
init<T>(from value: T) {
|
||||
var target = value
|
||||
self = Swift.withUnsafeBytes(of: &target) {
|
||||
Data($0)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
//
|
||||
// DispatchGroup+Extensions.swift
|
||||
// CapCollector
|
||||
//
|
||||
// Created by iMac on 13.01.21.
|
||||
// Copyright © 2021 CH. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension DispatchGroup {
|
||||
|
||||
typealias AsyncSuccessCallback = (Bool) -> Void
|
||||
|
||||
static func singleTask(timeout: TimeInterval = 30, _ block: (@escaping AsyncSuccessCallback) -> Void) -> Bool {
|
||||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
var result = true
|
||||
block { success in
|
||||
result = success
|
||||
group.leave()
|
||||
}
|
||||
guard group.wait(timeout: .now() + timeout) == .success else {
|
||||
return false
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
//
|
||||
// UIAlertControllerExtensions.swift
|
||||
// CapFinder
|
||||
//
|
||||
// Created by User on 23.03.18.
|
||||
// Copyright © 2018 User. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
extension UIAlertController {
|
||||
|
||||
private struct AssociatedKeys {
|
||||
static var blurStyleKey = "UIAlertController.blurStyleKey"
|
||||
}
|
||||
|
||||
public var blurStyle: UIBlurEffect.Style {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.blurStyleKey) as? UIBlurEffect.Style ?? .extraLight
|
||||
} set (style) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.blurStyleKey, style, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
|
||||
view.setNeedsLayout()
|
||||
view.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
public var cancelButtonColor: UIColor? {
|
||||
return blurStyle == .dark ? UIColor(red: 28.0/255.0, green: 28.0/255.0, blue: 28.0/255.0, alpha: 1.0) : nil
|
||||
}
|
||||
|
||||
private var visualEffectView: UIVisualEffectView? {
|
||||
if let presentationController = presentationController, presentationController.responds(to: Selector(("popoverView"))), let view = presentationController.value(forKey: "popoverView") as? UIView // We're on an iPad and visual effect view is in a different place.
|
||||
{
|
||||
return view.recursiveSubviews.compactMap({$0 as? UIVisualEffectView}).first
|
||||
}
|
||||
|
||||
return view.recursiveSubviews.compactMap({$0 as? UIVisualEffectView}).first
|
||||
}
|
||||
|
||||
private var cancelActionView: UIView? {
|
||||
return view.recursiveSubviews.compactMap({
|
||||
$0 as? UILabel}
|
||||
).first(where: {
|
||||
$0.text == actions.first(where: { $0.style == .cancel })?.title
|
||||
})?.superview?.superview
|
||||
}
|
||||
|
||||
public convenience init(title: String?, message: String?, preferredStyle: UIAlertController.Style, blurStyle: UIBlurEffect.Style) {
|
||||
self.init(title: title, message: message, preferredStyle: preferredStyle)
|
||||
self.blurStyle = blurStyle
|
||||
}
|
||||
|
||||
open override func viewWillLayoutSubviews() {
|
||||
super.viewWillLayoutSubviews()
|
||||
|
||||
visualEffectView?.effect = UIBlurEffect(style: blurStyle)
|
||||
cancelActionView?.backgroundColor = cancelButtonColor
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
//
|
||||
// UIColor+Extensions.swift
|
||||
// CapCollector
|
||||
//
|
||||
// Created by Christoph on 14.04.20.
|
||||
// Copyright © 2020 CH. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIColor {
|
||||
|
||||
var rgb: (red: Double, green: Double, blue: Double) {
|
||||
var fRed: CGFloat = 0
|
||||
var fGreen: CGFloat = 0
|
||||
var fBlue: CGFloat = 0
|
||||
var fAlpha: CGFloat = 0
|
||||
getRed(&fRed, green: &fGreen, blue: &fBlue, alpha: &fAlpha)
|
||||
return (Double(fRed), Double(fGreen), Double(fBlue))
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,3 @@
|
||||
//
|
||||
// UIImageExtensions.swift
|
||||
// CapFinder
|
||||
//
|
||||
// Created by User on 13.02.18.
|
||||
// Copyright © 2018 User. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
@ -22,7 +14,7 @@ extension UIImage {
|
||||
func resize(to targetSize: CGSize) -> UIImage {
|
||||
let rect = CGRect(x: 0, y: 0, width: targetSize.width, height: targetSize.height)
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(targetSize, false, 1.0)
|
||||
UIGraphicsBeginImageContextWithOptions(targetSize, false, scale)
|
||||
self.draw(in: rect)
|
||||
let newImage = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
@ -35,7 +27,7 @@ extension UIImage {
|
||||
- returns: The cropped image
|
||||
*/
|
||||
func crop(factor: CGFloat) -> UIImage {
|
||||
let width = self.size.width * factor
|
||||
let width = size.width * scale * factor
|
||||
return crop(to: width)
|
||||
}
|
||||
|
||||
@ -45,15 +37,11 @@ extension UIImage {
|
||||
- returns: The cropped image
|
||||
*/
|
||||
func crop(to size: CGFloat) -> UIImage {
|
||||
var rect = CGRect(
|
||||
x: (self.size.height - size) / 2,
|
||||
y: (self.size.width - size) / 2,
|
||||
width: size,
|
||||
height: size)
|
||||
rect.origin.x *= scale
|
||||
rect.origin.y *= scale
|
||||
rect.size.width *= scale
|
||||
rect.size.height *= scale
|
||||
let rect = CGRect(
|
||||
x: (self.size.height * scale - size) / 2,
|
||||
y: (self.size.width * scale - size) / 2,
|
||||
width: size * scale,
|
||||
height: size * scale)
|
||||
|
||||
let imageRef = cgImage!.cropping(to: rect)
|
||||
return UIImage(cgImage: imageRef!, scale: scale, orientation: imageOrientation)
|
||||
@ -77,10 +65,10 @@ extension UIImage {
|
||||
return nil
|
||||
}
|
||||
UIBezierPath(ovalIn: breadthRect).addClip()
|
||||
UIImage(cgImage: cgImage, scale: 1, orientation: imageOrientation).draw(in: breadthRect)
|
||||
UIImage(cgImage: cgImage, scale: scale, orientation: imageOrientation).draw(in: breadthRect)
|
||||
return UIGraphicsGetImageFromCurrentImageContext()
|
||||
}
|
||||
|
||||
|
||||
var averageColor: UIColor? {
|
||||
let image = ciImage ?? CIImage(cgImage: cgImage!)
|
||||
return image.averageColor
|
||||
@ -88,14 +76,14 @@ extension UIImage {
|
||||
}
|
||||
|
||||
extension CIImage {
|
||||
|
||||
|
||||
func averageColor(context: CIContext) -> UIColor? {
|
||||
let extentVector = CIVector(
|
||||
x: extent.origin.x,
|
||||
y: extent.origin.y,
|
||||
z: extent.size.width,
|
||||
w: extent.size.height)
|
||||
|
||||
|
||||
guard let filter = CIFilter(name: "CIAreaAverage", parameters: [kCIInputImageKey: self, kCIInputExtentKey: extentVector]) else {
|
||||
log("Failed to create filter")
|
||||
return nil
|
||||
@ -104,27 +92,27 @@ extension CIImage {
|
||||
log("Failed get filter output")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
var bitmap = [UInt8](repeating: 0, count: 4)
|
||||
|
||||
|
||||
context.render(outputImage, toBitmap: &bitmap, rowBytes: 4,
|
||||
bounds: CGRect(x: 0, y: 0, width: 1, height: 1),
|
||||
format: .RGBA8, colorSpace: nil)
|
||||
|
||||
|
||||
return UIColor(
|
||||
red: saturate(bitmap[0]),
|
||||
green: saturate(bitmap[1]),
|
||||
blue: saturate(bitmap[2]),
|
||||
alpha: CGFloat(bitmap[3]) / 255)
|
||||
}
|
||||
|
||||
|
||||
var averageColor: UIColor? {
|
||||
let extentVector = CIVector(
|
||||
x: extent.origin.x,
|
||||
y: extent.origin.y,
|
||||
z: extent.size.width,
|
||||
w: extent.size.height)
|
||||
|
||||
|
||||
guard let filter = CIFilter(name: "CIAreaAverage", parameters: [kCIInputImageKey: self, kCIInputExtentKey: extentVector]) else {
|
||||
log("Failed to create filter")
|
||||
return nil
|
||||
@ -133,7 +121,7 @@ extension CIImage {
|
||||
log("Failed get filter output")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
var bitmap = [UInt8](repeating: 0, count: 4)
|
||||
guard let null = kCFNull else {
|
||||
return nil
|
||||
@ -142,13 +130,13 @@ extension CIImage {
|
||||
context.render(outputImage, toBitmap: &bitmap, rowBytes: 4,
|
||||
bounds: CGRect(x: 0, y: 0, width: 1, height: 1),
|
||||
format: .RGBA8, colorSpace: nil)
|
||||
|
||||
|
||||
let color = UIColor(
|
||||
red: saturate(bitmap[0]),
|
||||
green: saturate(bitmap[1]),
|
||||
blue: saturate(bitmap[2]),
|
||||
alpha: CGFloat(bitmap[3]) / 255)
|
||||
|
||||
|
||||
return color
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
//
|
||||
// UINavigationItem+Extensions.swift
|
||||
// CapCollector
|
||||
//
|
||||
// Created by Christoph on 12.05.20.
|
||||
// Copyright © 2020 CH. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UINavigationItem {
|
||||
|
||||
func setTitle(_ title: String, subtitle: String) {
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.text = title
|
||||
titleLabel.font = .systemFont(ofSize: 17.0)
|
||||
titleLabel.textColor = .black
|
||||
|
||||
let subtitleLabel = UILabel()
|
||||
subtitleLabel.text = subtitle
|
||||
subtitleLabel.font = .systemFont(ofSize: 12.0)
|
||||
subtitleLabel.textColor = .gray
|
||||
|
||||
let stackView = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel])
|
||||
stackView.distribution = .equalCentering
|
||||
stackView.alignment = .center
|
||||
stackView.axis = .vertical
|
||||
|
||||
self.titleView = stackView
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
//
|
||||
// UIViewExtensions.swift
|
||||
// CapFinder
|
||||
//
|
||||
// Created by User on 23.03.18.
|
||||
// Copyright © 2018 User. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
|
||||
var recursiveSubviews: [UIView] {
|
||||
var subviews = self.subviews.compactMap{ $0 }
|
||||
subviews.forEach { subviews.append(contentsOf: $0.recursiveSubviews) }
|
||||
return subviews
|
||||
}
|
||||
|
||||
func fromNib<T : UIView>() -> T { // 2
|
||||
return Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)!.first! as! T
|
||||
}
|
||||
}
|
25
Caps/Extensions/URL+Extensions.swift
Normal file
25
Caps/Extensions/URL+Extensions.swift
Normal file
@ -0,0 +1,25 @@
|
||||
import Foundation
|
||||
|
||||
extension URL {
|
||||
|
||||
var attributes: [FileAttributeKey : Any]? {
|
||||
do {
|
||||
return try FileManager.default.attributesOfItem(atPath: path)
|
||||
} catch let error as NSError {
|
||||
print("FileAttribute error: \(error)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var fileSize: Int {
|
||||
return Int(attributes?[.size] as? UInt64 ?? 0)
|
||||
}
|
||||
|
||||
var fileSizeString: String {
|
||||
return ByteCountFormatter.string(fromByteCount: Int64(fileSize), countStyle: .file)
|
||||
}
|
||||
|
||||
var creationDate: Date? {
|
||||
return attributes?[.creationDate] as? Date
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
//
|
||||
// ViewControllerExtensions.swift
|
||||
// CapFinder
|
||||
//
|
||||
// Created by User on 18.03.18.
|
||||
// Copyright © 2018 User. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
// MARK: Alerts
|
||||
|
||||
/// Present an alert with a message to the user
|
||||
func showAlert(_ message: String, title: String = "Error") {
|
||||
let alertController = UIAlertController(
|
||||
title: title,
|
||||
message: message,
|
||||
preferredStyle: .alert)//,
|
||||
//blurStyle: .dark)
|
||||
|
||||
alertController.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
|
||||
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user