I have a NSTokenField Where the tokens are created upon hitting enter. I would like to limit the number of tokens in this field. Say for example, User should be allowed to enter only 2 tokens one after the other. Later, neither user should be allowed to set the Token nor user should be allowed to search further. In short, User should be blocked after 2 tokens.
Could any one please help me in achieving this???
Thanks in advance :)
The solution is divided in 2 parts:
-(NSArray *)tokenField:(NSTokenField *)tokenField shouldAddObjects:(NSArray *)tokens atIndex:(NSUInteger)index
{
//limit the tokens
if(self.tokensLimit)
{
NSArray * tokensArray = [_tokenField objectValue];
if([tokensArray count] > 0)
{
if([tokens isEqualToArray:tokensArray])
{
return tokens;
}
else if([tokensArray count]>=self.tokensLimit)
{
return #[];
}
else if([tokens count]>0)
{
tokens = [tokens subarrayWithRange:NSMakeRange(0, MIN([tokens
count], self.tokensLimit))];
}
else
return #[];
}
else
{
tokens = [tokens subarrayWithRange:NSMakeRange(0, MIN([tokens count], self.tokensLimit))];
}
}
return tokens;
}
where tokensLimit is an int > 0
the delegate covers all the cases like tokens added by copy/paste, completion list, drag&drop, manually written etc..
this other delegate cover the case where the user write a string and hit "TAB"
- (BOOL)control:(NSControl *)control isValidObject:(id)object
{
if(self.tokensLimit)
{
NSArray * tokensArray = [_tokenField objectValue];
tokensArray = [tokensArray subarrayWithRange:NSMakeRange(0, MIN([tokensArray count], self.tokensLimit))];
[_tokenField setObjectValue:tokensArray];
}
return YES;
}
If you save the tokens in a db, you can count the number of rows of the particular users id, and add an if statement to limit it to 2.
Behold:
var maximumTokens: Int = 2
func tokenField(_ tokenField: NSTokenField, shouldAdd tokens: [Any], at index: Int) -> [Any] {
var count = 0
if let textView = tokenField.currentEditor() as? NSTextView {
for scalar in textView.string.unicodeScalars {
if scalar.value == unichar(NSAttachmentCharacter) {
count += 1
}
}
}
return tokens.filter({ _ in
count += 1
return count <= maximimTokens
})
}
I've tested it and it works when you are typing tags or even copying & pasting them in.
Related
Not sure why i'm getting this.. any suggestions would be grateful!
I ran into issues with my original coding where I had Firebase pod and Firebase Package.. so I started from scratch since that wasnt fixing itself.. now I get this.. and I am at a loss for how to resolve it.
static func fetchUsers() -> AnyPublisher<[UserProfile], Error> {
Future< [UserProfile], Error > { promise in
self.db.collection("Users")
.getDocuments { (snapshot, error) in
if let error = error {
promise(.failure(error))
return
}
guard let snapshot = snapshot else {
promise(.failure(FirebaseError.badSnapshot))
return
}
var users = [UserProfile]()
snapshot.documents.forEach { document in
print(users.count)
if let user = try? document.data(as: UserProfile.self){
if users.contains(where: { $0.id == user.id}) {return}
users.append(user)
} else {
print("Not working")
}
}
promise(.success(users))
}
}
.eraseToAnyPublisher()
}
I believe this is the syntax you're after:
var users = [UserProfile]()
users = snapshot.documents.compactMap { (document) -> UserProfile? in
if users.contains(where: { $0.id == user.id}) {
return nil
} else {
return try? document.data(as: UserProfile.self)
}
}
Also be aware that when you iterate something in Swift and encounter a false condition on an iteration, return will return out of the greater scope, not just that iteration. Therefore, use continue.
for x in y {
guard x > 0 else {
continue // continues loop
}
// ...
}
Let's imagine that we can fetch a fixed number of messages asynchronously (one request, containing N elements)
func fetchMessages(max: UInt, from: Offset) -> SignalProducer<Message,NoError>
Now, I'd like to turn this into an unbounded SignalProducer that will lazily call fetchMessages when the previous stream completes.
func stream(from: Offset) -> SignalProducer<Message, NoError> {
// challenge is to implement this function
}
An initial idea that could work, but that would still require pre-computing all the ranges would be to genericize the following code
func lazyFetchFrom(from: Offset) -> SignalProducer<Message,NoError> {
return SignalProducer<Message,NoError> { (observer, disposable) in
fetchMessages(from).start(observer)
}
}
let lazyStream =
fetchMessages(1000, from)
.concat(lazyFetchFrom(from + 1000))
.concat(lazyFetchFrom(from + 2000))
.... // could probably be done generically using a flatMap
Now, I'd like to go one step further and evaluate the next call to lazyFetchFrom once the previous values have been consumed. Is that possible?
Thanks
PS: to be clear, my main concern is to provide some sort of backpressure so that the producer doesn't produce too quickly compared to the consumer
Edit: here is my latest attempt at implementing some backpressure. However, when we observeOn the signal, the backpressure disappears, and everything is queued in memory
- (RACSignal *)allContentFromId:(NSInteger)contentId afterDate:(NSDate *)date fetchSize:(NSInteger)fetchSize {
RACSignal *signalNextPagination = [self nextPaginationAllContentFromId:contentId afterDate:date fetchSize:fetchSize];
//in signalNextPagination will be send next fetch size data and send complete only after downloaded all data
//so we used aggregate
return [signalNextPagination aggregateWithStart:#[] reduce:^id(NSArray *before, NSArray *next) {
return [before arrayByAddingObjectsFromArray:next];
}];
}
- (RACSignal *)nextPaginationAllContentFromId:(NSInteger)contentId afterDate:(NSDate *)date fetchSize:(NSInteger)fetchSize {
//command will be send one fetch request
//after recv data in command need try size fetch
//if size eq fetch size, so need repeat command with new offset
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(NSDate *date) {
return [self requestContentFromId:contentId afterDate:date limit:fetchSize];
}];
command.allowsConcurrentExecution = YES;
RACSignal *download = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[command.executionSignals flattenMap:^RACStream *(id value) {
return value;
}] subscribeNext:^(NSArray *datas) {
[subscriber sendNext:datas];
if ([datas count] == fetchSize) {
NSDate *date = [[datas firstObject] pubDate];
[command execute:date];
} else {
[subscriber sendCompleted];
}
} error:^(NSError *error) {
[subscriber sendError:error];
[subscriber sendCompleted];
}];
[command execute:date];
return nil;
}];
return download;
}
This question already has answers here:
Shorthand setter declaration for a subscript of an array in Swift
(2 answers)
Closed 8 years ago.
To keep it short, what I want to achieve is for example:
var actions: [String]{
get{
if (_actions==nil){
_actions = []
}
return _actions!
}
set{
_actions = newValue
}
subscript(index:Int) -> String{
set {
assert(index<_actions.count && index>=0, "Index out of range")
_actions[index] = newValue
}
}
}
I know subscript isn't an accessor for array, but then what is the most convinient alternative to do just that?
I truly appreciate for succinct answers if possible! Thank you very much!
Edit:
To extend my explanation for #jrturton,
What I am trying to achieve is whenever actions[i] is set to a newValue, I would like to do some extra computations, such as repositioning actions[i]'s respective subview.
But if i say actions[3] = "randomMethod", the computed setter for the entire array will get called. Right? So I'd like to find a way so that when actions[3] is set to a newValue, a function repositionView(3) can get called, for example.
I know other ways to do it, but my question simply askes if there is a more convinient way, like the example above: a computed setter, to do what I want?
Edit 2:
To show #Vatsal Manot what I truly mean, I removed getter for subscript, and here is a complete example.swift(which wont run due to error):
import UIKit
import Foundation
class DWActionsSubmenu: UIView{
var actions: [DWAction]{
get{
if (_actions==nil){
_actions = []
}
return _actions!
}
set{
_actions = newValue
}
subscript(index:Int) -> DWAction{
set {
assert(index<_actions.count && index>=0, "Index out of range")
_actions[index] = newValue
a()
}
}
}
var _actions: [DWAction]?
init(actions:[DWAction]?){
super.init()
_actions = actions
}
required init(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
}
func a(){
}
}
I'd wrap your actions list in a custom class that you can then access via subscripting. You can then add a block to be run whenever a subscripted member is set:
class ActionList {
private var actions = [String]()
var actionDidChange : ((Int) -> ())?
subscript(actionIndex:Int) -> String {
get {
return actions[actionIndex]
}
set {
actions[actionIndex] = newValue
if let actionDidChange = actionDidChange {
actionDidChange(actionIndex)
}
}
}
func addAction(action: String) {
actions.append(action)
}
func addActions(newActions:[String]) {
actions += newActions
}
}
Usage (in a playground):
let actionList = ActionList()
actionList.actionDidChange = {
actionIndex in
println("Action \(actionIndex) did change")
}
actionList.addActions(["One", "Two", "Three"])
actionList[2] = "New"
// Prints "Action 2 did change"
The following should work:
var actions: [String] = []
subscript(index:Int) -> String
{
get
{
assert(index < actions.count && index >= 0, "Index out of range")
return actions[index]
}
set(newValue)
{
assert(index < actions.count && index >= 0, "Index out of range")
actions[index] = newValue
}
}
I am trying to learn the Swift language, and I followed a lot of tutorials on Youtube. Since most of them are for iOS, I wanted to make the OSX version of this one: https://www.youtube.com/watch?v=8PrVHrs10to (SideScroller game)
I followed it carefully but I am stuck at about 22 minutes of the video when I have to update the player position. My first problem was to get the pressed keys. I come from C# language, so I wanted to find something like
If(Keyboard.GetState().IsKeyDown(Keys.Right))
man.Position.x += 5 //player position
but this doesn't exist, so I figured out this:
override func keyDown(theEvent: NSEvent!)
{
updateManPosition(theEvent)
}
func updateManPosition(theEvent:NSEvent)
{
if theEvent.keyCode == 123
{
man.position.x -= 2
}
else if theEvent.keyCode == 124
{
man.position.x += 2
}
else if theEvent.keyCode == 126
{
println("jump")
}
}
I found the corresponding value(123/124/126) by using println(theEvent.keyCode) but it's not very useful if I have to recognize a lot of keys.
Anyway, it works for this game, the position of the player changes. But I have another probem which is that the function keyDown doesn't seem to be called at each update (so 60 times per seconds) which prevent the player to move smoothly.
SO, here is my question: How can I have keyDown called at each update, and does anybody have a cleaner way to get the pressed Keys ?
Thank you
Okay I found how to update, so I post it here 'cause it might help others. The idea is to use a boolean:
var manIsMoving:Bool = false
override func keyDown(theEvent: NSEvent!) // A key is pressed
{
if theEvent.keyCode == 123
{
direction = "left" //get the pressed key
}
else if theEvent.keyCode == 124
{
direction = "right" //get the pressed key
}
else if theEvent.keyCode == 126
{
println("jump")
}
manIsMoving = true //setting the boolean to true
}
override func keyUp(theEvent: NSEvent!)
{
manIsMoving = false
}
override func update(currentTime: CFTimeInterval)
{
if manIsMoving
{
updateManPosition(direction)
}
else
{
cancelMovement()
}
}
It may help others.
But getting the pressed key's characters is still unclear...
In combination with the above method I have created a method to change a key to a character:
private func returnChar(theEvent: NSEvent) -> Character?{
let s: String = theEvent.characters!
for char in s{
return char
}
return nil
}
This will return a character (from the key that was pressed).
Then you can do something like this:
override func keyUp(theEvent: NSEvent) {
let s: String = String(self.returnChar(theEvent)!)
switch(s){
case "w":
println("w")
break
case "s":
println("s")
break
case "d":
println("d")
break
case "a":
println("a")
break
default:
println("default")
}
}
You could combine this method and the method from above to create something like a key listener.
I have a drag operation that only allows to drag a single file, and I want to capture this on "draggingEntered" like so:
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
if ([[sender draggingPasteboard] count]] == 1) {
return NSDragOperationCopy;
}
else {
return NSDragOperationNone;
}
}
But count is not valid method or property, but I can't figure out what to replace it with, so which is the best way to see how many items there are on the draggingPasteboard? Should I get the array of filenames on the draggingPasteboard using something like propertyListForType: NSFilenamsPboardType, and then get the index of that, or is there a more clever way to do this?
If You want to use count You need to use pasteboardItems which is items array who response to count.
It can be done like this:
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
if([[[sender draggingPasteboard] pasteboardItems] count] == 1) {
return NSDragOperationCopy;
}
else {
return NSDragOperationNone;
}
}