mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
213 lines
7.0 KiB
Swift
213 lines
7.0 KiB
Swift
//
|
|
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
public extension UIEdgeInsets {
|
|
public init(top: CGFloat, leading: CGFloat, bottom: CGFloat, trailing: CGFloat) {
|
|
self.init(top: top,
|
|
left: CurrentAppContext().isRTL ? trailing : leading,
|
|
bottom: bottom,
|
|
right: CurrentAppContext().isRTL ? leading : trailing)
|
|
}
|
|
}
|
|
|
|
@objc
|
|
public extension UINavigationController {
|
|
@objc
|
|
public func pushViewController(viewController: UIViewController,
|
|
animated: Bool,
|
|
completion: (() -> Void)?) {
|
|
CATransaction.begin()
|
|
CATransaction.setCompletionBlock(completion)
|
|
pushViewController(viewController, animated: animated)
|
|
CATransaction.commit()
|
|
}
|
|
|
|
@objc
|
|
public func popViewController(animated: Bool,
|
|
completion: (() -> Void)?) {
|
|
CATransaction.begin()
|
|
CATransaction.setCompletionBlock(completion)
|
|
popViewController(animated: animated)
|
|
CATransaction.commit()
|
|
}
|
|
|
|
@objc
|
|
public func popToViewController(viewController: UIViewController,
|
|
animated: Bool,
|
|
completion: (() -> Void)?) {
|
|
CATransaction.begin()
|
|
CATransaction.setCompletionBlock(completion)
|
|
self.popToViewController(viewController, animated: animated)
|
|
CATransaction.commit()
|
|
}
|
|
}
|
|
|
|
extension UIView {
|
|
public func renderAsImage() -> UIImage? {
|
|
return renderAsImage(opaque: false, scale: UIScreen.main.scale)
|
|
}
|
|
|
|
public func renderAsImage(opaque: Bool, scale: CGFloat) -> UIImage? {
|
|
if #available(iOS 10, *) {
|
|
let format = UIGraphicsImageRendererFormat()
|
|
format.scale = scale
|
|
format.opaque = opaque
|
|
let renderer = UIGraphicsImageRenderer(bounds: self.bounds,
|
|
format: format)
|
|
return renderer.image { (context) in
|
|
self.layer.render(in: context.cgContext)
|
|
}
|
|
} else {
|
|
UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, scale)
|
|
if let _ = UIGraphicsGetCurrentContext() {
|
|
drawHierarchy(in: bounds, afterScreenUpdates: true)
|
|
let image = UIGraphicsGetImageFromCurrentImageContext()
|
|
UIGraphicsEndImageContext()
|
|
return image
|
|
}
|
|
owsFailDebug("Could not create graphics context.")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
@objc
|
|
public class func spacer(withWidth width: CGFloat) -> UIView {
|
|
let view = UIView()
|
|
view.autoSetDimension(.width, toSize: width)
|
|
return view
|
|
}
|
|
|
|
@objc
|
|
public class func spacer(withHeight height: CGFloat) -> UIView {
|
|
let view = UIView()
|
|
view.autoSetDimension(.height, toSize: height)
|
|
return view
|
|
}
|
|
|
|
@objc
|
|
public class func hStretchingSpacer() -> UIView {
|
|
let view = UIView()
|
|
view.setContentHuggingHorizontalLow()
|
|
view.setCompressionResistanceHorizontalLow()
|
|
return view
|
|
}
|
|
|
|
@objc
|
|
public class func vStretchingSpacer() -> UIView {
|
|
let view = UIView()
|
|
view.setContentHuggingVerticalLow()
|
|
view.setCompressionResistanceVerticalLow()
|
|
return view
|
|
}
|
|
|
|
@objc
|
|
public func applyScaleAspectFitLayout(subview: UIView, aspectRatio: CGFloat) -> [NSLayoutConstraint] {
|
|
guard subviews.contains(subview) else {
|
|
owsFailDebug("Not a subview.")
|
|
return []
|
|
}
|
|
|
|
// This emulates the behavior of contentMode = .scaleAspectFit using
|
|
// iOS auto layout constraints.
|
|
//
|
|
// This allows ConversationInputToolbar to place the "cancel" button
|
|
// in the upper-right hand corner of the preview content.
|
|
var constraints = [NSLayoutConstraint]()
|
|
constraints.append(contentsOf: subview.autoCenterInSuperview())
|
|
constraints.append(subview.autoPin(toAspectRatio: aspectRatio))
|
|
constraints.append(subview.autoMatch(.width, to: .width, of: self, withMultiplier: 1.0, relation: .lessThanOrEqual))
|
|
constraints.append(subview.autoMatch(.height, to: .height, of: self, withMultiplier: 1.0, relation: .lessThanOrEqual))
|
|
return constraints
|
|
}
|
|
}
|
|
|
|
public extension CGFloat {
|
|
// Linear interpolation
|
|
public func lerp(_ minValue: CGFloat, _ maxValue: CGFloat) -> CGFloat {
|
|
return CGFloatLerp(minValue, maxValue, self)
|
|
}
|
|
|
|
// Inverse linear interpolation
|
|
public func inverseLerp(_ minValue: CGFloat, _ maxValue: CGFloat, shouldClamp: Bool = false) -> CGFloat {
|
|
let value = CGFloatInverseLerp(self, minValue, maxValue)
|
|
return (shouldClamp ? CGFloatClamp01(value) : value)
|
|
}
|
|
|
|
public static let halfPi: CGFloat = CGFloat.pi * 0.5
|
|
}
|
|
|
|
public extension CGPoint {
|
|
public func toUnitCoordinates(viewBounds: CGRect, shouldClamp: Bool) -> CGPoint {
|
|
return CGPoint(x: (x - viewBounds.origin.x).inverseLerp(0, viewBounds.width, shouldClamp: shouldClamp),
|
|
y: (y - viewBounds.origin.y).inverseLerp(0, viewBounds.height, shouldClamp: shouldClamp))
|
|
}
|
|
|
|
public func toUnitCoordinates(viewSize: CGSize, shouldClamp: Bool) -> CGPoint {
|
|
return toUnitCoordinates(viewBounds: CGRect(origin: .zero, size: viewSize), shouldClamp: shouldClamp)
|
|
}
|
|
|
|
public func fromUnitCoordinates(viewSize: CGSize) -> CGPoint {
|
|
return CGPoint(x: x.lerp(0, viewSize.width),
|
|
y: y.lerp(0, viewSize.height))
|
|
}
|
|
|
|
public func inverse() -> CGPoint {
|
|
return CGPoint(x: -x, y: -y)
|
|
}
|
|
|
|
public func plus(_ value: CGPoint) -> CGPoint {
|
|
return CGPointAdd(self, value)
|
|
}
|
|
|
|
public func minus(_ value: CGPoint) -> CGPoint {
|
|
return CGPointSubtract(self, value)
|
|
}
|
|
|
|
public static let unit: CGPoint = CGPoint(x: 1.0, y: 1.0)
|
|
|
|
public static let unitMidpoint: CGPoint = CGPoint(x: 0.5, y: 0.5)
|
|
|
|
public func applyingInverse(_ transform: CGAffineTransform) -> CGPoint {
|
|
return applying(transform.inverted())
|
|
}
|
|
}
|
|
|
|
public extension CGRect {
|
|
public var center: CGPoint {
|
|
return CGPoint(x: midX, y: midY)
|
|
}
|
|
|
|
public var topLeft: CGPoint {
|
|
return origin
|
|
}
|
|
|
|
public var bottomRight: CGPoint {
|
|
return CGPoint(x: maxX, y: maxY)
|
|
}
|
|
}
|
|
|
|
public extension CGAffineTransform {
|
|
public static func translate(_ point: CGPoint) -> CGAffineTransform {
|
|
return CGAffineTransform(translationX: point.x, y: point.y)
|
|
}
|
|
|
|
public static func scale(_ scaling: CGFloat) -> CGAffineTransform {
|
|
return CGAffineTransform(scaleX: scaling, y: scaling)
|
|
}
|
|
|
|
public func translate(_ point: CGPoint) -> CGAffineTransform {
|
|
return translatedBy(x: point.x, y: point.y)
|
|
}
|
|
|
|
public func scale(_ scaling: CGFloat) -> CGAffineTransform {
|
|
return scaledBy(x: scaling, y: scaling)
|
|
}
|
|
|
|
public func rotate(_ angleRadians: CGFloat) -> CGAffineTransform {
|
|
return rotated(by: angleRadians)
|
|
}
|
|
}
|