Rearranging cells between sections - arrays

I know how to swap cells if they are underneath one section because internet is full of that but I'm struggling with the same having two sections. I cannot move every cell because obviously index is out of range. I thought it would be unnecessary to post the whole code so I'll paste only the important bits
I have declared:
let sections: [String] = ["Box", "Inventory"]
var s1Data: [UIImage] = [] //
var s2Data: [UIImage] = [] //these are filled by other function
let sectionsImages: [UIImage] = [#imageLiteral(resourceName: "blackBox"), #imageLiteral(resourceName: "blackBag")]
var sectionData: [[UIImage]] = []
In viewDidLoad():
tableView.isEditing = true
tableView.delegate = self
tableView.dataSource = self
sectionData = [s1Data, s2Data]
Then quite a number of tableView functions, but the one I cannot go through and the one I'm talking about:
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath)
{
let item = sectionData[sourceIndexPath.row]
sectionData.remove(at: sourceIndexPath.row)
sectionData.insert(item, at: destinationIndexPath.row)
}
And so, swapping goes fine until I try to swap the last image cell because of mentioned 'out of bounds' failure. I know I should declare item as something like:
let item = sectionData[sourceIndexPath.section][sourceIndexPath.row]
but what about "remove" and "insert"?
I would be thankful for your help
EDIT:
I did it, although I don't know if it is one of the simpler way. Anyway:
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath)
{
if sourceIndexPath.section == 0 && destinationIndexPath.section == 0
{
let item = sectionData[0][sourceIndexPath.row]
sectionData[0].remove(at: sourceIndexPath.row)
sectionData[0].insert(item, at: destinationIndexPath.row)
}
else if sourceIndexPath.section == 0 && destinationIndexPath.section == 1
{
let item = sectionData[0][sourceIndexPath.row]
sectionData[0].remove(at: sourceIndexPath.row)
sectionData[1].insert(item, at: destinationIndexPath.row)
}
else if sourceIndexPath.section == 1 && destinationIndexPath.section == 0
{
let item = sectionData[1][sourceIndexPath.row]
sectionData[1].remove(at: sourceIndexPath.row)
sectionData[0].insert(item, at: destinationIndexPath.row)
}
else if sourceIndexPath.section == 1 && destinationIndexPath.section == 1
{
let item = sectionData[1][sourceIndexPath.row]
sectionData[1].remove(at: sourceIndexPath.row)
sectionData[1].insert(item, at: destinationIndexPath.row)
}
else
{
print("ERROR - SWAP MALFUNCTION")
}
}

This is how I did it, the insert uses the destinationIndexPath.section:
groupItems[sourceIndexPath.section].remove(at: sourceIndexPath.row)
groupItems[destinationIndexPath.section].insert(movedObject, at: destinationIndexPath.row)

Related

SWIFT Array of HealthKt Samples divided up into Array groups by date then shown on UITableView

Could I please have some help making a table with section headers that are the days of the week, Starting with the current day, eg. If today is Saturday then Saturday is the first section head, and then for the past 7 days. The data comes from an array called workouts that is made up of HealthKit Samples. I can find the date by printing workout.startDate or workout.endDate
This is what workouts looks like with 3 sample workouts in the array that I got by running a HealthKit query. You can see that 2020-05-22 is repeated twice because there was two workouts on that day.
var workouts: [HKSample]?
print(workouts) = [0 0A48A590-67BD-4121-BE59-3DA80CC88880 "App_Name"
(1), "iPhone12,3" (13.5) (2020-05-24 14:36:18 +1000 - 2020-05-24
14:37:12 +1000), 0 F744E642-C3D0-4DB4-A93A-E9AF89B6197A "App_Name" (1),
"iPhone12,3" (13.5) (2020-05-22 17:13:31 +1000 - 2020-05-22 17:18:16
+1000), 0 674CDF99-ED50-4EB6-B733-1786184AD92F "App_Name" (1), "iPhone12,3" (13.5) (2020-05-22 17:03:19 +1000 - 2020-05-22 17:03:51
+1000)]
I would like to seperate those into an array that is like [[],[],[],[],[],[],[],[]] with seperate days so I can count them for the rows in the sections and count the section heads. Also if the array for that day isEmpty I would like to to NOT show it on the table.
var workoutsByDate: [[HKSample]]?
workoutsByDate = [[],[],[],[],[],[],[],[]]
Then id like to get the section head for the UITableview by just getting the day of the week -
let workoutDayFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.timeStyle = .none
formatter.dateFormat = "EEEE"
return formatter
}()
for workout in workouts! {
let workoutDay = workoutDayFormatter.string(from: workout.startDate)
print (workoutDay)
}
Prints
Sunday
Friday
Friday
Then make a table with day sections and the corresponding workout underneath. It would take me weeks to figure this out. At the moment the table just shows all that data with no sections.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let numOfSections = Int(workouts?.count ?? 0)
return numOfSections
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! woroutTableViewCell
let workout = workouts[indexPath.row]
let workoutStartDate = dateFormatter.string(from: workout.startDate)
let rawStartDate = workout.startDate.timeIntervalSinceReferenceDate
let rawEndDate = workout.endDate.timeIntervalSinceReferenceDate
let workoutStartTime = dateFormatter2.string(from: workout.startDate)
let cellTitleString = "\(workoutStartDate) at \(workoutStartTime)"
cell.dateLabel?.text = "\(cellTitleString)"
let sessionDurationTotalTime = TimeInterval(rawEndDate - rawStartDate)
var sessionDurationTimeInSeconds = TimeInterval(rawEndDate - rawStartDate)
let hours = Int(sessionDurationTimeInSeconds / 3600.0)
sessionDurationTimeInSeconds -= (TimeInterval(hours) * 3600)
let minutes = Int(sessionDurationTimeInSeconds / 60.0)
sessionDurationTimeInSeconds -= (TimeInterval(minutes) * 60)
let seconds = Int(sessionDurationTimeInSeconds)
sessionDurationTimeInSeconds -= TimeInterval(seconds)
let strMinutes = String(format: "%02d", minutes)
let strSeconds = String(format: "%02d", seconds)
let sessionDurationTimeString = "\(hours)hr \(strMinutes)min \(strSeconds)sec"
let cellTimeString = "\(sessionDurationTimeString)"
cell.durationLabel?.text = "\(cellTimeString)"
return cell
}
It's been a few weeks and I came up with the solution. I'll post it here for future reference or if it helps someone else. Feel free to provide your own answer or improve mine.
I created sections from the day of the week in the HKSample.startDate
struct Objects {
var sectionName : String!
var sectionObjects : [HKSample]!
}
var objectArray = [Objects]()
...
self.objectArray = [Objects]()
var userObjects = [HKSample] ()
var prevHeader = ""
let formatter = DateFormatter()
for i in 0...samples!.count-1 {
let sample = samples![i].startDate
formatter.setLocalizedDateFormatFromTemplate("eeee")
let dayOfWeek = formatter.string(from: sample)
if prevHeader == "" {
userObjects.append(samples![i])
}
else if prevHeader == dayOfWeek {
userObjects.append(samples![i])
}
else {
self.objectArray.append(Objects(sectionName: prevHeader, sectionObjects: userObjects))
userObjects = [HKSample] ()
userObjects.append(samples![i])
}
prevHeader = dayOfWeek
}
self.objectArray.append(Objects(sectionName: prevHeader, sectionObjects: userObjects))
self.tableView.reloadData()
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! sampleTableViewCell
let sample = objectArray[indexPath.section].sectionObjects[indexPath.row]
cell.dateLabel?.text = ... decorate cell ...
}
func numberOfSections(in tableView: UITableView) -> Int {
return objectArray.count
}
func tableView(_ tableView: UITableView,
titleForHeaderInSection section: Int) -> String? {
return objectArray[section].sectionName.uppercased()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objectArray[section].sectionObjects.count
}

how to append something to array correctly?

I'm going to make a custom cell, already have some labels on it, then I create a cell object and array, try to append that object to array then show on table, but after append, there's no content in my array's properties
I've tried to find out solutions but likely no one has these problems
//tableview implement
var recordCell : [RecordCell] = []
let note = RecordCell()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.recordCell.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "recordCell",for: indexPath) as! RecordCell
let indexPath = indexPath.row
cell.recordFileName?.text = self.recordCell[indexPath].recordFileName?.text
cell.recordDate?.text = self.recordCell[indexPath].recordDate?.text
cell.delegate = self
cell.playBtn.tag = indexPath
return cell
}
//append to array
let alertController = UIAlertController(title: "請輸入錄音名稱", message: "錄音名稱", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default) { (_) in
var name : String = ""
if(alertController.textFields![0].text == ""){
name = "record"
}else{
name = alertController.textFields![0].text!
}
guard self.audioRecorder == nil else{return}
self.recordNumber += 1
self.record.isEnabled = false
self.pause.isEnabled = true
self.stop.isEnabled = true
let destinationUrl = self.getFileURL().appendingPathComponent("\(name).m4a")
let settings = [AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
do {
self.audioRecorder = try AVAudioRecorder(url: destinationUrl, settings: settings)
self.audioRecorder.record()
self.note.recordFileName?.text = name
self.note.recordDate?.text = self.getDate()
self.recordCell.append(self.note)
} catch {
print("Record error:", error.localizedDescription)
}
}
let cancelAction = UIAlertAction(title: "取消", style: .cancel) { (_) in
self.dismiss(animated: true, completion: {
self.audioRecorder.stop()
})
}
alertController.addTextField { (textField) in
textField.placeholder = "輸入名稱"
textField.keyboardType = .default
}
alertController.addAction(okAction)
alertController.addAction(cancelAction)
self.present(alertController,animated: true,completion: nil)
}
I expect when I append something, there's something in array
After appending a new value to the array, call insertRows method like this
self.recordCell.append(self.note)
self.tableView.insertRows(at: [IndexPath(row: recordCell.count-1, section: 0)], with: .automatic)
I didnt see anything wrong.
Are you sure your code is called? Try giving a print() right before adding and see if it's called.
Maybe it's not called because of guard self.audioRecorder == nil else{return}

How to bold first word of array?

var userComment = ["Time these make me.jenny is ","I can't she did it.", "Hey! what a great play made by brad", "I can't she .", "Time like make is a badass", "I can't it.", "She is a mean chose to place","Time me a badass", "Wow! I am just like jenny.I would shit", "I can't did it."]
first word of array in capital ex [TIME,I,HEY,WOW] other is same as written
var attributeCommentArray:[NSAttributedString] = []
override func viewDidLoad() {
super.viewDidLoad()
for comment in userComment {
if comment.contains("") {
let firstCharacter = comment.components(separatedBy: "").first ?? ""
let myString:NSMutableAttributedString = NSMutableAttributedString.init(string:comment)
myString.addAttribute(NSAttributedString.Key.font,
value: UIFont(
name: "HelveticaNeue-Bold",
size: 18.0)!,
range: NSRange(
location:0,
length:firstCharacter.count))
attributeCommentArray.append(myString)
} else {
attributeCommentArray.append(NSMutableAttributedString.init(string:comment))
}
}
// self.navTitleWithImageAndText(titleText: "oneTwoThree", imageName: "")
self.navigationController?.navigationBar.isHidden = false
// chatView.makeCornerRadius(self.chatView.layer.bounds.height / 2)
chatView.layer.borderWidth = 1
chatView.setCorner(borderWidth: 1, borderColor: UIColor.darkGray.cgColor, cornerRadius: 25, clip: true)
self.tblView.rowHeight = UITableView.automaticDimension
self.tblView.estimatedRowHeight = 60
tblView.delegate = self
tblView.dataSource = self
self.loadXib()
}
private func loadXib() {
tblView.loadXibForCellResuse(LiveCell.identifier)
}
}
extension LiveChatVC:UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.userName.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tblView.dequeueReusableCell(withIdentifier: "LiveCell", for: indexPath) as! LiveCell
// cell.userName.text = self.userComment[indexPath.row]
// cell.userName.attributedText = myMutableString
cell.userName.attributedText = attributeCommentArray[indexPath.row]
return cell
}
}
[This is the implementation of code which is given by you please help it will not working in my controller it will print exactly the same text which is on the label,This is the implementation of code which is given by you please help it will not working in my controller it will print exactly the same text which is on the label]
Try the following code
var userComment = ["Time these make me.jenny is ","I can't she did it.", "Hey! what a great play made by brad", "I can't she .", "Time like make is a badass", "I can't it.", "She is a mean chose to place","Time me a badass", "Wow! I am just like jenny.I would shit", "I can't did it."]
var attributeCommentArray:[NSAttributedString] = []
for comment in userComment
{
if comment.contains(" ")
{
let firstWord = comment.components(separatedBy: " ").first ?? ""
let myString:NSMutableAttributedString = NSMutableAttributedString.init(string: comment)
myString.addAttribute(NSAttributedString.Key.font,
value: UIFont(
name: "HelveticaNeue-Bold",
size: 18.0)!,
range: NSRange(
location:0,
length:firstWord.count))
attributeCommentArray.append(myString)
}
else
{
attributeCommentArray.append(NSAttributedString.init(string: comment))
}
}
Create Attrinbuted String array and use that array in uitableview cell label
cellForRowMethod
lable.attributedText = attributeCommentArray[indexPath.row];

swift3 get the array in side closure

I would like to get the final result joblist array to use outside closure. Because I would like to setup a tableview by using Joblist array. .
let geoCoder = CLGeocoder()
geoCoder.geocodeAddressString(address) { (placemarks, error) in
if error == nil && (placemarks?.count)! > 0 {
let location2 = placemarks?[0].location
if let location1 = self.locationManager?.location {
let distanceInMeters = location1.distance(from: location2!)
let IntDis = Int(distanceInMeters)
//print(IntDis)
if IntDis < 40000 {
//print(address)
if let activityid = infoDictionary["ActivityID"] {self.newJob.ActivityID=activityid}
if let companyname = infoDictionary["CompanyName"] {self.newJob.CompanyName=companyname}
if let quantity = infoDictionary["Quantity"] {self.newJob.Quantity=quantity}
if let coupontitle = infoDictionary["Title"] {self.newJob.CouponTitle=coupontitle}
if let couponterms = infoDictionary["Terms"] {self.newJob.CouponTerms=couponterms}
if let expirdate = infoDictionary["ExpirDate"] {self.newJob.ExpirDate=expirdate}
if let contactperson = infoDictionary["ContactPerson"] {self.newJob.ContactPerson=contactperson}
if let tel = infoDictionary["TEL"] {self.newJob.TEL=tel}
self.joblist.append(self.newJob)
//print(self.joblist)
//self.tableView.reloadData()
}
}
}
}
You can do this in two ways:
Make the joblist variable global so it can be accessed from multiple places
Use a callback, which calls a new function while passing on previously gathered data. This is especially useful when the original "data getting" proces is a network task with a delay.
Global variables:
let geoCoder = CLGeocoder()
var jobList = [AnyClass]() //make a global array
geoCoder.geocodeAddressString(address) { (placemarks, error) in
...filtering code ...
joblist.append(self.newJob)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel = jobList[indexPath.row].ActivityID //or something else with the data
}
Using a callback(simple version which doesn't pass on itself to the next function):
geoCoder.geocodeAddressString(address) { (placemarks, error) in
...filtering code ...
doTableViewStuff(data: joblist)
}
func doTableViewStuff(data: [Jobs]) { //or whatever type of data
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel = data[indexPath.row].ActivityID //or something else with the data
}
}

swift 3 my array is empty even after I've added values to it

I'm currently working with swift 3 in order to create an application and I'm stumbling across a problem. Basically, what is happening is in my function, I add values to an array in a block and I print out the array and it seems fine, but when I exit the block, the array is the same as how I declared it globally.
This is my code..search for the comments to know where I append and print the array:
class listUsers: UITableViewController {
var usernames = [""]
var userIDs = [""]
var isFollowing = ["" : true]
//My array declared globally
var theArray = [1]
override func viewDidLoad() {
super.viewDidLoad()
let query = PFUser.query()
query?.findObjectsInBackground(block: { (objects, error) in
if error != nil {
print(error)
}else if let users = objects {
self.usernames.removeAll()
self.userIDs.removeAll()
self.isFollowing.removeAll()
//Notice here How i remove all the elements from my array so that it's empty
self.check.removeAll()
for object in users {
if let user = object as? PFUser {
self.usernames.append(user.username!)
self.userIDs.append(user.objectId!)
let query = PFQuery(className: "Followers")
query.whereKey("Follower", equalTo: (PFUser.current()?.objectId)!)
query.whereKey("Following", equalTo: user.objectId!)
query.findObjectsInBackground(block: { (objects, error) in
if let objects = objects {
if objects.count > 0 {
self.isFollowing[user.objectId!] = true
self.theArray.append(5)
//This prints out [5,5..]..the number of 5 is dependent on the for loop.
print(self.theArray)
}else{
self.isFollowing[user.objectId!] = false
}
if self.isFollowing.count == self.usernames.count {
self.tableView.reloadData()
}
}
})
}
}
}
self.tableView.reloadData()
})
//But when I reprint it here, it prints out [1] and I don't know how to fix it
print(theArray)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = usernames[indexPath.row]
print(theArray)
return cell
}
So in the cellForRowAt function when I print theArray, it prints it out as [1] a few times and than empty and than it prints it out with the actual numbers in it properly. But that's a problem since I only did that to debug but I will be user it to index and I'd need the actual values.
Any help would be great, thanks! (Also this piece of code simply queries through a table in a server but I believe what the rest of it is not that relevant to this question since elements are 100 percent being added to the array)
I figured it out...
I just had to delete the:
self.tableView.reloadData()
That was near the end of the viewDidLoad function

Resources