So I'm trying to make a grid/table show up in my app, and to do this I created an array of UILabels with:
var rows = 2
var columns = 2
var grid = [[UILabel]](count: rows, repeatedValue: [UILabel](count: columns, repeatedValue: UILabel()))
then I created each UILabel with:
for i in 0 ..< grid.count
{
for j in 0 ..< grid[0].count
{
grid[i][j].frame = CGRectMake(x + CGFloat(j*Int(gridSpace)), y + CGFloat(i*Int(gridSpace)), gridSpace, gridSpace)
grid[i][j].backgroundColor = UIColor.redColor()
grid[i][j].hidden = false
grid[i][j].textColor = UIColor.whiteColor()
grid[i][j].textAlignment = NSTextAlignment.Center
grid[i][j].text = "label: [\(i)][\(j)]"
self.view.addSubview(grid[i][j])
print("grid[\(i)][\(j)]X = \(grid[i][j].frame.origin.x) grid[\(i)][\(j)]Y = \(grid[i][j].frame.origin.y)")
}
}
What happens is actually pretty interesting. The code compiles and everything, but only one of the labels shows up. The dimensions and everything about the label are perfect, but only one of them shows up.
The label that shows up is always the grid[rows-1][columns-1] label. But when I print the x and y coordinates of the rest of the labels that are supposed to be in the grid array, all of the coordinates are exactly where they are supposed to be on the viewcontroller it's just that the labels don't show up. when I changed the for loop to
for i in 0 ..< grid.count-1
{
for j in 0 ..< grid[0].count-1
{
still only the last label (the grid[rows-1][columns-1] label) shows up yet the coordinates are exactly where they should be. Another weird thing is that if I change the line
grid[i][j].text = "test label: [\(i)][\(j)]"
to
if(j != grid[0].count-1)
{
grid[i][j].text = "test label: [\(i)][\(j)]"
}
then the label that shows up still has the coordinates of the grid[rows-1][columns-1] but has the labeltext of grid[rows-1][columns-2]
Can anyone fix my problem for me or explain whats going on?
It's because of the way that you have initialized your grid with repeated values. If the repeated value is of reference type, the array will actually create only one item and point all indexes to that item, so in your for loops you're changing the frame for the one and only UILabel over and over again. thats why you only see the last one on your screen.
To create the array you want replace this:
var grid = [[UILabel]](count: rows, repeatedValue: [UILabel](count: columns, repeatedValue: UILabel()))
with
var grid = [[UILabel]]()
for row in 0..<rows {
grid.append([UILabel]())
for column in 0..<columns{
grid[row].append(UILabel())
}
}
or
var grid = [[UILabel]]((0..<rows).map { _ in
[UILabel]((0..<columns).map { _ in
UILabel()
})
})
Related
I have a UILabel in a tableView cell that I would like to animate line by line. The string has line breaks with \n and lines.count returns 7 as expected but no line is being added to the label at all even though the string itself is not nil.
Code in cellForRow
var index = 0
let lines = messageList[indexPath.row].message.components(separatedBy: "\n")
senderCell.otherUserMessage.numberOfLines = lines.count
Timer.scheduledTimer(withTimeInterval: 3, repeats: true, block: { [weak self] timer in
if index != lines.count {
let char = lines[index]
let nChar = "\n\(char)"
senderCell.otherUserMessage.text! += nChar
print("char is \(nChar)") //nChar prints the proper text
print("senderCellText is \(senderCell.otherUserMessage.text!)") // but this prints an array of numbers
// Play sound each time
do {
self!.audioPlayer = try AVAudioPlayer(contentsOf: self!.koalaMessageSound)
self!.audioPlayer.play()
}catch{
// couldn't load file :(
}
index += 1
} else {
timer.invalidate()
}
})
Idea/Expected Result
Only way I found to animate a Label like this was to:
1. create a copy of Label for each line you have in your original Label (calculating frame.height / lineHeight)
2. mask each of those copies with rectangles for single line
3. animate each Label separately - each Label will be displaying one line only there so you can control them separately.
Note: Using \n is impossible, because sometimes text may go to next line due to it's length on smaller devices, so to get real count of lines, you need to calculate like like I described on point 1.
I am trying to make the X direction grid line and tick labels fixed at mid of the visible range, whether the chart is zoomed or paned.
I had try to create custom TickProvider for my xAxis:
class CustomTickProvider: SCIDateTimeTickProvider {
private var tickCount: Int
init(tickCount: Int) {
self.tickCount = tickCount
}
override func getMajorTicks(fromAxis axis: SCIAxisCoreProtocol!) -> SCIArrayController! {
let visibleRange = axis.visibleRange
let min = visibleRange?.min.doubleData
let max = visibleRange?.max.doubleData
let array: SCIArrayController = SCIArrayController.init(type: SCIDataType.double)
let step = (max! - min!) / Double(self.tickCount - 1)
var current = min!
while current <= max! {
array.append(SCIGeneric(current))
current += step
}
return array
}
}
xAxis.tickProvider = CustomTickProvider.init(tickCount: 3)
When I set xAxis.autoTicks = true, the grid line and tick labels will be relocated,they can't stay at the same position either.
When I set xAxis.autoTicks = false, no grid line and tick labels will be
drawn.
How can I get the effect of fixed grid line and tick labels?
Maybe not the cleanest way to do it, but it worked for me in a comparable situation:
Edit the MajorDelta/MinorDelta after the visibleRange changed.
I have done the following to assign an UIImage to different instance of UIView object. Inside my UIView, I have an UIImageView which holds the Image.
var transitionImageNames = ["1.png", "2.png", "3.png", "4.png"]
var count=0
for item in myList {
var transitionImageName = transitionImageNames[count]
item.transitionImageView.image = UIImage(named: transitionImageName)
// transitionImageName refers to 4 different images, let say 1,2,3,4.png
if (count<=3) {
count += 1
}
}
there are a number of different items in myList. I have printed out the item instance, all of them have different instance value. However, I found that for the transitionImageView to be displayed, even if I set 1, 2, 3, 4.png to 4 different items in the myList, they all display the same image (which is the last one set)
Any idea on why?
I think you need something like this:
var transitionImageNames = ["1.png", "2.png", "3.png", "4.png"]
for (index, item) in myList.enumerated() {
item.transitionImageView.image = UIImage(named: transitionImageNames[index])
}
I have two UIImageView arrays which I fill up with 18 blocks and circles
var myBlocks = [UIImageView]()
var myCircles = [UIImageView]()
So after I add my circles to the screen in a nice fashion, then my blocks overtop of them, I call a function to set the tags of the circles and block to match, if the colors match. Meaning if the block is bright red, I want to find the circle that is bright red, and tag them both 0, and so on. This line below where I set the tag is throwing the error:
Cannot subscript a value of type '[UIImageView]' with an index of type 'UIImageView'
func setTags() {
for x in myBlocks {
for y in myCircles {
if x.tintColor == y.tintColor {
myBlocks[x].tag = y //Error here
}
}
}
}
Is there an easier way to do this? The reason I don't tag both upon creation is because the circles are created using an array that is shuffled, being that I don't want the same colored circle and the same color block on top of each other when the game loads in.
Edit: I changed it to x.tag = y.tag and it seems better. However, I'm doing two print outs now when I tap one of my square blocks.
let objectDragging = recognizer.view?.tag
print(objectDragging)
//and
print("the tag of the object your touching is \(myBlocks[objectDragging!])")
and the log I'm getting during use is
Optional(13)
the tag of the object your touching is <UIImageView: 0x7f8d4ba103a0;
frame = (263 180; 100 100); opaque = NO; tintColor =
UIExtendedSRGBColorSpace 0.8 0.3 0.3 1; tag = 11; gestureRecognizers = <NSArray: 0x60000005fec0>; layer = <CALayer: 0x600000220580>>
So one is saying the block is tagged 13, and one is saying 11. The 13 is what it says when I print out myBlocks[count].tag, I just don't know where the 11 is coming from in the myBlocks[objectDragging] statement.
Edit 2: Is it because (myBlocks[objectDragging!]) is referencing a different block maybe?
You have two issues. First, myBlocks[x] will raise an error because you're looping through elements, not indices. Second, x.tag = y raises an error because the property tag is an Int and you're trying to assign a UIImageView.
for x in myBlocks {
for y in myCircles {
if x.tintColor == y.tintColor {
x.tag = y.tag //fix here
}
}
}
EDIT: Alternatively, if you want to loop through the indices:
for x in 0..<myBlocks.count {
for y in 0..<myCircles.count {
if myBlocks[x].tintColor == myCircles[y].tintColor {
myBlocks[x].tag = myCircles[y].tag
}
}
}
Finally, if you want the indices and the elements, you can do:
for (x, block) in myBlocks.enumerated() {
for (y, circle) in myCircles.enumerated() {
if block.tintColor == circle.tintColor {
myBlocks[x].tag = myCircles[y].tag
}
}
}
This line:
myBlocks[x].tag = y //Error here
y is an element of myCircles. It is not a tag.
Change that line to:
x.tag = y.tag
Basically, I wish to list some text like so, in a column view:
Item Colour Quantity
Glasses Black 3
It's not for user-input, more towards the user reading it. I know this can be accomplished via the use of labels, however, this does tend to get tedious. I have the text written up in a word document, tried to copy and paste it into a UITextView and mess around with the attributes, I get 99% close however the last "column" seems to always give me an issue.
Can anyone potentially shed some light onto how I can do this with ease?
P.S- I will probably have 10 "lists" each different.
You have basically three good possibilities.
1) You give it a try using labels
2) You take a UICollectionView or
3) some UITableViews side by side
I had the same problem and I decided to go with option 1)
Create a method which needs a 2d String array as input.
Determine the ScreenSize of the device.
func createRows(textName: [[String]])
{
dispatch_async(dispatch_get_main_queue())
{
if self.columnArrayActive || self.columnArray1.isEmpty
{
let screenSize: CGRect = UIScreen.mainScreen().bounds
let screenWidth = screenSize.width
self.ScrollViewMain.contentSize.height = CGFloat(30 * textName.count))
Now create two for loops to iterate through the array.
Create the labels and modify them.
for i1 in 0..<textName.count
{
var columnArray = [UILabel]()
for i2 in 0..<textName[i1].count
{
columnArray.append((UILabel(frame: CGRectMake(CGFloat((Int(screenWidth) / 9) * i2 - Int(screenWidth / 128)), (CGFloat(30 * i1) - modifyY), CGFloat(Int(screenWidth) / 8), 30))))
columnArray[i2].text = textName[i1][i2]
columnArray[i2].textAlignment = NSTextAlignment.Center
I have additionally structured the rows with two different colors for a better reading experience.
if i1 == 0
{
columnArray[i2].backgroundColor = UIColor(red: CGFloat(1), green: CGFloat(0.35), blue: CGFloat(0.35), alpha: CGFloat(1))
} else {
if self.rowSwitcher
{ columnArray[i2].backgroundColor = settings.vari.color1 as? UIColor}
else
{ columnArray[i2].backgroundColor = settings.vari.color2 as? UIColor}
}
self.scrollViewMain.addSubview(columnArray[i2])
}
if i1 != 0
{
if self.rowSwitcher
{ self.rowSwitcher = false }
else
{ self.rowSwitcher = true }
}
self.columnArray1.append(columnArray)
}
self.columnArrayActive = false
This part of the code is used when you already have created your list but you need to update the values of the labels.
} else {
for i3 in 0..<textName.count
{
for i4 in 0..<textName[i3].count
{
self.columnArray1[i3][i4].text = textName[i3][i4]
}
}
}
}
}
You need to define some global or class variables to get it work:
1)columnArray1 : [[UILabel]]
2)columnArrayActive : Bool
3)scrollViewMain : UIScrollView!
You create in the Interface Builder a UIScrollView filling you screen.
Then you create constraints.
Afterwards you create a reference to your class and in viewDidLoad you add:
scrollViewMain = self.delegate
This means that your class needs of course to inherit from UIScrollViewDelegate!
The result looks like this:
Don't forget you can draw text into a graphics context. For read-only text, this is definitely an option I would consider. Another option which might work for you is text containers in Text Kit.