mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-28 23:54:32 +00:00
Restored files from old Mac, force pushing
This commit is contained in:
parent
5dd95228b8
commit
5040381840
126 changed files with 10431 additions and 67 deletions
30
DynamicIsland/extensions/ActionBar.swift
Normal file
30
DynamicIsland/extensions/ActionBar.swift
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// ActionBar.swift
|
||||
// boringNotch
|
||||
//
|
||||
// Created by Richard Kunkli on 15/09/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension View {
|
||||
func actionBar<Content: View>(padding: CGFloat = 10, @ViewBuilder content: () -> Content) -> some View {
|
||||
self
|
||||
.padding(.bottom, 24)
|
||||
.overlay(alignment: .bottom) {
|
||||
VStack(spacing: -1) {
|
||||
Divider()
|
||||
HStack(spacing: 0) {
|
||||
content()
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
.frame(height: 16)
|
||||
.padding(.vertical, 4)
|
||||
.padding(.horizontal, padding)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.frame(height: 24)
|
||||
.background(.separator)
|
||||
}
|
||||
}
|
||||
}
|
||||
57
DynamicIsland/extensions/BundleInfos.swift
Normal file
57
DynamicIsland/extensions/BundleInfos.swift
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// BundleInfos.swift
|
||||
// boringNotch
|
||||
//
|
||||
// Created by Richard Kunkli on 08/08/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension Bundle {
|
||||
var releaseVersionNumber: String? {
|
||||
return infoDictionary?["CFBundleShortVersionString"] as? String
|
||||
}
|
||||
var buildVersionNumber: String? {
|
||||
return infoDictionary?["CFBundleVersion"] as? String
|
||||
}
|
||||
var releaseVersionNumberPretty: String {
|
||||
return "v\(releaseVersionNumber ?? "1.0.0")"
|
||||
}
|
||||
|
||||
var iconFileName: String? {
|
||||
guard let icons = infoDictionary?["CFBundleIcons"] as? [String: Any],
|
||||
let primaryIcon = icons["CFBundlePrimaryIcon"] as? [String: Any],
|
||||
let iconFiles = primaryIcon["CFBundleIconFiles"] as? [String],
|
||||
let iconFileName = iconFiles.last
|
||||
else { return nil }
|
||||
return iconFileName
|
||||
}
|
||||
}
|
||||
|
||||
struct BundleAppIcon: View {
|
||||
var body: some View {
|
||||
Bundle.main.iconFileName
|
||||
.flatMap { NSImage(named: $0) }
|
||||
.map { Image(nsImage: $0) }
|
||||
}
|
||||
}
|
||||
|
||||
func isNewVersion() -> Bool {
|
||||
let defaults = UserDefaults.standard
|
||||
let currentVersion = Bundle.main.releaseVersionNumber ?? "1.0"
|
||||
let savedVersion = defaults.string(forKey: "LastVersionRun") ?? ""
|
||||
|
||||
if currentVersion != savedVersion {
|
||||
defaults.set(currentVersion, forKey: "LastVersionRun")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isExtensionRunning(_ bundleID: String) -> Bool {
|
||||
if let _ = NSWorkspace.shared.runningApplications.first(where: {$0.bundleIdentifier == bundleID}) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
35
DynamicIsland/extensions/Button+Bouncing.swift
Normal file
35
DynamicIsland/extensions/Button+Bouncing.swift
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Button+Bouncing.swift
|
||||
// boringNotch
|
||||
//
|
||||
// Created by Harsh Vardhan Goswami on 19/08/24.
|
||||
//
|
||||
import SwiftUI
|
||||
import Defaults
|
||||
|
||||
struct BouncingButtonStyle: ButtonStyle {
|
||||
let vm: DynamicIslandViewModel
|
||||
@State private var isPressed = false
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.padding(12)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? 10 : MusicPlayerImageSizes.cornerRadiusInset.closed)
|
||||
.fill(Color(red: 20/255, green: 20/255, blue: 20/255))
|
||||
.strokeBorder(.white.opacity(0.04), lineWidth: 1)
|
||||
)
|
||||
.scaleEffect(isPressed ? 0.9 : 1.0)
|
||||
.onChange(of: configuration.isPressed) { _, _ in
|
||||
withAnimation(.spring(response: 0.3, dampingFraction: 0.3, blendDuration: 0.3)) {
|
||||
isPressed.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Button {
|
||||
func bouncingStyle(vm: DynamicIslandViewModel) -> some View {
|
||||
self.buttonStyle(BouncingButtonStyle(vm: vm))
|
||||
}
|
||||
}
|
||||
18
DynamicIsland/extensions/ConditionalModifier.swift
Normal file
18
DynamicIsland/extensions/ConditionalModifier.swift
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// ConditionalModifier.swift
|
||||
// boringNotch
|
||||
//
|
||||
// Created by Richard Kunkli on 20/08/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension View {
|
||||
@ViewBuilder func conditionalModifier<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
|
||||
if condition {
|
||||
transform(self)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
94
DynamicIsland/extensions/DataTypes+Extensions.swift
Normal file
94
DynamicIsland/extensions/DataTypes+Extensions.swift
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// DataTypes+Extensions.swift
|
||||
// boringNotch
|
||||
//
|
||||
// Created by Harsh Vardhan Goswami on 27/08/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
|
||||
extension Date {
|
||||
static var yesterday: Date { return Date().dayBefore }
|
||||
static var tomorrow: Date { return Date().dayAfter }
|
||||
var dayBefore: Date {
|
||||
return Calendar.current.date(byAdding: .day, value: -1, to: noon)!
|
||||
}
|
||||
var dayAfter: Date {
|
||||
return Calendar.current.date(byAdding: .day, value: 1, to: noon)!
|
||||
}
|
||||
var noon: Date {
|
||||
return Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)!
|
||||
}
|
||||
|
||||
var date: String {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "dd"
|
||||
return dateFormatter.string(from: self)
|
||||
}
|
||||
|
||||
var month: String {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "MMMM"
|
||||
return dateFormatter.string(from: self)
|
||||
}
|
||||
|
||||
func dayOfTheWeek(dayOfWeek: Int) -> String {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "EEE"
|
||||
let date = Calendar.current.date(bySetting: .weekday, value: dayOfWeek, of: self) ?? self
|
||||
return dateFormatter.string(from: date)
|
||||
}
|
||||
}
|
||||
|
||||
extension NSSize {
|
||||
var s: String { "\(width.i)×\(height.i)" }
|
||||
|
||||
var aspectRatio: Double {
|
||||
width / height
|
||||
}
|
||||
func scaled(by factor: Double) -> CGSize {
|
||||
CGSize(width: (width * factor).evenInt, height: (height * factor).evenInt)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Int {
|
||||
var s: String {
|
||||
String(self)
|
||||
}
|
||||
var d: Double {
|
||||
Double(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension Double {
|
||||
@inline(__always) @inlinable var intround: Int {
|
||||
rounded().i
|
||||
}
|
||||
|
||||
@inline(__always) @inlinable var i: Int {
|
||||
Int(self)
|
||||
}
|
||||
|
||||
var evenInt: Int {
|
||||
let x = intround
|
||||
return x + x % 2
|
||||
}
|
||||
}
|
||||
|
||||
extension CGFloat {
|
||||
@inline(__always) @inlinable var intround: Int {
|
||||
rounded().i
|
||||
}
|
||||
|
||||
@inline(__always) @inlinable var i: Int {
|
||||
Int(self)
|
||||
}
|
||||
|
||||
var evenInt: Int {
|
||||
let x = intround
|
||||
return x + x % 2
|
||||
}
|
||||
}
|
||||
88
DynamicIsland/extensions/KeyboardShortcutsHelper.swift
Normal file
88
DynamicIsland/extensions/KeyboardShortcutsHelper.swift
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// KeyboardShortcutsHelper.swift
|
||||
// boringNotch
|
||||
//
|
||||
// Created by Richard Kunkli on 16/08/2024.
|
||||
//
|
||||
|
||||
import KeyboardShortcuts
|
||||
import SwiftUI
|
||||
import Carbon
|
||||
|
||||
|
||||
extension View {
|
||||
|
||||
public func keyboardShortcut(_ shortcut: KeyboardShortcuts.Name) -> some View {
|
||||
if let shortcut = shortcut.shortcut {
|
||||
if let keyEquivalent = shortcut.toKeyEquivalent() {
|
||||
return AnyView(self.keyboardShortcut(keyEquivalent, modifiers: shortcut.toEventModifiers()))
|
||||
}
|
||||
}
|
||||
|
||||
return AnyView(self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension KeyboardShortcuts.Shortcut {
|
||||
|
||||
func toKeyEquivalent() -> KeyEquivalent? {
|
||||
let carbonKeyCode = UInt16(self.carbonKeyCode)
|
||||
let maxNameLength = 4
|
||||
var nameBuffer = [UniChar](repeating: 0, count : maxNameLength)
|
||||
var nameLength = 0
|
||||
|
||||
let modifierKeys = UInt32(alphaLock >> 8) & 0xFF // Caps Lock
|
||||
var deadKeys: UInt32 = 0
|
||||
let keyboardType = UInt32(LMGetKbdType())
|
||||
|
||||
let source = TISCopyCurrentKeyboardLayoutInputSource().takeRetainedValue()
|
||||
guard let ptr = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData) else {
|
||||
NSLog("Could not get keyboard layout data")
|
||||
return nil
|
||||
}
|
||||
let layoutData = Unmanaged<CFData>.fromOpaque(ptr).takeUnretainedValue() as Data
|
||||
let osStatus = layoutData.withUnsafeBytes {
|
||||
UCKeyTranslate($0.bindMemory(to: UCKeyboardLayout.self).baseAddress, carbonKeyCode, UInt16(kUCKeyActionDown),
|
||||
modifierKeys, keyboardType, UInt32(kUCKeyTranslateNoDeadKeysMask),
|
||||
&deadKeys, maxNameLength, &nameLength, &nameBuffer)
|
||||
}
|
||||
guard osStatus == noErr else {
|
||||
NSLog("Code: 0x%04X Status: %+i", carbonKeyCode, osStatus);
|
||||
return nil
|
||||
}
|
||||
|
||||
return KeyEquivalent(Character(String(utf16CodeUnits: nameBuffer, count: nameLength)))
|
||||
}
|
||||
|
||||
func toEventModifiers() -> SwiftUI.EventModifiers {
|
||||
var modifiers: SwiftUI.EventModifiers = []
|
||||
|
||||
if self.modifiers.contains(NSEvent.ModifierFlags.command) {
|
||||
modifiers.update(with: EventModifiers.command)
|
||||
}
|
||||
|
||||
if self.modifiers.contains(NSEvent.ModifierFlags.control) {
|
||||
modifiers.update(with: EventModifiers.control)
|
||||
}
|
||||
|
||||
if self.modifiers.contains(NSEvent.ModifierFlags.option) {
|
||||
modifiers.update(with: EventModifiers.option)
|
||||
}
|
||||
|
||||
if self.modifiers.contains(NSEvent.ModifierFlags.shift) {
|
||||
modifiers.update(with: EventModifiers.shift)
|
||||
}
|
||||
|
||||
if self.modifiers.contains(NSEvent.ModifierFlags.capsLock) {
|
||||
modifiers.update(with: EventModifiers.capsLock)
|
||||
}
|
||||
|
||||
if self.modifiers.contains(NSEvent.ModifierFlags.numericPad) {
|
||||
modifiers.update(with: EventModifiers.numericPad)
|
||||
}
|
||||
|
||||
return modifiers
|
||||
}
|
||||
|
||||
}
|
||||
18
DynamicIsland/extensions/MouseTracker.swift
Normal file
18
DynamicIsland/extensions/MouseTracker.swift
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// MouseTracker.swift
|
||||
// boringNotch
|
||||
//
|
||||
// Created by Richard Kunkli on 12/08/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension NSScreen {
|
||||
static var screenWithMouse: NSScreen? {
|
||||
let mouseLocation = NSEvent.mouseLocation
|
||||
let screens = NSScreen.screens
|
||||
let screenWithMouse = (screens.first { NSMouseInRect(mouseLocation, $0.frame, false) })
|
||||
|
||||
return screenWithMouse
|
||||
}
|
||||
}
|
||||
169
DynamicIsland/extensions/NSImage+Extensions.swift
Normal file
169
DynamicIsland/extensions/NSImage+Extensions.swift
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
//
|
||||
// Image2Color.swift
|
||||
// boringNotch
|
||||
//
|
||||
// Created by Richard Kunkli on 07/08/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AppKit
|
||||
import Cocoa
|
||||
import Foundation
|
||||
import CoreImage
|
||||
import CoreGraphics
|
||||
import CoreImage.CIFilterBuiltins
|
||||
|
||||
extension NSImage {
|
||||
|
||||
|
||||
func averageColor(completion: @escaping (NSColor?) -> Void) {
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
guard let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil) else {
|
||||
DispatchQueue.main.async {
|
||||
completion(nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let width = cgImage.width
|
||||
let height = cgImage.height
|
||||
let totalPixels = width * height
|
||||
|
||||
guard let context = CGContext(data: nil,
|
||||
width: width,
|
||||
height: height,
|
||||
bitsPerComponent: 8,
|
||||
bytesPerRow: width * 4,
|
||||
space: CGColorSpaceCreateDeviceRGB(),
|
||||
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else {
|
||||
DispatchQueue.main.async {
|
||||
completion(nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
|
||||
|
||||
guard let data = context.data else {
|
||||
DispatchQueue.main.async {
|
||||
completion(nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let pointer = data.bindMemory(to: UInt32.self, capacity: totalPixels)
|
||||
|
||||
var totalRed: UInt64 = 0
|
||||
var totalGreen: UInt64 = 0
|
||||
var totalBlue: UInt64 = 0
|
||||
|
||||
for i in 0..<totalPixels {
|
||||
let color = pointer[i]
|
||||
totalRed += UInt64(color & 0xFF)
|
||||
totalGreen += UInt64((color >> 8) & 0xFF)
|
||||
totalBlue += UInt64((color >> 16) & 0xFF)
|
||||
}
|
||||
|
||||
let averageRed = CGFloat(totalRed) / CGFloat(totalPixels) / 255.0
|
||||
let averageGreen = CGFloat(totalGreen) / CGFloat(totalPixels) / 255.0
|
||||
let averageBlue = CGFloat(totalBlue) / CGFloat(totalPixels) / 255.0
|
||||
|
||||
let minBrightness: CGFloat = 0.5
|
||||
let isNearBlack = averageRed < 0.03 && averageGreen < 0.03 && averageBlue < 0.03
|
||||
|
||||
var finalColor: NSColor
|
||||
|
||||
if isNearBlack {
|
||||
// If it's near black, just return a gray color with the minimum brightness
|
||||
finalColor = NSColor(white: minBrightness, alpha: 1.0)
|
||||
} else {
|
||||
var color = NSColor(red: averageRed, green: averageGreen, blue: averageBlue, alpha: 1.0)
|
||||
|
||||
var hue: CGFloat = 0
|
||||
var saturation: CGFloat = 0
|
||||
var brightness: CGFloat = 0
|
||||
var alpha: CGFloat = 0
|
||||
|
||||
color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
|
||||
|
||||
if brightness < minBrightness {
|
||||
// Increase brightness while maintaining hue and reducing saturation
|
||||
let saturationScale = brightness / minBrightness
|
||||
color = NSColor(hue: hue,
|
||||
saturation: saturation * saturationScale,
|
||||
brightness: minBrightness,
|
||||
alpha: alpha)
|
||||
}
|
||||
|
||||
finalColor = color
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
completion(finalColor)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getBrightness() -> CGFloat {
|
||||
guard let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil) else {
|
||||
return 0
|
||||
}
|
||||
|
||||
let inputImage = CIImage(cgImage: cgImage)
|
||||
|
||||
let filter = CIFilter.areaAverage()
|
||||
filter.inputImage = inputImage
|
||||
filter.extent = inputImage.extent
|
||||
|
||||
guard let outputImage = filter.outputImage else {
|
||||
return 0
|
||||
}
|
||||
|
||||
let context = CIContext(options: 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: CGColorSpaceCreateDeviceRGB())
|
||||
|
||||
let brightness = (0.2126 * CGFloat(bitmap[0]) + 0.7152 * CGFloat(bitmap[1]) + 0.0722 * CGFloat(bitmap[2])) / 255.0
|
||||
|
||||
return brightness
|
||||
}
|
||||
}
|
||||
|
||||
extension Color {
|
||||
func ensureMinimumBrightness(factor: CGFloat) -> Color {
|
||||
guard factor >= 0 && factor <= 1 else {
|
||||
return self // Return original color if factor is out of bounds
|
||||
}
|
||||
|
||||
let nsColor = NSColor(self)
|
||||
|
||||
// Convert to RGB color space
|
||||
guard let rgbColor = nsColor.usingColorSpace(.sRGB) else {
|
||||
return self // Return original color if conversion fails
|
||||
}
|
||||
|
||||
var red: CGFloat = 0
|
||||
var green: CGFloat = 0
|
||||
var blue: CGFloat = 0
|
||||
var alpha: CGFloat = 0
|
||||
|
||||
rgbColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
|
||||
|
||||
// Calculate perceived brightness using the formula: (0.299*R + 0.587*G + 0.114*B)
|
||||
let perceivedBrightness = (0.2126 * red + 0.7152 * green + 0.0722 * blue)
|
||||
|
||||
let scale = factor / perceivedBrightness
|
||||
red = min(red * scale, 1.0)
|
||||
green = min(green * scale, 1.0)
|
||||
blue = min(blue * scale, 1.0)
|
||||
|
||||
return Color(red: Double(red), green: Double(green), blue: Double(blue), opacity: Double(alpha))
|
||||
}
|
||||
}
|
||||
101
DynamicIsland/extensions/PanGesture.swift
Normal file
101
DynamicIsland/extensions/PanGesture.swift
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// PanGesture.swift
|
||||
// boringNotch
|
||||
//
|
||||
// Created by Richard Kunkli on 21/08/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AppKit
|
||||
|
||||
extension View {
|
||||
func panGesture(direction: PanDirection, action: @escaping (CGFloat, NSEvent.Phase) -> Void) -> some View {
|
||||
background(
|
||||
PanGestureView(direction: direction, action: action)
|
||||
.frame(maxWidth: 0, maxHeight: 0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct PanGestureView: NSViewRepresentable {
|
||||
let direction: PanDirection
|
||||
let action: (CGFloat, NSEvent.Phase) -> Void
|
||||
|
||||
func makeNSView(context: Context) -> NSView {
|
||||
let view = NSView()
|
||||
NSEvent.addLocalMonitorForEvents(matching: [.scrollWheel]) { event in
|
||||
if event.window == view.window {
|
||||
context.coordinator.handleEvent(event)
|
||||
}
|
||||
return event
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
func updateNSView(_ nsView: NSView, context: Context) {
|
||||
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(direction: direction, action: action)
|
||||
}
|
||||
|
||||
class Coordinator: NSObject {
|
||||
let direction: PanDirection
|
||||
let action: (CGFloat, NSEvent.Phase) -> Void
|
||||
|
||||
var accumulatedScrollDeltaX: CGFloat = 0
|
||||
var accumulatedScrollDeltaY: CGFloat = 0
|
||||
|
||||
init(direction: PanDirection, action: @escaping (CGFloat, NSEvent.Phase) -> Void) {
|
||||
self.direction = direction
|
||||
self.action = action
|
||||
}
|
||||
|
||||
@objc func handleEvent(_ event: NSEvent) {
|
||||
if event.type == .scrollWheel {
|
||||
accumulatedScrollDeltaX += event.scrollingDeltaX
|
||||
accumulatedScrollDeltaY += event.scrollingDeltaY
|
||||
|
||||
switch direction {
|
||||
case .down:
|
||||
if accumulatedScrollDeltaY > 0 {
|
||||
handle()
|
||||
}
|
||||
case .up:
|
||||
if accumulatedScrollDeltaY < 0 {
|
||||
handle()
|
||||
}
|
||||
case .left:
|
||||
if accumulatedScrollDeltaX < 0 {
|
||||
handle()
|
||||
}
|
||||
case .right:
|
||||
if accumulatedScrollDeltaX > 0 {
|
||||
handle()
|
||||
}
|
||||
}
|
||||
|
||||
func handle() {
|
||||
if (direction == .left || direction == .right) {
|
||||
action(abs(accumulatedScrollDeltaX), event.phase)
|
||||
} else {
|
||||
action(abs(accumulatedScrollDeltaY), event.phase)
|
||||
}
|
||||
}
|
||||
|
||||
if event.phase == .ended {
|
||||
accumulatedScrollDeltaY = 0
|
||||
accumulatedScrollDeltaX = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum PanDirection {
|
||||
case left
|
||||
case right
|
||||
case up
|
||||
case down
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue