Skip to content

Instantly share code, notes, and snippets.

@PaulWoodIII
Created August 21, 2019 13:25
Show Gist options
  • Select an option

  • Save PaulWoodIII/5440a5ef78bd48d246d40616b1a3bdc9 to your computer and use it in GitHub Desktop.

Select an option

Save PaulWoodIII/5440a5ef78bd48d246d40616b1a3bdc9 to your computer and use it in GitHub Desktop.
Insertion Sort Visualized on the Apple Watch using SwiftUI
//
// ContentView.swift
// WatchSort WatchKit Extension
//
// Created by Paul Wood on 8/21/19.
// Copyright © 2019 Paul Wood. All rights reserved.
//
// InsertionSortArray take from Apple's Sample Code and modified for SwiftUI
// https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/using_collection_view_compositional_layouts_and_diffable_data_sources
// Check out the talk on diffable datasources here: https://developer.apple.com/videos/play/wwdc2019/220
import SwiftUI
import Combine
public class InsertionSortArray: Hashable, Identifiable, ObservableObject, CustomStringConvertible {
public var description: String {
let allNodeDescription = self.nodes.map { (node) -> String in
return node.description
}
return allNodeDescription.joined(separator: ",")
}
public struct SortNode: Hashable, Identifiable, CustomStringConvertible {
public var description: String {
return String(value)
}
let value: Int
let color: UIColor
init(value: Int, maxValue: Int) {
self.value = value
let hue = CGFloat(value) / CGFloat(maxValue)
self.color = UIColor(hue: hue, saturation: 1.0, brightness: 1.0, alpha: 1.0)
}
private let identifier = UUID()
public var id: UUID {
return identifier
}
public func hash(into hasher: inout Hasher) {
hasher.combine(identifier)
}
public static func == (lhs: SortNode, rhs: SortNode) -> Bool {
return lhs.identifier == rhs.identifier
}
}
var values: [SortNode] {
return nodes
}
var isSorted: Bool {
return isSortedInternal
}
func sortNext() {
objectWillChange.send()
performNextSortStep()
}
init(count: Int) {
nodes = (0..<count).map { SortNode(value: $0, maxValue: count) }.shuffled()
}
func randomize() {
objectWillChange.send()
self.isSortedInternal = false
currentIndex = 1
nodes = nodes.shuffled()
}
public func hash(into hasher: inout Hasher) {
hasher.combine(identifier)
}
public static func == (lhs: InsertionSortArray, rhs: InsertionSortArray) -> Bool {
return lhs.identifier == rhs.identifier
}
private var identifier = UUID()
private var currentIndex = 1
private var isSortedInternal = false
private var nodes: [SortNode]
public var objectWillChange = PassthroughSubject<Void, Never>()
}
extension InsertionSortArray {
fileprivate func performNextSortStep() {
if isSortedInternal {
return
}
if nodes.count == 1 {
isSortedInternal = true
return
}
var index = currentIndex
let currentNode = nodes[index]
index -= 1
while index >= 0 && currentNode.value < nodes[index].value {
let tmp = nodes[index]
nodes[index] = currentNode
nodes[index + 1] = tmp
index -= 1
}
currentIndex += 1
if currentIndex >= nodes.count {
isSortedInternal = true
}
}
}
class Pulse: ObservableObject {
var shouldEmmit: CurrentValueSubject<Bool, Never>
public var publisher: AnyPublisher<Void, Never>
init() {
let shouldEmmit = CurrentValueSubject<Bool, Never>(false)
let publisher = Timer.publish(every: 0.5, on: RunLoop.main, in: .default)
.autoconnect()
.filter({ _ in shouldEmmit.value })
.map { _ -> Void in }
.eraseToAnyPublisher()
self.shouldEmmit = shouldEmmit
self.publisher = publisher
}
public func toggle() {
self.shouldEmmit.send(!self.shouldEmmit.value)
}
}
struct InsertionSortView: View {
private static let columnCount = 16
@ObservedObject var list = InsertionSortArray(count: columnCount)
@ObservedObject var pulse = Pulse()
private func handleTap() {
if !self.list.isSorted {
self.list.sortNext()
} else {
self.list.randomize()
}
}
var body: some View {
GeometryReader { context in
VStack(spacing: 0) {
ForEach(self.list.values) { (node: InsertionSortArray.SortNode) in
Rectangle()
.frame(width: context.size.width,
height: context.size.height / CGFloat(InsertionSortView.columnCount),
alignment: .center)
.foregroundColor(Color(node.color))
}
}.animation(.default)
.onTapGesture {
self.handleTap()
}.onReceive(self.pulse.publisher, perform: { _ in
self.handleTap()
})
.onLongPressGesture {
self.pulse.toggle()
}
}
}
}
struct InsertionSortView_Previews: PreviewProvider {
static var previews: some View {
InsertionSortView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment