I have 7 buttons for days of a week, which have tags from 1 to 7. I like to store those values in core data when tapped. Since arrays are not allowed in core data, how do I individually store them in core data?
Initially, I was getting tags from the button like these
#IBAction func dayButtonPressed(_ sender: Any) {
guard let button = sender as? UIButton else { return }
if(dayTag.contains((sender as AnyObject).tag!)) {
if let index = dayTag.firstIndex(of: (sender as AnyObject).tag!) {
dayTag.remove(at: index)
}
} else {
dayTag.append((sender as AnyObject).tag!)
}
}
and storing them to core data as follows
object.setValue(dayTag, forKey: "days")
I am not getting any idea of how to create 7 individual variables and store them into core data when the button is tapped. Any, help in the direction would be appreciated.
A reasonable solution is a computed property.
Declare days as
#NSManaged var days: String
and declare a computed property
var weekdays : [Int] {
get { return days.components(separatedBy: ",").map{Int($0)!) }
set { days = newValue.map(String.init).joined(separator: "," }
}
Setting weekdays converts the Int array to a comma separated string and updates days (and vice versa).
Related
I have 2 ViewControllers, one displays the UI and the 2nd one displays a segmented control used as a settings button. Im using the below code to save the segmented control state:
UserDefaults.standard.set(selectorLabel.selectedSegmentIndex, forKey: "stateSelected")
I then retrieve that usedefault on the viewdidload method:
if let value = UserDefaults.standard.value(forKey: "stateSelected"){
let selectedIndex = value as! Int
selectorLabel.selectedSegmentIndex = selectedIndex
}
So far this works as intended and the state of the segmented controlled is loaded properly each app load.
The segmented control has two text titles - one is "LBs & INs" and the second is "KGs & CMs".
How would I save those two segmented control text titles as UserDefaults and then call them on the first ViewController to set two labels on the viewdidload?
Define a model to represent data you want to store and restore:
struct SegmentedControlState: Codable {
let selectedIndex: Int
let titles: [String]
}
Initialize a model, encode and store it somewhere (like user default):
func saveState(of segmentedControl: UISegmentedControl) {
let state = SegmentedControlState(
selectedIndex: segmentedControl.selectedSegmentIndex,
titles: (0..<segmentedControl.numberOfSegments).map { segmentedControl.titleForSegment(at: $0) ?? ""})
let plist = try! PropertyListEncoder().encode(state)
UserDefaults.standard.set(plist, forKey: "SegmentedControlState")
//UserDefaults.standard.synchronize() //if targeting older iOS
}
for restoring, you should reverse the order like this:
func loadState(on segmentedControl: UISegmentedControl) {
guard let plist = UserDefaults.standard.value(forKey: "SegmentedControlState") as? Data else { return }
let state = try! PropertyListDecoder().decode(SegmentedControlState.self, from: plist)
for element in state.titles.enumerated() {
segmentedControl.setTitle(element.element, forSegmentAt: element.offset)
}
segmentedControl.selectedSegmentIndex = state.selectedIndex
}
usage:
// store `selectorLabel` data
saveState(of: selectorLabel)
// restore `selectorLabel` data
loadState(on: selectorLabel)
Note that it is not a good idea to store data like this to userdefaults at all. If you want to access some data from anywhere in code, you should follow singleton pattern and define your own singleton instance instead of standard userdefault.
Just store the value as a string, instead of an integer index.
UserDefaults.standard.set(selectorLabel.titleForSegment(at: selectorLabel.selectedSegmentIndex), forKey: "stateSelected")
And then to retrieve:
UserDefaults.standard.string(forKey: "stateSelected")
EDIT: #rmaddy is correct above - you should ideally be storing an index value like you're already doing, and then using an array to determine which title the index refers to (cleaner than just using a title as a reference). You could make this array global so you can access from anywhere, if you must.
segmentedControlTitles: [String] = ["LBs & INs", "KGs & CMs"]
And then call by
let index = UserDefaults.standard.integer(forKey: "stateSelected")
let title = segmentedControlTitles[index]
I have a app to show a value of bitcoin's price. I want sort when I click in the header of column.
I'am trying do this, the click has be responded, but nothing happens, the column continues not sorted.
struct info {
let name: String
let coin: String
let change: Double
let price: Double
}
var ticker: Array<info> = []
#objc func tapChangeFunction(sender: UITapGestureRecognizer) {
self.ticker.sorted(by: {
$0.change > $1.change
})
DispatchQueue.main.async {
self.table.reloadData()
}
}
It seems that you have just forgot to assign the sorted data back to the model:
self.ticker = self.ticker.sorted(by: {
$0.change > $1.change
})
sorted(by:) returns a new array that is sorted, the old one is not modified, read the docs:
Returns the elements of the collection, sorted using the given predicate as the comparison between elements.
Or use mutating version sort(by:) (docs):
Sorts the collection in place, using the given predicate as the comparison between elements.
self.ticker.sort(by: {
$0.change > $1.change
})
Sidenote:
You don't have to dispatch reloadData asynchronously, you are already on the main thread.
#objc func tapChangeFunction(sender: UITapGestureRecognizer) {
self.ticker = self.ticker.sorted(by: {
$0.change > $1.change
})
DispatchQueue.main.async {
self.table.reloadData()
}
}
Use the above function to fix the issue..
sorted method won't mutate the contents of the array ,It will return a new array instance
I am fairly new to coding and was wondering if it was possible to save data I have randomly generated into a label. I can randomly generate the data and input it into the label but I am having difficulties saving the previous data from the label to an array. Is it possible to do this? And how would it be done?
Essentially I want the user to be able to go back to the previous text in the label if they press a button
Thank you for any help.
code:
This is how I am generating the random variable from an array which contains quotes
//right hand button tapped
#IBAction func RHSButton(_ sender: Any) {
// randomises variable to display
var randomQuote: String {
let randomNumber = Int(arc4random_uniform(UInt32(myQuotes.count)))
return myQuotes[randomNumber]}
quotesLabel.text = randomQuote
}
I want to be able to store the data into an array called:
var randomGeneratedQuotes : [String] = []
But I am not quite sure how
hi seems like u want undo feature.
take one more array and add selected randomNumber
on clicking button display last index of array and remove it from array.
If I understand you correctly you could save the value of your label into your randomGeneratedQuotes array just before you generate a new random value.
var randomQuote: String {
let randomNumber = Int(arc4random_uniform(UInt32(myQuotes.count)))
return myQuotes[randomNumber]
}
#IBAction func RHSButton(_ sender: Any) {
//If a value is present, save it
if let oldValue = quotesLabel.text {
randomGeneratedQuotes.append(oldValue)
}
//and update the value
quotesLabel.text = randomQuote
}
From your previous questions I'm guessing you'd like a LHSButton function to give you the previous value, that could look like so:
#IBAction func LHSButton(_ sender: Any) {
//Do we have a previous value?
if randomGeneratedQuotes.count > 0 {
//take that
let previousValue = randomGeneratedQuotes.removeLast()
//and use it
quotesLabel.text = previousValue
}
}
Hope that works and helps.
I would like to know how to detect a String inside a String from stored data. All data is saved with UUID().uuidString. String data is saved in UITableView with its title String.
For example, if now I am in a DogTableView with its title in the navigation, "Dog", then the data is going to be "Dog". The data is "Dog" + uuid. And if now I move to a CatTableView with its title in the navigation, "Cat", then the data is going to be "Cat" + uuid.
Now we have two types of data, Dog + uuid and Cat + uuid.
So the problem is that I would like to extract only Dog + uuid data, by some algorithm. My attempt was in the following;
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "animals"){
let cell = sender as? UITableViewCell
let indexPath = tableView.indexPath(for: cell!)
let item:Animal
item = self.animals[indexPath!.row]
let dvc = segue.destination as! AnimalTableViewController
dvc.topTItle = item.name!
let singleton = AnimalManager.sharedInstance
//Animal data is saved with CoreData in AnimalTableViewController
let data = singleton.getAnimalData()
for d in data {
// This is not working..
if (d.uuid?.contains(item.name!))! {
dvc.displayArray.append(d)
}
}
// I thought this would work by counting the item length
// Then using `Range` or something to detect the Dog or Cat.
// But I do not know how to do....at all
let length = item.name!.characters.count
for i in 0...length - 1 {
}
}
}
How am I able to filter data and show the filtered data into the next AnimaltableViewController appropriately?
First of all, why for heaven's sake is uuid and name optional ?? I'm not aware of any animal without a name. And any nil value in uuid defeats the u representing unique.
Actually you can use the filter function to filter the items in one line.
dvc.displayArray = data.filter { $0.uuid.hasPrefix("Dog") && $0.uuid.contains(item.name) }
PS: However as you're dealing with Core Data it would be much more efficient to filter the data directly in Core Data.
Before I start, just wanted to say I'm very new to app development in general, I've only been at this for a month, so feel free to dumb it down to me as much as possible haha.
Ok, so I'm working on a quote app and so I've created an array that I can access from any view controller. This that will contain "liked" quotes, which is added from another another view.
Here is my global "likedArray". It resides in its own swift file.
import Foundation
struct Globals {
static var likedArray: [String] = ["Touch 'Liked' To Continue..."]
}
Quotes are added to likedArray by this method, from another view controller file.
#IBAction func likedbuttonPressed(sender: AnyObject) {
Globals.likedArray.append(quoteLabel.text)
}
And "liked" quotes are shown in another view via this method
// Like button method to move forward in array
#IBAction func likedButtonTouched(sender: AnyObject) {
self.favouritesLabel.fadeOut(completion: {
(finished: Bool) -> Void in
self.currentlikedArrayIndex++
if self.currentlikedArrayIndex < Globals.likedArray.count {
self.favouritesLabel.text = Globals.likedArray[self.currentlikedArrayIndex]
} else {
self.currentlikedArrayIndex--
}
self.favouritesLabel.fadeIn()
})
}
And this all works fine, except that when I quit the app, all liked quotes are gone.
So, my question is how do I save this data?
If you need any more information about my code, please let me know. Thanks in advance.
The most hassle free way is probably to use NSUserDefaults, can follow this tutorial to find out how exactly
the jist of the tutorial:
//for writing to storage
let defaults = NSUserDefaults.standardUserDefaults()
let array = ["Hello", "World"]
defaults.setObject(array, forKey: "SavedArray")
//for reading
let array = defaults.objectForKey("SavedArray") as? [String] ?? [String]()
So basically, you should make a setter method for your global array so each time the array is set, it will write to the NSUserDefaults, then on app launch it should populate the array with whats in the NSUserDefaults
update: (just did this off the top of my head)
struct Globals {
static var likedArray: [String] = ["Touch 'Liked' To Continue..."]
func addLikedString(likedString: String) {
self.likedArray.append(likedString)
NSUserDefaults.standardUserDefaults().setObject(self.likedArray, forKey: "SavedArray")
}
func getLikedStringAtIndex(index:Int) -> Int {
return self.likedArray[index]
}
}
//inside your app delegate, or somewhere appropriate, to load the array when the app starts
Globals.likedArray = NSUserDefaults.standardUserDefaults().objectForKey("SavedArray") as? [String] ?? [String]()