How can an item be removed from UIPasteboard in iOS? - ios

I see methods for adding items to a pasteboard. I also see that you can get notifications on pasteboard item removal. But I can't seem to find how to remove an item (both key and value) from a pasteboard. How can an item be removed from a pasteboard?

The only difference between the setValue("") and the removeObject() approach is the log provided by UIPasteboard.items. Functionally, either solution will clear your entry, and clear the associated dictionary as well.
pb.contains(pasteboardTypes: [UIPasteboardName.general.rawValue])
will returns false.
You can delete a single entry by manipulating the dictionary, not replacing it by "", unlike How to clear/empty pasteboard on viewWillDisappear.
Option 1
Use this if you want to clear all entries. pb.items will log [].
#IBAction func deleteEntirePasteboard(_ sender: AnyObject) {
UIPasteboard.remove(withName: UIPasteboardName(rawValue: pasteboardName()))
}
Option 2
Use this to remove an entry and its dictionary. pb.items will also log [] as all entries are cleared.
#IBAction func clearUsingRemove(_ sender: AnyObject) {
let pb = pasteBoard()
pb.items = [[String : Any]]()
}
Option 3
This is the generally accepted approach. pb.items will log [{}], showing an empty dictionary. While pb.containsPasteboardTypes will return nil as well, it is arguably not as thorough as the two other options above.
#IBAction func clearUsingQuotes(_ sender: AnyObject) {
let pb = pasteBoard()
pb.setValue("", forPasteboardType: UIPasteboardName.general.rawValue)
}
Use this support code to access the UIPasteboard in the 3 solutions listed above:
func pasteBoard() -> UIPasteboard {
return UIPasteboard(name: UIPasteboardName(rawValue: pasteboardName()),
create: true)!
}
func pasteboardName() -> String {
return "19172176"
}
► Find this solution on GitHub and additional details on Swift Recipes.

I had a similar issue. I needed to clean up several entries without touching other items:
let pasteboard = UIPasteboard.general
pasteboard.items = pasteboard.items.filter{ $0.keys.first != "type.to.be.removed" }
Now use whatever boolean expression in the filter closure to remove items.

Related

Tableview reload not accurate

I have a controller, that allows the user to type in a TextField.
Every time the user types a character, the string in that textfield is compared to an array of strings. If there is a match, the resulting array is displayed in a uitableview.
Here's the code:
func searchAutocompleteEntriesWithSubstring(substring:String){
let SUBSSTRING = substring.uppercased()
autocompleteStrings.removeAll()
for thisSchool in schoolArray{
if(thisSchool.name?.uppercased() .contains(SUBSSTRING))!{
autocompleteStrings.append(thisSchool)
}
}
autocompleteTableView.reloadData()
}
Basically, this works fine. BUT!
If the user types rather fast, the autocompleteTableView displays one or more (empty) rows than there actually are strings in the autocompleteStrings array.
I tried encapsulating the above code in DispatchQueue.main.async {}, but that made things even worse.
I guess it has something to do with NeedsLayout or NeedsDisplay, but I've never really understood the mechanism behind it, and how/where to apply these.
I hope you can advise me
Try this code
func searchAutocompleteEntriesWithSubstring(substring: String) {
let filtered = schoolArray.filter() { ($0.name ?? "").uppercased().range(of: substring.uppercased()) != nil }
autocompleteStrings = filtered.map() { $0.name! }
autocompleteTableView.reloadData()
}
Maybe you need a lock?
1.in a async queue.
2.lock locks.
3.matching and array appending
4.lock unlocks.
5.reload in mainqueue

fatal error: unexpectedly found nil while unwrapping an Optional value - UITableView Add Button

I am trying to add names into a UITableView. The names will be stored as a NSUserDefault.
My viewDidLoad:
var PlayersUserDefault = NSUserDefaults.standardUserDefaults()
if (PlayersUserDefault.arrayForKey("playersKey") != nil){
players = PlayersUserDefault.arrayForKey("playersKey")
}
Add name button:
#IBAction func addButtonAction(sender: AnyObject) {
players!.append(namesTextBox.text)
myTableView.reloadData()
}
When I press the addButtonAction, I am getting error on following:
players!.append(namesTextBox.text)
Players declaration:
var players = NSUserDefaults().arrayForKey("playersKey")
Any idea why I am getting error?
Error image:
In one line you use NSUserDefaults.standardUserDefaults() and in the other you create new instance of NSUserDefaults and ask for "playersKey". What you should do is to use NSUserDefaults.standardUserDefaults() for retrieving AND saving your players.
Take a look at the refence:
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/#//apple_ref/doc/constant_group/NSUserDefaults_Domains
For init:
This method does not put anything in the search list. Invoke it only
if you’ve allocated your own NSUserDefaults instance instead of using
the shared one.
and for shared instance:
If the shared defaults object does not exist yet, it is created with a
search list containing the names of the following domains, in this
order:
NSArgumentDomain, consisting of defaults parsed from the application’s
arguments
A domain identified by the application’s bundle identifier
NSGlobalDomain, consisting of defaults meant to be seen by all
applications
Separate domains for each of the user’s preferred languages
NSRegistrationDomain, a set of temporary defaults whose values can be
set by the application to ensure that searches will always be
successful
The defaults are initialized for the current user. Subsequent
modifications to the standard search list remain in effect even when
this method is invoked again—the search list is guaranteed to be
standard only the first time this method is invoked.
The players array is nil at the point of time thats why the array is not unwrapped. If you tap the button at the first time, your UserDefaults will not be having players, So the players array is nil. So you can solve this in two ways do like this.
On your viewDidLoad()
var PlayersUserDefault = NSUserDefaults.standardUserDefaults()
if (PlayersUserDefault.arrayForKey("playersKey") != nil){
players = PlayersUserDefault.arrayForKey("playersKey")
}else {
players = [String]()
}
Otherwise, initialise the players array with an empty string array like this:
var players = [String]()
This may help you.
You could try unwrapping the value of players before attempting to do append. Then you will know for sure if it's nil or not.
Try:
#IBAction func addButtonAction(sender: AnyObject) {
if let playerValue = players {
players!.append(namesTextBox.text)
myTableView.reloadData()
}else{
println("players is nil!")
}
}

Customizing QuickType Suggestions in iOS

Well, I suspect that this is a forlorn question, ultimately doomed to sadden and disappoint me, but I want to have the question on the record for the future.
I will add this to the existing questions: here and here
The scenario is that I am creating an administration app that is designed to allow folks to edit the values in a database on a server. I have a fairly zippy API that can be used to allow REST-style data exchange.
I have the ability to download a list of values that the user can apply as search terms when they log in, and I'd like to be able to help them to enter things quickly.
For example, town names. If they enter "B", then I'd like to be able to offer "Bayshore", "Babylon" and "Bohemia" as suggestions, and so on.
A completely legit application.
I am under the assumption that there currently does not exist a QuickType API.
Am I wrong?
There is no QuickType API but you can get similar functionality with the ACEAutocompleteBar library from GitHub. Just for the record, I have listed the steps to get this working with Swift:
1) Import the files from the folder "ACEAutocompleteBar" to your project.
2) Create a bridging header and write #import "ACEAutocompleteBar.h" at the top.
3) Make your view containing the text field an ACEAutocompleteDataSource and ACEAutocompleteDelegate.
4) Implement the minimumCharactersToTrigger function
func minimumCharactersToTrigger(inputView: ACEAutocompleteInputView!) -> UInt {
return 1
}
5) Implement the inputView function.
func inputView(inputView: ACEAutocompleteInputView!, itemsFor query: String!, result resultBlock: (([AnyObject]!) -> Void)!) {
inputView.hidden = false
inputView.alpha = 0.75
if resultBlock != nil{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
var data:NSMutableArray = []
if(self.myTextField.isFirstResponder()){
//query your data source with 'query' and add any of the results to data to pass back
}
dispatch_async(dispatch_get_main_queue()) {resultBlock(data as [AnyObject])}
}
}
}
6) Implement the textField function.
func textField(textField: UITextField!, didSelectObject object: AnyObject!, inInputView inputView: ACEAutocompleteInputView!) {
textField.text = String(object)
}
7) Call setAutocompleteWithDataSource on the textField.
self.myTextField.setAutocompleteWithDataSource(self, delegate: self, customize: {
inputView in
// customize the view (optional)
inputView.font = UIFont.systemFontOfSize(20)
inputView.textColor = UIColor.whiteColor()
inputView.backgroundColor = UIColor.blueColor()
inputView.hidden = false
})
Hope that helps anyone looking for this kind of functionality!

Swift Variables Scope Issue

I declare arr1 and arr2 as arrays globally in my viewcontroller.
In the following function, I add to them in a parse query. Xcode gets buggy when I don't include "self" before arr1 inside the query, so I included it. Outside the query, in the space where I have marked below, I try to access arr1 and arr2. But regardless of whether I try self.arr1 or arr1, the array turns out to be empty at that point. I think this is some sort of scope issue with swift, and I've had quite a bit of trouble with it so any help would be much appreciated.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "vF") {
var destVC = segue.destinationViewController as vF
destVC.namePassed = fV
var query = PFQuery(className:"class")
query.whereKey("createdBy", equalTo:fV)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
// The find succeeded.
NSLog("Successfully retrieved \(objects.count) records.")
// Do something with the found objects
for object in objects {
self.arr1.append(object["field1"]! as Int)
self.arr2.append(object["field2"]! as String)
}
} else {
// Log details of the failure
}
}
// I want to access arr1 and arr2 right here, but when I do they come up empty
// I have tried accessing with self.arr1 and arr1 but neither works
}
}
The findObjectsInBackgroundWithBlock is async so it will happen in the background thread while your program still running in the main thread, by the point your background thread comeback with the data you need you program is why passed the point you indicate.
One of the best solutions for your problem is to add observers for the arr1 and arr2 so you will be notified when it happens. Add observers to array can be a little bit trick and I want to keep it simple for you, so I would recommend you to create a boolean variable that tells you when the value finish change. To do so you will need to create the variable like this
var arrayDidChange:Bool = false {
didSet{
if arrayDidChange{
callFuncDataReadArrayHere()
}
}
}
Now everytime you change the values (add or edit) in the array you set arrayDidChange to true and in the end of callFuncDataReadArrayHere after do all you need to do you set arrayDidChange to false.
This way you will always be sure you will access the values in the array after it be populate.
I hope that helped you!
You should connect the segue directly from the controller instead of the cell. Implement didSelectRowAtIndexPath, and put the query there, and call performSegueWithIdentifier:sender: inside the completion block of the asynchronous method. Now, when you implement prepareForSegue, the arrays will be available to pass to the destination view controller.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var query = PFQuery(className:"class")
query.whereKey("createdBy", equalTo:fV)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
// The find succeeded.
NSLog("Successfully retrieved \(objects.count) records.")
// Do something with the found objects
for object in objects {
self.arr1.append(object["field1"]! as Int)
self.arr2.append(object["field2"]! as String)
}
self.performSegueWithIdentifier("vF", sender: self)
} else {
// Log details of the failure
}
}
}
There will be some delay before the segue actually proceeds with this approach, because it won't happen until findObjectsInBackgroundWithBlock returns its results. If that's not acceptable, then you could still pass the arrays in prepareForSegue (in the same place I show after the "for object in objects" clause), but then the arrays will not be immediately available in the destination view controller, and you will have to deal with that.

How do I access an Array stored in a Dictionary in Swift?

This is how I created my Dictionary with the Array:
#IBAction func didTapSaveButton(sender: UIButton) {
var hooObject = Dictionary<String, [String]>()
hooObject["\(dayLabel.text)"] = ["\(fromTimeLabel.text) - \(toTimeLabel.text)", "\(costField.text)"]
NSLog("RESULT: \(hooObject)")
}
NSLog result:
RESULT: [Optional("Wednesday"): [Optional("9:00 PM") - Optional("3:00 PM"), 40.00]]
I've tried this:
#IBAction func didTapSaveButton(sender: UIButton) {
var hooObject = Dictionary<String, [String]>()
hooObject["\(dayLabel.text)"] = ["\(fromTimeLabel.text) - \(toTimeLabel.text)", "\(costField.text)"]
var res = hooObject["Wednesday"]
NSLog("RESULT: \(res)")
}
This returns nil which doesn't make sense to me. Wednesday is the stored key so I would have thought it would be enough to return the array.
What I'm trying to do:
Basically I have a table with 7 sections and each represent a day. In each day I'm storing time slots with a cost for booking an activity in that slot.
When I use numberOfRowsInSection I will be able to use the Dictionary key to get the number of rows (time slots) for a particular day that will be shown in the tableView. This was the easiest way I could think to do it with NOSql.
I store the dictionaries in a column in parse.com and access that dictionary in my tableView.
Anyway I don't understand why I can't seem to access the array with the key. I could easily go and use an NSMutableDictionary but I'm using Swift for all my projects as a learning experience.
Wondering if it would be better to use a Dictionary with Dictionaries e.g. ["fromTime":value, "toTime":value, "cost":value]
Will appreciate any help given.
Thanks for your time.
The problem comes from using string interpolation on dayLabel.text -- since that's a String? and not a String, you end up with "Optional(Wednesday)" as your key instead of just "Wednesday". You need to unwrap it, like this:
#IBAction func didTapSaveButton(sender: UIButton) {
var hooObject = Dictionary<String, [String]>()
if let day = dayLabel.text {
hooObject["\(day)"] = ["\(fromTimeLabel.text) - \(toTimeLabel.text)", "\(costField.text)"]
NSLog("RESULT: \(hooObject)")
}
}
You'll probably need to do something similar with the from and to time fields to get the values you want.
My answer:
#IBAction func didTapSaveButton(sender: UIButton) {
var hooObject = Dictionary<String, [String]>()
hooObject["\(dayLabel.text!)"] = ["\(fromTimeLabel.text) - \(toTimeLabel.text)", "\(costField.text)"]
var res = hooObject["Wednesday"]
NSLog("RESULT: \(res)")
}
I had to change dayLabel.text to dayLabel.text!
Those exclamation marks really baffle me. At first I thought they were like pointers because they can't be used on Int, Float etc (so I think).
But the fact I need one at the end of dayLabel.text and not my other labels baffles me even more. I guess in time I will have my ah hah moment. I did read a few posts on here but still don't quite get it.

Resources