I am trying to make an application for users to show their actions on timeLine.
My timeline library named StepProgressView hasn't delegate or dataSource. So I can't change text color in iteration with my keys.
There is a titleLabel and subtitleLabel in my timeLine library. If you want how it works you can check the library in this link
I have title and subTitle array on my project. When I add the titles in my array it gives me timeLine with array items.
I want to change titleLabel text color.
This is my dictionary output when i print it.
[17: "17 Kind of long rambling explanation that no one reads in reality",
14: "14 Kind of long rambling explanation that no one reads in reality",
20: "20 Kind of long rambling explanation that no one reads in reality"
I want to use if else statement for colors. If dict value is 17 text color should be red or something.
But when I iterate it fills with last color of key value. This is my problem. I hope its clear.
for (key, _) in self.stepDetails {
progressView.pastTextColor = getColor(key: key)
}
func getColor(key: Int) -> UIColor {
switch key {
case 0...key:
return .red
case key ... key + 10:
return .blue
default:
return .gray
}
}
It always gives last color because it's in iteration so
What is the best approach in this case ?
Thank you !
Related
I have 15 UIButtons in my interface, to start with all the buttons are blurred out/disabled. I have code that randomly generates an array of numbers from 1 to 15 these are then used as tags on each of my UIButton's. I then loop over the buttons and see if the tags array contains the button tag that I am currently looping over.
func assignLabels() {
//Loop through the array of buttons.
for button in buttons {
//Check to see if the array of tags contains the current button tag.
if tags.contains(button.tag){
print(button.tag)
button.layer.cornerRadius = 8
button.alpha = 1.0
button.isUserInteractionEnabled = true
switch onStage{
case 1:
currentPhoneme = stage1[currentPhonemeNumber]
button.setTitle(stage1[currentTag], for: .normal)
// button.setTitle(button.tag.description, for: .normal)
case 2:
currentPhoneme = stage2[currentPhonemeNumber]
button.setTitle(stage2[currentTag], for: .normal)
default:
currentPhoneme = stage1[currentPhonemeNumber]
button.setTitle(stage1[currentTag], for: .normal)
}
}else{
button.alpha = 0.3
button.setTitle("-", for: .normal)
}
currentTag += 1
if currentTag == stageCount{
break
}
}
}
What is supposed to happen is that as we are looping over the buttons it checks to see if the buttons tag is in the array of tags and subsequently enables that button and assigns it a label. Although this works I get the same order for the buttons every single time the code is called even though the button tags are completely random below is what I get in my interface.
Check here
What should happen is that each time the function is called the buttons that are enabled should be in a random order across the screen like a different pattern every time. Any help on this behaviour would be great as I am at a loss as to why the order is always the same!
Your order is always the same because you are assigning the labels based upon the currentTag variable which is in the same order. Also, contains returns the same results no matter the order.
A simple fix would be to reorder the tags of your buttons and use the button's tag instead of currentTag:
func assignLabels() {
// New random button tag order
let newtags = Array(1...buttons.count).shuffled()
// Loop through the array of buttons.
for (button, newtag) in zip(buttons, newtags) {
// Assign new tag to button
button.tag = newtag
//Check to see if the array of tags contains the current button tag.
if tags.contains(button.tag){
let currentTag = button.tag
...
}
Be sure to delete this code as it isn't needed:
currentTag += 1
if currentTag == stageCount{
break
}
I have found that time of the string colouring depends on how many different NSColors are used. In code below if I use only one colour for the three cases then the text colouring process is 3 times faster than in the case when three different colours are used for these three cases, each colour for each case. Why ? Is there a way not to slow down the colouring for three different colours ?
for i in 0..<arrayOfNSRangesForA.count
{
textFromStorage.addAttribute(NSForegroundColorAttributeName, value: NSColor.green, range: arrayOfNSRangesForA[i])
}
for i in 0..<arrayOfNSRangesForT.count
{
textFromStorage.addAttribute(NSForegroundColorAttributeName, value: NSColor.green, range: arrayOfNSRangesForT[i])
}
for i in 0..<arrayOfNSRangesForC.count
{
textFromStorage.addAttribute(NSForegroundColorAttributeName, value: NSColor.green, range: arrayOfNSRangesForC[i])
}
Update
I have found one more BAD thing. When I changed colouring from NSForegroundColorAttributeNameto NSBackgroundColorAttributeName the running time has increased significantly - 10 times. For 20 000 characters, it was for one colour, for NSForegroundColorAttributeName- 1 sec, for NSBackgroundColorAttributeName - 10 sec; if three colours - 3 and 30 sec accordingly. For me it is very bad feature of Swift !!! It is not possible to do normal work with DNA (ATGC sequence) colouring, since the length of DNA is thousands of A,T,G,C characters!
Update
In comments I have a suggestion to colour only visible part of text. I have tried this approach and it is much worse even for shorter text in comparison with what I did in standard way. So, I had NSRange of text for visible part of text, and did colouring on fly while scrolling by using notification when scrolling is on. It is a bad way.
The biggest obstacle is laying out all these attributed characters in the text view. Colorize the DNA sequence takes minimal amount of time. Instead of writing your own layout manager or text storage class, you can adopt a divide-and-conquer approach by colorizing the text view in chunks at a time:
#IBOutlet var textView: NSTextView!
var dnaSequence: String!
var attributedDNASequence: NSAttributedString!
#IBAction func colorize(_ sender: Any) {
self.dnaSequence = "ACGT" // your plaintext DNA sequence
self.attributedDNASequence = self.makeAttributedDNASequence()
// Rendering long string with the same attributes throughout is extremely fast
self.textView.textStorage?.setAttributedString(NSAttributedString(string: dnaSequence))
let step = 10_000 // colorize 10k characters at a time
let delay = 0.2 // delay between each render
for (i, location) in stride(from: 0, to: self.dnaSequence.characters.count, by: step).enumerated() {
let length = min(step, self.dnaSequence.characters.count - location)
let range = NSMakeRange(location, length)
// Since we are modifying the textStorage of a GUI object (NSTextView)
// we should do it on the main thread
DispatchQueue.main.asyncAfter(deadline: .now() + (delay * Double(i))) {
let subtext = self.attributedDNASequence.attributedSubstring(from: range)
print("Replacing text in range \(location) to \(location + length)")
self.textView.textStorage?.replaceCharacters(in: range, with: subtext)
}
}
}
// MARK: -
var colorA = NSColor.red
var colorC = NSColor.green
var colorG = NSColor.blue
var colorT = NSColor.black
func makeAttributedDNASequence() -> NSAttributedString {
let attributedText = NSMutableAttributedString(string: dnaSequence)
var index = dnaSequence.startIndex
var color: NSColor!
for i in 0..<dnaSequence.characters.count {
switch dnaSequence[index] {
case "A":
color = colorA
case "C":
color = colorC
case "G":
color = colorG
case "T":
color = colorT
default:
color = NSColor.black
}
attributedText.addAttribute(NSForegroundColorAttributeName, value: color, range: NSMakeRange(i,1))
index = dnaSequence.index(after: index)
}
return attributedText
}
The trick is to make the application as responsive as possible so the user is unaware that things are still being done in the background. With a small delay (<= 0.3 second) I couldn't scroll my mouse fast enough to reach the end of text view before everything has been colorized (100k characters).
On a 100k-character test, it took 0.7 seconds to until the colorized string first appeared inside the text view instead of the 7 seconds if I did everything at once.
Have you tried using a ciColor instead of an attribute? ciColors can be used with text, images and backgrounds.
You can try like this:
txtField.textColor?.ciColor.red
I have an Array[String] in scala like this
my_array: Array[String] = Array(RED;BLUE, RED;PINK, RED;ORANGE, RED;WHITE, RED;YELLOW,
RED;GREY,GREEN;BLUE, GREEN;PINK, GREEN;BROWN, GREEN;ORANGE, GREEN;WHITE, GREEN;YELLOW, GREEN;GREY)
and I need to get this result
my_new_array: Array[Array[String]] = Array(Array(RED;BLUE, RED;PINK, RED;ORANGE, RED;WHITE,RED;YELLOW, RED;GREY),
Array(GREEN;BLUE, GREEN;PINK, GREEN;BROWN, GREEN;ORANGE, GREEN;WHITE, GREEN;YELLOW, GREEN;GREY),
Array(RED;BLUE, GREEN;BLUE), Array(RED;PINK, GREEN;PINK),
Array(RED;ORANGE, GREEN;ORANGE), Array(RED;WHITE, GREEN;WHITE),
Array(RED;YELLOW, GREEN;YELLOW), Array(RED;GREY, GREEN;GREY))
These should be te steps
get a list of unique colors. this means I have to split by ";" each string
once I have this list I have to create a new Array contained the original strings grouped by each single color
Does anyone have an hint?
Provided I've understood your question correctly, this should work (probably not the most efficient solution ever)
myArray
.flatMap(_.split(';')) // get all the colors
.distinct // get the unique set of colors
.map(color => myArray.filter(_.contains(color))) // map each color to each group containing it
I'm using contains assuming that for "YELLOW" you want to match both "YELLOW";"RED" and "RED";"YELLOW".
In case you want to match only the former, you can use startsWith intead.
I have an array, lets call it _persons.
I am populating this array with Value Objects, lets call this object PersonVO
Each PersonVO has a name and a score property.
What I am trying to do is search the array &
//PSEUDO CODE
1 Find any VO's with same name (there should only be at most 2)
2 Do a comparison of the score propertys
3 Keep ONLY the VO with the higher score, and delete remove the other from the _persons array.
I'm having trouble with the code implementation. Any AS3 wizards able to help?
You'd better use a Dictionary for this task, since you have a designated unique property to query. A dictionary approach is viable in case you only have one key property, in your case name, and you need to have only one object to have this property at any given time. An example:
var highscores:Dictionary;
// load it somehow
function addHighscore(name:String,score:Number):Boolean {
// returns true if this score is bigger than what was stored, aka personal best
var prevScore:Number=highscores[name];
if (isNaN(prevScore) || (prevScore<score)) {
// either no score, or less score - write a new value
highscores[name]=score;
return true;
}
// else don't write, the new score is less than what's stored
return false;
}
The dictionary in this example uses passed strings as name property, that is the "primary key" here, thus all records should have unique name part, passed into the function. The score is the value part of stored record. You can store more than one property in the dictionary as value, you'll need to wrap then into an Object in this case.
you want to loop though the array and check if there are any two people with the same name.
I have another solution that may help, if not please do say.
childrenOnStage = this.numChildren;
var aPerson:array = new array;
for (var c:int = 0; c < childrenOnStage; c++)
{
if (getChildAt(c).name == "person1")
{
aPerson:array =(getChildAt(c);
}
}
Then trace the array,
I'm not overly sure if this is possible, as I am not a frequent programmer, but I have a question.
I've got an array that generates one random word in a text box and then a second array that generates another random word in a different text box. What I want is for when a certain word out of array number one is generated, a certain image appears with it. Here's the code:
var firstChoice:Array = ["Do this", "Do that", "Do something else"];
var secondOption:Array = ["while doing this", "while doing that", "while doing something else"];
generate_btn.addEventListener(MouseEvent.CLICK, getTask);
function getTask(event:MouseEvent):void {
var randomChoice:Number = Math.floor(Math.random() * firstChoice.length);
var randomOption:Number = Math.floor(Math.random() * secondOption.length);
Final_Choice.text = firstChoice[randomChoice];
Final_Option.text = secondOption[randomOption];
}
So for instance, when I click the button and the first array generates "Do this," I want a specific graphic to appear with it.
Hopefully this is possible :/ I'm stumped!
probably you need to use a HashMap, such as:
var map:Object = new Object();
map.first_choice = /*url of your image associated with this choice*/
map.second_choice = /*url of your image associated with this choice*/
//etc
and when a word is generated, you just compare the word with keys of the map, using a foreach, and get the url of your image