Doubly Circular linked list in Swift using Generics

Let’s try writing the data structure to support the animation as shown below. Basically you need to have multiple views be able to move back and forth. I went with circular linked list since, it maintains a pointer to the next and previous nodes. I wrote the data structure using Generics, this way we are able to use it to support different data types.

The data structure is shown below.

//
//  CircularLinkedList.swift
//  swipeRight
//
//  Copyright © 2019 darshan. All rights reserved.
//

import Foundation
// -------------------------------------------------------------------------------------------
// Circular linked list
// -------------------------------------------------------------------------------------------
class Node<T : Equatable> {
	var next : Node<T>?
	var prev : Node<T>?
	var value : T?
	init(input : T?) {
		self.value = input
		self.next = nil
		self.prev = nil
	}
}

extension Node : Equatable {
	static func == (lhs: Node, rhs: Node) -> Bool {
		return lhs.value == rhs.value
	}
}

class circularlinkedlist<T : Equatable> {
	var head : Node<T>?
	var current : Node<T>?
	func insert(value : T) -> Node<T> {
		let newNode = Node(input: value)
		if self.head == nil {
			newNode.next = newNode
			newNode.prev = newNode
			self.head = newNode
			current = self.head
		} else {
			// If list is not empty
			/* Find last node */
			let last = self.head?.prev
			// Start is going to be next of new_node
			newNode.next = self.head
			// Make new node previous of start
			self.head?.prev = newNode
			// Make last previous of new node
			newNode.prev = last
			// Make new node next of old last
			last?.next = newNode
			current = last
		}
		return current!
	}
		
	func isEqual(left : Node<T>?, right : Node<T>?) -> Bool {
		if left?.value == right?.value  {
			return true
		}
		return false
	}
	
	func display() {
		var nextNode = self.head
		repeat {
			print(nextNode!.value!)
			nextNode = nextNode?.next
//		} while !self.isEqual(left: nextNode, right: self.head)
		} while !(nextNode == self.head)
	}
	
	func getCurrent() -> Node<T> {
		return current!
	}
	
	func setCurrent(node: Node<T>) {
		current = node
	}
	
	func getNext() -> Node<T>? {
		current?.next
	}
	
	func getPrev() -> Node<T>? {
		return current?.prev
	}

	func deleteNodeWith(value : T) {
		// iterate through the nodes
		if(self.head?.value == value) {
			// delete the head node
			var next = self.head?.next
			var prev : Node<T>?
			while next?.value != self.head?.value {
				prev = next
				next = next?.next
			}
			self.head = next?.next
			prev?.next = self.head
		} else {
			// lets iterate through all the nodes
			var prev : Node<T>?
			var next = self.head
			let delete = Node(input: value)
			while next?.value != delete.value {
				prev = next
				next = next?.next
			}
			prev?.next = next?.next
		}
	}
	
	func deleteNode(node : Node<T>) {
		// iterate through the nodes
		if(self.head == node) {
			// delete the head node
			var next = self.head?.next
			var prev : Node<T>?
			while next != self.head {
				prev = next
				next = next?.next
			}
			self.head = next?.next
			prev?.next = self.head
		} else {
			// lets iterate through all the nodes
			var prev : Node<T>?
			var next = self.head
			while next != node {
				prev = next
				next = next?.next
			}
			prev?.next = next?.next
		}
	}
}

You can test the code as shown below. As you can see, for now I have tested this with UIView, String and Int.

//let clist = circularlinkedlist<String>()
//let mon = clist.insert(value: "monday")
//let tues = clist.insert(value: "tuesday")
//let wed = clist.insert(value: "wednesday")
//let thur = clist.insert(value: "thursday")
//let friday = clist.insert(value: "friday")
//let saturday = clist.insert(value: "saturday")
//let sunday = clist.insert(value: "sunday")

//clist.deleteNode(node: thur)
//clist.deleteNodeWith(value: "monday")
//clist.display()

let clist = circularlinkedlist<Int>()
clist.insert(value: 1)
clist.insert(value: 2)
clist.insert(value: 3)
clist.insert(value: 4)
clist.insert(value: 5)
clist.deleteNodeWith(value: 2)
clist.display()

//let clist = circularlinkedlist<UIView>()
//let v1 = clist.insert(value: UIView())
//let v2 = clist.insert(value: UIView())
//let v3 = clist.insert(value: UIView())
//clist.deleteNode(node: v2)
//clist.display()

//print("Next ",clist.getNext()?.value," Prev ", clist.getPrev()?.value)
//print("Next ",clist.getNext()?.value," Prev ", clist.getPrev()?.value)
//print("Next ",clist.getNext()?.value," Prev ", clist.getPrev()?.value)

This is the code for testing the view controller animation

//
//  ViewController.swift
//  swipeRight
//
//  Created by darshan on 11/27/19.
//  Copyright © 2019 darshan. All rights reserved.
//

import UIKit
class ViewController: UIViewController {
	let weekdays = ["sunday", "saturday", "friday", "thursday", "wednesday", "tuesday", "monday"]
	let views : [UIView] = []
	let list = circularlinkedlist<Day>()

	override func viewDidLoad() {
		super.viewDidLoad()
		
		for day in weekdays {
			let view = Day(frame: self.view.frame)
			view.setName(day)
			_ = list.insert(value: view)
		}
		
		let currentView = list.getCurrent().value ?? Day()
		view.backgroundColor = .red
		self.view.addSubview(currentView)
		
		let rightSwipe = UISwipeGestureRecognizer(target: self, action:#selector(didSwipe(_:)))
		rightSwipe.direction = .right
		
		let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(didSwipe(_:)))
		leftSwipe.direction = .left
		
		self.view.addGestureRecognizer(rightSwipe)
		self.view.addGestureRecognizer(leftSwipe)
		
	}
	
	@objc
	func didSwipe(_ sender : UISwipeGestureRecognizer) {
		if sender.direction == .left {
			let node = list.getPrev()
			let currentView = self.list.getCurrent().value ?? Day()
			print("prev ", node?.prev?.value?.label?.text, " next ",node?.next?.value?.label?.text, " currently this ",node?.value?.label?.text)
			list.setCurrent(node: node!)
			let view = node!.value
			let beforeFrame = view?.frame
			view?.frame = CGRect(x: (view?.frame.size.width)!, y: 0, width: (view?.frame.size.width)!, height: (view?.frame.size.height)!)
			self.view .addSubview(node!.value!)
			
			UIView.animate(withDuration: 0.5) {
				view?.frame = beforeFrame ?? CGRect(x: 0, y: 0, width: 0, height: 0)
				currentView .removeFromSuperview()
			}
		}
		if sender.direction == .right {
			let currentView = list.getCurrent().value ?? Day()
			let node = list.getNext()
			print("prev ", node?.prev?.value?.label?.text, " next ",node?.next?.value?.label?.text, " currently this ",node?.value?.label?.text)
			list.setCurrent(node: node!)
			self.view .addSubview(node!.value!)
			
			let view = node!.value
			let beforeFrame = view?.frame
			view?.frame = CGRect(x: -(view?.frame.size.width)!, y: 0, width: (view?.frame.size.width)!, height: (view?.frame.size.height)!)
			UIView.animate(withDuration: 0.5) {
				view?.frame = beforeFrame ?? CGRect(x: 0, y: 0, width: 0, height: 0)
				currentView .removeFromSuperview()
			}
		}
	}
}

This is a subclass of the View

//
//  View.swift
//  swipeRight
//
//  Copyright © 2019 darshan. All rights reserved.
//

import Foundation
import UIKit

class Day : UIView {
	var label : UILabel?
	var name : String = ""
	override init(frame: CGRect) {
		super.init(frame: frame)
		label = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 100))
		label?.text = "test"
		label?.center = self.center
		label?.font = UIFont.italicSystemFont(ofSize: 40.0)
		self.addSubview(label!)
	}
	
	required init?(coder: NSCoder) {
		fatalError("init(coder:) has not been implemented")
	}
	
	func setName(_ name : String) {
		self.label?.text = name
	}
}

Leave a Reply

Your email address will not be published. Required fields are marked *