I have tried hard to find a solution but I'm stuck. have a custom table view with timer in each cell. when the timer expires the cell should get deleted even if the cell is Offscreen it should get deleted and should not be displayed.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! iDealCell
// cell.SponsorLogo.image = UIImage(named: "back.png")!
cell.SponsorName.text = iDeals[indexPath.row].SponsorName;
cell.Distance.text = iDeals[indexPath.row].Distance;
cell.Type.text = iDeals[indexPath.row].Type;
cell.iDealTimer.font = UIFont(name: "DBLCDTempBlack", size: 18.0)
onHourFromNow = NSDate(timeInterval: 10, sinceDate: timeNow)
let TimeDiffInSec = NSCalendar.currentCalendar().components(.Second, fromDate: timeNow, toDate: onHourFromNow, options: []).second
cell.TimeDiffInSec = TimeDiffInSec
cell.kickOffCountdown()
cell.backgroundColor = UIColor.clearColor()
cell.delegate = self
return cell
}
in cell class, three functions to initialise and run the timer
func kickOffCountdown(){
self.setCountDown()
Timer.invalidate()
Timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(iDealCell.setCountDown), userInfo: nil, repeats: true)
}
func secondsToHoursMinutesSeconds (seconds : Int) -> (String, String, String) {
let hours = seconds / 3600
let minutes = (seconds % 3600) / 60
let seconds = (seconds % 3600) % 60
let hoursString = hours < 10 ? "0\(hours)" : "\(hours)"
let minutesString = minutes < 10 ? "0\(minutes)" : "\(minutes)"
let secondsString = seconds < 10 ? "0\(seconds)" : "\(seconds)"
return (hoursString, minutesString, secondsString)
}
func setCountDown() {
if(TimeDiffInSec > 0) {
let (h,m,s) = secondsToHoursMinutesSeconds(TimeDiffInSec)
self.iDealTimer.text = "\(h):\(m):\(s)"
TimeDiffInSec = TimeDiffInSec - 1
}
else{
self.iDealTimer.text = "EXPIRED"
if let delegate = self.delegate {
delegate.DeleteiDealID(1)
}
Timer.invalidate()
}
}
Any help will be greatly appreaciated
You can do simple thing whenever your time have been expire you can remove those values from your array of dictionary whatever you used for row count.
Simple thing here is your all table cell depends on your row count remover particular cell with by removing particular array object.
example :
if timerExpire == true {
array.removeAtIndex(5)
self.table.reloadData()
}
This is a tricky problem, because you want:
Table rows to be deleted even if they are offscreen at the time the timer pops.
New rows can be added while the old rows are "ticking".
The first point means that you do not want the timer to be kept in the cell. It is the wrong place anyway, because cells get reused and you'd have a nightmare invalidating and restarting timers.
The second point means that the row number you want to delete at the time the timer is started could be different than the row number you delete when the timer pops. You may start your timer for row 5 to be deleted in 5 seconds, but in the meantime row 4 gets deleted, making the former row 5 now row 4. When the former row 5's timer pops, row 4 needs to be deleted from the table.
Here is the approach I suggest:
Give each row in your table a unique ID. This will just be a simple count that is maintained by your UITableViewController class.
var nextID = 0
Maintain a list of active ID's that correspond to the rows that are currently in your table. Add this property to your UITableViewController:
var activeIDs = [Int]()
Add a dictionary to your table that maps a NSTimer to an ID. Add this to your UITableViewController:
var timerIDmap: [NSTimer: Int]()
When you create a new row in your table:
let newID = nextID
activeIDs.append(newID)
nextID += 1
In cellForRowAtIndexPath, be sure to store the ID in a property of the cell.
cell.cellID = activeIDs[indexPath.row]
When you create a timer, you need to store the timer and its corresponding cell ID in the timerIDmap. Since you'll do this in the custom cell, the cell needs to have a weak reference to the tableViewController that holds it:
// add this property to your cell
weak var myTableVC: UITableViewController?
and assign that property in cellForRowAtIndexPath:
cell.myTableVC = self
so that when you create the timer:
let timer = NSTimer.scheduledTimerWithTimerInterval(...
myTableVC?.timerIDmap[timer] = cellID
When your timer ticks, you need to decrement the time left on that timer. That means the time left should also be kept in your model. Add this dictionary to your UITableViewController:
var timeLeft = [Int: Int]() // maps CellID to time left
that means that when you create the timer in the first place, you will store timeLeft in this dictionary
myTableVC?.timeLeft[cellID] = 50 // some appropriate value
OK, so now in your handleCountdown routine which should be implemented in your UITableViewController:
func handleCountdown(timer: NSTimer) {
let cellID = timerIDMap[timer]
// find the current row corresponding to the cellID
let row = activeIDs.indexOf(cellID)
// decrement time left
let timeRemaining = timeLeft[cellID] - 1
timeLeft[cellID] = timeRemaining
if timeRemaining == 0 {
timer.invalidate
timerIDmap[timer] = nil
activeIDs.removeAtIndex(row)
tableView.deleteRowsAtIndexPaths(NSIndexPath(row: row, section: 0), withRowAnimation: ...
} else {
tableView.reloadRowsAtIndexPaths(NSIndexPath(row: row, section: 0), withRowAnimation: ...
}
}
This leaves very little work for your custom cell. It should merely take the time left on the timer and format it for display. In cellForRowAtIndexPath, tell the cell how much time is left on the timer:
cell.timeLeft = timeLeft[activeIDs[indexPath.row]]
The number of items in your table is the same as the number of items in activeIDs, so in tableView:numberOfRowsInSection return
return activeIDs.count
I think, this will be better way:
Inside tableView:cellForRowAtIndexPath calculate (or fetch) time and add it's value to label in cell.
Remove timer from cell.
Add timer in main class (where tableView placed) in viewDidAppear (or inside block where you fetch data), that will every second call method, that check and remove expired objects (or you can apply filter) and fire tableView.reloadData() (or delete needed rows animated).
In viewDidDisappear invalidate timer.
I have been trying these solutions but I dont think they are viable towards the goal. Just wanted to let people know. If you have found something that works or have found the same can you please let us know
This is the solution I have come up with. Its not a perfect solution by any means however, it does solve the problem.
In the viewcontroller:
func handleDate(timer: Timer) {
DispatchQueue.main.async() {
if self.posts.count < 1 {
print("Empty")
timer.invalidate()
} else {
let calendar = Calendar.current
let date = Date()
let componentsCurrent = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date)
var components = DateComponents()
components.hour = componentsCurrent.hour
components.minute = componentsCurrent.minute
components.second = componentsCurrent.second
components.year = componentsCurrent.year
components.month = componentsCurrent.month
components.day = componentsCurrent.day
let currentTime = calendar.date(from: components)!
for post in self.posts {
let cellID = post.postID
let row = self.postsInFeed.index(of: cellID)
let endDate = TimeInterval(post.time)
if currentTime.timeIntervalSince1970 >= endDate {
self.tableView.beginUpdates()
timer.invalidate()
print(post.postID)
print("Deleting tableview row")
self.postsInFeed.removeFirst()
self.posts.removeFirst()
let store: Dictionary<String, Any> = ["caption": post.caption, "mediaURL": post.imageUrl as Any, "likes": post.likes, "user_ID": FriendSystem.system.CURRENT_USER_ID, "time": post.time]
let firebasePost = FriendSystem().GROUP_REF.child(self.group.groupID).child("storage").child(post.postID)
firebasePost.setValue(store)
FriendSystem().GROUP_REF.child(self.group.groupID).child("posts").child(cellID).removeValue()
self.tableView.deleteRows(at: [IndexPath(row: row!, section: 0)] , with: UITableViewRowAnimation.fade)
self.tableView.endUpdates()
self.tableView.reloadData()
}
}
}
}
}
In the tableviewcell:
func tick(timer: Timer) {
guard let expiresAt = endDate else {
return
}
let calendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)
if let components = calendar?.components([.hour, .minute, .second], from: NSDate() as Date, to: expiresAt, options: []) {
currentTime = formatDateComponents(components: components as NSDateComponents)
self.timerLbl.text = currentTime
if Date() >= endDate! {
timer.invalidate()
}
}
}
func formatDateComponents(components: NSDateComponents) -> String {
let hours = components.hour
let minutes = components.minute
let seconds = components.second
return "\(hours):\(minutes):\(seconds)"
}
Related
This is the code I am using:
In viewDidLoad-
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.tapLeaveView))
self.collectionView.addGestureRecognizer(longPressRecognizer)
The method-
#objc func tapLeaveView(longPressGestureRecognizer: UILongPressGestureRecognizer){
if longPressGestureRecognizer.state == UIGestureRecognizer.State.began {
let touchPoint = longPressGestureRecognizer.location(in: self.view)
if let indexPath = collectionView.indexPathForItem(at: touchPoint) {
// code for the view I want
}
}
}
It works but it opens view on every row and I want it to open it at row no- 2 only.
The problem is in your code, you are getting the wrong touchpoint, it gets the wrong indexPath.
To get correct touchPoint
let touchPoint = longPressGestureRecognizer.location(in: collectionView)
Also, first, you need to find all the indexes of the particular row and need to check whether it contains in a row or not. If contain then you need to perform your action.
// Calculation for finding row index for particuler row
let numberOfColumn = 5 // Number of column in collection
let rowForTap = 2 // Your particuler row
let startingIndex = ((rowForTap * numberOfColumn) - numberOfColumn)
let arrGestureIndex: [Int] = Array((startingIndex)..<startingIndex + numberOfColumn) // Indexes of particuler row
In last
#objc func tapLeaveView(longPressGestureRecognizer: UILongPressGestureRecognizer){
if longPressGestureRecognizer.state == UIGestureRecognizer.State.began {
let touchPoint = longPressGestureRecognizer.location(in: collectionView)
if let indexPath = collectionView.indexPathForItem(at: touchPoint), arrGestureIndex.contains(indexPath.item) {
print(indexPath)
// code for the view you want
}
}
}
Another way you can add Gesture for particular cell inside the cellForItem method.
This is a timer App and I would like for the timer to pause when the app gets killed and when you reopen the app I would like the timer to display the last known time.
I have setup a struct for the data to be saved to.
let defaults = UserDefaults.standard
private enum TimeData {
static let allCurrentTime = "allCurrentTime"
}
override func viewDidLoad() {
super.viewDidLoad()
checkForSavedTimer()
marksSavedData()
theTimer.text = "00:00:00"
theTimer.font = UIFont(name: "Menlo-BoldItalic", size: 40)
marksTableView.isHidden = false
}
//I set the timer info to the struct (see below)
#objc func updateTimerLabel() {
seconds += 1
if seconds == 60 {
minutes += 1
seconds = 0
}else if minutes == 60 {
hours += 1
minutes = 0
}
timerString = "\(hours):\(minutes):\(seconds)"
theTimer.text = timerString
defaults.set(theTimer.text, forKey: TimeData.allCurrentTime)
}
// I then created a function which I called in the viewdidload above
func checkForSavedTimer() {
let allCurrentTime = defaults.value(forKey: TimeData.allCurrentTime) as? String ?? ""
theTimer.text = allCurrentTime
}
I thought this would take the string from theTimer save it to the UserDefaults and then in viewDidLoad bring it back out and save it to "theTimer.text" BUT the result is 00:00:00
When you are calling your checkForSavedTimer() function, it is probably getting the correct value from UserDefaults but then you are setting the timer text back to 00:00:00 two lines later. You will need to remove that line and add some logic within your checkForSavedTimer() function to set the value to 00:00:00 if the value from UserDefaults is nil
override func viewDidLoad() {
super.viewDidLoad()
checkForSavedTimer()
marksSavedData()
// Remove this -> theTimer.text = "00:00:00"
theTimer.font = UIFont(name: "Menlo-BoldItalic", size: 40)
marksTableView.isHidden = false
}
func checkForSavedTimer() {
// Set the timer text to the value from UserDefaults or "00:00:00" if the value is nil
theTimer.text = defaults.string(forKey: TimeData.allCurrentTime) ?? "00:00:00"
}
Also it is better to use .string(forKey:) to get a String from UserDefaults because then the value is already typed for you and therefore you don't need to add as? String
Is there anyway to lock buttons until the following day? For example, my app has coupons in the form of buttons and when you click the button "Coupon 1" it toggles to say "USED + the current date". I want the user to be able to only use 2 coupons per ViewController/page per dag, and am wondering if its possible to lock the other coupons once two are pressed by the user, and the coupons unlock the following day. I don't want the buttons to disappear, just want them to be locked. Below is the code for one of my buttons/coupons. It would be ideal if a lock actually appeared in the corner of the remaining buttons after 2 are pressed.
var didClick : Bool = false
#IBAction func special1BTNpressed(_ sender: UIButton) {
if !didClick {
didClick = true
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "MM/dd/yyyy"
let result = formatter.string(from: date)
sender.setTitle("USED " + result, for: .normal)
You can store the value of the date of which you did the action and then later compare it with today
func canPerformActionForSpecialBTN() -> Bool {
let timeInterval = UserDefaults.standard.double(forKey: "ActionDateForSpecialBTN")
guard timeInterval.isNormal else { return true }
let date = Date(timeIntervalSince1970: timeInterval)
guard Calendar.current.isDateInToday(date) else { return true }
return false
}
func performSpecialBTNAction() {
guard canPerformActionForSpecialBTN() else { return }
defer { UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "ActionDateForSpecialBTN") }
//implement action here
}
and maybe in the viewDidAppear you can enable or disable the button
button.isEnabled = !canPerformActionForSpecialBTN()
I have a UITableView which contains rows of posts that are timestamped based on when they enter into my database. I want to have the posts (rows) of each cell automatically delete after a certain amount of time has passed. Currently this is what I have towards this goal:
func deletePost(timer: Timer) {
for post in posts {
let startTime = Date(timeIntervalSince1970: TimeInterval(post.time))
let date = Date()
let endTime = startTime + 25
let call = Calendar.current
let componentsCurrent = call.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date)
var components = DateComponents()
components.hour = componentsCurrent.hour
components.minute = componentsCurrent.minute
components.second = componentsCurrent.second
components.year = componentsCurrent.year
components.month = componentsCurrent.month
components.day = componentsCurrent.day
let currentTime = call.date(from: components)!
print(currentTime)
print(" 2" , endTime)
if currentTime.timeIntervalSince1970 == endTime.timeIntervalSince1970 {
timer.invalidate()
print("ENDDDDD")
posts.removeLast()
tableView.reloadData()
}
}
}
The problem I am seeing is that current date only records the date once but doesn't continue to update. I have looked through other posts regarding timers and I have implemented them however it has not worked within this context. I assume once I can get these variables to equal the other code will be straight forward.
cellForRowAt is not the place for any kind of logic such as this. Its only purpose is to provide the requested cell. That's all.
What you want is to setup a timer, say once a minute (or some other appropriate timespan). When the timer goes off, scan your data source for rows that should be removed. Delete the appropriate values from the data source and update the table view accordingly using deleteRows passing in an array of IndexPath that correspond to the removed entries.
Your code will check if it's time to delete something only when your tableView is
reloadingData(); .
//That's not good, because `cellForRow`
//is called as many times as this method returns
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count;
}
You should remove the data from your array, and then call tableView.reloadData();
To do that by specific time, you can :
timeToRemoveCell = 10:00;
and check this time in viewWillAppear.
or you can make a timer as rmaddy said.
Current Behavior
My core data keeps track of the values for a UITableView list. Each row has a title and description. My core data is working for appending new records and later deleting them. Core data is also working great for editing the content of the existing records.
Problem/Question
I just added drag and drop functionality in the table. On the surface, it works perfectly. I can drag the top item to the bottom, middle to the top, etc. However, the new list order does not persist after app shutdown because the Core Data records aren't being updated.
I found a few tutorials on this but none seem to work with my code. I also spent a few hours trying to use and adapt my current Core Data skill set (update, delete, edit) to invent a solution. I have no sweet moves or code kung fu.
Should you choose to accept this mission, below are the details and code you might need.
Information
Coding in Swift
Using X-Code 6.4
Core Data Info:
File name is: CD_Model
Entity name: TodayTask
Attribute names: "name" and "desc"
Code:
Main list variable:
var todayTaskList = [NSManagedObject]()
ViewDidLoad for main list page with UITableView
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//Break
//Load the list from Core Data
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName:"TodayTask")
var error: NSError?
let fetchedResults = managedContext.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObject]
if let results = fetchedResults {
todayTaskList = results
} else {
println("Could not fetch \(error), \(error!.userInfo)")
}
//Break
//This provides a variable height for each row
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 80.0
//Break
//Part of code for cell drag and drop functionality
let longpress = UILongPressGestureRecognizer(target: self, action: "longPressGestureRecognized:")
tableView.addGestureRecognizer(longpress)
}
Table Setup
//***** ----- ***** ------ ***** ----- ***** ----- *****
//Table View & Cell Setup
//***** ----- ***** ------ ***** ----- ***** ----- *****
#IBOutlet weak var name_Label: UILabel!
#IBOutlet weak var desc_Label: UILabel!
//Tells the table how many rows it should render
//*Looks to the Core Data NSObject to count tasks
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return todayTaskList.count
}
//Creates the individual cells. If the above function returns 3, this runs 3 times
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//Setup variables
let cellIdentifier = "BasicCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! CustomTableViewCell
let task = todayTaskList[indexPath.row]
//Create table cell with values from Core Data attribute lists
cell.nameLabel!.text = task.valueForKey("name") as? String
cell.descLabel!.text = task.valueForKey("desc") as? String
//Make sure the row heights adjust properly
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 80.0
return cell
}
And here is where my problem is, the drag and drop. This code works, but it is missing code that rearranges the core data. Without that code, any drag/drop re-ordering will reset when I close the gap:
//This function initiates the Drag & Drop code.
func longPressGestureRecognized(gestureRecognizer: UIGestureRecognizer) {
let longPress = gestureRecognizer as! UILongPressGestureRecognizer
let state = longPress.state
var locationInView = longPress.locationInView(tableView)
var indexPath = tableView.indexPathForRowAtPoint(locationInView)
struct My {
static var cellSnapshot : UIView? = nil
}
struct Path {
static var initialIndexPath : NSIndexPath? = nil
}
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as! CustomTableViewCell;
var dragCellName = currentCell.nameLabel!.text
var dragCellDesc = currentCell.descLabel.text
//Steps to take a cell snapshot. Function to be called in switch statement
func snapshopOfCell(inputView: UIView) -> UIView {
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
inputView.layer.renderInContext(UIGraphicsGetCurrentContext())
let image = UIGraphicsGetImageFromCurrentImageContext() as UIImage
UIGraphicsEndImageContext()
let cellSnapshot : UIView = UIImageView(image: image)
cellSnapshot.layer.masksToBounds = false
cellSnapshot.layer.cornerRadius = 0.0
cellSnapshot.layer.shadowOffset = CGSizeMake(-5.0, 0.0)
cellSnapshot.layer.shadowRadius = 5.0
cellSnapshot.layer.shadowOpacity = 0.4
return cellSnapshot
}
switch state {
case UIGestureRecognizerState.Began:
//Calls above function to take snapshot of held cell, animate pop out
//Run when a long-press gesture begins on a cell
if indexPath != nil {
Path.initialIndexPath = indexPath
let cell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!
My.cellSnapshot = snapshopOfCell(cell)
var center = cell.center
My.cellSnapshot!.center = center
My.cellSnapshot!.alpha = 0.0
tableView.addSubview(My.cellSnapshot!)
UIView.animateWithDuration(0.25, animations: { () -> Void in
center.y = locationInView.y
My.cellSnapshot!.center = center
My.cellSnapshot!.transform = CGAffineTransformMakeScale(1.05, 1.05)
My.cellSnapshot!.alpha = 0.98
cell.alpha = 0.0
}, completion: { (finished) -> Void in
if finished {
cell.hidden = true
}
})
}
case UIGestureRecognizerState.Changed:
//Runs when the user "lets go" of the cell
//Sets CG Y-Coordinate of snapshot cell to center of current location in table (snaps into place)
//If the indexPath is not 0 AND is not the same as it began (didn't move)...
//Update array and table row order
var center = My.cellSnapshot!.center
center.y = locationInView.y
My.cellSnapshot!.center = center
if ((indexPath != nil) && (indexPath != Path.initialIndexPath)) {
swap(&todayTaskList[indexPath!.row], &todayTaskList[Path.initialIndexPath!.row])
tableView.moveRowAtIndexPath(Path.initialIndexPath!, toIndexPath: indexPath!)
Path.initialIndexPath = indexPath
}
default:
//Runs continuously as there's a long press recognized?
//Animates cell movement
//Completion block:
//Removes snapshot of cell, cleans everything up
let cell = tableView.cellForRowAtIndexPath(Path.initialIndexPath!) as UITableViewCell!
cell.hidden = false
cell.alpha = 0.0
UIView.animateWithDuration(0.25, animations: { () -> Void in
My.cellSnapshot!.center = cell.center
My.cellSnapshot!.transform = CGAffineTransformIdentity
My.cellSnapshot!.alpha = 0.0
cell.alpha = 1.0
}, completion: { (finished) -> Void in
if finished {
Path.initialIndexPath = nil
My.cellSnapshot!.removeFromSuperview()
My.cellSnapshot = nil
}
})
}
I am pretty sure the code I need would go inside the second case statement:
case UIGestureRecognizerState.Changed:
I also think the code I need would start with something like...
var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
var context: NSManagedObjectContext = appDel.managedObjectContext!
But then is there a special code for rearranging? Do I have to delete and insert? If so, how?
HUGE thanks in advance to anyone who can help resolve this one!
Well first of all, you might find it easier to create classes for each entity so that you don't have to work with objects vaguely typed to NSManagedObject or read and cast with valueForKey(_:). In the solution below, I've included code samples for that.
So to solve your order problem, there are two things you could do:
1) Add a property that defines the order of your Task entity. This can be a simple as an NSNumber called displayOrder. Your fetch request can then order the results according to that property. Then, when your table cells are re-arranged, iterate through the task list and update the displayOrder property of each task to reflect the order in which they are being displayed. Save your managed object context and the next time your fetch request loads, it will order them accordingly.
class Task: NSManagedObject {
#NSManaged var name: NSString
#NSManaged var desc: NSString
#NSManaged var displayOrder: NSNumber
}
let fetchRequest = NSFetchRequest()
let sortDescriptor = NSSortDescriptor(key: "displayOrder", ascending: true )
fetchRequest.sortDescriptors = [ sortDescriptor ]
2) Create a CoreData entity that represents a list with a to-many relationship that stores each task entity in an ordered set. Then, when you add tasks to the set, they will be remain saved in the order you've added them.
class TaskList: NSManagedObject {
#NSManaged var tasks: NSOrderedSet?
}
class Task: NSManagedObject {
#NSManaged var name: NSString
#NSManaged var desc: NSString
#NSManaged var parentList: TaskList?
}
Update to answer remaining questions:
I highly recommend you use your own custom classes instead of NSManagedObject, but until you figure that part out here's what you can do to your code as is.
To update display order after rearranging or deleting:
func updateDisplayOrder() {
for i in 0..<todayTaskList.count {
let task = todayTaskList[i]
task.setValue( i, forKey: "displayOrder" )
}
}
To append a new task:
func addTask( task: NSManagedObject, displayOrder: Int ) {
todayTaskList.insert( task, atIndex: displayOrder )
updateDisplayOrder()
}