How to create an iOS Time Zone Picker? - xcode

My app is a logbook for pilots and I'd like to give the user the option of putting the app in the time zone of their choice. Airline pilots may fly across the world but have to reference their home base time zone when entering flight information. I want to offer a UI that allows them to select a time zone option such as "Local" (which is device local, depending on their location), "UTC", and then the hard one "custom".
I'm using MonoTouch.Dialog which allows you to wire up a collection but the problem is in iOS the list of time zones is rather long. I use the System.TimeZoneInfo namespace to retrieve the list of time zones just like in Windows. However, iOS uses a presentation such as "United States/New York, United States/XYZ, etc.". So to keep this list manageable I'm going to need to break this up by splitting on the / so I can first present a list of countries, the user will tap that and a secondary list will then present in a UITableView which is the regions such as "New York." I'll then capture the time zone and use the System.TimeZoneInfo methods to convert from UTC to the custom time zone.
My question is how, particularly in MonoTouch.Dialog, can I have a collection and then a sub-collection. i.e. a UITableView of Countries with a disclosure indicator that takes you to the regions for the country?
On this same note, has anyone else implemented a "time zone" selector/picker in iOS (Xcode or MonoTouch)?
Thank you for your ideas and help with this problem.

The following will get you the UI you are looking for:
var groups = from tz in TimeZoneInfo.GetSystemTimeZones ()
let name = tz.ToString ()
let idx = name.IndexOf ('/')
where idx != -1
let first = name.Substring (0, idx)
group tz by first into g
select new { Region = g.Key, Zones=g };
var root = new RootElement ("TimeZones"){
new Section ("Regions"){
from r in groups
select (Element) new RootElement (r.Region.ToString ()){
new Section () {
from z in r.Zones
select (Element) new StringElement (z.ToString ())
}
}
}
};
dvc = new DialogViewController (root);
It produces this UI:
(source: tirania.org)
And the nested elements look like this:
(source: tirania.org)

On this same note, has anyone else implemented a "time zone" selector/picker in iOS (Xcode or MonoTouch)?
I have not seen one doing exactly this. However this article shows you how to use UIPickerView to implement your own pickers (close to what you want). It should not be too hard to convert this into C#.
Once you have your own UITimeZonePicker then it should be easy to create a TimeZoneElement for MonoTouch.Dialog (mostly copy/pasting DateElement).

Related

Rendering database UTC timestamp as local with Telerik controls

We're using the Telerik (PHP UI) controls but there appears to be something that I'm not able to crack.
We store timestamps in the backend database in UTC, and when using a Grid to display such items, I want to be able to show that UTC timestamp converted into the local users timezone (the TZ data is stored as a PHP variable, different people logging in could be in different TZs). It appears I'm not the only one that's asking this, as the Telerik forum someone else asks the same question but without an answer (scroll to the bottom of that forum post).
From the Telerik site I it appears that all I'd have to do is to format the date with a format with "zzz" appended to the date, but all this does is add the offset to the displayed time (eg 2020-02-27 10:00:00 -> 2020-02-27 10:00:00-0800) ... but it does do this "auto-magically" which I suppose is nice ... (heavy sarcasm)
This forum post also shows that an onRequestEnd call should do what I need it to do, but when I attempt this, nothing appears to change.
Can anyone offer up any advice?
Note : I've created the outer table with the following:
$grid = new \Kendo\UI\Grid('grid');
Solution: I achieved this by adding the following to the page:
function dataSource_requestEnd(e){
var data = e.sender.options.data.data;
for(var i=0; i<data.length; i++){
var td = data[i];
for (var key in td){
if (key === "NameOfDateField"){
var offset = new Date().getTimezoneOffset();
new Date(td[key].setMinutes(td[key].getMinutes() - offset));
}
}
}
}
$(document).ready(function() {
var dataSource = $("#grid").data("kendoGrid").dataSource;
dataSource.bind("requestEnd", dataSource_requestEnd);
dataSource.fetch();
});
Please note that NameOfDateField was the name of the key in the array that was returned from the dataSource.
TBH, I'm not at all sure how the data got converted, but certainly the offset and localTime variables are vital to this succeeding (although to my mind, they don't actually update anything?!)

Xcode UITest scrolling to the bottom of an UITableView

I am writing an UI test case, in which I need to perform an action, and then on the current page, scroll the only UITableView to the bottom to check if specific text shows up inside the last cell in the UITableView.
Right now the only way I can think of is to scroll it using app.tables.cells.element(boundBy: 0).swipeUp(), but if there are too many cells, it doesn't scroll all the way to the bottom. And the number of cells in the UITableView is not always the same, I cannot swipe up more than once because there might be only one cell in the table.
One way you could go about this is by getting the last cell from the tableView. Then, run a while loop that scrolls and checks to see if the cell isHittable between each scroll. Once it's determined that isHittable == true, the element can then be asserted against.
https://developer.apple.com/documentation/xctest/xcuielement/1500561-ishittable
It would look something like this (Swift answer):
In your XCTestCase file, write a query to identify the table. Then, a subsequent query to identify the last cell.
let tableView = app.descendants(matching: .table).firstMatch
guard let lastCell = tableView.cells.allElementsBoundByIndex.last else { return }
Use a while loop to determine whether or not the cell isHittable/is on screen. Note: isHittable relies on the cell's userInteractionEnabled property being set to true
//Add in a count, so that the loop can escape if it's scrolled too many times
let MAX_SCROLLS = 10
var count = 0
while lastCell.isHittable == false && count < MAX_SCROLLS {
apps.swipeUp()
count += 1
}
Check the cell's text using the label property, and compare it against the expected text.
//If there is only one label within the cell
let textInLastCell = lastCell.descendants(matching: .staticText).firstMatch
XCTAssertTrue(textInLastCell.label == "Expected Text" && textInLastCell.isHittable)
Blaines answer lead me to dig a little bit more into this topic and I found a different solution that worked for me:
func testTheTest() {
let app = XCUIApplication()
app.launch()
// Opens a menu in my app which contains the table view
app.buttons["openMenu"].tap()
// Get a handle for the tableView
let listpagetableviewTable = app.tables["myTableView"]
// Get a handle for the not yet existing cell by its content text
let cell = listpagetableviewTable.staticTexts["This text is from the cell"]
// Swipe down until it is visible
while !cell.exists {
app.swipeUp()
}
// Interact with it when visible
cell.tap()
}
One thing I had to do for this in order to work is set isAccessibilityElement to true and also assign accessibilityLabel as a String to the table view so it can be queried by it within the test code.
This might not be best practice but for what I could see in my test it works very well. I don't know how it would work when the cell has no text, one might be able to reference the cell(which is not really directly referenced here) by an image view or something else. It's obviously missing the counter from Blaines answer but I left it out for simplicity reasons.

Limiting the results of drop-down in Google Sheets

I work as an operations manager at at small business and I'm trying to set up an order sheet that is easy for the salesman to use. In the order sheet, I've used the OFFSET function to refer to a master list containing customers and prices. Under Customer, I type the customer and it draws it from the master list via an auto-complete drop-down. The same happens with the product.
Here is the order sheet:
Order Sheet Example
My issue is I'll begin typing in the product e.g. 'prawn'. We have over a dozen prawn lines, but a particular customer will only take one. All of the other prawn results have no prices for that customer. However, the auto-complete function will offer up all the 'prawn' results.
In the Master list, I've entered prices for only the products that the particular customer uses. Take a look at what the Master List looks like:
Master List look
Without an excellent memory of what customer wants what, it's an exercise in trial and error. In the above example, I could type 'topside' and if I select the wrong one, no price comes up.
This is frustrating.
I was hoping for a way to limit the auto-complete so that when I type 'prawn' or 'topside' for that customer, it only comes up with auto-complete fields that have the price in it. Can anyone help? Or does anyone know of any work-arounds? I'd be really thankful, the current order system is quite difficult.
I believe this will do what you want. It is a little difficult to tell from your sample data. It uses google apps script and sheet names and/or columms may need to be changed in the script for your data. I am attaching a sample spreadsheet you can copy then try. You will have to approve the script in the copy you make.
function onEdit(e) {
var cust=e.value //The value of the edited cell
var sh=e.source.getActiveSheet().getSheetName()//Name of the active sheet.
var col=e.range.getColumn()//The edited column
var r=e.range.getRow()//The edited row
var row=e.range.offset(0,1).getA1Notation()//Cell A1 notation of cell in same row one column to the right.
var ss=SpreadsheetApp.getActiveSpreadsheet()
var s=ss.getActiveSheet().getSheetName()//Name of active sheet
var s1=ss.getSheetByName("Sheet1")//Variables for sheets
var s2=ss.getSheetByName("Sheet2")
var s3=ss.getSheetByName("TEMP")
var rng=s2.getDataRange().getValues()//Customer/Products
if(sh=="Sheet1" && col==1){//If sheet1 is active sheet and Customer (column A) is edited.
var array=[]//Array to hold customers products
for (var i=0;i<rng.length;i++){
if(rng[i][0]==cust ){
for(var j=1;j<rng[0].length-1;j++){
if(rng[i][j]!="" ){//If customers product has $ entry add to Array.
array.push([rng[0][j]])
}}}}
s3.clearContents()//Clear old product list from TEMP.
s1.getRange(row).clearContent()//Clear product dropdown
s3.getRange(1,1,array.length,1).setValues(array)//Set new customer product list in TEMP.
drop(row,cust)// Call drop function to build new dropdown.
}
if(sh=="Sheet1" && col==2){//If sheet1 is active sheet and Product (column B) is edited.
var cust1=e.range.offset(0,-1).getValue()//Get customer in A
var prod=e.range.getValue()//Get selected product
for (var i=0;i<rng.length;i++){//Get the customer/product price
if(rng[i][0]==cust1 ){
for(var j=1;j<rng[0].length-1;j++){
if(rng[0][j]==prod){
price=rng[i][j]
s1.getRange(r,5).setValue(price)//Set the price in column E
s1.getRange(r,2).clearDataValidations() //Remove the data validation dropdown in column B
}
}}
}}}
function drop(row,cust){
var ss=SpreadsheetApp.getActiveSpreadsheet()
var s3=ss.getSheetByName("TEMP")
var s1=ss.getSheetByName("Sheet1")
var cell = s1.getRange(row);//set validation in B
var ocell=s1.getRange(row).offset(0, -1).getValue()//evaluate value in A
var cellVal=cell.getValue()
if(ocell==cust){
var lr= ss.getSheetByName("TEMP").getLastRow()
var range = ss.getSheetByName("TEMP").getRange(1, 1, lr, 1)
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(range).build();//Build the dropdown
cell.setDataValidation(rule)}//Set the validation rules (Customers
products)
}
Test spreadsheet:
https://docs.google.com/spreadsheets/d/1u86sdf1_mO-Mv7hQM_hRZl3Gma-Y2lsu9ZvxSW4jA1U/edit?usp=sharing

UI Testing in Xcode 7: Selecting a row number in a UIPickerView / PickerWheel

Is there a way in Xcode 7 UI Testing to select the 3rd row in a UIPickerView?
I have tried various things like this to identify the rows in each picker but all the requests below return 0 found:
XCUIApplication().pickers.element.cells.count
XCUIApplication().pickers.element.staticTexts.count
Any ideas?
Updated: I am aware of adjustToPickerWheelValue method where you can select a particular value you already know, but I am trying to select the 3rd value (index = 4) when I don't know the values that exist in the picker.
You can use -adjustToPickerWheelValue: to select items on a UIPickerView.
When there is one UIPickerView on the screen you can select the element directly, like so.
let app = XCUIApplication()
app.launch()
app.pickerWheels.element.adjustToPickerWheelValue("Books")
If the picker has multiple wheels you will need to first select the wheel via it's accessibility identifier, then adjust it.
app.pickerWheels["Feet"].adjustToPickerWheelValue("5")
Here's a GitHub repo with a working example. And some more information in a blog post I wrote.
Workaround:
let pickerWheel = XCUIApplication().pickers.pickerWheels.element
let appWindowHeight = XCUIApplication().windows.elementBoundByIndex(0).frame.height
let pickerWheelCellHeight = GFloat(44)
func advancePickerWheelOneValue() -> Self {
let topScrollPoint = Double(pickerWheel.frame.midY/appWindowHeight)
let bottomScrollPoint = Double((pickerWheel.frame.midY + pickerWheelCellHeight)/appWindowHeight)
let topScreenPoint = XCUIApplication().windows.elementBoundByIndex(0).coordinateWithNormalizedOffset(CGVector(dx: 0.5, dy: topScrollPoint))
let bottomScreenPoint = XCUIApplication().windows.elementBoundByIndex(0).coordinateWithNormalizedOffset(CGVector(dx: 0.5, dy: bottomScrollPoint))
bottomScreenPoint.pressForDuration(0, thenDragToCoordinate: topScreenPoint)
return self
}
You may need to adjust the dx value depending on where the picker is.
.coordinateWithNormalizedOffset(CGVector(dx: 0.5,
But 0.5 works if the picker occupies the width of the screen.
With this method you an move the picker on value at a time. Call it multiple times and you get to the value you want.
Its not ideal but it can help if you want to check certain indexes. Hopefully you would have to use it on a picker with hundreds of values ;)
Example with Date picker (UIDatePicker)
XCUIApplication().datePickers.pickerWheels.elementBoundByIndex(0).adjustToPickerWheelValue("March")
XCUIApplication().datePickers.pickerWheels.elementBoundByIndex(1).adjustToPickerWheelValue("24")
XCUIApplication().datePickers.pickerWheels.elementBoundByIndex(2).adjustToPickerWheelValue("2000")
And here is example with Picker view (UIPickerView)
XCUIApplication().pickerWheels.element.adjustToPickerWheelValue("Male")

MATLAB date selection popup calendar for gui

Does anyone know of a method to display a popup date selection calendar in a MATLAB gui? I know the financial toolbox has a uicalendar function, but unfortunately I don't have that toolbox.
I have a hunch I'm going to have to use some Java or some other language for this one, which I know nothing about.
I'm looking for something similar to this:
(source: welie.com)
which would return a date string after the user selects the date.
Here are two approaches that would give you a professional-looking calendar component in Matlab without too much programming work:
Use a Java calendar component (for example, one of these or these). Once you download the relevant Java class or Jar-file, add it to your static Java classpath (use the edit('classpath.txt') command from the Matlab Command Prompt). Finally, use the built-in javacomponent function to place the component in your Matlab figure window.
If you are using a Windows OS, you can embed any Active-X calendar control that is available. Use the built-in actxcontrolselect function to choose your favorite calendar control (for example, Microsoft Office's "Calendar Control 11.0" - MSCAL.Calendar.7 - which is automatically installed with Office 2003; or "Microsoft Date and Time Picker Control 6.0" - MSComCtl2.DTPicker.2, or ...). Then use the actxcontrol function to place the component in your Matlab figure window.
Matlab has some pretty useful built-in calendar (date-selection) controls - I posted an article about them today
I don't have much time for a more complete answer, unfortunately, but I'd try uitable to create a table and to define the CellSelectionCallback to get the date.
Here's a bit to get you started:
dates = calendar;
dates(~any(dates,2),:) = [];
fh = figure;
uh = uitable('parent',fh,'data',dates,'ColumnWidth',repmat({20},1,7),...
'ColumnName',{'S','M','T','W','T','F','S'});
I'd start with the calendar() function which outputs a matrix containing the calendar for any month. I assume you could combine this with a user-clickable interface to retrieve a specific date?
The following code is really ugly, but could help you get started...
WINDOW_WIDTH = 300;
WINDOW_HEIGHT = 200;
f= figure('Position',[300 300 WINDOW_WIDTH WINDOW_HEIGHT]);
NB_ROWS = 6;
NB_COLS = 7;
width = round(WINDOW_WIDTH/NB_COLS);
height = round(WINDOW_HEIGHT/NB_ROWS);
buttons = nan(NB_ROWS,NB_COLS);
dates = calendar();
for row = 1:NB_ROWS
for col = 1:NB_COLS
if dates(row,col) == 0
mydate = '';
else
mydate = sprintf('%i', dates(row,col));
end
buttons(row,col) = uicontrol('Style', 'PushButton', ...
'String', mydate, ...
'Position', [(col-1)*width (NB_ROWS - row)*height width height]);
end
end

Resources