Swift - Update and store position of a multiple programmatically created buttons - arrays

I have a button which creates other buttons based on the class Clip seen below. Those newly created buttons are added to an array and stored in a plist.
class Clip: Encodable, Decodable {
var name: String = ""
var xCoordinate: Int = 100
var yCoordinate: Int = 300
// more parameter will be added later on e.g color, scale etc..
}
Each button can be moved around the view and the new x & y coordinates are stored in a plist.
#objc func handlePan(sender: UIPanGestureRecognizer){
let uIViewSelected = sender.view!
switch sender.state {
case .began, .changed :
moveViewWithPan(view: uIViewSelected, sender: sender)
break
case .ended:
//Finds the position when the button is no longer being dragged
let x = Int(uIViewSelected.center.x)
let y = Int(uIViewSelected.center.y)
//clipArray[0] need to be the corresponding clicked button e.g clipArray[2]
clipArray[0].xCoordinate = x
clipArray[0].yCoordinate = y
saveData()
break
default:
break
}
}
The above works only if I create one button. When more buttons are added, the above lines only change the first clip from the array. I need a way to update the value to the correct button clicked.
How can identify the array position of the click button as I am creating all them programmatically? At the moment I am placing at value 0 of the clipArray.
clipArray[0].xCoordinate = x
clipArray[0].yCoordinate = y
I am not even sure if using a plist is the best way to store the buttons in the first place.
Any help or documentation would be much appreciated.
Thanks

Following from dfd response, I added tags to each button which are created and it solved the issue for now.
let x = Int(uIViewSelected.center.x)
let y = Int(uIViewSelected.center.y)
//clipArray.append(clip)
var tagNo = uIViewSelected.tag
clipArray[tagNo].xCoordinate = x
clipArray[tagNo].yCoordinate = y

Related

set value on sidebar

I am having one google sheet having more than 100 rows with column of "NAME, PLACE, PHONE". I want to change /correct the phone number on specific person Ex.John in the side bar (Form.html) and the correct place & phone number to be edit in that specific row of my google sheet "Phonelist". The code.gs given below which is not working. Could you lease rectify the same?
function sidebar() {
var html = HtmlService.createHtmlOutputFromFile("Form").setTitle('Phone Details');
SpreadsheetApp.getUi().sidebar(html);
}
function result(form) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ws = ss.getSheetByName("Phonelist");
var data = ws.getDataRange().getValues();
var name = form.name;
var place = form.place;
var phone = form.phone;
for (var i = 1; i < data.length; i++) {
if(data[i][1] == "John"){
var result = [name,place,phone];
ws.getRange(dat[i]).setValue(result);
}
}
}
It is difficult to understand what you exactly need. But there are some issues which are visible.
ws.getRange(data[i])is not valid. See docs. You need a row and a column at least, and in your case also the number of columns since your are inserting a range. Currently you only have a column. The solution is `
const startColumn = 1 // start at column A
const numberOfRows = 1 // update one row at a time
const numberOfColumns = result.length // this will be 3
ws.getRange(data[i], startColumn, numberOfRows, result.length)
.setValues(result) // setValues is correct, setValue is incorrect
The second issue is that you said that NAME is in the first column, but your test is testing against the second column. Array start at 0, i.e. the first item is actual accessed by [0]. therefore your test if(data[i][1] == "John") actually checks if the second column PLACE is equal to "John". To fix this, replace the [1] with [0], so:
if(data[i][0] == "John")
The third issue is handled in the first answer. You are using setValue() which is only to be used to set one cell. But since you are setting a number of cells at one time, you should use setValues() instead.

How to fix my random button enabler code showing the same order every time?

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
}

Data and Series label not showing on WinForm Chart control

I am having a problem getting the default WinForms Chart control to work. I have a single chart with a single area. In this area, I want to display three (3) series whose labels are on a single legend.
Each of the value arrays shown in the code below contains six (6) values.
When I run the application the chart is only showing the background with the title and the name of the FIRST series I defined, the others seem to be ignored. Also, no grid and no data points or lines are displayed. The chart is basically blank.
this.chart.SuspendLayout();
this.chart.ChartAreas.Clear();
this.chart.Series.Clear();
ChartType chartType = ChartType.Column;
// prepare the area
const string AREA_NAME = "ChartAreaBP";
ChartArea bpChartArea = new ChartArea(AREA_NAME);
bpChartArea.AxisX.LabelStyle.Format = "dd/MMM\nyyyy";
bpChartArea.AxisX.MajorGrid.LineColor = System.Drawing.Color.LightGray;
bpChartArea.AxisY.MajorGrid.LineColor = System.Drawing.Color.LightGray;
bpChartArea.BackColor = System.Drawing.Color.LimeGreen;
bpChartArea.BackGradientStyle = GradientStyle.DiagonalRight;
bpChartArea.Position.Auto = true;
bpChartArea.InnerPlotPosition.Auto = true;
this.chart.ChartAreas.Add(bpChartArea);
// prepare the values. X is Date/time all other 3 are BYTE/INT
var xvals = from x in items select x.TimeStamp;
var yvalsSys = from y in items select y.Systolic;
var yvalsDia = from y in items select y.Diastolic;
var yvalsRhy = from y in items select y.Rhythm;
// The first series, other 2 omitted from HERE for simplicity
const string SYS_SERIES = "Systolic";
Series sysBPSeries = new Series(SYS_SERIES, 4);
sysBPSeries.ChartType = chartType;
sysBPSeries.ChartArea = AREA_NAME;
sysBPSeries.XValueType = ChartValueType.Auto;
sysBPSeries.YValueType = ChartValueType.Date;
sysBPSeries.XAxisType = AxisType.Primary;
sysBPSeries.YAxisType = AxisType.Primary;
sysBPSeries.Enabled = true;
this.chart.Series.Add(sysBPSeries);
this.chart.Series[SYS_SERIES].Points.DataBindXY(xvals, yvalsSys);
// here the other two series are defined.
But when I run the application only the legend of the FIRST series is shown even though the other two are defined in the code (I omitted them from this listing) just the same way as the first series.
And as I stated above, no grid nor values are shown. However, the chart shown in design mode does show all three labels on the first and only legend and all three lines.

dragAndDrop NOT working on Chrome

I am trying to perform a Drag and Drop operation on our Angular application using Protractor Jasmine. I am able to get hold of the source item but as the test runs, the source element gets selected but nothing happens thereafter; the drag and drop operation does not take place. There are no errors shown in the console.
An interesting thing about the destination container is that the items dropped here can be resized as per user wish. Also, there is no clearly marked place/area in the destination container where the dragged item will get dropped! But the container does have an ID; though that has still not helped here.
Here is the code:
let dragAndDrop = require('html-dnd').code;
.
.
.
function dragAndDropListItems(fdIndex: number): void {
let dragElement = element.all(by.repeater('listDefinition in lists')).get(fdIndex); // Select the first repeater corresponding to the first List Item in the list
let dragElementh5 = dragElement.all(by.css('a')).get(0); // Select the first List Item
let printFD = dragElementh5.getText().then((text: string) => {
console.log(text); // Print the innerHTML text from the chosen List Item to the Console
});
let finalDrop = element.all(by.css('[id="dashboardContainerDiv"]')).get(0);
dragElement.click();
browser.actions().dragAndDrop(dragElement, finalDrop).perform();
};
I have tried using coordinate based DragNDrop operation as well but the same in every case.
Other tried options include:
//browser.executeScript(dragAndDrop, dragElement, finalDrop); // Perform the drag and drop operation
//browser.driver.actions().dragAndDrop(dragElement, finalDrop).perform();
//browser.actions().dragAndDrop(dragElement, { x: 400, y: 400 }).perform();
// browser.driver.actions().mouseDown(dragElement).mouseMove(finalDrop).mouseUp(finalDrop).perform();
Kindly suggest a solution to this issue.
#FlorentB. I have attached the Code with your scripts imported.
let JS_DRAG_DROP = require('./drag-drop.js');
function dragAndDropListItems(fdIndex: number): void {
/*
let source = driver.find_element_by_css_selector("#drag")
target = driver.find_element_by_css_selector("#drop")
driver.execute_async_script(JS_DRAG_DROP, source, target)
# drag and drop an element by offset {x:500, y:200}
source = driver.find_element_by_css_selector("#drag")
driver.execute_async_script(JS_DRAG_DROP, source, None, 500, 200)
# drag and drop an element with a delay of 101ms before the drop
source = driver.find_element_by_css_selector("#drag")
target = driver.find_element_by_css_selector("#drop")
driver.execute_async_script(JS_DRAG_DROP, source, target, 0, 0, 101)
*/
let source = element.all(by.repeater('listDefinition in
lists')).get(fdIndex); // Select the first repeater corresponding to the
first List Item in the list
let dragElementh5 = source.all(by.css('a')).get(0); // Select the first List
Item
let printFD = dragElementh5.getText().then((text: string) => {
console.log(text); // Print the innerHTML text from the chosen List Item
to the Console
});
//browser.driver.switchTo().frame('dashboardContainerDiv');
/*
let finalDropClass = element.all(by.css('[class="dashboard mb10"]')).get(0);
let finalDropCon =
finalDropClass.all(by.css('[id="dashboardContainerDiv"]')).get(0);
let finalDrop =
finalDropCon.all(by.css('[id="dashboardContainerUl"]')).get(0);
*/
let target = element.all(by.css('[id="dashboardContainerDiv"]')).get(0);
//dragElement.click();
browser.executeScript(JS_DRAG_DROP, source, target); // Perform the drag and
drop operation
//browser.actions().dragAndDrop(dragElement, finalDrop).perform();
//browser.driver.actions().dragAndDrop(dragElement, finalDrop).perform();
//browser.actions().dragAndDrop(dragElement, { x: 400, y: 400 }).perform();
// browser.driver.actions().mouseDown(dragElement).mouseMove(finalDrop).mouseUp(finalDrop).perform();
};
Try
https://github.com/html-dnd/html-dnd
Installing it and using the typescript example worked for me.

Array whose each clip is linked to its equal clip of another array

Hello I apologize in advance for my question which I'm sure is pretty basic.
On a map are set 33 landmarks with an array calling a class in the library.
A second array defines the coordinates of those landmarks.
for (var i:uint = 0; i < 33; i++) {
mark[i] = new landMark();
landMarks.addChild(mark[i]);
mark[i].x = lmxy[i]['x'];
mark[i].y = lmxy[i]['y'];
}
var lmxy:Array = [{x:1620,y:880},{x:1850, y:1050},etc...];
So far so good, the landmarks show each in its right place.
The third array contains different legends supposed to show when a landmark is clicked.
So the landmark [1] should show the legend [1] and the landmark [31] the legend [31]
var lgd:Array = [lgdA, lgdB, etc... ];
var legends:MovieClip;
for (var j:uint=0;j<lgd.length;j++) {
legends = new lgd[j]();
legends.x = 300;legends.y = 170;
}
Edit cause obviously that was unclear :
I tried that in the loop to link the marks to the legends but I get an error :
mark[i].addEventListener(MouseEvent.CLICK, getLgd);
function getLgd(e:Event):void {stage.addChild (lgd[i]);}
Any help would be very welcome !
The problem is that the variable i doesn't have a definition. The only way for you to find out which of the landmarks were clicked is to find its index in the array, then you can add the legend that has the same index. Because the index isn't passed from the event listener, you need to use e which has the target property.
This should do the trick:
mark[i].addEventListener(MouseEvent.CLICK, getLgd);
function getLgd(e:Event):void
{
var i:int = mark.indexOf(e.target);
stage.addChild(lgd[i]);
}

Resources